251216:0946 Docunment Number: Update specs/ and backend/
Some checks failed
Spec Validation / validate-markdown (push) Has been cancelled
Spec Validation / validate-diagrams (push) Has been cancelled
Spec Validation / check-todos (push) Has been cancelled

This commit is contained in:
admin
2025-12-16 09:46:37 +07:00
parent 78370fb590
commit 9c1e175b76
17 changed files with 932 additions and 3259 deletions

View File

@@ -41,4 +41,13 @@ export class DocumentNumberingController {
getErrorLogs(@Query('limit') limit?: number) { getErrorLogs(@Query('limit') limit?: number) {
return this.numberingService.getErrorLogs(limit ? Number(limit) : 100); return this.numberingService.getErrorLogs(limit ? Number(limit) : 100);
} }
@Patch('counters/:id')
@Roles(Role.ADMIN)
async updateCounter(
@Param('id') id: number,
@Body('sequence') sequence: number
) {
return this.service.setCounterValue(id, sequence);
}
} }

View File

@@ -2,8 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing';
import { DocumentNumberingService } from './document-numbering.service'; import { DocumentNumberingService } from './document-numbering.service';
import { getRepositoryToken } from '@nestjs/typeorm'; import { getRepositoryToken } from '@nestjs/typeorm';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import { Repository, OptimisticLockVersionMismatchError } from 'typeorm'; import { DataSource } from 'typeorm';
import { InternalServerErrorException } from '@nestjs/common';
import { DocumentNumberCounter } from './entities/document-number-counter.entity'; import { DocumentNumberCounter } from './entities/document-number-counter.entity';
import { DocumentNumberFormat } from './entities/document-number-format.entity'; import { DocumentNumberFormat } from './entities/document-number-format.entity';
import { Project } from '../project/entities/project.entity'; import { Project } from '../project/entities/project.entity';
@@ -17,6 +16,7 @@ import { DocumentNumberError } from './entities/document-number-error.entity';
// Mock Redis and Redlock // Mock Redis and Redlock
const mockRedis = { const mockRedis = {
disconnect: jest.fn(), disconnect: jest.fn(),
on: jest.fn(),
}; };
const mockRedlock = { const mockRedlock = {
acquire: jest.fn(), acquire: jest.fn(),
@@ -37,9 +37,7 @@ jest.mock('redlock', () => {
describe('DocumentNumberingService', () => { describe('DocumentNumberingService', () => {
let service: DocumentNumberingService; let service: DocumentNumberingService;
let module: TestingModule; let module: TestingModule;
let counterRepo: Repository<DocumentNumberCounter>; let formatRepo: jest.Mocked<{ findOne: jest.Mock }>;
let formatRepo: Repository<DocumentNumberFormat>;
let auditRepo: Repository<DocumentNumberAudit>;
const mockProject = { id: 1, projectCode: 'LCBP3' }; const mockProject = { id: 1, projectCode: 'LCBP3' };
const mockOrg = { id: 1, name: 'Google' }; const mockOrg = { id: 1, name: 'Google' };
@@ -79,11 +77,17 @@ describe('DocumentNumberingService', () => {
}, },
{ {
provide: getRepositoryToken(DocumentNumberAudit), provide: getRepositoryToken(DocumentNumberAudit),
useValue: { save: jest.fn() }, useValue: {
create: jest.fn().mockReturnValue({ id: 1 }),
save: jest.fn().mockResolvedValue({ id: 1 }),
},
}, },
{ {
provide: getRepositoryToken(DocumentNumberError), provide: getRepositoryToken(DocumentNumberError),
useValue: { save: jest.fn() }, useValue: {
create: jest.fn().mockReturnValue({}),
save: jest.fn().mockResolvedValue({}),
},
}, },
// Mock other dependencies used inside generateNextNumber lookups // Mock other dependencies used inside generateNextNumber lookups
{ {
@@ -106,16 +110,26 @@ describe('DocumentNumberingService', () => {
provide: getRepositoryToken(CorrespondenceSubType), provide: getRepositoryToken(CorrespondenceSubType),
useValue: { findOne: jest.fn() }, useValue: { findOne: jest.fn() },
}, },
{
provide: DataSource,
useValue: {
transaction: jest.fn((cb) =>
cb({
findOne: jest.fn().mockResolvedValue(null),
create: jest.fn().mockReturnValue({ lastSequence: 0 }),
save: jest.fn().mockResolvedValue({ lastSequence: 1 }),
})
),
},
},
], ],
}).compile(); }).compile();
service = module.get<DocumentNumberingService>(DocumentNumberingService); service = module.get<DocumentNumberingService>(DocumentNumberingService);
counterRepo = module.get(getRepositoryToken(DocumentNumberCounter));
formatRepo = module.get(getRepositoryToken(DocumentNumberFormat)); formatRepo = module.get(getRepositoryToken(DocumentNumberFormat));
auditRepo = module.get(getRepositoryToken(DocumentNumberAudit));
}); });
afterEach(async () => { afterEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
// Don't call onModuleDestroy - redisClient is mocked and would cause undefined error // Don't call onModuleDestroy - redisClient is mocked and would cause undefined error
}); });
@@ -136,46 +150,46 @@ describe('DocumentNumberingService', () => {
(typeRepo.findOne as jest.Mock).mockResolvedValue(mockType); (typeRepo.findOne as jest.Mock).mockResolvedValue(mockType);
(disciplineRepo.findOne as jest.Mock).mockResolvedValue(mockDiscipline); (disciplineRepo.findOne as jest.Mock).mockResolvedValue(mockDiscipline);
(formatRepo.findOne as jest.Mock).mockResolvedValue({ (formatRepo.findOne as jest.Mock).mockResolvedValue({
formatTemplate: '{SEQ}', formatTemplate: '{SEQ:4}',
resetSequenceYearly: true,
}); });
(counterRepo.findOne as jest.Mock).mockResolvedValue(null); // First time
(counterRepo.save as jest.Mock).mockResolvedValue({ lastNumber: 1 });
service.onModuleInit(); service.onModuleInit();
const result = await service.generateNextNumber(mockContext); const result = await service.generateNextNumber(mockContext);
expect(result).toBe('0001'); // Default padding 4 (see replaceTokens method) // Service returns object with number and auditId
expect(counterRepo.save).toHaveBeenCalled(); expect(result).toHaveProperty('number');
// expect(auditRepo.save).toHaveBeenCalled(); // Disabled in implementation expect(result).toHaveProperty('auditId');
expect(result.number).toBe('0001'); // Padded to 4 digits
}); });
it('should throw InternalServerErrorException if max retries exceeded', async () => { it('should throw error when transaction fails', async () => {
const projectRepo = module.get(getRepositoryToken(Project)); const projectRepo = module.get(getRepositoryToken(Project));
const orgRepo = module.get(getRepositoryToken(Organization)); const orgRepo = module.get(getRepositoryToken(Organization));
const typeRepo = module.get(getRepositoryToken(CorrespondenceType)); const typeRepo = module.get(getRepositoryToken(CorrespondenceType));
const disciplineRepo = module.get(getRepositoryToken(Discipline)); const disciplineRepo = module.get(getRepositoryToken(Discipline));
const dataSource = module.get(DataSource);
(projectRepo.findOne as jest.Mock).mockResolvedValue(mockProject); (projectRepo.findOne as jest.Mock).mockResolvedValue(mockProject);
(orgRepo.findOne as jest.Mock).mockResolvedValue(mockOrg); (orgRepo.findOne as jest.Mock).mockResolvedValue(mockOrg);
(typeRepo.findOne as jest.Mock).mockResolvedValue(mockType); (typeRepo.findOne as jest.Mock).mockResolvedValue(mockType);
(disciplineRepo.findOne as jest.Mock).mockResolvedValue(mockDiscipline); (disciplineRepo.findOne as jest.Mock).mockResolvedValue(mockDiscipline);
(formatRepo.findOne as jest.Mock).mockResolvedValue({ (formatRepo.findOne as jest.Mock).mockResolvedValue({
formatTemplate: '{SEQ}', formatTemplate: '{SEQ:4}',
resetSequenceYearly: true,
}); });
(counterRepo.findOne as jest.Mock).mockResolvedValue({ lastNumber: 1 });
// Always fail // Mock transaction to throw error
(counterRepo.save as jest.Mock).mockRejectedValue( (dataSource.transaction as jest.Mock).mockRejectedValue(
new OptimisticLockVersionMismatchError('Counter', 1, 2) new Error('Transaction failed')
); );
service.onModuleInit(); service.onModuleInit();
await expect(service.generateNextNumber(mockContext)).rejects.toThrow( await expect(service.generateNextNumber(mockContext)).rejects.toThrow(
InternalServerErrorException Error
); );
expect(counterRepo.save).toHaveBeenCalledTimes(3); // Max retries
}); });
}); });
}); });

View File

