150 lines
4.9 KiB
TypeScript
150 lines
4.9 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
|
|
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
|
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
|
/* eslint-disable @typescript-eslint/no-floating-promises */
|
|
|
|
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
|
|
import { Reflector } from '@nestjs/core';
|
|
import { ExecutionContext, UnauthorizedException } from '@nestjs/common';
|
|
|
|
describe('JwtAuthGuard', () => {
|
|
let guard: JwtAuthGuard;
|
|
let reflector: Reflector;
|
|
let mockContext: ExecutionContext;
|
|
let mockRequest: any;
|
|
|
|
beforeEach(() => {
|
|
reflector = new Reflector();
|
|
guard = new JwtAuthGuard(reflector);
|
|
|
|
mockRequest = {
|
|
url: '/api/test',
|
|
method: 'GET',
|
|
headers: {},
|
|
query: {},
|
|
};
|
|
|
|
mockContext = {
|
|
switchToHttp: jest.fn().mockReturnValue({
|
|
getRequest: jest.fn().mockReturnValue(mockRequest),
|
|
getResponse: jest.fn(),
|
|
}),
|
|
getHandler: jest.fn(),
|
|
getClass: jest.fn(),
|
|
} as any;
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
describe('canActivate', () => {
|
|
it('должен вернуть true для публичного маршрута', () => {
|
|
jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(true);
|
|
|
|
const result = guard.canActivate(mockContext);
|
|
|
|
expect(result).toBe(true);
|
|
});
|
|
|
|
it('должен вызвать super.canActivate для защищенного маршрута', () => {
|
|
jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(false);
|
|
|
|
const superCanActivate = jest
|
|
.spyOn(JwtAuthGuard.prototype, 'canActivate' as any)
|
|
.mockImplementation(() => true);
|
|
|
|
guard.canActivate(mockContext);
|
|
|
|
expect(superCanActivate).toHaveBeenCalled();
|
|
});
|
|
|
|
it('НЕ должен добавлять токен, если authorization уже есть', () => {
|
|
jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(false);
|
|
mockRequest.query.token = 'test-jwt-token';
|
|
mockRequest.headers.authorization = 'Bearer existing-token';
|
|
|
|
const _superCanActivate = jest
|
|
.spyOn(JwtAuthGuard.prototype, 'canActivate' as any)
|
|
.mockImplementation(() => true);
|
|
|
|
guard.canActivate(mockContext);
|
|
|
|
expect(mockRequest.headers.authorization).toBe('Bearer existing-token');
|
|
});
|
|
|
|
it('должен вернуть результат super.canActivate', () => {
|
|
jest.spyOn(reflector, 'getAllAndOverride').mockReturnValue(false);
|
|
|
|
const _superCanActivate = jest
|
|
.spyOn(JwtAuthGuard.prototype, 'canActivate' as any)
|
|
.mockImplementation(() => 'PENDING_RESULT');
|
|
|
|
const result = guard.canActivate(mockContext);
|
|
|
|
expect(result).toBe('PENDING_RESULT');
|
|
});
|
|
});
|
|
|
|
describe('handleRequest', () => {
|
|
it('должен вернуть пользователя при успешной аутентификации', () => {
|
|
const user = { username: 'admin' };
|
|
|
|
const result = guard.handleRequest(null, user, null);
|
|
|
|
expect(result).toEqual(user);
|
|
});
|
|
|
|
it('должен бросить ошибку при наличии ошибки', () => {
|
|
const _error = new Error('Invalid token');
|
|
|
|
expect(() => guard.handleRequest(_error, null, null)).toThrow(Error);
|
|
});
|
|
|
|
it('должен бросить UnauthorizedException, если пользователь null и нет ошибки', () => {
|
|
expect(() => guard.handleRequest(null, null, null)).toThrow(
|
|
UnauthorizedException,
|
|
);
|
|
});
|
|
|
|
it('должен бросить ошибку с сообщением из Error', () => {
|
|
const _error = new Error('Custom error message');
|
|
|
|
try {
|
|
guard.handleRequest(_error, null, null);
|
|
} catch (e) {
|
|
expect((e as Error).message).toContain('Custom error message');
|
|
}
|
|
});
|
|
|
|
it('должен бросить ошибку с сообщением из строки', () => {
|
|
const _error = 'String error message';
|
|
|
|
// handleRequest пробрасывает строковую ошибку как есть через throw
|
|
expect(() => guard.handleRequest(_error as any, null, null)).toThrow(
|
|
'String error message',
|
|
);
|
|
});
|
|
|
|
it('должен бросить ошибку с JSON сообщением', () => {
|
|
const _error = { message: 'JSON error' };
|
|
|
|
// Для объекта берётся message поле
|
|
expect(() => guard.handleRequest(_error as any, null, null)).toThrow(
|
|
'JSON error',
|
|
);
|
|
});
|
|
|
|
it('должен бросить ошибку с сообщением "null", если error=null и user=null', () => {
|
|
try {
|
|
guard.handleRequest(null, null, null);
|
|
} catch (_e: any) {
|
|
// UnauthorizedException имеет пустое сообщение по умолчанию
|
|
expect(_e).toBeInstanceOf(UnauthorizedException);
|
|
}
|
|
});
|
|
});
|
|
});
|