import { Test, TestingModule } from '@nestjs/testing'; import { getRepositoryToken } from '@nestjs/typeorm'; import { DataSource } from 'typeorm'; import { AssessmentService } from './assessment.service'; import { AssessmentSession, AssessmentStatus } from './entities/assessment-session.entity'; import { AssessmentQuestion } from './entities/assessment-question.entity'; import { AssessmentAnswer } from './entities/assessment-answer.entity'; import { AssessmentCertificate } from './entities/assessment-certificate.entity'; import { QuestionBank } from './entities/question-bank.entity'; import { QuestionBankItem } from './entities/question-bank-item.entity'; import { KnowledgeBaseService } from '../knowledge-base/knowledge-base.service'; import { KnowledgeGroupService } from '../knowledge-group/knowledge-group.service'; import { ModelConfigService } from '../model-config/model-config.service'; import { ConfigService } from '@nestjs/config'; import { TemplateService } from './services/template.service'; import { ContentFilterService } from './services/content-filter.service'; import { QuestionOutlineService } from './services/question-outline.service'; import { QuestionBankService } from './services/question-bank.service'; import { RagService } from '../rag/rag.service'; import { ChatService } from '../chat/chat.service'; import { I18nService } from '../i18n/i18n.service'; import { TenantService } from '../tenant/tenant.service'; import { NotFoundException } from '@nestjs/common'; describe('AssessmentService', () => { let service: AssessmentService; let sessionRepository: any; let certificateRepository: any; let dataSource: any; const mockRepository = () => ({ delete: jest.fn(), find: jest.fn(), findOne: jest.fn(), save: jest.fn(), create: jest.fn(), }); const mockService = () => ({}); const regularUser = { id: 'user-1', role: 'user' }; const adminUser = { id: 'admin-1', role: 'admin' }; const mockManager = (overrides?: any) => ({ findOne: jest.fn(), delete: jest.fn().mockResolvedValue({ affected: 1 }), save: jest.fn(), ...overrides, }); const mockDataSource = (manager?: any) => ({ transaction: jest.fn(async (cb: any) => { return cb(manager || mockManager()); }), }); beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ providers: [ AssessmentService, { provide: getRepositoryToken(AssessmentSession), useFactory: mockRepository }, { provide: getRepositoryToken(AssessmentQuestion), useFactory: mockRepository }, { provide: getRepositoryToken(AssessmentAnswer), useFactory: mockRepository }, { provide: getRepositoryToken(AssessmentCertificate), useFactory: mockRepository }, { provide: getRepositoryToken(QuestionBank), useFactory: mockRepository }, { provide: getRepositoryToken(QuestionBankItem), useFactory: mockRepository }, { provide: KnowledgeBaseService, useFactory: mockService }, { provide: KnowledgeGroupService, useFactory: mockService }, { provide: ModelConfigService, useFactory: mockService }, { provide: ConfigService, useFactory: mockService }, { provide: TemplateService, useFactory: mockService }, { provide: ContentFilterService, useFactory: mockService }, { provide: QuestionOutlineService, useFactory: mockService }, { provide: QuestionBankService, useFactory: mockService }, { provide: RagService, useFactory: mockService }, { provide: ChatService, useFactory: mockService }, { provide: I18nService, useFactory: mockService }, { provide: TenantService, useFactory: mockService }, { provide: DataSource, useFactory: () => mockDataSource(mockManager()) }, ], }).compile(); service = module.get(AssessmentService); sessionRepository = module.get(getRepositoryToken(AssessmentSession)); certificateRepository = module.get(getRepositoryToken(AssessmentCertificate)); dataSource = module.get(DataSource); }); it('should be defined', () => { expect(service).toBeDefined(); }); describe('deleteSession', () => { it('should delete a session when non-admin user owns it', async () => { const manager = mockManager({ findOne: jest.fn().mockResolvedValue({ id: 'session-id', userId: 'user-1' }), }); dataSource.transaction.mockImplementation(async (cb: any) => cb(manager)); await expect(service.deleteSession('session-id', regularUser)).resolves.not.toThrow(); expect(manager.findOne).toHaveBeenCalledWith(AssessmentSession, { where: { id: 'session-id', userId: 'user-1' } }); expect(manager.delete).toHaveBeenCalledWith(AssessmentCertificate, { sessionId: 'session-id' }); expect(manager.delete).toHaveBeenCalledWith(AssessmentSession, { id: 'session-id' }); }); it('should delete any session when admin user', async () => { const manager = mockManager({ findOne: jest.fn().mockResolvedValue({ id: 'other-session', userId: 'user-2' }), }); dataSource.transaction.mockImplementation(async (cb: any) => cb(manager)); await expect(service.deleteSession('other-session', adminUser)).resolves.not.toThrow(); expect(manager.findOne).toHaveBeenCalledWith(AssessmentSession, { where: { id: 'other-session' } }); }); it('should throw NotFoundException if session not found', async () => { const manager = mockManager({ findOne: jest.fn().mockResolvedValue(null), }); dataSource.transaction.mockImplementation(async (cb: any) => cb(manager)); await expect(service.deleteSession('non-existent', regularUser)).rejects.toThrow(NotFoundException); }); }); describe('generateCertificate', () => { const completedSession = { id: 'session-1', userId: 'user-1', status: AssessmentStatus.COMPLETED, finalScore: 85, templateId: 'template-1', }; it('should throw NotFoundException when session does not exist', async () => { sessionRepository.findOne.mockResolvedValue(null); await expect( service.generateCertificate('no-session', 'user-1', 'tenant-1'), ).rejects.toThrow(NotFoundException); }); it('should throw Error when session is not completed', async () => { sessionRepository.findOne.mockResolvedValue({ ...completedSession, status: AssessmentStatus.IN_PROGRESS, }); await expect( service.generateCertificate('session-1', 'user-1', 'tenant-1'), ).rejects.toThrow('Session not completed'); }); it('should return existing certificate if already generated (idempotent)', async () => { const existingCert = { id: 'cert-1', sessionId: 'session-1' }; sessionRepository.findOne.mockResolvedValue(completedSession); certificateRepository.findOne.mockResolvedValue(existingCert); const result = await service.generateCertificate('session-1', 'user-1', 'tenant-1'); expect(result).toEqual(existingCert); expect(certificateRepository.create).not.toHaveBeenCalled(); }); it('should create a new certificate with correct level for score >= 90 (Expert)', async () => { sessionRepository.findOne.mockResolvedValue({ ...completedSession, finalScore: 95 }); certificateRepository.findOne.mockResolvedValue(null); certificateRepository.create.mockReturnValue({ id: 'cert-new' }); certificateRepository.save.mockResolvedValue({ id: 'cert-new', level: 'Expert' }); const result = await service.generateCertificate('session-1', 'user-1', 'tenant-1'); expect(result).toBeDefined(); expect(certificateRepository.create).toHaveBeenCalledWith( expect.objectContaining({ level: 'Expert', totalScore: 95 }), ); }); it('should create a new certificate with Advanced level for score 75-89', async () => { sessionRepository.findOne.mockResolvedValue(completedSession); certificateRepository.findOne.mockResolvedValue(null); certificateRepository.create.mockReturnValue({ id: 'cert-new' }); certificateRepository.save.mockResolvedValue({ id: 'cert-new', level: 'Advanced' }); const result = await service.generateCertificate('session-1', 'user-1', 'tenant-1'); expect(result).toBeDefined(); expect(certificateRepository.create).toHaveBeenCalledWith( expect.objectContaining({ level: 'Advanced', totalScore: 85 }), ); }); it('should create a new certificate with Novice level for score < 60', async () => { sessionRepository.findOne.mockResolvedValue({ ...completedSession, finalScore: 45 }); certificateRepository.findOne.mockResolvedValue(null); certificateRepository.create.mockReturnValue({ id: 'cert-new' }); certificateRepository.save.mockResolvedValue({ id: 'cert-new', level: 'Novice' }); const result = await service.generateCertificate('session-1', 'user-1', 'tenant-1'); expect(result).toBeDefined(); expect(certificateRepository.create).toHaveBeenCalledWith( expect.objectContaining({ level: 'Novice', totalScore: 45 }), ); }); }); });