@@ -1,736 +1,302 @@
// File: src/modules/document-numbering/document-numbering.service.ts // backend/src/modules/document-numbering/document-numbering.service.ts
import {
Injectable, import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
OnModuleInit,
OnModuleDestroy,
InternalServerErrorException,
NotFoundException,
Logger,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { import { Repository, DataSource, IsNull } from 'typeorm';
Repository,
EntityManager,
OptimisticLockVersionMismatchError,
} from 'typeorm';
import { ConfigService } from '@nestjs/config'; import { ConfigService } from '@nestjs/config';
import Redis from 'ioredis'; import Redis from 'ioredis';
import Redlock from 'redlock'; import Redlock from 'redlock';
// Entities
import { DocumentNumberCounter } from './entities/document-number-counter.entity'; import { DocumentNumberCounter } from './entities/document-number-counter.entity';
import { DocumentNumberFormat } from './entities/document-number-format.entity'; import { DocumentNumberFormat } from './entities/document-number-format.entity';
import { Project } from '../project/entities/project.entity'; // สมมติ path import { DocumentNumberAudit } from './entities/document-number-audit.entity';
import { Organization } from '../organization/entities/organization.entity'; import { DocumentNumberError } from './entities/document-number-error.entity';
import { Project } from '../project/entities/project.entity';
import { CorrespondenceType } from '../correspondence/entities/correspondence-type.entity'; import { CorrespondenceType } from '../correspondence/entities/correspondence-type.entity';
import { Organization } from '../organization/entities/organization.entity';
import { Discipline } from '../master/entities/discipline.entity'; import { Discipline } from '../master/entities/discipline.entity';
import { CorrespondenceSubType } from '../correspondence/entities/correspondence-sub-type.entity';
import { DocumentNumberAudit } from './entities/document-number-audit.entity'; // [P0-4]
import { DocumentNumberError } from './entities/document-number-error.entity'; // [P0-4]
// Interfaces
import { import {
GenerateNumberContext, GenerateNumberContext,
DecodedTokens, DecodedTokens,
} from './interfaces/document-numbering.interface.js'; } from './interfaces/document-numbering.interface';
@Injectable() @Injectable()
export class DocumentNumberingService implements OnModuleInit, OnModuleDestroy { export class DocumentNumberingService implements OnModuleInit {
private readonly logger = new Logger(DocumentNumberingService.name); private readonly logger = new Logger(DocumentNumberingService.name);
private redisClient!: Redis; private redisClient: Redis;
private redlock!: Redlock; private redlock: Redlock;
constructor( constructor(
@InjectRepository(DocumentNumberCounter) @InjectRepository(DocumentNumberCounter)
private counterRepo: Repository<DocumentNumberCounter>, private counterRepo: Repository<DocumentNumberCounter>,
@InjectRepository(DocumentNumberFormat) @InjectRepository(DocumentNumberFormat)
private formatRepo: Repository<DocumentNumberFormat>, private formatRepo: Repository<DocumentNumberFormat>,
@InjectRepository(DocumentNumberAudit)
// Inject Repositories สำหรับดึง Code มาทำ Token Replacement private auditRepo: Repository<DocumentNumberAudit>,
@InjectRepository(Project) private projectRepo: Repository<Project>, @InjectRepository(DocumentNumberError)
@InjectRepository(Organization) private orgRepo: Repository<Organization>, private errorRepo: Repository<DocumentNumberError>,
@InjectRepository(Project)
private projectRepo: Repository<Project>,
@InjectRepository(CorrespondenceType) @InjectRepository(CorrespondenceType)
private typeRepo: Repository<CorrespondenceType>, private typeRepo: Repository<CorrespondenceType>,
@InjectRepository(Organization)
private orgRepo: Repository<Organization>,
@InjectRepository(Discipline) @InjectRepository(Discipline)
private disciplineRepo: Repository<Discipline>, private disciplineRepo: Repository<Discipline>,
@InjectRepository(CorrespondenceSubType) private dataSource: DataSource,
private subTypeRepo: Repository<CorrespondenceSubType>,
@InjectRepository(DocumentNumberAudit) // [P0-4]
private auditRepo: Repository<DocumentNumberAudit>,
@InjectRepository(DocumentNumberError) // [P0-4]
private errorRepo: Repository<DocumentNumberError>,
private configService: ConfigService private configService: ConfigService
) {} ) {}
onModuleInit() { onModuleInit() {
// 1. Setup Redis Connection & Redlock
const host = this.configService.get<string>('REDIS_HOST', 'localhost'); const host = this.configService.get<string>('REDIS_HOST', 'localhost');
const port = this.configService.get<number>('REDIS_PORT', 6379); const port = this.configService.get<number>('REDIS_PORT', 6379);
const password = this.configService.get<string>('REDIS_PASSWORD');
this.redisClient = new Redis({ host, port, password }); this.redisClient = new Redis({
host,
// Config Redlock สำหรับ Distributed Lock port,
this.redlock = new Redlock([this.redisClient], { retryStrategy: (times) => Math.min(times * 50, 2000),
driftFactor: 0.01, maxRetriesPerRequest: 3,
retryCount: 10, // Retry 10 ครั้ง
retryDelay: 200, // รอ 200ms ต่อครั้ง
retryJitter: 200,
}); });
this.logger.log( this.redisClient.on('error', (err) => {
`Document Numbering Service initialized (Redis: ${host}:${port})` this.logger.error('Redis Client Error', err);
});
this.redlock = new Redlock([this.redisClient], {
driftFactor: 0.01,
retryCount: 3,
retryDelay: 200,
retryJitter: 200,
});
}
async generateNextNumber(
ctx: GenerateNumberContext
): Promise<{ number: string; auditId: number }> {
const currentYear = new Date().getFullYear();
// 1. Resolve Format & Determine Counter Scope & Reset Rule
const { template, counterTypeId, resetSequenceYearly } =
await this.resolveFormatAndScope(ctx);
const tokens = await this.resolveTokens(ctx, currentYear);
// 2. Determine Counter Year Key
// If resetSequenceYearly is true => Use current year (2025)
// If resetSequenceYearly is false => Use year 0 (Continuous)
const counterYear = resetSequenceYearly ? currentYear : 0;
// 3. Build Lock Key
const resourceKey = `counter:${ctx.projectId}:${counterTypeId ?? 'shared'}:${counterYear}`;
let lock: any;
try {
try {
lock = await this.redlock.acquire([`locks:${resourceKey}`], 5000);
} catch (e) {
this.logger.warn(
`Redlock failed for ${resourceKey}, proceeding with DB optimistic lock.`
); );
} }
onModuleDestroy() { // 4. Increment Counter (Atomic Transaction)
this.redisClient.disconnect(); const result = await this.dataSource.transaction(async (manager) => {
} let counter = await manager.findOne(DocumentNumberCounter, {
/**
* สร้างเลขที่เอกสารใหม่ (Thread-Safe & Gap-free)
*/
async generateNextNumber(ctx: GenerateNumberContext): Promise<string> {
const year = ctx.year || new Date().getFullYear();
const disciplineId = ctx.disciplineId || 0;
// 1. Resolve Tokens Outside Lock
const tokens = await this.resolveTokens(ctx, year);
// 2. Get Format Template WITH META (Padding)
const { template, paddingLength } = await this.getFormatTemplateWithMeta(
ctx.projectId,
ctx.typeId,
disciplineId
);
// 3. Resource Key
const resourceKey = `doc_num:${ctx.projectId}:${ctx.typeId}:${disciplineId}:${year}`;
const lockTtl = 5000;
let lock;
try {
lock = await this.redlock.acquire([resourceKey], lockTtl);
const maxRetries = 3;
for (let i = 0; i < maxRetries; i++) {
try {
const recipientId = ctx.recipientOrganizationId ?? -1;
const subTypeId = ctx.subTypeId ?? 0;
const rfaTypeId = ctx.rfaTypeId ?? 0;
let counter = await this.counterRepo.findOne({
where: { where: {
projectId: ctx.projectId, projectId: ctx.projectId,
originatorId: ctx.originatorId, correspondenceTypeId:
recipientOrganizationId: recipientId, counterTypeId === null ? IsNull() : counterTypeId,
typeId: ctx.typeId, year: counterYear,
subTypeId: subTypeId,
rfaTypeId: rfaTypeId,
disciplineId: disciplineId,
year: year,
}, },
}); });
if (!counter) { if (!counter) {
counter = this.counterRepo.create({ counter = manager.create(DocumentNumberCounter, {
projectId: ctx.projectId, projectId: ctx.projectId,
originatorId: ctx.originatorId, correspondenceTypeId: counterTypeId, // Can be null
recipientOrganizationId: recipientId, year: counterYear,
typeId: ctx.typeId, lastSequence: 0,
subTypeId: subTypeId,
rfaTypeId: rfaTypeId,
disciplineId: disciplineId,
year: year,
lastNumber: 0,
}); });
} }
counter.lastNumber += 1; counter.lastSequence += 1;
await this.counterRepo.save(counter); return await manager.save(counter);
});
// 5. Generate Final String
const generatedNumber = this.replaceTokens( const generatedNumber = this.replaceTokens(
template, template,
tokens, tokens,
counter.lastNumber, result.lastSequence
paddingLength // Pass padding from template
); );
// Audit skipped for brevity in this block, assumed handled or TBD // 6. Audit Log
return generatedNumber; const audit = await this.logAudit({
} catch (err) { generatedNumber,
if (err instanceof OptimisticLockVersionMismatchError) { counterKey: resourceKey,
continue; templateUsed: template,
} context: ctx,
throw err; isSuccess: true,
} });
}
throw new InternalServerErrorException('Failed to generate number'); return { number: generatedNumber, auditId: audit.id };
} catch (error: any) { } catch (error) {
// Error logging... await this.logError(error, ctx, resourceKey);
throw error; throw error;
} finally { } finally {
if (lock) await lock.release().catch(() => {}); if (lock) await lock.release().catch((e) => this.logger.error(e));
} }
} }
async previewNextNumber( // --- Helper Methods ---
ctx: GenerateNumberContext
): Promise<{ number: string; isDefaultTemplate: boolean }> {
const year = ctx.year || new Date().getFullYear();
const disciplineId = ctx.disciplineId || 0;
const tokens = await this.resolveTokens(ctx, year); private async resolveFormatAndScope(ctx: GenerateNumberContext): Promise<{
template: string;
const { template, isDefault, paddingLength } = counterTypeId: number | null;
await this.getFormatTemplateWithMeta( resetSequenceYearly: boolean;
ctx.projectId, }> {
ctx.typeId, // A. Try Specific Format
disciplineId const specificFormat = await this.formatRepo.findOne({
); where: { projectId: ctx.projectId, correspondenceTypeId: ctx.typeId },
const recipientId = ctx.recipientOrganizationId ?? -1;
const subTypeId = ctx.subTypeId ?? 0;
const rfaTypeId = ctx.rfaTypeId ?? 0;
const counter = await this.counterRepo.findOne({
where: {
projectId: ctx.projectId,
originatorId: ctx.originatorId,
recipientOrganizationId: recipientId,
typeId: ctx.typeId,
subTypeId: subTypeId,
rfaTypeId: rfaTypeId,
disciplineId: disciplineId,
year: year,
},
}); });
const nextSeq = (counter?.lastNumber || 0) + 1; if (specificFormat) {
const generatedNumber = this.replaceTokens(
template,
tokens,
nextSeq,
paddingLength
);
return { return {
number: generatedNumber, template: specificFormat.formatTemplate,
isDefaultTemplate: isDefault, counterTypeId: ctx.typeId,
resetSequenceYearly: specificFormat.resetSequenceYearly,
};
}
// B. Try Default Format (Type = NULL)
const defaultFormat = await this.formatRepo.findOne({
where: { projectId: ctx.projectId, correspondenceTypeId: IsNull() },
});
if (defaultFormat) {
return {
template: defaultFormat.formatTemplate,
counterTypeId: null, // Use shared counter
resetSequenceYearly: defaultFormat.resetSequenceYearly,
};
}
// C. System Fallback
return {
template: '{ORG}-{RECIPIENT}-{SEQ:4}-{YEAR:BE}',
counterTypeId: null, // Use shared counter
resetSequenceYearly: true, // Default fallback behavior
}; };
} }
/**
* Helper: ดึงข้อมูล Code ต่างๆ จาก ID เพื่อนำมาแทนที่ใน Template
*/
private async resolveTokens( private async resolveTokens(
ctx: GenerateNumberContext, ctx: GenerateNumberContext,
year: number year: number
): Promise<DecodedTokens> { ): Promise<DecodedTokens> {
const [project, org, type] = await Promise.all([ const [project, type, recipientCode, disciplineCode, orgCode] =
this.projectRepo.findOne({ where: { id: ctx.projectId } }), await Promise.all([
this.orgRepo.findOne({ where: { id: ctx.originatorId } }), this.projectRepo.findOne({
this.typeRepo.findOne({ where: { id: ctx.typeId } }), where: { id: ctx.projectId },
select: ['projectCode'],
}),
this.typeRepo.findOne({
where: { id: ctx.typeId },
select: ['typeCode'],
}),
this.resolveRecipientCode(ctx.recipientOrganizationId),
this.resolveDisciplineCode(ctx.disciplineId),
this.resolveOrgCode(ctx.originatorOrganizationId),
]); ]);
if (!project || !org || !type) {
throw new NotFoundException('Project, Organization, or Type not found');
}
// [v1.5.1] Support Custom Tokens Override
const custom = ctx.customTokens || {};
let disciplineCode = custom.DISCIPLINE_CODE || '000';
if (!custom.DISCIPLINE_CODE && ctx.disciplineId) {
const discipline = await this.disciplineRepo.findOne({
where: { id: ctx.disciplineId },
});
if (discipline) disciplineCode = discipline.disciplineCode;
}
let subTypeCode = custom.SUB_TYPE_CODE || '00';
let subTypeNumber = custom.SUB_TYPE_NUMBER || '00';
if (!custom.SUB_TYPE_CODE && ctx.subTypeId) {
const subType = await this.subTypeRepo.findOne({
where: { id: ctx.subTypeId },
});
if (subType) {
subTypeCode = subType.subTypeCode;
subTypeNumber = subType.subTypeNumber || '00';
}
}
// Convert Christian Year to Buddhist Year if needed (Req usually uses Christian, but prepared logic)
// ใน Req 6B ตัวอย่างใช้ 2568 (พ.ศ.) ดังนั้นต้องแปลง
const yearTh = (year + 543).toString();
// [v1.5.1] Resolve recipient organization
let recipientCode = custom.RECIPIENT_CODE || custom.REC_CODE || '';
if (
!recipientCode &&
ctx.recipientOrganizationId &&
ctx.recipientOrganizationId > 0
) {
const recipient = await this.orgRepo.findOne({
where: { id: ctx.recipientOrganizationId },
});
if (recipient) {
recipientCode = recipient.organizationCode;
}
}
return { return {
projectCode: project.projectCode, '{PROJECT}': project?.projectCode || 'PROJ',
orgCode: org.organizationCode, '{TYPE}': type?.typeCode || 'DOC',
typeCode: type.typeCode, '{ORG}': orgCode,
disciplineCode, '{RECIPIENT}': recipientCode,
subTypeCode, '{DISCIPLINE}': disciplineCode,
subTypeNumber, '{YEAR}': year.toString().substring(2),
year: yearTh, '{YEAR:BE}': (year + 543).toString().substring(2),
yearShort: yearTh.slice(-2), // 68 '{REV}': '0',
recipientCode, // [P1-4]
}; };
} }
// --- Template Management ---
async getTemplates(): Promise<DocumentNumberFormat[]> {
const templates = await this.formatRepo.find({
relations: ['project'], // Join project for names if needed
order: {
projectId: 'ASC',
correspondenceTypeId: 'ASC',
disciplineId: 'ASC',
},
});
// Add documentTypeName via manual join or map if needed.
// Ideally we should relation to CorrespondenceType too, but for now we might need to fetch manually if relation not strict
return templates;
}
async saveTemplate(
dto: Partial<DocumentNumberFormat>
): Promise<DocumentNumberFormat> {
if (dto.id) {
await this.formatRepo.update(dto.id, dto);
return this.formatRepo.findOneOrFail({ where: { id: dto.id } });
}
const newTemplate = this.formatRepo.create(dto);
return this.formatRepo.save(newTemplate);
}
/**
* Helper: Find Template from DB or use Default (with metadata)
* Supports Specific Discipline -> Global Discipline Fallback
*/
private async getFormatTemplateWithMeta(
projectId: number,
typeId: number,
disciplineId: number = 0
): Promise<{ template: string; isDefault: boolean; paddingLength: number }> {
// 1. Try Specific Discipline
let format = await this.formatRepo.findOne({
where: {
projectId,
correspondenceTypeId: typeId,
disciplineId: disciplineId,
},
});
// 2. Fallback to All Disciplines (0) if specific not found
if (!format && disciplineId !== 0) {
format = await this.formatRepo.findOne({
where: { projectId, correspondenceTypeId: typeId, disciplineId: 0 },
});
}
if (format) {
return {
template: format.formatTemplate,
isDefault: false,
paddingLength: format.paddingLength,
};
}
// Default Fallback Format
return {
template: '{ORG}-{RECIPIENT}-{SEQ:4}-{YEAR}',
isDefault: true,
paddingLength: 4,
};
}
/**
* Legacy wrapper for backward compatibility
*/
private async getFormatTemplate(
projectId: number,
typeId: number,
disciplineId: number = 0
): Promise<string> {
const { template } = await this.getFormatTemplateWithMeta(
projectId,
typeId,
disciplineId
);
return template;
}
/**
* Helper: แทนที่ Token ใน Template ด้วยค่าจริง
*/
private replaceTokens( private replaceTokens(
template: string, template: string,
tokens: DecodedTokens, tokens: DecodedTokens,
seq: number, sequence: number
defaultPadding: number = 4
): string { ): string {
let result = template; let result = template;
for (const [key, value] of Object.entries(tokens)) {
const replacements: Record<string, string> = { result = result.replace(
'{PROJECT}': tokens.projectCode, new RegExp(key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'),
'{ORG}': tokens.orgCode, value
'{TYPE}': tokens.typeCode, );
'{DISCIPLINE}': tokens.disciplineCode, }
'{SUBTYPE}': tokens.subTypeCode, const seqMatch = result.match(/{SEQ:(\d+)}/);
'{SUBTYPE_NUM}': tokens.subTypeNumber, // [Req 6B] For Transmittal/RFA if (seqMatch) {
'{RECIPIENT}': tokens.recipientCode, // [P1-4] Recipient organization const padding = parseInt(seqMatch[1], 10);
'{YEAR}': tokens.year, result = result.replace(
'{YEAR_SHORT}': tokens.yearShort, seqMatch[0],
}; sequence.toString().padStart(padding, '0')
);
// 1. Replace Standard Tokens
for (const [key, value] of Object.entries(replacements)) {
// ใช้ Global Replace
result = result.split(key).join(value);
} }
// 2. Replace Sequence Token {SEQ:n} e.g., {SEQ:4}
// If n is provided in token, use it. If not, use Template Padding setting.
result = result.replace(/{SEQ(?::(\d+))?}/g, (_, digits) => {
const padLength = digits ? parseInt(digits, 10) : defaultPadding;
return seq.toString().padStart(padLength, '0');
});
return result; return result;
} }
/** private async resolveRecipientCode(recipientId?: number): Promise<string> {
* [P0-4] Log successful number generation to audit table if (!recipientId) return 'GEN';
*/ const org = await this.orgRepo.findOne({
private async logAudit( where: { id: recipientId },
auditData: Partial<DocumentNumberAudit> select: ['organizationCode'],
): Promise<void> {
try {
// Ensure operation is set, default to CONFIRM if not provided
const dataToSave = {
...auditData,
operation: auditData.operation || 'CONFIRM',
};
await this.auditRepo.save(dataToSave);
} catch (error) {
this.logger.error('Failed to log audit', error);
}
}
/**
* [P0-4] Log error to error table
*/
private async logError(
errorData: Partial<DocumentNumberError>
): Promise<void> {
try {
await this.errorRepo.save(errorData);
} catch (error) {
this.logger.error('Failed to log error', error);
}
}
/**
* [P0-4] Classify error type for logging
*/
private classifyError(error: any): string {
if (error.message?.includes('lock') || error.message?.includes('Lock')) {
return 'LOCK_TIMEOUT';
}
if (error instanceof OptimisticLockVersionMismatchError) {
return 'VERSION_CONFLICT';
}
if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT') {
return 'REDIS_ERROR';
}
if (error.name === 'QueryFailedError') {
return 'DB_ERROR';
}
return 'VALIDATION_ERROR';
}
// --- Log Retrieval for Admin UI ---
async getAuditLogs(limit = 100): Promise<DocumentNumberAudit[]> {
return this.auditRepo.find({
order: { createdAt: 'DESC' },
take: limit,
}); });
return org ? org.organizationCode : 'GEN';
} }
async getErrorLogs(limit = 100): Promise<DocumentNumberError[]> { private async resolveOrgCode(orgId?: number): Promise<string> {
return this.errorRepo.find({ if (!orgId) return 'GEN';
order: { createdAt: 'DESC' }, const org = await this.orgRepo.findOne({
take: limit, where: { id: orgId },
select: ['organizationCode'],
}); });
return org ? org.organizationCode : 'GEN';
} }
// --- Admin Operations --- private async resolveDisciplineCode(disciplineId?: number): Promise<string> {
if (!disciplineId) return 'GEN';
/** const discipline = await this.disciplineRepo.findOne({
* Manual Override: Force set the counter to a specific number. where: { id: disciplineId },
* Useful for aligning with legacy systems or skipping numbers. select: ['code'],
*/
async manualOverride(dto: any): Promise<void> {
const {
projectId,
originatorId,
typeId,
disciplineId,
year,
newSequence,
reason,
userId,
} = dto;
const resourceKey = `doc_num:${projectId}:${typeId}:${disciplineId || 0}:${year || new Date().getFullYear()}`;
const lockTtl = 5000;
let lock;
try {
lock = await this.redlock.acquire([resourceKey], lockTtl);
// Find or Create Counter
let counter = await this.counterRepo.findOne({
where: {
projectId,
originatorId,
recipientOrganizationId: dto.recipientOrganizationId ?? -1,
typeId,
subTypeId: dto.subTypeId ?? 0,
rfaTypeId: dto.rfaTypeId ?? 0,
disciplineId: disciplineId || 0,
year: year || new Date().getFullYear(),
},
});
if (!counter) {
counter = this.counterRepo.create({
projectId,
originatorId,
recipientOrganizationId: dto.recipientOrganizationId ?? -1,
typeId,
subTypeId: dto.subTypeId ?? 0,
rfaTypeId: dto.rfaTypeId ?? 0,
disciplineId: disciplineId || 0,
year: year || new Date().getFullYear(),
lastNumber: 0,
}); });
return discipline ? discipline.code : 'GEN';
} }
const oldNumber = counter.lastNumber; private async logAudit(data: any): Promise<DocumentNumberAudit> {
if (newSequence <= oldNumber) { const audit = this.auditRepo.create({
// Warning: Manual override to lower number might cause collisions ...data,
this.logger.warn( projectId: data.context.projectId,
`Manual override to lower sequence: ${oldNumber} -> ${newSequence}` createdBy: data.context.userId,
ipAddress: data.context.ipAddress,
});
return await this.auditRepo.save(audit);
}
private async logError(error: any, ctx: any, key: string) {
this.logger.error(
`Document Numbering Error: ${error.message}`,
error.stack
); );
}
counter.lastNumber = newSequence;
await this.counterRepo.save(counter);
// Log Audit
await this.logAudit({
generatedNumber: `MANUAL-${newSequence}`,
counterKey: { key: resourceKey },
templateUsed: 'MANUAL_OVERRIDE',
documentId: 0,
userId: userId,
operation: 'MANUAL_OVERRIDE',
metadata: { reason, oldNumber, newNumber: newSequence },
});
} catch (error) {
throw error;
} finally {
if (lock) await lock.release().catch(() => {});
}
}
/**
* Bulk Import: Set initial counters for migration.
*/
async bulkImport(items: any[]): Promise<void> {
for (const item of items) {
// Reuse manualOverride logic loosely, or implement bulk specific logic
// optimizing by not locking if we assume offline migration
// For safety, let's just update repo directly
await this.manualOverride(item);
}
}
/**
* Cancel Number: Mark a number as cancelled/skipped in Audit.
* Does NOT rollback counter (unless specified).
*/
async cancelNumber(dto: any): Promise<void> {
const { userId, generatedNumber, reason } = dto;
await this.logAudit({
generatedNumber,
counterKey: {},
templateUsed: 'N/A',
documentId: 0,
userId,
operation: 'CANCEL',
metadata: { reason },
});
}
/**
* Void and Replace: Mark old number as void, generate new one to replace it.
* Used when users made a mistake in critical fields.
*/
async voidAndReplace(dto: any): Promise<string> {
const { oldNumber, reason, newGenerationContext } = dto;
// 1. Audit old number as VOID_REPLACE
await this.logAudit({
generatedNumber: oldNumber,
counterKey: {},
templateUsed: 'N/A',
documentId: 0, // Should link to doc if possible
userId: newGenerationContext.userId,
operation: 'VOID_REPLACE',
metadata: { reason, replacedByNewGeneration: true },
});
// 2. Generate New Number
return this.generateNextNumber(newGenerationContext);
}
/**
* Update Number for Draft:
* Handles logic when a Draft document changes critical fields (Project, Type, etc.)
* - Tries to rollback the old number if it's the latest one.
* - Otherwise, voids the old number.
* - Generates a new number for the new context.
*/
async updateNumberForDraft(
oldNumber: string,
oldCtx: GenerateNumberContext,
newCtx: GenerateNumberContext
): Promise<string> {
const year = oldCtx.year || new Date().getFullYear();
const disciplineId = oldCtx.disciplineId || 0;
const resourceKey = `doc_num:${oldCtx.projectId}:${oldCtx.typeId}:${disciplineId}:${year}`;
const lockTtl = 5000;
let lock;
try { try {
// 1. Try Rollback Old Number const errorRecord = this.errorRepo.create({
lock = await this.redlock.acquire([resourceKey], lockTtl); projectId: ctx.projectId,
errorType: error.name || 'UnknownError',
const recipientId = oldCtx.recipientOrganizationId ?? -1; errorMessage: error.message,
const subTypeId = oldCtx.subTypeId ?? 0; stackTrace: error.stack,
const rfaTypeId = oldCtx.rfaTypeId ?? 0; counterKey: key,
inputPayload: JSON.stringify(ctx),
const counter = await this.counterRepo.findOne({
where: {
projectId: oldCtx.projectId,
originatorId: oldCtx.originatorId,
recipientOrganizationId: recipientId,
typeId: oldCtx.typeId,
subTypeId: subTypeId,
rfaTypeId: rfaTypeId,
disciplineId: disciplineId,
year: year,
},
});
if (counter && counter.lastNumber > 0) {
// Construct what the number SHOULD be if it matches lastNumber
const tokens = await this.resolveTokens(oldCtx, year);
const { template } = await this.getFormatTemplateWithMeta(
oldCtx.projectId,
oldCtx.typeId
);
const expectedNumber = this.replaceTokens(
template,
tokens,
counter.lastNumber
);
if (expectedNumber === oldNumber) {
// MATCH! We can rollback.
counter.lastNumber -= 1;
await this.counterRepo.save(counter);
await this.logAudit({
generatedNumber: oldNumber,
counterKey: { key: resourceKey },
templateUsed: template,
documentId: 0,
userId: newCtx.userId,
operation: 'RESERVE', // Use RESERVE or CANCEL to indicate rollback/freed up
metadata: {
action: 'ROLLBACK_DRAFT',
reason: 'Critical field changed in Draft',
},
});
this.logger.log(
`Rolled back number ${oldNumber} (Seq ${counter.lastNumber + 1})`
);
} else {
// NO MATCH. Cannot rollback. Mark as VOID_REPLACE.
await this.logAudit({
generatedNumber: oldNumber,
counterKey: { key: resourceKey },
templateUsed: 'N/A',
documentId: 0,
userId: newCtx.userId,
operation: 'VOID_REPLACE',
metadata: {
reason:
'Critical field changed in Draft (Rollback failed - not latest)',
},
}); });
await this.errorRepo.save(errorRecord);
} catch (e) {
this.logger.error('Failed to save error log', e);
} }
} else {
// Counter not found or 0. Just Void.
await this.logAudit({
generatedNumber: oldNumber,
counterKey: {},
templateUsed: 'N/A',
documentId: 0,
userId: newCtx.userId,
operation: 'VOID_REPLACE',
metadata: { reason: 'Critical field changed (Counter not found)' },
});
}
} catch (err) {
this.logger.warn(`Failed to rollback number ${oldNumber}: ${err as any}`);
// Fallback: Ensure we at least void it in audit if rollback failed logic
await this.logAudit({
generatedNumber: oldNumber,
counterKey: {},
templateUsed: 'N/A',
documentId: 0,
userId: newCtx.userId,
operation: 'VOID_REPLACE',
metadata: { reason: 'Rollback error' },
});
} finally {
if (lock) await lock.release().catch(() => {});
}
// 2. Generate New Number
return this.generateNextNumber(newCtx);
} }
} }

