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:
parent
fa726e5864
commit
c9a08c8621
4 changed files with 221 additions and 0 deletions
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
49
marketplace/src/services/auth.integration.test.ts
Normal file
49
marketplace/src/services/auth.integration.test.ts
Normal 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()
|
||||
})
|
||||
})
|
||||
})
|
||||
92
marketplace/src/services/auth.test.ts
Normal file
92
marketplace/src/services/auth.test.ts
Normal 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')
|
||||
})
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue