/** * AuthService unit tests */ import { assertEquals, assertExists } from 'jsr:@std/assert'; import { describe, it, beforeAll, afterAll, beforeEach } from 'jsr:@std/testing/bdd'; import { setupTestDb, teardownTestDb, cleanupTestDb, createTestUser } from '../../helpers/index.ts'; import { AuthService } from '../../../ts/services/auth.service.ts'; import { Session } from '../../../ts/models/session.ts'; import { testConfig } from '../../test.config.ts'; describe('AuthService', () => { let authService: AuthService; beforeAll(async () => { await setupTestDb(); authService = new AuthService({ jwtSecret: testConfig.jwt.secret, accessTokenExpiresIn: 60, // 1 minute for tests refreshTokenExpiresIn: 300, // 5 minutes for tests }); }); afterAll(async () => { await teardownTestDb(); }); beforeEach(async () => { await cleanupTestDb(); }); describe('login', () => { it('should successfully login with valid credentials', async () => { const { user, password } = await createTestUser({ email: 'login@example.com', status: 'active', }); const result = await authService.login(user.email, password, { userAgent: 'TestAgent/1.0', ipAddress: '127.0.0.1', }); assertEquals(result.success, true); assertExists(result.user); assertEquals(result.user.id, user.id); assertExists(result.accessToken); assertExists(result.refreshToken); assertExists(result.sessionId); }); it('should fail with invalid email', async () => { const result = await authService.login('nonexistent@example.com', 'password'); assertEquals(result.success, false); assertEquals(result.errorCode, 'INVALID_CREDENTIALS'); }); it('should fail with invalid password', async () => { const { user } = await createTestUser({ email: 'wrongpass@example.com' }); const result = await authService.login(user.email, 'wrongpassword'); assertEquals(result.success, false); assertEquals(result.errorCode, 'INVALID_CREDENTIALS'); }); it('should fail for inactive user', async () => { const { user, password } = await createTestUser({ email: 'inactive@example.com', status: 'suspended', }); const result = await authService.login(user.email, password); assertEquals(result.success, false); assertEquals(result.errorCode, 'ACCOUNT_INACTIVE'); }); it('should create a session on successful login', async () => { const { user, password } = await createTestUser({ email: 'session@example.com' }); const result = await authService.login(user.email, password); assertEquals(result.success, true); assertExists(result.sessionId); const session = await Session.findValidSession(result.sessionId!); assertExists(session); assertEquals(session.userId, user.id); }); }); describe('refresh', () => { it('should refresh access token with valid refresh token', async () => { const { user, password } = await createTestUser({ email: 'refresh@example.com' }); const loginResult = await authService.login(user.email, password); assertEquals(loginResult.success, true); const refreshResult = await authService.refresh(loginResult.refreshToken!); assertEquals(refreshResult.success, true); assertExists(refreshResult.accessToken); assertEquals(refreshResult.sessionId, loginResult.sessionId); }); it('should fail with invalid refresh token', async () => { const result = await authService.refresh('invalid-token'); assertEquals(result.success, false); assertEquals(result.errorCode, 'INVALID_TOKEN'); }); it('should fail when session is invalidated', async () => { const { user, password } = await createTestUser({ email: 'invalidsession@example.com' }); const loginResult = await authService.login(user.email, password); // Invalidate session const session = await Session.findValidSession(loginResult.sessionId!); await session!.invalidate('test'); const refreshResult = await authService.refresh(loginResult.refreshToken!); assertEquals(refreshResult.success, false); assertEquals(refreshResult.errorCode, 'SESSION_INVALID'); }); }); describe('validateAccessToken', () => { it('should validate valid access token', async () => { const { user, password } = await createTestUser({ email: 'validate@example.com' }); const loginResult = await authService.login(user.email, password); const validation = await authService.validateAccessToken(loginResult.accessToken!); assertExists(validation); assertEquals(validation.user.id, user.id); assertEquals(validation.sessionId, loginResult.sessionId); }); it('should reject invalid access token', async () => { const validation = await authService.validateAccessToken('invalid-token'); assertEquals(validation, null); }); it('should reject when session is invalidated', async () => { const { user, password } = await createTestUser({ email: 'invalidated@example.com' }); const loginResult = await authService.login(user.email, password); // Invalidate session const session = await Session.findValidSession(loginResult.sessionId!); await session!.invalidate('test'); const validation = await authService.validateAccessToken(loginResult.accessToken!); assertEquals(validation, null); }); }); describe('logout', () => { it('should invalidate session', async () => { const { user, password } = await createTestUser({ email: 'logout@example.com' }); const loginResult = await authService.login(user.email, password); const success = await authService.logout(loginResult.sessionId!); assertEquals(success, true); const session = await Session.findValidSession(loginResult.sessionId!); assertEquals(session, null); }); it('should return false for non-existent session', async () => { const success = await authService.logout('non-existent-session-id'); assertEquals(success, false); }); }); describe('logoutAll', () => { it('should invalidate all user sessions', async () => { const { user, password } = await createTestUser({ email: 'logoutall@example.com' }); // Create multiple sessions await authService.login(user.email, password); await authService.login(user.email, password); await authService.login(user.email, password); const count = await authService.logoutAll(user.id); assertEquals(count, 3); const sessions = await Session.getUserSessions(user.id); assertEquals(sessions.length, 0); }); }); describe('static password methods', () => { it('should hash and verify password', async () => { const password = 'MySecurePassword123!'; const hash = await AuthService.hashPassword(password); const isValid = await AuthService.verifyPassword(password, hash); assertEquals(isValid, true); const isInvalid = await AuthService.verifyPassword('WrongPassword', hash); assertEquals(isInvalid, false); }); it('should generate different hashes for same password', async () => { const password = 'SamePassword'; const hash1 = await AuthService.hashPassword(password); const hash2 = await AuthService.hashPassword(password); assertEquals(hash1 !== hash2, true); // But both should verify assertEquals(await AuthService.verifyPassword(password, hash1), true); assertEquals(await AuthService.verifyPassword(password, hash2), true); }); }); });