Files
lcbp3/backend/src/modules/document-numbering/document-numbering.service.spec.ts
T
admin 5a1eeca7f3
Build and Deploy / deploy (push) Failing after 10m2s
260318:1635 Fix UUID #10
2026-03-18 16:35:00 +07:00

180 lines
5.8 KiB
TypeScript

import { Test, TestingModule } from '@nestjs/testing';
import { DocumentNumberingService } from './services/document-numbering.service';
import { CounterService } from './services/counter.service';
import { ReservationService } from './services/reservation.service';
import { FormatService } from './services/format.service';
import { getRepositoryToken } from '@nestjs/typeorm';
import { ConfigService } from '@nestjs/config';
import { DocumentNumberFormat } from './entities/document-number-format.entity';
import { DocumentNumberAudit } from './entities/document-number-audit.entity';
import { DocumentNumberError } from './entities/document-number-error.entity';
import { DocumentNumberingLockService } from './services/document-numbering-lock.service';
import { ManualOverrideService } from './services/manual-override.service';
import { MetricsService } from './services/metrics.service';
describe('DocumentNumberingService', () => {
let service: DocumentNumberingService;
let module: TestingModule;
let counterService: CounterService;
let formatService: FormatService;
const mockContext = {
projectId: 1,
originatorOrganizationId: 1,
recipientOrganizationId: 1,
typeId: 1,
subTypeId: 1,
rfaTypeId: 1,
disciplineId: 1,
year: 2025,
customTokens: { TYPE_CODE: 'COR', ORG_CODE: 'GGL' },
};
beforeEach(async () => {
module = await Test.createTestingModule({
providers: [
DocumentNumberingService,
{
provide: ConfigService,
useValue: { get: jest.fn().mockReturnValue('localhost') },
},
{
provide: CounterService,
useValue: {
incrementCounter: jest.fn().mockResolvedValue(1),
getCurrentSequence: jest.fn().mockResolvedValue(0),
},
},
{
provide: ReservationService,
useValue: {
reserve: jest.fn(),
confirm: jest.fn(),
cancel: jest.fn(),
},
},
{
provide: FormatService,
useValue: {
format: jest.fn().mockResolvedValue('0001'),
},
},
{
provide: DocumentNumberingLockService,
useValue: {
acquireLock: jest.fn().mockResolvedValue({ release: jest.fn() }),
releaseLock: jest.fn(),
},
},
{
provide: ManualOverrideService,
useValue: { applyOverride: jest.fn() },
},
{
provide: MetricsService,
useValue: {
numbersGenerated: { inc: jest.fn() },
lockFailures: { inc: jest.fn() },
},
},
{
provide: getRepositoryToken(DocumentNumberFormat),
useValue: { findOne: jest.fn() },
},
{
provide: getRepositoryToken(DocumentNumberAudit),
useValue: {
create: jest.fn().mockReturnValue({ id: 1 }),
save: jest.fn().mockResolvedValue({ id: 1 }),
findOne: jest.fn(),
},
},
{
provide: getRepositoryToken(DocumentNumberError),
useValue: {
create: jest.fn().mockReturnValue({}),
save: jest.fn().mockResolvedValue({}),
},
},
],
}).compile();
service = module.get<DocumentNumberingService>(DocumentNumberingService);
counterService = module.get<CounterService>(CounterService);
formatService = module.get<FormatService>(FormatService);
});
afterEach(() => {
jest.clearAllMocks();
});
it('should be defined', () => {
expect(service).toBeDefined();
});
describe('generateNextNumber', () => {
it('should generate a new number successfully', async () => {
(counterService.incrementCounter as jest.Mock).mockResolvedValue(1);
(formatService.format as jest.Mock).mockResolvedValue('DOC-0001');
const result = await service.generateNextNumber(mockContext);
// Service returns object with number and auditId
expect(result).toHaveProperty('number');
expect(result).toHaveProperty('auditId');
expect(result.number).toBe('DOC-0001');
expect(counterService.incrementCounter).toHaveBeenCalled();
expect(formatService.format).toHaveBeenCalled();
});
it('should throw error when increment fails', async () => {
// Mock CounterService to throw error
(counterService.incrementCounter as jest.Mock).mockRejectedValue(
new Error('Transaction failed')
);
await expect(service.generateNextNumber(mockContext)).rejects.toThrow(
'Transaction failed'
);
});
});
describe('Admin Operations', () => {
it('voidAndReplace should verify audit log exists', async () => {
const auditRepo = module.get(getRepositoryToken(DocumentNumberAudit));
(auditRepo.findOne as jest.Mock).mockResolvedValue({
documentNumber: 'DOC-001',
counterKey: JSON.stringify({ projectId: 1, correspondenceTypeId: 1 }),
templateUsed: 'test',
});
(auditRepo.save as jest.Mock).mockResolvedValue({ id: 2 });
const result = await service.voidAndReplace({
documentNumber: 'DOC-001',
reason: 'test',
replace: false,
});
expect(result.status).toBe('VOIDED');
expect(auditRepo.save).toHaveBeenCalled();
});
it('cancelNumber should log cancellation', async () => {
const auditRepo = module.get(getRepositoryToken(DocumentNumberAudit));
(auditRepo.findOne as jest.Mock).mockResolvedValue({
documentNumber: 'DOC-002',
counterKey: {},
});
(auditRepo.save as jest.Mock).mockResolvedValue({ id: 3 });
const result = await service.cancelNumber({
documentNumber: 'DOC-002',
reason: 'bad',
projectId: 1,
});
expect(result.status).toBe('CANCELLED');
expect(auditRepo.save).toHaveBeenCalled();
});
});
});