View File

@@ -1,3 +1,5 @@
// backend/src/modules/document-numbering/entities/document-number-format.entity.ts
import { import {
Entity, Entity,
Column, Column,
@@ -5,43 +7,46 @@ import {
ManyToOne, ManyToOne,
JoinColumn, JoinColumn,
Unique, Unique,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm'; } from 'typeorm';
import { Project } from '../../project/entities/project.entity'; import { Project } from '../../project/entities/project.entity';
// เรายังไม่มี CorrespondenceType Entity เดี๋ยวสร้าง Dummy ไว้ก่อน หรือข้าม Relation ไปก่อนได้ import { CorrespondenceType } from '../../correspondence/entities/correspondence-type.entity';
// แต่ตามหลักควรมี CorrespondenceType (Master Data)
@Entity('document_number_formats') @Entity('document_number_formats')
@Unique(['projectId', 'correspondenceTypeId']) // 1 Project + 1 Type มีได้แค่ 1 Format @Unique(['projectId', 'correspondenceTypeId'])
export class DocumentNumberFormat { export class DocumentNumberFormat {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id!: number; id: number;
@Column({ name: 'project_id' }) @Column({ name: 'project_id' })
projectId!: number; projectId: number;
@Column({ name: 'correspondence_type_id' }) @Column({ name: 'correspondence_type_id', nullable: true })
correspondenceTypeId!: number; correspondenceTypeId: number | null;
@Column({ name: 'format_template', length: 255 }) @Column({ name: 'format_template', length: 100 })
formatTemplate!: string; formatTemplate: string;
@Column({ name: 'discipline_id', default: 0 }) @Column({ name: 'description', nullable: true })
disciplineId!: number; description: string;
@Column({ name: 'example_number', length: 100, nullable: true }) // [NEW] Control yearly reset behavior
exampleNumber?: string; @Column({ name: 'reset_sequence_yearly', default: true })
resetSequenceYearly: boolean;
@Column({ name: 'padding_length', default: 4 }) @CreateDateColumn({ name: 'created_at' })
paddingLength!: number; createdAt: Date;
@Column({ name: 'reset_annually', default: true }) @UpdateDateColumn({ name: 'updated_at' })
resetAnnually!: boolean; updatedAt: Date;
@Column({ name: 'is_active', default: true }) // Relations
isActive!: boolean;
// Relation
@ManyToOne(() => Project) @ManyToOne(() => Project)
@JoinColumn({ name: 'project_id' }) @JoinColumn({ name: 'project_id' })
project?: Project; project: Project;
@ManyToOne(() => CorrespondenceType)
@JoinColumn({ name: 'correspondence_type_id' })
correspondenceType: CorrespondenceType | null;
} }

View File

@@ -1,332 +0,0 @@
> backend@1.5.1 test
> jest --forceExit
FAIL src/modules/project/project.controller.spec.ts
ΓùÅ Test suite failed to run
Cannot find module './project.service.js' from 'modules/project/project.controller.spec.ts'
1 | import { Controller, Get, UseGuards } from '@nestjs/common';
> 2 | import { ProjectService } from './project.service.js';
| ^
3 | import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard.js';
4 |
5 | @Controller('projects')
at Resolver._throwModNotFoundError (../node_modules/jest-resolve/build/index.js:863:11)
at Object.<anonymous> (modules/project/project.controller.spec.ts:2:1)
FAIL src/common/auth/auth.controller.spec.ts
ΓùÅ Test suite failed to run
Cannot find module './auth.service.js' from 'common/auth/auth.controller.spec.ts'
1 | import { Controller, Post, Body, UnauthorizedException } from '@nestjs/common';
> 2 | import { AuthService } from './auth.service.js';
| ^
3 | import { LoginDto } from './dto/login.dto.js'; // <--- Import DTO
4 | import { RegisterDto } from './dto/register.dto.js'; // <--- Import DTO
5 |
at Resolver._throwModNotFoundError (../node_modules/jest-resolve/build/index.js:863:11)
at Object.<anonymous> (common/auth/auth.controller.spec.ts:2:1)
PASS src/app.controller.spec.ts
[Nest] 12996 - 12/09/2025, 8:21:59 AM ERROR [WorkflowDslParser] Failed to parse stored DSL for definition 1
[Nest] 12996 - 12/09/2025, 8:21:59 AM ERROR [WorkflowDslParser] ZodError: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
at WorkflowDslParser.getParsedDsl (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.ts:163:32)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.spec.ts:178:22)
FAIL src/modules/workflow-engine/dsl/parser.service.spec.ts
● WorkflowDslParser › parse › should parse valid RFA workflow DSL
expect(received).toBe(expected) // Object.is equality
Expected: "RFA_APPROVAL"
Received: undefined
41 |
42 | expect(result).toBeDefined();
> 43 | expect(result.name).toBe('RFA_APPROVAL');
| ^
44 | expect(result.version).toBe('1.0.0');
45 | expect(result.isActive).toBe(true);
46 | expect(mockRepository.save).toHaveBeenCalled();
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:43:27)
● WorkflowDslParser › getParsedDsl › should retrieve and parse stored DSL
BadRequestException: Invalid stored DSL: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
167 | error
168 | );
> 169 | throw new BadRequestException(`Invalid stored DSL: ${error?.message}`);
| ^
170 | }
171 | }
172 |
at WorkflowDslParser.getParsedDsl (modules/workflow-engine/dsl/parser.service.ts:169:13)
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:178:22)
FAIL src/common/file-storage/file-storage.controller.spec.ts
ΓùÅ Test suite failed to run
Cannot find module './file-storage.service.js' from 'common/file-storage/file-storage.controller.ts'
Require stack:
common/file-storage/file-storage.controller.ts
common/file-storage/file-storage.controller.spec.ts
19 | import type { Response } from 'express';
20 | import { FileInterceptor } from '@nestjs/platform-express';
> 21 | import { FileStorageService } from './file-storage.service.js';
| ^
22 | import { JwtAuthGuard } from '../guards/jwt-auth.guard.js';
23 |
24 | // Interface เพื่อระบุ Type ของ Request ที่ผ่าน JwtAuthGuard มาแล้ว
at Resolver._throwModNotFoundError (../node_modules/jest-resolve/build/index.js:863:11)
at Object.<anonymous> (common/file-storage/file-storage.controller.ts:21:1)
at Object.<anonymous> (common/file-storage/file-storage.controller.spec.ts:2:1)
[Nest] 47932 - 12/09/2025, 8:21:59 AM ERROR [FileStorageService] Failed to write file: D:\nap-dms.lcbp3\backend\uploads\temp\52879b7a-b717-41b2-8b41-54bf707b187b.pdf
[Nest] 47932 - 12/09/2025, 8:21:59 AM ERROR [FileStorageService] Error: Write error
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\common\file-storage\file-storage.service.spec.ts:90:9)
at Promise.finally.completed (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1557:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1497:10)
at _callCircusTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1007:40)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at _runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:947:3)
at D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:849:7
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:862:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at run (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:761:3)
at runAndTransformResultsToJestFormat (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1918:21)
at jestAdapter (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\runner.js:101:19)
at runTestInternal (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:277:16)
at runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:345:7)
at Object.worker (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:499:12)
PASS src/common/file-storage/file-storage.service.spec.ts
PASS src/modules/user/user.service.spec.ts
PASS src/common/auth/casl/ability.factory.spec.ts
[Nest] 45332 - 12/09/2025, 8:21:59 AM ERROR [DocumentNumberingService] Error generating number for doc_num:1:1:1:2025
[Nest] 45332 - 12/09/2025, 8:21:59 AM ERROR [DocumentNumberingService] InternalServerErrorException: Failed to generate document number after retries.
at DocumentNumberingService.generateNextNumber (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.ts:182:13)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.spec.ts:175:7) {
response: {
message: 'Failed to generate document number after retries.',
error: 'Internal Server Error',
statusCode: 500
},
status: 500,
options: {}
}
[Nest] 33588 - 12/09/2025, 8:21:59 AM ERROR [WorkflowEngineService] Transition Failed for inst-1: DB Error
FAIL src/modules/document-numbering/document-numbering.service.spec.ts
● DocumentNumberingService › should be defined
TypeError: Cannot read properties of undefined (reading 'disconnect')
86 |
87 | onModuleDestroy() {
> 88 | this.redisClient.disconnect();
| ^
89 | }
90 |
91 | /**
at DocumentNumberingService.onModuleDestroy (modules/document-numbering/document-numbering.service.ts:88:22)
at Object.<anonymous> (modules/document-numbering/document-numbering.service.spec.ts:120:13)
● DocumentNumberingService › generateNextNumber › should generate a new number successfully
expect(received).toBe(expected) // Object.is equality
Expected: "000001"
Received: "0001"
146 | const result = await service.generateNextNumber(mockContext);
147 |
> 148 | expect(result).toBe('000001'); // Default padding 6
| ^
149 | expect(counterRepo.save).toHaveBeenCalled();
150 | expect(auditRepo.save).toHaveBeenCalled();
151 | });
at Object.<anonymous> (modules/document-numbering/document-numbering.service.spec.ts:148:22)
FAIL src/modules/project/project.service.spec.ts
● ProjectService › should be defined
Nest can't resolve dependencies of the ProjectService (?, OrganizationRepository). Please make sure that the argument "ProjectRepository" at index [0] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If "ProjectRepository" is a provider, is it part of the current RootTestModule?
- If "ProjectRepository" is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing "ProjectRepository" */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
6 |
7 | beforeEach(async () => {
> 8 | const module: TestingModule = await Test.createTestingModule({
| ^
9 | providers: [ProjectService],
10 | }).compile();
11 |
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 0)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadProvider (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:103:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:56:13
at async Promise.all (index 3)
at TestingInstanceLoader.createInstancesOfProviders (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:55:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:40:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/project/project.service.spec.ts:8:35)
PASS src/modules/workflow-engine/workflow-engine.service.spec.ts
FAIL src/common/auth/auth.service.spec.ts
ΓùÅ Test suite failed to run
Cannot find module '../../modules/user/user.service.js' from 'common/auth/auth.service.ts'
Require stack:
common/auth/auth.service.ts
common/auth/auth.service.spec.ts
20 | import * as crypto from 'crypto';
21 |
> 22 | import { UserService } from '../../modules/user/user.service.js';
| ^
23 | import { User } from '../../modules/user/entities/user.entity';
24 | import { RegisterDto } from './dto/register.dto.js';
25 | import { RefreshToken } from './entities/refresh-token.entity'; // [P2-2]
at Resolver._throwModNotFoundError (../node_modules/jest-resolve/build/index.js:863:11)
at Object.<anonymous> (common/auth/auth.service.ts:22:1)
at Object.<anonymous> (common/auth/auth.service.spec.ts:2:1)
PASS src/modules/json-schema/json-schema.controller.spec.ts
FAIL src/modules/correspondence/correspondence.service.spec.ts
● CorrespondenceService › should be defined
Nest can't resolve dependencies of the CorrespondenceService (?, CorrespondenceRevisionRepository, CorrespondenceTypeRepository, CorrespondenceStatusRepository, RoutingTemplateRepository, CorrespondenceRoutingRepository, CorrespondenceReferenceRepository, DocumentNumberingService, JsonSchemaService, WorkflowEngineService, UserService, DataSource, SearchService). Please make sure that the argument "CorrespondenceRepository" at index [0] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If "CorrespondenceRepository" is a provider, is it part of the current RootTestModule?
- If "CorrespondenceRepository" is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing "CorrespondenceRepository" */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
6 |
7 | beforeEach(async () => {
> 8 | const module: TestingModule = await Test.createTestingModule({
| ^
9 | providers: [CorrespondenceService],
10 | }).compile();
11 |
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 0)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadProvider (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:103:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:56:13
at async Promise.all (index 3)
at TestingInstanceLoader.createInstancesOfProviders (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:55:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:40:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/correspondence/correspondence.service.spec.ts:8:35)
FAIL src/modules/correspondence/correspondence.controller.spec.ts
● CorrespondenceController › should be defined
Nest can't resolve dependencies of the RbacGuard (Reflector, ?). Please make sure that the argument UserService at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If UserService is a provider, is it part of the current RootTestModule?
- If UserService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing UserService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
7 |
8 | beforeEach(async () => {
> 9 | const module: TestingModule = await Test.createTestingModule({
| ^
10 | controllers: [CorrespondenceController],
11 | providers: [
12 | {
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadInjectable (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:99:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:80:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstancesOfInjectables (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:79:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:41:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/correspondence/correspondence.controller.spec.ts:9:35)
Test Suites: 9 failed, 6 passed, 15 total
Tests: 7 failed, 37 passed, 44 total
Snapshots: 0 total
Time: 5.054 s
Ran all test suites.
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?

View File

@@ -1,461 +0,0 @@
> backend@1.5.1 test
> jest --forceExit
FAIL src/modules/project/project.controller.spec.ts
ΓùÅ Test suite failed to run
Cannot find module './project.service.js' from 'modules/project/project.controller.spec.ts'
1 | import { Controller, Get, UseGuards } from '@nestjs/common';
> 2 | import { ProjectService } from './project.service.js';
| ^
3 | import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard.js';
4 |
5 | @Controller('projects')
at Resolver._throwModNotFoundError (../node_modules/jest-resolve/build/index.js:863:11)
at Object.<anonymous> (modules/project/project.controller.spec.ts:2:1)
FAIL src/common/auth/auth.controller.spec.ts
ΓùÅ Test suite failed to run
Cannot find module './auth.service.js' from 'common/auth/auth.controller.spec.ts'
1 | import { Controller, Post, Body, UnauthorizedException } from '@nestjs/common';
> 2 | import { AuthService } from './auth.service.js';
| ^
3 | import { LoginDto } from './dto/login.dto.js'; // <--- Import DTO
4 | import { RegisterDto } from './dto/register.dto.js'; // <--- Import DTO
5 |
at Resolver._throwModNotFoundError (../node_modules/jest-resolve/build/index.js:863:11)
at Object.<anonymous> (common/auth/auth.controller.spec.ts:2:1)
PASS src/app.controller.spec.ts
[Nest] 15476 - 12/09/2025, 8:24:46 AM ERROR [WorkflowEngineService] Transition Failed for inst-1: DB Error
PASS src/modules/workflow-engine/workflow-engine.service.spec.ts
PASS src/common/auth/casl/ability.factory.spec.ts
FAIL src/modules/project/project.service.spec.ts
● ProjectService › should be defined
Nest can't resolve dependencies of the ProjectService (?, OrganizationRepository). Please make sure that the argument "ProjectRepository" at index [0] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If "ProjectRepository" is a provider, is it part of the current RootTestModule?
- If "ProjectRepository" is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing "ProjectRepository" */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
6 |
7 | beforeEach(async () => {
> 8 | const module: TestingModule = await Test.createTestingModule({
| ^
9 | providers: [ProjectService],
10 | }).compile();
11 |
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 0)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadProvider (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:103:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:56:13
at async Promise.all (index 3)
at TestingInstanceLoader.createInstancesOfProviders (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:55:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:40:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/project/project.service.spec.ts:8:35)
PASS src/modules/user/user.service.spec.ts
[Nest] 11892 - 12/09/2025, 8:24:47 AM ERROR [DocumentNumberingService] Error generating number for doc_num:1:1:1:2025
[Nest] 11892 - 12/09/2025, 8:24:47 AM ERROR [DocumentNumberingService] InternalServerErrorException: Failed to generate document number after retries.
at DocumentNumberingService.generateNextNumber (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.ts:182:13)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.spec.ts:175:7) {
response: {
message: 'Failed to generate document number after retries.',
error: 'Internal Server Error',
statusCode: 500
},
status: 500,
options: {}
}
FAIL src/modules/document-numbering/document-numbering.service.spec.ts
● DocumentNumberingService › should be defined
TypeError: Cannot read properties of undefined (reading 'disconnect')
86 |
87 | onModuleDestroy() {
> 88 | this.redisClient.disconnect();
| ^
89 | }
90 |
91 | /**
at DocumentNumberingService.onModuleDestroy (modules/document-numbering/document-numbering.service.ts:88:22)
at Object.<anonymous> (modules/document-numbering/document-numbering.service.spec.ts:120:13)
● DocumentNumberingService › generateNextNumber › should generate a new number successfully
expect(received).toBe(expected) // Object.is equality
Expected: "000001"
Received: "0001"
146 | const result = await service.generateNextNumber(mockContext);
147 |
> 148 | expect(result).toBe('000001'); // Default padding 6
| ^
149 | expect(counterRepo.save).toHaveBeenCalled();
150 | expect(auditRepo.save).toHaveBeenCalled();
151 | });
at Object.<anonymous> (modules/document-numbering/document-numbering.service.spec.ts:148:22)
[Nest] 25292 - 12/09/2025, 8:24:47 AM ERROR [WorkflowDslParser] Failed to parse stored DSL for definition 1
[Nest] 25292 - 12/09/2025, 8:24:47 AM ERROR [WorkflowDslParser] ZodError: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
at WorkflowDslParser.getParsedDsl (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.ts:163:32)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.spec.ts:178:22)
FAIL src/modules/workflow-engine/dsl/parser.service.spec.ts
● WorkflowDslParser › parse › should parse valid RFA workflow DSL
expect(received).toBe(expected) // Object.is equality
Expected: "RFA_APPROVAL"
Received: undefined
41 |
42 | expect(result).toBeDefined();
> 43 | expect(result.name).toBe('RFA_APPROVAL');
| ^
44 | expect(result.version).toBe('1.0.0');
45 | expect(result.isActive).toBe(true);
46 | expect(mockRepository.save).toHaveBeenCalled();
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:43:27)
● WorkflowDslParser › getParsedDsl › should retrieve and parse stored DSL
BadRequestException: Invalid stored DSL: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
167 | error
168 | );
> 169 | throw new BadRequestException(`Invalid stored DSL: ${error?.message}`);
| ^
170 | }
171 | }
172 |
at WorkflowDslParser.getParsedDsl (modules/workflow-engine/dsl/parser.service.ts:169:13)
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:178:22)
[Nest] 23608 - 12/09/2025, 8:24:47 AM ERROR [FileStorageService] Failed to write file: D:\nap-dms.lcbp3\backend\uploads\temp\96ed1798-25e1-45c8-8a5c-9875978ce586.pdf
[Nest] 23608 - 12/09/2025, 8:24:47 AM ERROR [FileStorageService] Error: Write error
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\common\file-storage\file-storage.service.spec.ts:90:9)
at Promise.finally.completed (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1557:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1497:10)
at _callCircusTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1007:40)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at _runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:947:3)
at D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:849:7
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:862:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at run (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:761:3)
at runAndTransformResultsToJestFormat (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1918:21)
at jestAdapter (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\runner.js:101:19)
at runTestInternal (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:277:16)
at runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:345:7)
at Object.worker (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:499:12)
PASS src/common/file-storage/file-storage.service.spec.ts
FAIL src/common/auth/auth.service.spec.ts
● AuthService › should be defined
TypeError: Cannot redefine property: compare
at Function.defineProperty (<anonymous>)
94 | // Mock bcrypt
95 | jest
> 96 | .spyOn(bcrypt, 'compare')
| ^
97 | .mockImplementation(() => Promise.resolve(true));
98 | jest
99 | .spyOn(bcrypt, 'hash')
at ModuleMocker.spyOn (../node_modules/jest-mock/build/index.js:616:16)
at Object.<anonymous> (common/auth/auth.service.spec.ts:96:8)
● AuthService › validateUser › should return user without password if validation succeeds
TypeError: Cannot redefine property: compare
at Function.defineProperty (<anonymous>)
94 | // Mock bcrypt
95 | jest
> 96 | .spyOn(bcrypt, 'compare')
| ^
97 | .mockImplementation(() => Promise.resolve(true));
98 | jest
99 | .spyOn(bcrypt, 'hash')
at ModuleMocker.spyOn (../node_modules/jest-mock/build/index.js:616:16)
at Object.<anonymous> (common/auth/auth.service.spec.ts:96:8)
● AuthService › validateUser › should return null if user not found
TypeError: Cannot redefine property: compare
at Function.defineProperty (<anonymous>)
94 | // Mock bcrypt
95 | jest
> 96 | .spyOn(bcrypt, 'compare')
| ^
97 | .mockImplementation(() => Promise.resolve(true));
98 | jest
99 | .spyOn(bcrypt, 'hash')
at ModuleMocker.spyOn (../node_modules/jest-mock/build/index.js:616:16)
at Object.<anonymous> (common/auth/auth.service.spec.ts:96:8)
● AuthService › validateUser › should return null if password mismatch
TypeError: Cannot redefine property: compare
at Function.defineProperty (<anonymous>)
94 | // Mock bcrypt
95 | jest
> 96 | .spyOn(bcrypt, 'compare')
| ^
97 | .mockImplementation(() => Promise.resolve(true));
98 | jest
99 | .spyOn(bcrypt, 'hash')
at ModuleMocker.spyOn (../node_modules/jest-mock/build/index.js:616:16)
at Object.<anonymous> (common/auth/auth.service.spec.ts:96:8)
● AuthService › login › should return access and refresh tokens
TypeError: Cannot redefine property: compare
at Function.defineProperty (<anonymous>)
94 | // Mock bcrypt
95 | jest
> 96 | .spyOn(bcrypt, 'compare')
| ^
97 | .mockImplementation(() => Promise.resolve(true));
98 | jest
99 | .spyOn(bcrypt, 'hash')
at ModuleMocker.spyOn (../node_modules/jest-mock/build/index.js:616:16)
at Object.<anonymous> (common/auth/auth.service.spec.ts:96:8)
● AuthService › register › should register a new user
TypeError: Cannot redefine property: compare
at Function.defineProperty (<anonymous>)
94 | // Mock bcrypt
95 | jest
> 96 | .spyOn(bcrypt, 'compare')
| ^
97 | .mockImplementation(() => Promise.resolve(true));
98 | jest
99 | .spyOn(bcrypt, 'hash')
at ModuleMocker.spyOn (../node_modules/jest-mock/build/index.js:616:16)
at Object.<anonymous> (common/auth/auth.service.spec.ts:96:8)
● AuthService › refreshToken › should return new tokens if valid
TypeError: Cannot redefine property: compare
at Function.defineProperty (<anonymous>)
94 | // Mock bcrypt
95 | jest
> 96 | .spyOn(bcrypt, 'compare')
| ^
97 | .mockImplementation(() => Promise.resolve(true));
98 | jest
99 | .spyOn(bcrypt, 'hash')
at ModuleMocker.spyOn (../node_modules/jest-mock/build/index.js:616:16)
at Object.<anonymous> (common/auth/auth.service.spec.ts:96:8)
● AuthService › refreshToken › should throw UnauthorizedException if token revoked
TypeError: Cannot redefine property: compare
at Function.defineProperty (<anonymous>)
94 | // Mock bcrypt
95 | jest
> 96 | .spyOn(bcrypt, 'compare')
| ^
97 | .mockImplementation(() => Promise.resolve(true));
98 | jest
99 | .spyOn(bcrypt, 'hash')
at ModuleMocker.spyOn (../node_modules/jest-mock/build/index.js:616:16)
at Object.<anonymous> (common/auth/auth.service.spec.ts:96:8)
FAIL src/modules/correspondence/correspondence.service.spec.ts
● CorrespondenceService › should be defined
Nest can't resolve dependencies of the CorrespondenceService (?, CorrespondenceRevisionRepository, CorrespondenceTypeRepository, CorrespondenceStatusRepository, RoutingTemplateRepository, CorrespondenceRoutingRepository, CorrespondenceReferenceRepository, DocumentNumberingService, JsonSchemaService, WorkflowEngineService, UserService, DataSource, SearchService). Please make sure that the argument "CorrespondenceRepository" at index [0] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If "CorrespondenceRepository" is a provider, is it part of the current RootTestModule?
- If "CorrespondenceRepository" is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing "CorrespondenceRepository" */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
6 |
7 | beforeEach(async () => {
> 8 | const module: TestingModule = await Test.createTestingModule({
| ^
9 | providers: [CorrespondenceService],
10 | }).compile();
11 |
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 0)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadProvider (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:103:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:56:13
at async Promise.all (index 3)
at TestingInstanceLoader.createInstancesOfProviders (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:55:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:40:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/correspondence/correspondence.service.spec.ts:8:35)
FAIL src/common/file-storage/file-storage.controller.spec.ts
● FileStorageController › should be defined
Nest can't resolve dependencies of the FileStorageController (?). Please make sure that the argument FileStorageService at index [0] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If FileStorageService is a provider, is it part of the current RootTestModule?
- If FileStorageService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing FileStorageService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
6 |
7 | beforeEach(async () => {
> 8 | const module: TestingModule = await Test.createTestingModule({
| ^
9 | controllers: [FileStorageController],
10 | }).compile();
11 |
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 0)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadController (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:94:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:68:13
at async Promise.all (index 0)
at TestingInstanceLoader.createInstancesOfControllers (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:67:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:42:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (common/file-storage/file-storage.controller.spec.ts:8:35)
PASS src/modules/json-schema/json-schema.controller.spec.ts
FAIL src/modules/correspondence/correspondence.controller.spec.ts
● CorrespondenceController › should be defined
Nest can't resolve dependencies of the RbacGuard (Reflector, ?). Please make sure that the argument UserService at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If UserService is a provider, is it part of the current RootTestModule?
- If UserService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing UserService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
7 |
8 | beforeEach(async () => {
> 9 | const module: TestingModule = await Test.createTestingModule({
| ^
10 | controllers: [CorrespondenceController],
11 | providers: [
12 | {
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadInjectable (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:99:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:80:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstancesOfInjectables (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:79:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:41:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/correspondence/correspondence.controller.spec.ts:9:35)
Test Suites: 9 failed, 6 passed, 15 total
Tests: 16 failed, 37 passed, 53 total
Snapshots: 0 total
Time: 5.448 s
Ran all test suites.
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?

View File

@@ -1,440 +0,0 @@
> backend@1.5.1 test
> jest --forceExit
PASS src/app.controller.spec.ts
[Nest] 18060 - 12/09/2025, 8:27:42 AM ERROR [DocumentNumberingService] Error generating number for doc_num:1:1:1:2025
[Nest] 18060 - 12/09/2025, 8:27:43 AM ERROR [DocumentNumberingService] InternalServerErrorException: Failed to generate document number after retries.
at DocumentNumberingService.generateNextNumber (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.ts:182:13)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.spec.ts:175:7) {
response: {
message: 'Failed to generate document number after retries.',
error: 'Internal Server Error',
statusCode: 500
},
status: 500,
options: {}
}
FAIL src/modules/document-numbering/document-numbering.service.spec.ts
● DocumentNumberingService › should be defined
TypeError: Cannot read properties of undefined (reading 'disconnect')
86 |
87 | onModuleDestroy() {
> 88 | this.redisClient.disconnect();
| ^
89 | }
90 |
91 | /**
at DocumentNumberingService.onModuleDestroy (modules/document-numbering/document-numbering.service.ts:88:22)
at Object.<anonymous> (modules/document-numbering/document-numbering.service.spec.ts:120:13)
● DocumentNumberingService › generateNextNumber › should generate a new number successfully
expect(received).toBe(expected) // Object.is equality
Expected: "000001"
Received: "0001"
146 | const result = await service.generateNextNumber(mockContext);
147 |
> 148 | expect(result).toBe('000001'); // Default padding 6
| ^
149 | expect(counterRepo.save).toHaveBeenCalled();
150 | expect(auditRepo.save).toHaveBeenCalled();
151 | });
at Object.<anonymous> (modules/document-numbering/document-numbering.service.spec.ts:148:22)
[Nest] 14304 - 12/09/2025, 8:27:43 AM ERROR [WorkflowEngineService] Transition Failed for inst-1: DB Error
PASS src/modules/workflow-engine/workflow-engine.service.spec.ts
[Nest] 15080 - 12/09/2025, 8:27:43 AM ERROR [WorkflowDslParser] Failed to parse stored DSL for definition 1
[Nest] 15080 - 12/09/2025, 8:27:43 AM ERROR [WorkflowDslParser] ZodError: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
at WorkflowDslParser.getParsedDsl (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.ts:163:32)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.spec.ts:178:22)
[Nest] 32376 - 12/09/2025, 8:27:43 AM ERROR [FileStorageService] Failed to write file: D:\nap-dms.lcbp3\backend\uploads\temp\8d470748-51dd-4d41-8b23-4c597fac61ae.pdf
PASS src/common/auth/casl/ability.factory.spec.ts
[Nest] 32376 - 12/09/2025, 8:27:43 AM ERROR [FileStorageService] Error: Write error
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\common\file-storage\file-storage.service.spec.ts:90:9)
at Promise.finally.completed (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1557:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1497:10)
at _callCircusTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1007:40)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at _runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:947:3)
at D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:849:7
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:862:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at run (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:761:3)
at runAndTransformResultsToJestFormat (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1918:21)
at jestAdapter (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\runner.js:101:19)
at runTestInternal (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:277:16)
at runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:345:7)
at Object.worker (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:499:12)
PASS src/common/file-storage/file-storage.service.spec.ts
PASS src/modules/user/user.service.spec.ts
FAIL src/modules/workflow-engine/dsl/parser.service.spec.ts
● WorkflowDslParser › parse › should parse valid RFA workflow DSL
expect(received).toBe(expected) // Object.is equality
Expected: "RFA_APPROVAL"
Received: undefined
41 |
42 | expect(result).toBeDefined();
> 43 | expect(result.name).toBe('RFA_APPROVAL');
| ^
44 | expect(result.version).toBe('1.0.0');
45 | expect(result.isActive).toBe(true);
46 | expect(mockRepository.save).toHaveBeenCalled();
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:43:27)
● WorkflowDslParser › getParsedDsl › should retrieve and parse stored DSL
BadRequestException: Invalid stored DSL: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
167 | error
168 | );
> 169 | throw new BadRequestException(`Invalid stored DSL: ${error?.message}`);
| ^
170 | }
171 | }
172 |
at WorkflowDslParser.getParsedDsl (modules/workflow-engine/dsl/parser.service.ts:169:13)
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:178:22)
FAIL src/modules/project/project.service.spec.ts
ΓùÅ Test suite failed to run
Cannot find module '../organization/entities/organization.entity' from 'modules/project/project.service.spec.ts'
3 | import { ProjectService } from './project.service';
4 | import { Project } from './entities/project.entity';
> 5 | import { Organization } from '../organization/entities/organization.entity';
| ^
6 |
7 | describe('ProjectService', () => {
8 | let service: ProjectService;
at Resolver._throwModNotFoundError (../node_modules/jest-resolve/build/index.js:863:11)
at Object.<anonymous> (modules/project/project.service.spec.ts:5:1)
PASS src/common/auth/auth.service.spec.ts
ΓùÅ Console
console.log
🔍 Checking login for: testuser
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
console.log
🔍 Checking login for: unknown
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
console.log
❌ User not found in database
at AuthService.validateUser (common/auth/auth.service.ts:51:15)
console.log
🔍 Checking login for: testuser
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
PASS src/common/file-storage/file-storage.controller.spec.ts
FAIL src/modules/correspondence/correspondence.service.spec.ts (5.059 s)
● CorrespondenceService › findAll › should return paginated correspondences
expect(received).toBeDefined()
Received: undefined
119 | it('should return paginated correspondences', async () => {
120 | const result = await service.findAll({ projectId: 1 });
> 121 | expect(result.data).toBeDefined();
| ^
122 | expect(result.meta).toBeDefined();
123 | });
124 | });
at Object.<anonymous> (modules/correspondence/correspondence.service.spec.ts:121:27)
PASS src/common/auth/auth.controller.spec.ts (5.065 s)
PASS src/modules/json-schema/json-schema.controller.spec.ts
FAIL src/modules/project/project.controller.spec.ts (5.155 s)
● ProjectController › should be defined
Nest can't resolve dependencies of the RbacGuard (Reflector, ?). Please make sure that the argument UserService at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If UserService is a provider, is it part of the current RootTestModule?
- If UserService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing UserService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
17 | };
18 |
> 19 | const module: TestingModule = await Test.createTestingModule({
| ^
20 | controllers: [ProjectController],
21 | providers: [
22 | {
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadInjectable (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:99:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:80:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstancesOfInjectables (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:79:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:41:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/project/project.controller.spec.ts:19:35)
● ProjectController › findAll › should call projectService.findAll
Nest can't resolve dependencies of the RbacGuard (Reflector, ?). Please make sure that the argument UserService at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If UserService is a provider, is it part of the current RootTestModule?
- If UserService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing UserService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
17 | };
18 |
> 19 | const module: TestingModule = await Test.createTestingModule({
| ^
20 | controllers: [ProjectController],
21 | providers: [
22 | {
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadInjectable (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:99:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:80:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstancesOfInjectables (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:79:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:41:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/project/project.controller.spec.ts:19:35)
● ProjectController › findAllOrganizations › should call projectService.findAllOrganizations
Nest can't resolve dependencies of the RbacGuard (Reflector, ?). Please make sure that the argument UserService at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If UserService is a provider, is it part of the current RootTestModule?
- If UserService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing UserService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
17 | };
18 |
> 19 | const module: TestingModule = await Test.createTestingModule({
| ^
20 | controllers: [ProjectController],
21 | providers: [
22 | {
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadInjectable (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:99:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:80:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstancesOfInjectables (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:79:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:41:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/project/project.controller.spec.ts:19:35)
FAIL src/modules/correspondence/correspondence.controller.spec.ts (5.56 s)
● CorrespondenceController › should be defined
Nest can't resolve dependencies of the RbacGuard (Reflector, ?). Please make sure that the argument UserService at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If UserService is a provider, is it part of the current RootTestModule?
- If UserService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing UserService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
21 | };
22 |
> 23 | const module: TestingModule = await Test.createTestingModule({
| ^
24 | controllers: [CorrespondenceController],
25 | providers: [
26 | {
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadInjectable (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:99:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:80:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstancesOfInjectables (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:79:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:41:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/correspondence/correspondence.controller.spec.ts:23:35)
● CorrespondenceController › findAll › should return paginated correspondences
Nest can't resolve dependencies of the RbacGuard (Reflector, ?). Please make sure that the argument UserService at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If UserService is a provider, is it part of the current RootTestModule?
- If UserService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing UserService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
21 | };
22 |
> 23 | const module: TestingModule = await Test.createTestingModule({
| ^
24 | controllers: [CorrespondenceController],
25 | providers: [
26 | {
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadInjectable (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:99:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:80:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstancesOfInjectables (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:79:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:41:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/correspondence/correspondence.controller.spec.ts:23:35)
● CorrespondenceController › create › should create a correspondence
Nest can't resolve dependencies of the RbacGuard (Reflector, ?). Please make sure that the argument UserService at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If UserService is a provider, is it part of the current RootTestModule?
- If UserService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing UserService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
21 | };
22 |
> 23 | const module: TestingModule = await Test.createTestingModule({
| ^
24 | controllers: [CorrespondenceController],
25 | providers: [
26 | {
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadInjectable (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:99:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:80:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstancesOfInjectables (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:79:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:41:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/correspondence/correspondence.controller.spec.ts:23:35)
Test Suites: 6 failed, 9 passed, 15 total
Tests: 11 failed, 52 passed, 63 total
Snapshots: 0 total
Time: 6.881 s
Ran all test suites.
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?

View File

@@ -1,202 +0,0 @@
> backend@1.5.1 test
> jest --forceExit
PASS src/app.controller.spec.ts
PASS src/modules/user/user.service.spec.ts
[Nest] 3520 - 12/09/2025, 8:29:38 AM ERROR [FileStorageService] Failed to write file: D:\nap-dms.lcbp3\backend\uploads\temp\0db75d72-efc1-4d36-a739-6fdeccb9f53a.pdf
[Nest] 3520 - 12/09/2025, 8:29:38 AM ERROR [FileStorageService] Error: Write error
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\common\file-storage\file-storage.service.spec.ts:90:9)
at Promise.finally.completed (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1557:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1497:10)
at _callCircusTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1007:40)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at _runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:947:3)
at D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:849:7
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:862:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at run (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:761:3)
at runAndTransformResultsToJestFormat (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1918:21)
at jestAdapter (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\runner.js:101:19)
at runTestInternal (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:277:16)
at runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:345:7)
at Object.worker (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:499:12)
PASS src/common/file-storage/file-storage.service.spec.ts
[Nest] 38888 - 12/09/2025, 8:29:38 AM ERROR [DocumentNumberingService] Error generating number for doc_num:1:1:1:2025
[Nest] 38888 - 12/09/2025, 8:29:38 AM ERROR [DocumentNumberingService] InternalServerErrorException: Failed to generate document number after retries.
at DocumentNumberingService.generateNextNumber (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.ts:182:13)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.spec.ts:175:7) {
response: {
message: 'Failed to generate document number after retries.',
error: 'Internal Server Error',
statusCode: 500
},
status: 500,
options: {}
}
[Nest] 16508 - 12/09/2025, 8:29:38 AM ERROR [WorkflowEngineService] Transition Failed for inst-1: DB Error
PASS src/modules/workflow-engine/workflow-engine.service.spec.ts
FAIL src/modules/document-numbering/document-numbering.service.spec.ts
● DocumentNumberingService › should be defined
TypeError: Cannot read properties of undefined (reading 'disconnect')
86 |
87 | onModuleDestroy() {
> 88 | this.redisClient.disconnect();
| ^
89 | }
90 |
91 | /**
at DocumentNumberingService.onModuleDestroy (modules/document-numbering/document-numbering.service.ts:88:22)
at Object.<anonymous> (modules/document-numbering/document-numbering.service.spec.ts:120:13)
● DocumentNumberingService › generateNextNumber › should generate a new number successfully
expect(received).toBe(expected) // Object.is equality
Expected: "000001"
Received: "0001"
146 | const result = await service.generateNextNumber(mockContext);
147 |
> 148 | expect(result).toBe('000001'); // Default padding 6
| ^
149 | expect(counterRepo.save).toHaveBeenCalled();
150 | expect(auditRepo.save).toHaveBeenCalled();
151 | });
at Object.<anonymous> (modules/document-numbering/document-numbering.service.spec.ts:148:22)
PASS src/common/auth/casl/ability.factory.spec.ts
PASS src/modules/project/project.service.spec.ts
[Nest] 16436 - 12/09/2025, 8:29:39 AM ERROR [WorkflowDslParser] Failed to parse stored DSL for definition 1
[Nest] 16436 - 12/09/2025, 8:29:39 AM ERROR [WorkflowDslParser] ZodError: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
at WorkflowDslParser.getParsedDsl (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.ts:163:32)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.spec.ts:178:22)
PASS src/common/auth/auth.service.spec.ts
ΓùÅ Console
console.log
🔍 Checking login for: testuser
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
console.log
🔍 Checking login for: unknown
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
console.log
❌ User not found in database
at AuthService.validateUser (common/auth/auth.service.ts:51:15)
console.log
🔍 Checking login for: testuser
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
FAIL src/modules/workflow-engine/dsl/parser.service.spec.ts
● WorkflowDslParser › parse › should parse valid RFA workflow DSL
expect(received).toBe(expected) // Object.is equality
Expected: "RFA_APPROVAL"
Received: undefined
41 |
42 | expect(result).toBeDefined();
> 43 | expect(result.name).toBe('RFA_APPROVAL');
| ^
44 | expect(result.version).toBe('1.0.0');
45 | expect(result.isActive).toBe(true);
46 | expect(mockRepository.save).toHaveBeenCalled();
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:43:27)
● WorkflowDslParser › getParsedDsl › should retrieve and parse stored DSL
BadRequestException: Invalid stored DSL: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
167 | error
168 | );
> 169 | throw new BadRequestException(`Invalid stored DSL: ${error?.message}`);
| ^
170 | }
171 | }
172 |
at WorkflowDslParser.getParsedDsl (modules/workflow-engine/dsl/parser.service.ts:169:13)
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:178:22)
PASS src/common/file-storage/file-storage.controller.spec.ts
PASS src/modules/project/project.controller.spec.ts
PASS src/modules/json-schema/json-schema.controller.spec.ts
PASS src/common/auth/auth.controller.spec.ts
FAIL src/modules/correspondence/correspondence.service.spec.ts
● CorrespondenceService › findAll › should return paginated correspondences
expect(received).toBeDefined()
Received: undefined
119 | it('should return paginated correspondences', async () => {
120 | const result = await service.findAll({ projectId: 1 });
> 121 | expect(result.data).toBeDefined();
| ^
122 | expect(result.meta).toBeDefined();
123 | });
124 | });
at Object.<anonymous> (modules/correspondence/correspondence.service.spec.ts:121:27)
FAIL src/modules/correspondence/correspondence.controller.spec.ts (5.39 s)
● CorrespondenceController › create › should create a correspondence
expect(jest.fn()).toHaveBeenCalledWith(...expected)
- Expected
+ Received
{"correspondence_type_id": 1, "project_id": 1, "subject": "Test Subject"},
- 1,
+ {"userId": 1},
Number of calls: 1
76 | const result = await controller.create(createDto as any, mockReq as any);
77 |
> 78 | expect(mockCorrespondenceService.create).toHaveBeenCalledWith(
| ^
79 | createDto,
80 | 1
81 | );
at Object.<anonymous> (modules/correspondence/correspondence.controller.spec.ts:78:48)
Test Suites: 4 failed, 11 passed, 15 total
Tests: 6 failed, 60 passed, 66 total
Snapshots: 0 total
Time: 6.662 s
Ran all test suites.
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?

View File

@@ -1,148 +0,0 @@
> backend@1.5.1 test
> jest --forceExit
PASS src/app.controller.spec.ts
[Nest] 2908 - 12/09/2025, 8:31:31 AM ERROR [WorkflowDslParser] Failed to parse stored DSL for definition 1
[Nest] 2908 - 12/09/2025, 8:31:31 AM ERROR [WorkflowDslParser] ZodError: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
at WorkflowDslParser.getParsedDsl (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.ts:163:32)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.spec.ts:178:22)
FAIL src/modules/workflow-engine/dsl/parser.service.spec.ts
● WorkflowDslParser › parse › should parse valid RFA workflow DSL
expect(received).toBe(expected) // Object.is equality
Expected: "RFA_APPROVAL"
Received: undefined
41 |
42 | expect(result).toBeDefined();
> 43 | expect(result.name).toBe('RFA_APPROVAL');
| ^
44 | expect(result.version).toBe('1.0.0');
45 | expect(result.isActive).toBe(true);
46 | expect(mockRepository.save).toHaveBeenCalled();
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:43:27)
● WorkflowDslParser › getParsedDsl › should retrieve and parse stored DSL
BadRequestException: Invalid stored DSL: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
167 | error
168 | );
> 169 | throw new BadRequestException(`Invalid stored DSL: ${error?.message}`);
| ^
170 | }
171 | }
172 |
at WorkflowDslParser.getParsedDsl (modules/workflow-engine/dsl/parser.service.ts:169:13)
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:178:22)
PASS src/common/auth/casl/ability.factory.spec.ts
PASS src/common/auth/auth.service.spec.ts
ΓùÅ Console
console.log
🔍 Checking login for: testuser
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
console.log
🔍 Checking login for: unknown
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
console.log
❌ User not found in database
at AuthService.validateUser (common/auth/auth.service.ts:51:15)
console.log
🔍 Checking login for: testuser
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
PASS src/modules/user/user.service.spec.ts
[Nest] 8380 - 12/09/2025, 8:31:32 AM ERROR [WorkflowEngineService] Transition Failed for inst-1: DB Error
PASS src/modules/workflow-engine/workflow-engine.service.spec.ts
PASS src/modules/project/project.service.spec.ts
[Nest] 7492 - 12/09/2025, 8:31:32 AM ERROR [FileStorageService] Failed to write file: D:\nap-dms.lcbp3\backend\uploads\temp\d95da96c-c9b6-4b11-9d67-9f4e19c76b8b.pdf
[Nest] 7492 - 12/09/2025, 8:31:32 AM ERROR [FileStorageService] Error: Write error
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\common\file-storage\file-storage.service.spec.ts:90:9)
at Promise.finally.completed (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1557:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1497:10)
at _callCircusTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1007:40)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at _runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:947:3)
at D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:849:7
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:862:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at run (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:761:3)
at runAndTransformResultsToJestFormat (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1918:21)
at jestAdapter (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\runner.js:101:19)
at runTestInternal (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:277:16)
at runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:345:7)
at Object.worker (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:499:12)
PASS src/modules/json-schema/json-schema.controller.spec.ts
[Nest] 45732 - 12/09/2025, 8:31:32 AM ERROR [DocumentNumberingService] Error generating number for doc_num:1:1:1:2025
PASS src/common/file-storage/file-storage.service.spec.ts
[Nest] 45732 - 12/09/2025, 8:31:32 AM ERROR [DocumentNumberingService] InternalServerErrorException: Failed to generate document number after retries.
at DocumentNumberingService.generateNextNumber (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.ts:182:13)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.spec.ts:175:7) {
response: {
message: 'Failed to generate document number after retries.',
error: 'Internal Server Error',
statusCode: 500
},
status: 500,
options: {}
}
PASS src/modules/document-numbering/document-numbering.service.spec.ts
PASS src/modules/project/project.controller.spec.ts
PASS src/common/file-storage/file-storage.controller.spec.ts
FAIL src/modules/correspondence/correspondence.service.spec.ts
● CorrespondenceService › findAll › should return paginated correspondences
expect(received).toBeDefined()
Received: undefined
119 | it('should return paginated correspondences', async () => {
120 | const result = await service.findAll({ projectId: 1 });
> 121 | expect(result.data).toBeDefined();
| ^
122 | expect(result.meta).toBeDefined();
123 | });
124 | });
at Object.<anonymous> (modules/correspondence/correspondence.service.spec.ts:121:27)
PASS src/common/auth/auth.controller.spec.ts
PASS src/modules/correspondence/correspondence.controller.spec.ts (5.12 s)
Test Suites: 2 failed, 13 passed, 15 total
Tests: 3 failed, 63 passed, 66 total
Snapshots: 0 total
Time: 6.447 s
Ran all test suites.
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?

View File

@@ -1,270 +0,0 @@
> backend@1.5.1 test
> jest --forceExit
PASS src/app.controller.spec.ts
PASS src/common/auth/casl/ability.factory.spec.ts
PASS src/modules/user/user.service.spec.ts
PASS src/modules/project/project.service.spec.ts
[Nest] 29008 - 12/09/2025, 9:27:51 AM ERROR [WorkflowEngineService] Transition Failed for inst-1: DB Error
PASS src/modules/workflow-engine/workflow-engine.service.spec.ts
[Nest] 38744 - 12/09/2025, 9:27:51 AM ERROR [WorkflowDslParser] Failed to parse stored DSL for definition 1
[Nest] 38744 - 12/09/2025, 9:27:51 AM ERROR [WorkflowDslParser] ZodError: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
at WorkflowDslParser.getParsedDsl (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.ts:163:32)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.spec.ts:178:22)
FAIL src/modules/workflow-engine/dsl/parser.service.spec.ts
● WorkflowDslParser › parse › should parse valid RFA workflow DSL
expect(received).toBe(expected) // Object.is equality
Expected: "RFA_APPROVAL"
Received: undefined
41 |
42 | expect(result).toBeDefined();
> 43 | expect(result.name).toBe('RFA_APPROVAL');
| ^
44 | expect(result.version).toBe('1.0.0');
45 | expect(result.isActive).toBe(true);
46 | expect(mockRepository.save).toHaveBeenCalled();
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:43:27)
● WorkflowDslParser › getParsedDsl › should retrieve and parse stored DSL
BadRequestException: Invalid stored DSL: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
167 | error
168 | );
> 169 | throw new BadRequestException(`Invalid stored DSL: ${error?.message}`);
| ^
170 | }
171 | }
172 |
at WorkflowDslParser.getParsedDsl (modules/workflow-engine/dsl/parser.service.ts:169:13)
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:178:22)
[Nest] 8912 - 12/09/2025, 9:27:52 AM ERROR [DocumentNumberingService] Error generating number for doc_num:1:1:1:2025
[Nest] 8912 - 12/09/2025, 9:27:52 AM ERROR [DocumentNumberingService] InternalServerErrorException: Failed to generate document number after retries.
at DocumentNumberingService.generateNextNumber (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.ts:182:13)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.spec.ts:175:7) {
response: {
message: 'Failed to generate document number after retries.',
error: 'Internal Server Error',
statusCode: 500
},
status: 500,
options: {}
}
PASS src/modules/document-numbering/document-numbering.service.spec.ts
[Nest] 2912 - 12/09/2025, 9:27:52 AM ERROR [FileStorageService] Failed to write file: D:\nap-dms.lcbp3\backend\uploads\temp\14ebbf1a-e734-4b42-aa7b-bc93f75a48f4.pdf
[Nest] 2912 - 12/09/2025, 9:27:52 AM ERROR [FileStorageService] Error: Write error
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\common\file-storage\file-storage.service.spec.ts:90:9)
at Promise.finally.completed (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1557:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1497:10)
at _callCircusTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1007:40)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at _runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:947:3)
at D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:849:7
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:862:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at run (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:761:3)
at runAndTransformResultsToJestFormat (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1918:21)
at jestAdapter (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\runner.js:101:19)
at runTestInternal (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:277:16)
at runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:345:7)
at Object.worker (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:499:12)
PASS src/common/file-storage/file-storage.service.spec.ts
PASS src/common/auth/auth.service.spec.ts
ΓùÅ Console
console.log
🔍 Checking login for: testuser
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
console.log
🔍 Checking login for: unknown
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
console.log
❌ User not found in database
at AuthService.validateUser (common/auth/auth.service.ts:51:15)
console.log
🔍 Checking login for: testuser
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
PASS src/common/file-storage/file-storage.controller.spec.ts
PASS src/modules/json-schema/json-schema.controller.spec.ts
PASS src/modules/project/project.controller.spec.ts
PASS src/common/auth/auth.controller.spec.ts
FAIL src/modules/correspondence/correspondence.service.spec.ts
● CorrespondenceService › findAll › should return paginated correspondences
expect(received).toBeDefined()
Received: undefined
119 | it('should return paginated correspondences', async () => {
120 | const result = await service.findAll({ projectId: 1 });
> 121 | expect(result.data).toBeDefined();
| ^
122 | expect(result.meta).toBeDefined();
123 | });
124 | });
at Object.<anonymous> (modules/correspondence/correspondence.service.spec.ts:121:27)
FAIL src/modules/correspondence/correspondence.controller.spec.ts
● CorrespondenceController › should be defined
Nest can't resolve dependencies of the CorrespondenceController (CorrespondenceService, ?). Please make sure that the argument CorrespondenceWorkflowService at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If CorrespondenceWorkflowService is a provider, is it part of the current RootTestModule?
- If CorrespondenceWorkflowService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing CorrespondenceWorkflowService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
23 | };
24 |
> 25 | const module: TestingModule = await Test.createTestingModule({
| ^
26 | controllers: [CorrespondenceController],
27 | providers: [
28 | {
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadController (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:94:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:68:13
at async Promise.all (index 0)
at TestingInstanceLoader.createInstancesOfControllers (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:67:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:42:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/correspondence/correspondence.controller.spec.ts:25:35)
● CorrespondenceController › findAll › should return paginated correspondences
Nest can't resolve dependencies of the CorrespondenceController (CorrespondenceService, ?). Please make sure that the argument CorrespondenceWorkflowService at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If CorrespondenceWorkflowService is a provider, is it part of the current RootTestModule?
- If CorrespondenceWorkflowService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing CorrespondenceWorkflowService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
23 | };
24 |
> 25 | const module: TestingModule = await Test.createTestingModule({
| ^
26 | controllers: [CorrespondenceController],
27 | providers: [
28 | {
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadController (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:94:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:68:13
at async Promise.all (index 0)
at TestingInstanceLoader.createInstancesOfControllers (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:67:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:42:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/correspondence/correspondence.controller.spec.ts:25:35)
● CorrespondenceController › create › should create a correspondence
Nest can't resolve dependencies of the CorrespondenceController (CorrespondenceService, ?). Please make sure that the argument CorrespondenceWorkflowService at index [1] is available in the RootTestModule context.
Potential solutions:
- Is RootTestModule a valid NestJS module?
- If CorrespondenceWorkflowService is a provider, is it part of the current RootTestModule?
- If CorrespondenceWorkflowService is exported from a separate @Module, is that module imported within RootTestModule?
@Module({
imports: [ /* the Module containing CorrespondenceWorkflowService */ ]
})
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors
23 | };
24 |
> 25 | const module: TestingModule = await Test.createTestingModule({
| ^
26 | controllers: [CorrespondenceController],
27 | providers: [
28 | {
at TestingInjector.lookupComponentInParentModules (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:286:19)
at TestingInjector.resolveComponentWrapper (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-injector.js:19:45)
at resolveParam (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:140:38)
at async Promise.all (index 1)
at TestingInjector.resolveConstructorParams (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:169:27)
at TestingInjector.loadInstance (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:75:13)
at TestingInjector.loadController (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/injector.js:94:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:68:13
at async Promise.all (index 0)
at TestingInstanceLoader.createInstancesOfControllers (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:67:9)
at ../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:42:13
at async Promise.all (index 1)
at TestingInstanceLoader.createInstances (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:39:9)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+core@11.1.9_@nestjs_89e063bd3a6d5071b082cab065bf34d7/node_modules/@nestjs/core/injector/instance-loader.js:22:13)
at TestingInstanceLoader.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-instance-loader.js:9:9)
at TestingModuleBuilder.createInstancesOfDependencies (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:118:9)
at TestingModuleBuilder.compile (../../node_modules/.pnpm/@nestjs+testing@11.1.9_@nes_5fa0f54bf7d8c8acec998f5e81836857/node_modules/@nestjs/testing/testing-module.builder.js:74:9)
at Object.<anonymous> (modules/correspondence/correspondence.controller.spec.ts:25:35)
Test Suites: 3 failed, 12 passed, 15 total
Tests: 6 failed, 60 passed, 66 total
Snapshots: 0 total
Time: 5.303 s
Ran all test suites.
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?

View File

@@ -1,148 +0,0 @@
> backend@1.5.1 test
> jest --forceExit
PASS src/app.controller.spec.ts
PASS src/modules/user/user.service.spec.ts
[Nest] 1796 - 12/09/2025, 9:47:09 AM ERROR [WorkflowEngineService] Transition Failed for inst-1: DB Error
PASS src/modules/workflow-engine/workflow-engine.service.spec.ts
[Nest] 35984 - 12/09/2025, 9:47:09 AM ERROR [WorkflowDslParser] Failed to parse stored DSL for definition 1
[Nest] 35984 - 12/09/2025, 9:47:09 AM ERROR [WorkflowDslParser] ZodError: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
at WorkflowDslParser.getParsedDsl (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.ts:163:32)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\workflow-engine\dsl\parser.service.spec.ts:178:22)
FAIL src/modules/workflow-engine/dsl/parser.service.spec.ts
● WorkflowDslParser › parse › should parse valid RFA workflow DSL
expect(received).toBe(expected) // Object.is equality
Expected: "RFA_APPROVAL"
Received: undefined
41 |
42 | expect(result).toBeDefined();
> 43 | expect(result.name).toBe('RFA_APPROVAL');
| ^
44 | expect(result.version).toBe('1.0.0');
45 | expect(result.isActive).toBe(true);
46 | expect(mockRepository.save).toHaveBeenCalled();
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:43:27)
● WorkflowDslParser › getParsedDsl › should retrieve and parse stored DSL
BadRequestException: Invalid stored DSL: [
{
"expected": "object",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected object, received undefined"
}
]
167 | error
168 | );
> 169 | throw new BadRequestException(`Invalid stored DSL: ${error?.message}`);
| ^
170 | }
171 | }
172 |
at WorkflowDslParser.getParsedDsl (modules/workflow-engine/dsl/parser.service.ts:169:13)
at Object.<anonymous> (modules/workflow-engine/dsl/parser.service.spec.ts:178:22)
PASS src/modules/project/project.service.spec.ts
PASS src/common/auth/casl/ability.factory.spec.ts
[Nest] 22776 - 12/09/2025, 9:47:09 AM ERROR [DocumentNumberingService] Error generating number for doc_num:1:1:1:2025
[Nest] 22776 - 12/09/2025, 9:47:09 AM ERROR [DocumentNumberingService] InternalServerErrorException: Failed to generate document number after retries.
at DocumentNumberingService.generateNextNumber (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.ts:182:13)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\modules\document-numbering\document-numbering.service.spec.ts:175:7) {
response: {
message: 'Failed to generate document number after retries.',
error: 'Internal Server Error',
statusCode: 500
},
status: 500,
options: {}
}
PASS src/modules/document-numbering/document-numbering.service.spec.ts
[Nest] 39316 - 12/09/2025, 9:47:09 AM ERROR [FileStorageService] Failed to write file: D:\nap-dms.lcbp3\backend\uploads\temp\cc1049ce-8717-4cc5-807b-a5793373fa3f.pdf
[Nest] 39316 - 12/09/2025, 9:47:09 AM ERROR [FileStorageService] Error: Write error
at Object.<anonymous> (D:\nap-dms.lcbp3\backend\src\common\file-storage\file-storage.service.spec.ts:90:9)
at Promise.finally.completed (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1557:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1497:10)
at _callCircusTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1007:40)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at _runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:947:3)
at D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:849:7
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:862:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at _runTestsForDescribeBlock (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:857:11)
at run (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:761:3)
at runAndTransformResultsToJestFormat (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\jestAdapterInit.js:1918:21)
at jestAdapter (D:\nap-dms.lcbp3\backend\node_modules\jest-circus\build\runner.js:101:19)
at runTestInternal (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:277:16)
at runTest (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:345:7)
at Object.worker (D:\nap-dms.lcbp3\backend\node_modules\jest-runner\build\testWorker.js:499:12)
PASS src/common/file-storage/file-storage.service.spec.ts
PASS src/common/auth/auth.service.spec.ts
ΓùÅ Console
console.log
🔍 Checking login for: testuser
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
console.log
🔍 Checking login for: unknown
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
console.log
❌ User not found in database
at AuthService.validateUser (common/auth/auth.service.ts:51:15)
console.log
🔍 Checking login for: testuser
at AuthService.validateUser (common/auth/auth.service.ts:43:13)
PASS src/common/file-storage/file-storage.controller.spec.ts
PASS src/modules/project/project.controller.spec.ts
PASS src/common/auth/auth.controller.spec.ts
PASS src/modules/json-schema/json-schema.controller.spec.ts
FAIL src/modules/correspondence/correspondence.service.spec.ts
● CorrespondenceService › findAll › should return paginated correspondences
expect(received).toBeDefined()
Received: undefined
119 | it('should return paginated correspondences', async () => {
120 | const result = await service.findAll({ projectId: 1 });
> 121 | expect(result.data).toBeDefined();
| ^
122 | expect(result.meta).toBeDefined();
123 | });
124 | });
at Object.<anonymous> (modules/correspondence/correspondence.service.spec.ts:121:27)
PASS src/modules/correspondence/correspondence.controller.spec.ts
Test Suites: 2 failed, 13 passed, 15 total
Tests: 3 failed, 64 passed, 67 total
Snapshots: 0 total
Time: 4.901 s
Ran all test suites.
Force exiting Jest: Have you considered using `--detectOpenHandles` to detect async operations that kept running after all tests finished?

View File

@@ -42,9 +42,9 @@
"next": "^16.0.7", "next": "^16.0.7",
"next-auth": "5.0.0-beta.30", "next-auth": "5.0.0-beta.30",
"next-themes": "^0.4.6", "next-themes": "^0.4.6",
"react": "^18", "react": "^19.0.0",
"react-day-picker": "^9.12.0", "react-day-picker": "^9.12.0",
"react-dom": "^18", "react-dom": "^19.0.0",
"react-dropzone": "^14.3.8", "react-dropzone": "^14.3.8",
"react-hook-form": "^7.66.1", "react-hook-form": "^7.66.1",
"reactflow": "^11.11.4", "reactflow": "^11.11.4",
@@ -61,8 +61,8 @@
"@testing-library/react": "^16.3.0", "@testing-library/react": "^16.3.0",
"@testing-library/user-event": "^14.6.1", "@testing-library/user-event": "^14.6.1",
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^18", "@types/react": "^19",
"@types/react-dom": "^18", "@types/react-dom": "^19",
"@types/uuid": "^11.0.0", "@types/uuid": "^11.0.0",
"@vitejs/plugin-react": "^5.1.2", "@vitejs/plugin-react": "^5.1.2",
"autoprefixer": "^10.4.22", "autoprefixer": "^10.4.22",

1087
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,10 +2,10 @@
--- ---
title: 'Implementation Guide: Document Numbering System' title: 'Implementation Guide: Document Numbering System'
version: 1.5.1 version: 1.6.1
status: draft status: draft
owner: Development Team owner: Development Team
last_updated: 2025-12-02 last_updated: 2025-12-16
related: related:
- specs/01-requirements/03.11-document-numbering.md - specs/01-requirements/03.11-document-numbering.md
@@ -31,6 +31,21 @@ related:
### 1.1. Counter Table Schema ### 1.1. Counter Table Schema
```sql ```sql
CREATE TABLE document_number_formats (
id INT AUTO_INCREMENT PRIMARY KEY,
project_id INT NOT NULL,
correspondence_type_id INT NULL, -- NULL indicates default format for the project
format_template VARCHAR(100) NOT NULL,
reset_sequence_yearly TINYINT(1) DEFAULT 1,
description VARCHAR(255),
created_at DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6),
updated_at DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
UNIQUE KEY idx_unique_project_type (project_id, correspondence_type_id),
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE
);
CREATE TABLE document_number_counters ( CREATE TABLE document_number_counters (
project_id INT NOT NULL, project_id INT NOT NULL,
originator_organization_id INT NOT NULL, originator_organization_id INT NOT NULL,
@@ -73,14 +88,15 @@ CREATE TABLE document_number_counters (
```sql ```sql
CREATE TABLE document_number_audit ( CREATE TABLE document_number_audit (
id BIGINT AUTO_INCREMENT PRIMARY KEY, id BIGINT AUTO_INCREMENT PRIMARY KEY,
document_id INT NOT NULL, document_id INT NULL COMMENT 'FK to documents (NULL initially, updated after doc creation)',
generated_number VARCHAR(100) NOT NULL, generated_number VARCHAR(100) NOT NULL,
counter_key JSON NOT NULL COMMENT 'Counter key used (JSON format)', counter_key JSON NOT NULL COMMENT 'Counter key used (JSON format)',
template_used VARCHAR(200) NOT NULL, template_used VARCHAR(200) NOT NULL,
user_id INT NOT NULL, user_id INT NULL COMMENT 'FK to users (Allow NULL for system generation)',
ip_address VARCHAR(45), ip_address VARCHAR(45),
user_agent TEXT, user_agent TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_success BOOLEAN DEFAULT TRUE COMMENT 'Track success/failure status',
-- Performance & Error Tracking -- Performance & Error Tracking
retry_count INT DEFAULT 0, retry_count INT DEFAULT 0,
@@ -152,7 +168,50 @@ src/modules/document-numbering/
└── metrics.service.ts └── metrics.service.ts
``` ```
### 2.2. TypeORM Entity ### 2.2. Number Generation Process
#### 2.2.1. Resolve Format Template:
* Query document_number_formats by project_id + type_id.
* If no result, query by project_id + NULL (Default Project Format).
* If still no result, apply System Default Template: `{ORG}-{RECIPIENT}-{SEQ:4}-{YEAR:BE}`.
* Determine resetSequenceYearly flag from the found format (default: true)
#### 2.2.2. Determine Counter Key:
* If resetSequenceYearly is True: Use Current Year (e.g., 2025).
* If resetSequenceYearly is False: Use 0 (Continuous).
* Use type_id from the resolved format (Specific ID or NULL).
#### 2.2.3. Generate Number:
* Use format template to generate number.
* Replace tokens with actual values:
* {PROJECT} -> Project Code
* {ORG} -> Originator Organization Code
* {RECIPIENT} -> Recipient Organization Code
* {TYPE} -> Type Code
* {YEAR} -> Current Year
* {SEQ} -> Sequence Number
* {REV} -> Revision Number
#### 2.2.4. Validate Number:
* Check if generated number is unique.
* If not unique, increment sequence and retry.
#### 2.2.5. Update Counter:
* Update document_number_counters with new sequence.
#### 2.2.6. Generate Audit Record:
* Create audit record with:
* Generated number
* Counter key used
* Template used
* User ID
* IP Address
* User Agent
#### 2.2.7. Return Generated Number:
* Return generated number to caller.
### 2.3. TypeORM Entity
```typescript ```typescript
// File: src/modules/document-numbering/entities/document-number-counter.entity.ts // File: src/modules/document-numbering/entities/document-number-counter.entity.ts
@@ -192,7 +251,7 @@ export class DocumentNumberCounter {
} }
``` ```
### 2.3. Redis Lock Service ### 2.4. Redis Lock Service
```typescript ```typescript
// File: src/modules/document-numbering/services/document-numbering-lock.service.ts // File: src/modules/document-numbering/services/document-numbering-lock.service.ts

View File

@@ -122,20 +122,19 @@ LCBP3-DMS ต้องสร้างเลขที่เอกสารอั
```sql ```sql
-- Format Templates -- Format Templates
CREATE TABLE document_number_configs ( CREATE TABLE document_number_formats (
id INT PRIMARY KEY AUTO_INCREMENT, id INT PRIMARY KEY AUTO_INCREMENT,
project_id INT NOT NULL, project_id INT NOT NULL,
doc_type_id INT NOT NULL COMMENT 'Correspondence, RFA, Transmittal, Drawing', correspondence_type_id INT NULL COMMENT 'Specific Type ID, or NULL for Project Default', -- CHANGED: Allow NULL
sub_type_id INT DEFAULT 0 COMMENT 'ประเภทย่อย (nullable, use 0 for fallback)', format_template VARCHAR(100) NOT NULL COMMENT 'e.g. {PROJECT}-{TYPE}-{YEAR}-{SEQ:4}',
discipline_id INT DEFAULT 0 COMMENT 'สาขาวิชา (nullable, use 0 for fallback)',
template VARCHAR(255) NOT NULL COMMENT 'e.g. {PROJECT}-{ORG}-{TYPE}-{DISCIPLINE}-{SEQ:4}-{REV}',
description TEXT, description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
version INT DEFAULT 0 NOT NULL COMMENT 'For template versioning',
FOREIGN KEY (project_id) REFERENCES projects(id), FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY (doc_type_id) REFERENCES document_types(id), FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE,
UNIQUE KEY unique_config (project_id, doc_type_id, sub_type_id, discipline_id) -- Note: Application logic must enforce single default format per project
UNIQUE KEY unique_format (project_id, correspondence_type_id)
) ENGINE=InnoDB COMMENT='Template configurations for document numbering'; ) ENGINE=InnoDB COMMENT='Template configurations for document numbering';
-- Counter Table with Optimistic Locking -- Counter Table with Optimistic Locking
@@ -208,6 +207,19 @@ CREATE TABLE document_number_audit (
> >
> **Always refer to**: [03.11-document-numbering.md](../01-requirements/03.11-document-numbering.md) as source of truth > **Always refer to**: [03.11-document-numbering.md](../01-requirements/03.11-document-numbering.md) as source of truth
### Format Resolution Strategy (Fallback Logic)
The system resolves the numbering format using the following priority:
1. **Specific Format:** Search for a record matching both `project_id` and `correspondence_type_id`.
2. **Default Format:** If not found, search for a record with matching `project_id` where `correspondence_type_id` is `NULL`.
3. **System Fallback:** If neither exists, use the hardcoded system default: `{ORG}-{RECIPIENT}-{SEQ:4}-{YEAR:BE}`.
| Priority | Scenario | Template Source | Counter Scope (Key) | Reset Behavior |
| --- | --- | --- | --- | --- |
| 1 | Specific Format Found | Database (project_id, type_id) | Specific Type (type_id) | Based on reset_sequence_yearly flag |
| 2 | Default Format Found | Database (project_id, type_id=NULL) | Shared Counter (type_id=NULL) | Based on reset_sequence_yearly flag |
| 3 | Fallback (No Config) | System Default: {ORG}-{RECIPIENT}-{SEQ:4}-{YEAR:BE} | Shared Counter (type_id=NULL) | Reset Yearly (Default: True) |
### Format Examples by Document Type ### Format Examples by Document Type
#### 1. Correspondence (หนังสือราชการ) #### 1. Correspondence (หนังสือราชการ)

View File

@@ -107,7 +107,7 @@ LCBP3-DMS ต้องเลือก Technology Stack สำหรับพั
| Component | Technology | Rationale | | Component | Technology | Rationale |
| :-------------------- | :------------------ | :------------------------------------- | | :-------------------- | :------------------ | :------------------------------------- |
| **Framework** | Next.js 14+ | App Router, SSR/SSG, React integration | | **Framework** | Next.js 14+ | App Router, SSR/SSG, React integration |
| **UI Library** | React 18 | Industry standard, large ecosystem | | **UI Library** | React 19 | Industry standard, large ecosystem |
| **Language** | TypeScript 5.x | Consistency with backend | | **Language** | TypeScript 5.x | Consistency with backend |
| **Styling** | Tailwind CSS | Utility-first, fast development | | **Styling** | Tailwind CSS | Utility-first, fast development |
| **Component Library** | shadcn/ui | Accessible, customizable, TypeScript | | **Component Library** | shadcn/ui | Accessible, customizable, TypeScript |

View File

@@ -963,9 +963,10 @@ CREATE TABLE contract_drawing_attachments (
CREATE TABLE document_number_formats ( CREATE TABLE document_number_formats (
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
project_id INT NOT NULL COMMENT 'โครงการ', project_id INT NOT NULL COMMENT 'โครงการ',
correspondence_type_id INT NOT NULL COMMENT 'ประเภทเอกสาร', correspondence_type_id INT NULL COMMENT 'ประเภทเอกสาร',
discipline_id INT DEFAULT 0 COMMENT 'สาขางาน (0 = ทุกสาขา/ไม่ระบุ)', discipline_id INT DEFAULT 0 COMMENT 'สาขางาน (0 = ทุกสาขา/ไม่ระบุ)',
format_template VARCHAR(255) NOT NULL COMMENT 'รูปแบบ Template (เช่น {ORG_CODE}-{TYPE_CODE}-{SEQ:4})', format_template VARCHAR(255) NOT NULL COMMENT 'รูปแบบ Template (เช่น {ORG}-{RECIPIENT}-{SEQ:4}-{YEAR:BE})',
reset_sequence_yearly TINYINT(1) DEFAULT 1,
example_number VARCHAR(100) COMMENT 'ตัวอย่างเลขที่ได้จาก Template', example_number VARCHAR(100) COMMENT 'ตัวอย่างเลขที่ได้จาก Template',
padding_length INT DEFAULT 4 COMMENT 'ความยาวของลำดับเลข (Padding)', padding_length INT DEFAULT 4 COMMENT 'ความยาวของลำดับเลข (Padding)',
reset_annually BOOLEAN DEFAULT TRUE COMMENT 'เริ่มนับใหม่ทุกปี', reset_annually BOOLEAN DEFAULT TRUE COMMENT 'เริ่มนับใหม่ทุกปี',
@@ -975,10 +976,9 @@ CREATE TABLE document_number_formats (
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด',
FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE, FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE,
FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types (id) ON DELETE CASCADE, FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types (id) ON DELETE CASCADE,
UNIQUE KEY uk_proj_type_disc ( UNIQUE KEY unique_format (
project_id, project_id,
correspondence_type_id, correspondence_type_id
discipline_id
) )
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร'; ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร';