From 591fb5be8321b16d5f6f588bc6397c002a4bcc75 Mon Sep 17 00:00:00 2001 From: evann Date: Sat, 11 Oct 2025 15:30:28 +0700 Subject: [PATCH 1/2] fix: auth integration test --- apps/api/.env.test | 30 +++++++++++++++++++ .../auth/__tests__/auth.integration.test.ts | 2 ++ .../auth/__tests__/auth.service.test.ts | 9 +++++- apps/api/src/utils/jwt.ts | 8 ++++- apps/api/vitest.setup.ts | 2 +- 5 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 apps/api/.env.test diff --git a/apps/api/.env.test b/apps/api/.env.test new file mode 100644 index 0000000..b03f94e --- /dev/null +++ b/apps/api/.env.test @@ -0,0 +1,30 @@ +# Test Environment Configuration +NODE_ENV=test + +# Test Database (PostgreSQL in Docker) +DATABASE_URL="postgresql://postgres:postgres@192.168.107.2:5432/nginx_waf_test?schema=public" + +# JWT Secrets (test values) +JWT_ACCESS_SECRET="test-access-secret-key-12345" +JWT_REFRESH_SECRET="test-refresh-secret-key-12345" +JWT_ACCESS_EXPIRES_IN="15m" +JWT_REFRESH_EXPIRES_IN="7d" + +# Session +SESSION_SECRET="test-session-secret-12345" + +# CORS +CORS_ORIGIN="http://localhost:5173,http://localhost:3000" + +# BCrypt (lower rounds for faster tests) +BCRYPT_ROUNDS="4" + +# 2FA +TWO_FACTOR_APP_NAME="Nginx WAF Admin Test" + +# Paths (test paths) +BACKUP_DIR="./test-backups" +SSL_DIR="./test-ssl" + +# Server +PORT=3001 \ No newline at end of file diff --git a/apps/api/src/domains/auth/__tests__/auth.integration.test.ts b/apps/api/src/domains/auth/__tests__/auth.integration.test.ts index 94ac007..5711f95 100644 --- a/apps/api/src/domains/auth/__tests__/auth.integration.test.ts +++ b/apps/api/src/domains/auth/__tests__/auth.integration.test.ts @@ -31,6 +31,7 @@ describe('Auth Integration Tests', () => { fullName: 'Test User', role: 'admin', status: 'active', + isFirstLogin: false, }, }); testUserId = user.id; @@ -286,6 +287,7 @@ describe('Auth Integration Tests', () => { fullName: 'Test User 2FA', role: 'admin', status: 'active', + isFirstLogin: false, }, }); user2FAId = user.id; diff --git a/apps/api/src/domains/auth/__tests__/auth.service.test.ts b/apps/api/src/domains/auth/__tests__/auth.service.test.ts index 34560e1..da4e068 100644 --- a/apps/api/src/domains/auth/__tests__/auth.service.test.ts +++ b/apps/api/src/domains/auth/__tests__/auth.service.test.ts @@ -39,6 +39,7 @@ describe('AuthService', () => { phone: null, timezone: 'Asia/Ho_Chi_Minh', language: 'en', + isFirstLogin: false, lastLogin: null, createdAt: new Date(), updatedAt: new Date(), @@ -336,12 +337,18 @@ describe('AuthService', () => { vi.spyOn(authRepository, 'findRefreshToken').mockResolvedValue(mockTokenRecord); vi.spyOn(authRepository, 'isRefreshTokenValid').mockReturnValue(true); vi.spyOn(jwtUtil, 'generateAccessToken').mockReturnValue(mockAccessToken); + vi.spyOn(jwtUtil, 'generateRefreshToken').mockReturnValue('new-refresh-token'); + vi.spyOn(authRepository, 'saveRefreshToken').mockResolvedValue(undefined); + vi.spyOn(authRepository, 'revokeRefreshToken').mockResolvedValue(undefined); // Act const result = await authService.refreshAccessToken(refreshDto); // Assert - expect(result).toEqual({ accessToken: mockAccessToken }); + expect(result).toEqual({ + accessToken: mockAccessToken, + refreshToken: 'new-refresh-token' + }); }); it('should throw AuthenticationError for non-existent token', async () => { diff --git a/apps/api/src/utils/jwt.ts b/apps/api/src/utils/jwt.ts index 39958f6..c8cc0bf 100644 --- a/apps/api/src/utils/jwt.ts +++ b/apps/api/src/utils/jwt.ts @@ -15,7 +15,13 @@ export const generateAccessToken = (payload: TokenPayload): string => { }; export const generateRefreshToken = (payload: TokenPayload): string => { - return jwt.sign(payload, config.jwt.refreshSecret, { + // Add a random jti (JWT ID) to ensure uniqueness + const payloadWithJti = { + ...payload, + jti: Math.random().toString(36).substring(2) + Date.now().toString(36), + }; + + return jwt.sign(payloadWithJti, config.jwt.refreshSecret, { expiresIn: config.jwt.refreshExpiresIn, } as any); }; diff --git a/apps/api/vitest.setup.ts b/apps/api/vitest.setup.ts index de0b381..579b744 100644 --- a/apps/api/vitest.setup.ts +++ b/apps/api/vitest.setup.ts @@ -28,7 +28,7 @@ beforeAll(async () => { } catch (error: any) { console.error('❌ Failed to setup test database:', error.message); console.log('💡 Make sure PostgreSQL is running and test database exists.'); - console.log(' Create test database: createdb nginx_love_test'); + console.log(' Create test database: createdb nginx_waf_test'); // Don't throw - allow tests to run but they may fail } }); From d0f6587030d54a4b49cf2a5c2708397d7163c39f Mon Sep 17 00:00:00 2001 From: evann Date: Sat, 11 Oct 2025 15:35:01 +0700 Subject: [PATCH 2/2] feat: update database configuration for API tests and enhance JWT token generation --- .github/workflows/api-test.yml | 9 ++++----- apps/api/.env.test | 30 ------------------------------ apps/api/src/utils/jwt.ts | 5 +++-- 3 files changed, 7 insertions(+), 37 deletions(-) delete mode 100644 apps/api/.env.test diff --git a/.github/workflows/api-test.yml b/.github/workflows/api-test.yml index 6cb8ca7..d4ab9a1 100644 --- a/.github/workflows/api-test.yml +++ b/.github/workflows/api-test.yml @@ -29,7 +29,7 @@ jobs: env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres - POSTGRES_DB: nginx_waf_test + POSTGRES_DB: nginx_waf ports: - 5432:5432 options: >- @@ -59,8 +59,7 @@ jobs: - name: Setup test environment working-directory: apps/api run: | - cp .env.test .env - echo "DATABASE_URL=postgresql://postgres:postgres@localhost:5432/nginx_waf_test?schema=public" >> .env + cp .env.example .env - name: Generate Prisma Client working-directory: apps/api @@ -70,13 +69,13 @@ jobs: working-directory: apps/api run: pnpm prisma migrate deploy env: - DATABASE_URL: postgresql://postgres:postgres@localhost:5432/nginx_waf_test?schema=public + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/nginx_waf?schema=public - name: Run tests working-directory: apps/api run: pnpm test:coverage env: - DATABASE_URL: postgresql://postgres:postgres@localhost:5432/nginx_waf_test?schema=public + DATABASE_URL: postgresql://postgres:postgres@localhost:5432/nginx_waf?schema=public NODE_ENV: test JWT_ACCESS_SECRET: test-access-secret-key-12345 JWT_REFRESH_SECRET: test-refresh-secret-key-12345 diff --git a/apps/api/.env.test b/apps/api/.env.test deleted file mode 100644 index b03f94e..0000000 --- a/apps/api/.env.test +++ /dev/null @@ -1,30 +0,0 @@ -# Test Environment Configuration -NODE_ENV=test - -# Test Database (PostgreSQL in Docker) -DATABASE_URL="postgresql://postgres:postgres@192.168.107.2:5432/nginx_waf_test?schema=public" - -# JWT Secrets (test values) -JWT_ACCESS_SECRET="test-access-secret-key-12345" -JWT_REFRESH_SECRET="test-refresh-secret-key-12345" -JWT_ACCESS_EXPIRES_IN="15m" -JWT_REFRESH_EXPIRES_IN="7d" - -# Session -SESSION_SECRET="test-session-secret-12345" - -# CORS -CORS_ORIGIN="http://localhost:5173,http://localhost:3000" - -# BCrypt (lower rounds for faster tests) -BCRYPT_ROUNDS="4" - -# 2FA -TWO_FACTOR_APP_NAME="Nginx WAF Admin Test" - -# Paths (test paths) -BACKUP_DIR="./test-backups" -SSL_DIR="./test-ssl" - -# Server -PORT=3001 \ No newline at end of file diff --git a/apps/api/src/utils/jwt.ts b/apps/api/src/utils/jwt.ts index c8cc0bf..8942512 100644 --- a/apps/api/src/utils/jwt.ts +++ b/apps/api/src/utils/jwt.ts @@ -1,4 +1,5 @@ import jwt, { SignOptions } from 'jsonwebtoken'; +import { randomUUID } from 'crypto'; import { config } from '../config'; export interface TokenPayload { @@ -15,10 +16,10 @@ export const generateAccessToken = (payload: TokenPayload): string => { }; export const generateRefreshToken = (payload: TokenPayload): string => { - // Add a random jti (JWT ID) to ensure uniqueness + // Add a cryptographically secure random jti (JWT ID) to ensure uniqueness const payloadWithJti = { ...payload, - jti: Math.random().toString(36).substring(2) + Date.now().toString(36), + jti: randomUUID(), }; return jwt.sign(payloadWithJti, config.jwt.refreshSecret, {