test: add automated tests for admin login

Backend:
- TestAdminLogin_Success: verify admin login with username
- TestAdminLogin_WrongPassword: verify 401 for wrong password

Frontend (Marketplace):
- auth.test.ts: mocked tests for login/logout (5 tests)
- auth.integration.test.ts: real API tests (3 tests, skipped in CI)
This commit is contained in:
Tiago Yamamoto 2025-12-22 00:31:26 -03:00
parent fa726e5864
commit c9a08c8621
4 changed files with 221 additions and 0 deletions

View file

@ -19,3 +19,8 @@ MARKETPLACE_COMMISSION=2.5
# CORS Configuration (comma-separated list of allowed origins, use * for all)
CORS_ORIGINS=*
ADMIN_NAME=Administrator
ADMIN_USERNAME=admin
ADMIN_EMAIL=admin@saveinmed.com
ADMIN_PASSWORD=admin123

View file

@ -372,6 +372,81 @@ func TestLoginInvalidCredentials(t *testing.T) {
}
}
func TestAdminLogin_Success(t *testing.T) {
repo := NewMockRepository()
gateway := &MockPaymentGateway{}
svc := usecase.NewService(repo, gateway, 0.05, "test-secret", time.Hour, "test-pepper")
h := New(svc)
// Create admin user through service (which hashes password)
companyID, _ := uuid.NewV4()
user := &domain.User{
CompanyID: companyID,
Role: "admin",
Name: "Admin User",
Username: "admin",
Email: "admin@test.com",
}
err := svc.CreateUser(context.Background(), user, "admin123")
if err != nil {
t.Fatalf("failed to create user: %v", err)
}
// Update mock with hashed user
repo.users[0] = *user
// Login with correct credentials
payload := `{"username":"admin","password":"admin123"}`
req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/login", bytes.NewReader([]byte(payload)))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
h.Login(rec, req)
if rec.Code != http.StatusOK {
t.Errorf("expected status %d, got %d: %s", http.StatusOK, rec.Code, rec.Body.String())
}
// Verify response contains token
body := rec.Body.String()
if !strings.Contains(body, "token") {
t.Errorf("expected response to contain token, got: %s", body)
}
if !strings.Contains(body, "expires_at") {
t.Errorf("expected response to contain expires_at, got: %s", body)
}
}
func TestAdminLogin_WrongPassword(t *testing.T) {
repo := NewMockRepository()
gateway := &MockPaymentGateway{}
svc := usecase.NewService(repo, gateway, 0.05, "test-secret", time.Hour, "test-pepper")
h := New(svc)
// Create admin user
companyID, _ := uuid.NewV4()
user := &domain.User{
CompanyID: companyID,
Role: "admin",
Name: "Admin User",
Username: "admin",
Email: "admin@test.com",
}
svc.CreateUser(context.Background(), user, "correctpassword")
repo.users[0] = *user
// Login with wrong password
payload := `{"username":"admin","password":"wrongpassword"}`
req := httptest.NewRequest(http.MethodPost, "/api/v1/auth/login", bytes.NewReader([]byte(payload)))
req.Header.Set("Content-Type", "application/json")
rec := httptest.NewRecorder()
h.Login(rec, req)
if rec.Code != http.StatusUnauthorized {
t.Errorf("expected status %d, got %d", http.StatusUnauthorized, rec.Code)
}
}
func TestListOrders(t *testing.T) {
h := newTestHandler()

View file

@ -0,0 +1,49 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest'
import { authService } from './auth'
/**
* Integration tests that require a running backend.
*
* To run these tests:
* 1. Start the backend: cd backend && go run ./cmd/api
* 2. Ensure admin user exists (backend seeds it automatically)
* 3. Run: npm test -- auth.integration
*
* These tests are skipped by default in CI.
*/
const BACKEND_URL = process.env.VITE_API_URL || 'http://localhost:8214/api'
const SKIP_INTEGRATION = process.env.CI === 'true' || !process.env.RUN_INTEGRATION
describe.skipIf(SKIP_INTEGRATION)('authService Integration Tests', () => {
describe('login with real backend', () => {
it('should login with admin credentials', async () => {
const result = await authService.login({
username: 'admin',
password: 'admin123'
})
expect(result.token).toBeDefined()
expect(result.token.length).toBeGreaterThan(10)
expect(result.expiresAt).toBeDefined()
})
it('should reject invalid password', async () => {
await expect(
authService.login({
username: 'admin',
password: 'wrongpassword'
})
).rejects.toThrow()
})
it('should reject non-existent user', async () => {
await expect(
authService.login({
username: 'nonexistent_user_12345',
password: 'anypassword'
})
).rejects.toThrow()
})
})
})

View file

@ -0,0 +1,92 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
import { authService } from './auth'
import { apiClient } from './apiClient'
// Mock the apiClient module
vi.mock('./apiClient', () => ({
apiClient: {
post: vi.fn(),
setToken: vi.fn()
}
}))
describe('authService', () => {
beforeEach(() => {
vi.clearAllMocks()
})
afterEach(() => {
vi.restoreAllMocks()
})
describe('login', () => {
it('should login successfully with valid credentials', async () => {
const mockResponse = {
data: {
token: 'jwt-token-123',
expires_at: '2024-12-23T00:00:00Z'
}
}
vi.mocked(apiClient.post).mockResolvedValue(mockResponse)
const result = await authService.login({
username: 'admin',
password: 'admin123'
})
expect(apiClient.post).toHaveBeenCalledWith('/v1/auth/login', {
username: 'admin',
password: 'admin123'
})
expect(result.token).toBe('jwt-token-123')
expect(result.expiresAt).toBe('2024-12-23T00:00:00Z')
})
it('should throw error for invalid credentials', async () => {
const error = new Error('Unauthorized')
; (error as any).response = { status: 401 }
vi.mocked(apiClient.post).mockRejectedValue(error)
await expect(
authService.login({
username: 'admin',
password: 'wrongpassword'
})
).rejects.toThrow('Unauthorized')
expect(apiClient.post).toHaveBeenCalledWith('/v1/auth/login', {
username: 'admin',
password: 'wrongpassword'
})
})
it('should throw error for non-existent user', async () => {
const error = new Error('User not found')
; (error as any).response = { status: 401 }
vi.mocked(apiClient.post).mockRejectedValue(error)
await expect(
authService.login({
username: 'nonexistent',
password: 'password'
})
).rejects.toThrow()
})
})
describe('logout', () => {
it('should call logout endpoint', async () => {
vi.mocked(apiClient.post).mockResolvedValue({})
await authService.logout()
expect(apiClient.post).toHaveBeenCalledWith('/v1/auth/logout')
})
it('should handle logout errors gracefully', async () => {
vi.mocked(apiClient.post).mockRejectedValue(new Error('Network error'))
await expect(authService.logout()).rejects.toThrow('Network error')
})
})
})