test: add failing specs for AuthService login API integration
This commit is contained in:
@@ -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');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user