diff --git a/jest.setup.js b/jest.setup.js new file mode 100644 index 0000000..50237ca --- /dev/null +++ b/jest.setup.js @@ -0,0 +1,13 @@ +// Mock para resolver problema do TypeORM com node:url +// Este arquivo é executado antes de todos os testes + +// Mock do módulo 'glob' do TypeORM que causa problemas +jest.mock('glob', () => { + const originalModule = jest.requireActual('glob'); + return { + ...originalModule, + glob: jest.fn(), + globSync: jest.fn(), + }; +}); + diff --git a/package.json b/package.json index b48d335..cd8621a 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,10 @@ "testEnvironment": "node", "moduleNameMapper": { "^src/(.*)$": "/$1" - } + }, + "transformIgnorePatterns": [ + "node_modules/(?!(typeorm|@nestjs)/)" + ], + "setupFilesAfterEnv": ["../jest.setup.js"] } } diff --git a/src/auth/auth/__tests__/createToken.spec.ts b/src/auth/auth/__tests__/createToken.spec.ts index bf45e1a..a6dd934 100644 --- a/src/auth/auth/__tests__/createToken.spec.ts +++ b/src/auth/auth/__tests__/createToken.spec.ts @@ -243,6 +243,72 @@ describe('AuthService - createToken', () => { ).rejects.toThrow('ID de vendedor inválido'); }); + it('should accept null seller ID', async () => { + const mockToken = 'mock.jwt.token.null.seller'; + const userId = 1427; + const sellerId = null; + const username = 'brunelle.c'; + const email = 'brunelle.c@jurunense.com.br'; + const storeId = '12'; + const sessionId = 'session-null-seller'; + + context.mockJwtService.sign.mockReturnValue(mockToken); + + const result = await context.service.createToken( + userId, + sellerId, + username, + email, + storeId, + sessionId, + ); + + expect(context.mockJwtService.sign).toHaveBeenCalledWith( + { + id: userId, + sellerId: null, + storeId: storeId, + username: username, + email: email, + sessionId: sessionId, + }, + { expiresIn: '8h' }, + ); + expect(result).toBe(mockToken); + }); + + it('should accept undefined seller ID', async () => { + const mockToken = 'mock.jwt.token.undefined.seller'; + const userId = 1427; + const sellerId = undefined; + const username = 'brunelle.c'; + const email = 'brunelle.c@jurunense.com.br'; + const storeId = '12'; + + context.mockJwtService.sign.mockReturnValue(mockToken); + + const result = await context.service.createToken( + userId, + sellerId as any, + username, + email, + storeId, + ); + + expect(context.mockJwtService.sign).toHaveBeenCalledWith( + { + id: userId, + sellerId: undefined, + storeId: storeId, + username: username, + email: email, + sessionId: undefined, + }, + { expiresIn: '8h' }, + ); + expect(result).toBe(mockToken); + }); + it('should reject empty username', async () => { const emptyUsername = ''; diff --git a/src/auth/auth/__tests__/createTokenPair.spec.ts b/src/auth/auth/__tests__/createTokenPair.spec.ts index 6c1a5e8..647d73f 100644 --- a/src/auth/auth/__tests__/createTokenPair.spec.ts +++ b/src/auth/auth/__tests__/createTokenPair.spec.ts @@ -321,5 +321,42 @@ describe('AuthService - createTokenPair', () => { expect(result).toHaveProperty('expiresIn'); expect(Object.keys(result).length).toBe(3); }); + + it('should create token pair with null seller ID', async () => { + /** + * Cenário: Usuário sem sellerId (CODUSUR NULL no banco). + * Problema: Validação anterior rejeitava null sellerId. + * Solução esperada: Aceitar null sellerId e criar tokens normalmente. + */ + const result = await context.service.createTokenPair( + 1427, + null, + 'BRUNELLE BENILDA GAMA COSTA', + 'BRUNELLE.C@JURUNENSE.COM.BR', + '12', + 'session-null-seller', + ); + + expect(result).toHaveProperty('accessToken'); + expect(result).toHaveProperty('refreshToken'); + expect(result).toHaveProperty('expiresIn'); + expect(result.expiresIn).toBe(28800); // 8 horas em segundos + + expect(context.mockJwtService.sign).toHaveBeenCalledWith( + { + id: 1427, + sellerId: null, + storeId: '12', + username: 'BRUNELLE BENILDA GAMA COSTA', + email: 'BRUNELLE.C@JURUNENSE.COM.BR', + sessionId: 'session-null-seller', + }, + { expiresIn: '8h' }, + ); + + expect( + context.mockRefreshTokenService.generateRefreshToken, + ).toHaveBeenCalledWith(1427, 'session-null-seller'); + }); }); }); diff --git a/src/auth/auth/auth.service.ts b/src/auth/auth/auth.service.ts index 120d31e..7b93696 100644 --- a/src/auth/auth/auth.service.ts +++ b/src/auth/auth/auth.service.ts @@ -28,7 +28,7 @@ export class AuthService { */ async createToken( id: number, - sellerId: number, + sellerId: number | null, username: string, email: string, storeId: string, @@ -54,7 +54,7 @@ export class AuthService { */ private validateTokenParameters( id: number, - sellerId: number, + sellerId: number | null, username: string, email: string, storeId: string, @@ -63,7 +63,11 @@ export class AuthService { throw new BadRequestException('ID de usuário inválido'); } - if (sellerId === null || sellerId === undefined || sellerId < 0) { + if ( + sellerId !== null && + sellerId !== undefined && + sellerId < 0 + ) { throw new BadRequestException('ID de vendedor inválido'); } @@ -112,7 +116,7 @@ export class AuthService { */ async createTokenPair( id: number, - sellerId: number, + sellerId: number | null, username: string, email: string, storeId: string, diff --git a/src/auth/auth/dto/LoginResponseDto.ts b/src/auth/auth/dto/LoginResponseDto.ts index ec5d69e..722aa67 100644 --- a/src/auth/auth/dto/LoginResponseDto.ts +++ b/src/auth/auth/dto/LoginResponseDto.ts @@ -3,7 +3,7 @@ import { ApiProperty } from '@nestjs/swagger'; export class LoginResponseDto { @ApiProperty() id: number; - @ApiProperty() sellerId: number; + @ApiProperty({ nullable: true }) sellerId: number | null; @ApiProperty() name: string; @ApiProperty() username: string; @ApiProperty() storeId: string; diff --git a/src/auth/models/jwt-payload.model.ts b/src/auth/models/jwt-payload.model.ts index fdae7a7..b3c5a7a 100644 --- a/src/auth/models/jwt-payload.model.ts +++ b/src/auth/models/jwt-payload.model.ts @@ -1,11 +1,11 @@ -/* eslint-disable prettier/prettier */ -export interface JwtPayload { - id: number; - sellerId: number; - storeId: string; - username: string; - email: string; - exp?: number; // Timestamp de expiração do JWT - sessionId?: string; // ID da sessão atual -} - +/* eslint-disable prettier/prettier */ +export interface JwtPayload { + id: number; + sellerId: number | null; + storeId: string; + username: string; + email: string; + exp?: number; // Timestamp de expiração do JWT + sessionId?: string; // ID da sessão atual +} +