Files
3dp-manager/server/test/auth/jwt-auth.guard.spec.ts
T
2026-03-29 03:52:43 +03:00

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);
}
});
});
});