// File: backend/tests/performance/approval-matrix.perf-spec.ts // Change Log: // - 2026-05-21: แก้ไขการจำลองข้อมูลและการคอมไพล์ไทป์ใน ResponseCodeRule // - 2026-05-16: Performance test for Approval Matrix Service with 1000+ rules import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { ResponseCodeService } from '../../src/modules/response-code/response-code.service'; import { ResponseCode } from '../../src/modules/response-code/entities/response-code.entity'; import { ResponseCodeRule } from '../../src/modules/response-code/entities/response-code-rule.entity'; import { ResponseCodeCategory } from '../../src/modules/common/enums/review.enums'; describe('ApprovalMatrixService Performance', () => { let service: ResponseCodeService; let responseCodeRepo: Repository; let responseCodeRuleRepo: Repository; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ ResponseCodeService, { provide: getRepositoryToken(ResponseCode), useClass: Repository, }, { provide: getRepositoryToken(ResponseCodeRule), useClass: Repository, }, ], }).compile(); service = module.get(ResponseCodeService); responseCodeRepo = module.get>( getRepositoryToken(ResponseCode) ); responseCodeRuleRepo = module.get>( getRepositoryToken(ResponseCodeRule) ); }); it('should lookup 1000+ response code rules within 100ms', async () => { // Arrange: Create 1000+ mock response code rules const mockRules: Partial[] = Array.from( { length: 1000 }, (_, i) => ({ id: i + 1, responseCodeId: (i % 10) + 1, documentTypeId: 1, isRequired: i % 3 === 0, priority: (i % 5) + 1, responseCode: { id: (i % 10) + 1, code: `CODE-${i % 10}`, isActive: true, } as unknown as ResponseCode, }) ); jest.spyOn(responseCodeRepo, 'find').mockResolvedValue([]); jest .spyOn(responseCodeRuleRepo, 'find') .mockResolvedValue(mockRules as ResponseCodeRule[]); // Act: Measure lookup time const startTime = Date.now(); const _result = await service.findByDocumentType(1, 100); const endTime = Date.now(); // Assert: Must complete within 100ms const queryTime = endTime - startTime; expect(queryTime).toBeLessThan(100); // Log performance metric process.stdout.write( `Lookup ${mockRules.length} rules: ${queryTime}ms (target: <100ms)\n` ); }); it('should handle concurrent lookups efficiently', async () => { // Arrange: Mock dataset const mockCodes: Partial[] = Array.from( { length: 50 }, (_, i): Partial => ({ id: i + 1, code: `CODE-${i}`, category: ( ['ENGINEERING', 'CONTRACT', 'QUALITY'] as ResponseCodeCategory[] )[i % 3], descriptionTh: `Description for code ${i}`, }) ); const mockRules: Partial[] = mockCodes.map((code, i) => ({ id: i + 1, responseCodeId: code.id, documentTypeId: 1, responseCode: code as ResponseCode, })); jest.spyOn(responseCodeRepo, 'find').mockResolvedValue([]); jest .spyOn(responseCodeRuleRepo, 'find') .mockResolvedValue(mockRules as ResponseCodeRule[]); // Act: Run 10 concurrent lookups const startTime = Date.now(); const promises = Array.from({ length: 10 }, () => service.findByDocumentType(1, 100) ); await Promise.all(promises); const endTime = Date.now(); // Assert: Total time should still be reasonable const totalTime = endTime - startTime; expect(totalTime).toBeLessThan(500); // Log performance metric process.stdout.write( `Concurrent lookups (50 codes): ${totalTime}ms (target: <500ms)\n` ); }); });