import { describe, it, expect, vi, beforeEach } from 'vitest'; import { AuthPageService } from '@/lib/services/auth/AuthPageService'; import { AuthPageParams } from '@/lib/services/auth/AuthPageParams'; import { LoginPageDTO } from '@/lib/services/auth/types/LoginPageDTO'; import { ForgotPasswordPageDTO } from '@/lib/services/auth/types/ForgotPasswordPageDTO'; import { ResetPasswordPageDTO } from '@/lib/services/auth/types/ResetPasswordPageDTO'; import { SignupPageDTO } from '@/lib/services/auth/types/SignupPageDTO'; describe('AuthPageService', () => { let service: AuthPageService; beforeEach(() => { service = new AuthPageService(); }); describe('processLoginParams', () => { describe('happy paths', () => { it('should process login params with returnTo', async () => { const params: AuthPageParams = { returnTo: '/dashboard', }; const result = await service.processLoginParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe('/dashboard'); expect(dto.hasInsufficientPermissions).toBe(true); }); it('should process login params with null returnTo', async () => { const params: AuthPageParams = { returnTo: null, }; const result = await service.processLoginParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe('/dashboard'); expect(dto.hasInsufficientPermissions).toBe(false); }); it('should process login params with undefined returnTo', async () => { const params: AuthPageParams = {}; const result = await service.processLoginParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe('/dashboard'); expect(dto.hasInsufficientPermissions).toBe(false); }); it('should process login params with empty string returnTo', async () => { const params: AuthPageParams = { returnTo: '', }; const result = await service.processLoginParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe(''); expect(dto.hasInsufficientPermissions).toBe(true); }); }); describe('decision branches', () => { it('should handle different returnTo paths', async () => { const paths = [ '/dashboard', '/settings', '/profile', '/admin', '/projects/123', '/projects/123/tasks', ]; for (const path of paths) { const params: AuthPageParams = { returnTo: path, }; const result = await service.processLoginParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe(path); expect(dto.hasInsufficientPermissions).toBe(true); } }); it('should handle special characters in returnTo path', async () => { const paths = [ '/dashboard?param=value', '/dashboard#section', '/dashboard/with/slashes', '/dashboard/with-dashes', '/dashboard/with_underscores', ]; for (const path of paths) { const params: AuthPageParams = { returnTo: path, }; const result = await service.processLoginParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe(path); expect(dto.hasInsufficientPermissions).toBe(true); } }); it('should handle different returnTo values and hasInsufficientPermissions', async () => { const testCases = [ { returnTo: '/dashboard', expectedHasInsufficientPermissions: true }, { returnTo: null, expectedHasInsufficientPermissions: false }, { returnTo: undefined, expectedHasInsufficientPermissions: false }, { returnTo: '', expectedHasInsufficientPermissions: true }, ]; for (const testCase of testCases) { const params: AuthPageParams = { returnTo: testCase.returnTo as string | null | undefined, }; const result = await service.processLoginParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.hasInsufficientPermissions).toBe(testCase.expectedHasInsufficientPermissions); } }); }); describe('aggregation logic', () => { it('should aggregate login params into DTO correctly', async () => { const params: AuthPageParams = { returnTo: '/dashboard', }; const result = await service.processLoginParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); // Verify all fields are correctly aggregated expect(dto.returnTo).toBe('/dashboard'); expect(dto.hasInsufficientPermissions).toBe(true); expect(typeof dto.returnTo).toBe('string'); expect(typeof dto.hasInsufficientPermissions).toBe('boolean'); }); it('should handle empty params object', async () => { const params: AuthPageParams = {}; const result = await service.processLoginParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); // Verify default values are used expect(dto.returnTo).toBe('/dashboard'); expect(dto.hasInsufficientPermissions).toBe(false); }); }); }); describe('processForgotPasswordParams', () => { describe('happy paths', () => { it('should process forgot password params with returnTo', async () => { const params: AuthPageParams = { returnTo: '/auth/login', }; const result = await service.processForgotPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe('/auth/login'); }); it('should process forgot password params with null returnTo', async () => { const params: AuthPageParams = { returnTo: null, }; const result = await service.processForgotPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe('/auth/login'); }); it('should process forgot password params with undefined returnTo', async () => { const params: AuthPageParams = {}; const result = await service.processForgotPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe('/auth/login'); }); }); describe('decision branches', () => { it('should handle different returnTo paths', async () => { const paths = [ '/auth/login', '/auth/signup', '/dashboard', '/settings', ]; for (const path of paths) { const params: AuthPageParams = { returnTo: path, }; const result = await service.processForgotPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe(path); } }); it('should handle special characters in returnTo path', async () => { const paths = [ '/auth/login?param=value', '/auth/login#section', '/auth/login/with/slashes', ]; for (const path of paths) { const params: AuthPageParams = { returnTo: path, }; const result = await service.processForgotPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe(path); } }); }); describe('aggregation logic', () => { it('should aggregate forgot password params into DTO correctly', async () => { const params: AuthPageParams = { returnTo: '/auth/login', }; const result = await service.processForgotPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); // Verify all fields are correctly aggregated expect(dto.returnTo).toBe('/auth/login'); expect(typeof dto.returnTo).toBe('string'); }); it('should handle empty params object', async () => { const params: AuthPageParams = {}; const result = await service.processForgotPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); // Verify default values are used expect(dto.returnTo).toBe('/auth/login'); }); }); }); describe('processResetPasswordParams', () => { describe('happy paths', () => { it('should process reset password params with token and returnTo', async () => { const params: AuthPageParams = { token: 'reset-token-123', returnTo: '/auth/login', }; const result = await service.processResetPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.token).toBe('reset-token-123'); expect(dto.returnTo).toBe('/auth/login'); }); it('should process reset password params with token and null returnTo', async () => { const params: AuthPageParams = { token: 'reset-token-123', returnTo: null, }; const result = await service.processResetPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.token).toBe('reset-token-123'); expect(dto.returnTo).toBe('/auth/login'); }); it('should process reset password params with token and undefined returnTo', async () => { const params: AuthPageParams = { token: 'reset-token-123', }; const result = await service.processResetPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.token).toBe('reset-token-123'); expect(dto.returnTo).toBe('/auth/login'); }); }); describe('failure modes', () => { it('should return error when token is missing', async () => { const params: AuthPageParams = { returnTo: '/auth/login', }; const result = await service.processResetPasswordParams(params); expect(result.isErr()).toBe(true); expect(result.getError().type).toBe('validation'); expect(result.getError().message).toBe('Missing reset token'); }); it('should return error when token is null', async () => { const params: AuthPageParams = { token: null, returnTo: '/auth/login', }; const result = await service.processResetPasswordParams(params); expect(result.isErr()).toBe(true); expect(result.getError().type).toBe('validation'); expect(result.getError().message).toBe('Missing reset token'); }); it('should return error when token is empty string', async () => { const params: AuthPageParams = { token: '', returnTo: '/auth/login', }; const result = await service.processResetPasswordParams(params); expect(result.isErr()).toBe(true); expect(result.getError().type).toBe('validation'); expect(result.getError().message).toBe('Missing reset token'); }); }); describe('decision branches', () => { it('should handle different token formats', async () => { const tokens = [ 'reset-token-123', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c', 'token-with-special-chars-!@#$%^&*()', ]; for (const token of tokens) { const params: AuthPageParams = { token, returnTo: '/auth/login', }; const result = await service.processResetPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.token).toBe(token); } }); it('should handle different returnTo paths', async () => { const paths = [ '/auth/login', '/auth/signup', '/dashboard', '/settings', ]; for (const path of paths) { const params: AuthPageParams = { token: 'reset-token-123', returnTo: path, }; const result = await service.processResetPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe(path); } }); it('should handle special characters in returnTo path', async () => { const paths = [ '/auth/login?param=value', '/auth/login#section', '/auth/login/with/slashes', ]; for (const path of paths) { const params: AuthPageParams = { token: 'reset-token-123', returnTo: path, }; const result = await service.processResetPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe(path); } }); }); describe('aggregation logic', () => { it('should aggregate reset password params into DTO correctly', async () => { const params: AuthPageParams = { token: 'reset-token-123', returnTo: '/auth/login', }; const result = await service.processResetPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); // Verify all fields are correctly aggregated expect(dto.token).toBe('reset-token-123'); expect(dto.returnTo).toBe('/auth/login'); expect(typeof dto.token).toBe('string'); expect(typeof dto.returnTo).toBe('string'); }); it('should handle params with only token', async () => { const params: AuthPageParams = { token: 'reset-token-123', }; const result = await service.processResetPasswordParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); // Verify default returnTo is used expect(dto.token).toBe('reset-token-123'); expect(dto.returnTo).toBe('/auth/login'); }); }); }); describe('processSignupParams', () => { describe('happy paths', () => { it('should process signup params with returnTo', async () => { const params: AuthPageParams = { returnTo: '/onboarding', }; const result = await service.processSignupParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe('/onboarding'); }); it('should process signup params with null returnTo', async () => { const params: AuthPageParams = { returnTo: null, }; const result = await service.processSignupParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe('/onboarding'); }); it('should process signup params with undefined returnTo', async () => { const params: AuthPageParams = {}; const result = await service.processSignupParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe('/onboarding'); }); }); describe('decision branches', () => { it('should handle different returnTo paths', async () => { const paths = [ '/onboarding', '/dashboard', '/settings', '/projects', ]; for (const path of paths) { const params: AuthPageParams = { returnTo: path, }; const result = await service.processSignupParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe(path); } }); it('should handle special characters in returnTo path', async () => { const paths = [ '/onboarding?param=value', '/onboarding#section', '/onboarding/with/slashes', ]; for (const path of paths) { const params: AuthPageParams = { returnTo: path, }; const result = await service.processSignupParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); expect(dto.returnTo).toBe(path); } }); }); describe('aggregation logic', () => { it('should aggregate signup params into DTO correctly', async () => { const params: AuthPageParams = { returnTo: '/onboarding', }; const result = await service.processSignupParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); // Verify all fields are correctly aggregated expect(dto.returnTo).toBe('/onboarding'); expect(typeof dto.returnTo).toBe('string'); }); it('should handle empty params object', async () => { const params: AuthPageParams = {}; const result = await service.processSignupParams(params); expect(result.isOk()).toBe(true); const dto = result.unwrap(); // Verify default values are used expect(dto.returnTo).toBe('/onboarding'); }); }); }); describe('error handling', () => { it('should handle unexpected error types in processLoginParams', async () => { const params: AuthPageParams = { returnTo: '/dashboard', }; // This should not throw an error const result = await service.processLoginParams(params); expect(result.isOk()).toBe(true); }); it('should handle unexpected error types in processForgotPasswordParams', async () => { const params: AuthPageParams = { returnTo: '/auth/login', }; // This should not throw an error const result = await service.processForgotPasswordParams(params); expect(result.isOk()).toBe(true); }); it('should handle unexpected error types in processResetPasswordParams', async () => { const params: AuthPageParams = { token: 'reset-token-123', returnTo: '/auth/login', }; // This should not throw an error const result = await service.processResetPasswordParams(params); expect(result.isOk()).toBe(true); }); it('should handle unexpected error types in processSignupParams', async () => { const params: AuthPageParams = { returnTo: '/onboarding', }; // This should not throw an error const result = await service.processSignupParams(params); expect(result.isOk()).toBe(true); }); }); });