test: add failing specs for AuthService login API integration

This commit is contained in:
Chris Chen
2026-05-26 20:34:49 -07:00
parent aa0c5403a1
commit dc7909e247
@@ -0,0 +1,206 @@
import { TestBed } from '@angular/core/testing';
import {
HttpClientTestingModule,
HttpTestingController
} from '@angular/common/http/testing';
import {
AuthService,
LoginResultType,
UserInfo
} from './auth.service';
import { ApiConfigService } from '../../core/services/api-config.service';
const MOCK_USER: UserInfo = {
id: 'user-123',
email: 'test@example.com',
roles: ['Admin'],
languagePreference: 'en'
};
const MOCK_API_RESPONSE = {
accessToken: 'mock-access-token',
expiresIn: 900,
user: MOCK_USER
};
describe('AuthService', () => {
let service: AuthService;
let httpMock: HttpTestingController;
let apiConfig: ApiConfigService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [AuthService, ApiConfigService]
});
service = TestBed.inject(AuthService);
httpMock = TestBed.inject(HttpTestingController);
apiConfig = TestBed.inject(ApiConfigService);
});
afterEach(() => {
httpMock.verify();
});
// ── login() ────────────────────────────────────────────────────────────────
describe('login()', () => {
it('should POST to /api/auth/login with email and password', () => {
service.login({ email: 'test@example.com', password: 'secret' }).subscribe();
const req = httpMock.expectOne(`${apiConfig.authUrl}/login`);
expect(req.request.method).toBe('POST');
expect(req.request.body).toEqual({ email: 'test@example.com', password: 'secret' });
expect(req.request.withCredentials).toBeTrue();
req.flush(MOCK_API_RESPONSE);
});
it('should return LoginResultType.Success and store token + user on 200', () => {
let result: any;
service.login({ email: 'test@example.com', password: 'secret' }).subscribe(r => result = r);
httpMock.expectOne(`${apiConfig.authUrl}/login`).flush(MOCK_API_RESPONSE);
expect(result.result).toBe(LoginResultType.Success);
expect(result.responseData).toEqual(MOCK_USER);
expect(service.getToken()).toBe('mock-access-token');
expect(service.getCurrentUser()).toEqual(MOCK_USER);
expect(service.isAuthenticated()).toBeTrue();
});
it('should return LoginResultType.InvalidCredentials on 401', () => {
let result: any;
service.login({ email: 'bad@example.com', password: 'wrong' }).subscribe(r => result = r);
httpMock.expectOne(`${apiConfig.authUrl}/login`).flush(
{ message: 'Invalid credentials' },
{ status: 401, statusText: 'Unauthorized' }
);
expect(result.result).toBe(LoginResultType.InvalidCredentials);
expect(service.getToken()).toBeNull();
expect(service.isAuthenticated()).toBeFalse();
});
it('should return LoginResultType.Error on non-401 HTTP error', () => {
let result: any;
service.login({ email: 'test@example.com', password: 'secret' }).subscribe(r => result = r);
httpMock.expectOne(`${apiConfig.authUrl}/login`).flush(
{ message: 'Server error' },
{ status: 500, statusText: 'Internal Server Error' }
);
expect(result.result).toBe(LoginResultType.Error);
});
});
// ── refresh() ──────────────────────────────────────────────────────────────
describe('refresh()', () => {
it('should POST to /api/auth/refresh with withCredentials', () => {
service.refresh().subscribe();
const req = httpMock.expectOne(`${apiConfig.authUrl}/refresh`);
expect(req.request.method).toBe('POST');
expect(req.request.withCredentials).toBeTrue();
req.flush(MOCK_API_RESPONSE);
});
it('should return true and update token + user on 200', () => {
let result: boolean | undefined;
service.refresh().subscribe(r => result = r);
httpMock.expectOne(`${apiConfig.authUrl}/refresh`).flush(MOCK_API_RESPONSE);
expect(result).toBeTrue();
expect(service.getToken()).toBe('mock-access-token');
expect(service.getCurrentUser()).toEqual(MOCK_USER);
});
it('should return false and leave state unchanged on 401', () => {
let result: boolean | undefined;
service.refresh().subscribe(r => result = r);
httpMock.expectOne(`${apiConfig.authUrl}/refresh`).flush(
{ message: 'Refresh token expired' },
{ status: 401, statusText: 'Unauthorized' }
);
expect(result).toBeFalse();
expect(service.getToken()).toBeNull();
expect(service.isAuthenticated()).toBeFalse();
});
});
// ── logout() ───────────────────────────────────────────────────────────────
describe('logout()', () => {
it('should clear token and user from memory immediately', () => {
// Seed state
service['accessToken$'].next('some-token');
service['currentUser$'].next(MOCK_USER);
service.logout();
httpMock.expectOne(`${apiConfig.authUrl}/logout`).flush(null, { status: 204, statusText: 'No Content' });
expect(service.getToken()).toBeNull();
expect(service.getCurrentUser()).toBeNull();
expect(service.isAuthenticated()).toBeFalse();
});
it('should POST to /api/auth/logout with withCredentials', () => {
service.logout();
const req = httpMock.expectOne(`${apiConfig.authUrl}/logout`);
expect(req.request.method).toBe('POST');
expect(req.request.withCredentials).toBeTrue();
req.flush(null, { status: 204, statusText: 'No Content' });
});
it('should not throw if the logout API call fails', () => {
expect(() => {
service.logout();
httpMock.expectOne(`${apiConfig.authUrl}/logout`).flush(
{ message: 'Server error' },
{ status: 500, statusText: 'Internal Server Error' }
);
}).not.toThrow();
});
});
// ── initializeFromRefreshToken() ───────────────────────────────────────────
describe('initializeFromRefreshToken()', () => {
it('should resolve even when refresh returns 401 (does not block bootstrap)', async () => {
const promise = service.initializeFromRefreshToken();
httpMock.expectOne(`${apiConfig.authUrl}/refresh`).flush(
{ message: 'No cookie' },
{ status: 401, statusText: 'Unauthorized' }
);
await expectAsync(promise).toBeResolved();
});
it('should resolve and authenticate user when refresh succeeds', async () => {
const promise = service.initializeFromRefreshToken();
httpMock.expectOne(`${apiConfig.authUrl}/refresh`).flush(MOCK_API_RESPONSE);
await expectAsync(promise).toBeResolved();
expect(service.isAuthenticated()).toBeTrue();
});
});
// ── setCurrentUser() / getCurrentUser() ────────────────────────────────────
describe('setCurrentUser()', () => {
it('should update currentUser$ and mark authenticated', () => {
service.setCurrentUser(MOCK_USER);
expect(service.getCurrentUser()).toEqual(MOCK_USER);
expect(service.isAuthenticated()).toBeTrue();
});
});
// ── redirect URL helpers ────────────────────────────────────────────────────
describe('redirect URL helpers', () => {
it('should default redirect to /dashboard', () => {
expect(service.getRedirectUrl()).toBe('/dashboard');
});
it('should store and return a custom redirect URL', () => {
service.setRedirectUrl('/members');
expect(service.getRedirectUrl()).toBe('/members');
});
});
});