Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 94 additions & 94 deletions biblionode-ui/src/app/core/interceptors/auth.interceptor.spec.ts
Original file line number Diff line number Diff line change
@@ -1,94 +1,94 @@
import { TestBed } from '@angular/core/testing';
import {
HttpClient,
HttpInterceptorFn,
provideHttpClient,
withInterceptors,
} from '@angular/common/http';

import { authInterceptor } from './auth.interceptor';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { AuthService } from '../services/auth.service';
import { Router } from '@angular/router';

describe('authInterceptor', () => {
const interceptor: HttpInterceptorFn = (req, next) =>
TestBed.runInInjectionContext(() => authInterceptor(req, next));

let httpTesting: HttpTestingController;
let httpClient: HttpClient;
let authService: AuthService;
let router: Router;

beforeEach(() => {
TestBed.configureTestingModule({
providers: [
provideHttpClient(withInterceptors([authInterceptor])),
provideHttpClientTesting(),
{
provide: AuthService,
useValue: { logout: vi.fn() },
},
{
provide: Router,
useValue: { navigate: vi.fn() },
},
],
});

httpTesting = TestBed.inject(HttpTestingController);
httpClient = TestBed.inject(HttpClient);
authService = TestBed.inject(AuthService);
router = TestBed.inject(Router);

localStorage.clear();
});

afterEach(() => {
httpTesting.verify();
});

it('should be created', () => {
expect(interceptor).toBeTruthy();
});

it('should add Authorization header when token is present', () => {
const token = 'fake-jwt-token';
localStorage.setItem('token', token);

httpClient.get('api/test').subscribe();

const req = httpTesting.expectOne('api/test');
expect(req.request.headers.has('Authorization')).toBe(true);
expect(req.request.headers.get('Authorization')).toBe(`Bearer ${token}`);
});

it('should not add Authorization header when token is absent', () => {
httpClient.get('api/test').subscribe();

const req = httpTesting.expectOne('api/test');
expect(req.request.headers.has('Authorization')).toBe(false);
});

it('should handle 401 error by logging out', () => {
httpClient.get('api/test').subscribe({
error: () => {},
});

const req = httpTesting.expectOne('api/test');
req.flush('Unauthorized', { status: 401, statusText: 'Unauthorized' });

expect(authService.logout).toHaveBeenCalled();
});

it('should navigate to login page on 401 error', async () => {
httpClient.get('api/test').subscribe({
error: () => {},
});

const req = httpTesting.expectOne('api/test');
req.flush('Unauthorized', { status: 401, statusText: 'Unauthorized' });

expect(router.navigate).toHaveBeenCalledWith(['/auth/login']);
});
});
import { TestBed } from '@angular/core/testing';
import {
HttpClient,
HttpInterceptorFn,
provideHttpClient,
withInterceptors,
} from '@angular/common/http';
import { authInterceptor } from './auth.interceptor';
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
import { AuthService } from '../../features/auth/data/services/auth.service';
import { Router } from '@angular/router';
describe('authInterceptor', () => {
const interceptor: HttpInterceptorFn = (req, next) =>
TestBed.runInInjectionContext(() => authInterceptor(req, next));
let httpTesting: HttpTestingController;
let httpClient: HttpClient;
let authService: AuthService;
let router: Router;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
provideHttpClient(withInterceptors([authInterceptor])),
provideHttpClientTesting(),
{
provide: AuthService,
useValue: { logout: vi.fn() },
},
{
provide: Router,
useValue: { navigate: vi.fn() },
},
],
});
httpTesting = TestBed.inject(HttpTestingController);
httpClient = TestBed.inject(HttpClient);
authService = TestBed.inject(AuthService);
router = TestBed.inject(Router);
localStorage.clear();
});
afterEach(() => {
httpTesting.verify();
});
it('should be created', () => {
expect(interceptor).toBeTruthy();
});
it('should add Authorization header when token is present', () => {
const token = 'fake-jwt-token';
localStorage.setItem('token', token);
httpClient.get('api/test').subscribe();
const req = httpTesting.expectOne('api/test');
expect(req.request.headers.has('Authorization')).toBe(true);
expect(req.request.headers.get('Authorization')).toBe(`Bearer ${token}`);
});
it('should not add Authorization header when token is absent', () => {
httpClient.get('api/test').subscribe();
const req = httpTesting.expectOne('api/test');
expect(req.request.headers.has('Authorization')).toBe(false);
});
it('should handle 401 error by logging out', () => {
httpClient.get('api/test').subscribe({
error: () => {},
});
const req = httpTesting.expectOne('api/test');
req.flush('Unauthorized', { status: 401, statusText: 'Unauthorized' });
expect(authService.logout).toHaveBeenCalled();
});
it('should navigate to login page on 401 error', async () => {
httpClient.get('api/test').subscribe({
error: () => {},
});
const req = httpTesting.expectOne('api/test');
req.flush('Unauthorized', { status: 401, statusText: 'Unauthorized' });
expect(router.navigate).toHaveBeenCalledWith(['/auth/login']);
});
});
58 changes: 29 additions & 29 deletions biblionode-ui/src/app/core/interceptors/auth.interceptor.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';
import { inject } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../services/auth.service';

export const authInterceptor: HttpInterceptorFn = (req, next) => {
const authService = inject(AuthService);
const router = inject(Router);
const token = localStorage.getItem('token');

let clonedReq = req;
if (token) {
clonedReq = req.clone({
setHeaders: { Authorization: `Bearer ${token}` },
});
}

return next(clonedReq).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
authService.logout();
localStorage.removeItem('token');
router.navigate(['/auth/login']);
}
return throwError(() => error);
}),
);
};
import { HttpErrorResponse, HttpInterceptorFn } from '@angular/common/http';
import { catchError, throwError } from 'rxjs';
import { inject } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from '../../features/auth/data/services/auth.service';
export const authInterceptor: HttpInterceptorFn = (req, next) => {
const authService = inject(AuthService);
const router = inject(Router);
const token = localStorage.getItem('token');
let clonedReq = req;
if (token) {
clonedReq = req.clone({
setHeaders: { Authorization: `Bearer ${token}` },
});
}
return next(clonedReq).pipe(
catchError((error: HttpErrorResponse) => {
if (error.status === 401) {
authService.logout();
localStorage.removeItem('token');
router.navigate(['/auth/login']);
}
return throwError(() => error);
}),
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface LoginRequest {
email: string;
password: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

export interface LoginResponse {
token: string;
email: string;
roles: string[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface RegisterRequest {
email: string;
password: string;
fullName: string;
}
Loading
Loading