251216:0946 Docunment Number: Update specs/ and backend/
This commit is contained in:
@@ -41,4 +41,13 @@ export class DocumentNumberingController {
|
||||
getErrorLogs(@Query('limit') limit?: number) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { DocumentNumberingService } from './document-numbering.service';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Repository, OptimisticLockVersionMismatchError } from 'typeorm';
|
||||
import { InternalServerErrorException } from '@nestjs/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { DocumentNumberCounter } from './entities/document-number-counter.entity';
|
||||
import { DocumentNumberFormat } from './entities/document-number-format.entity';
|
||||
import { Project } from '../project/entities/project.entity';
|
||||
@@ -17,6 +16,7 @@ import { DocumentNumberError } from './entities/document-number-error.entity';
|
||||
// Mock Redis and Redlock
|
||||
const mockRedis = {
|
||||
disconnect: jest.fn(),
|
||||
on: jest.fn(),
|
||||
};
|
||||
const mockRedlock = {
|
||||
acquire: jest.fn(),
|
||||
@@ -37,9 +37,7 @@ jest.mock('redlock', () => {
|
||||
describe('DocumentNumberingService', () => {
|
||||
let service: DocumentNumberingService;
|
||||
let module: TestingModule;
|
||||
let counterRepo: Repository<DocumentNumberCounter>;
|
||||
let formatRepo: Repository<DocumentNumberFormat>;
|
||||
let auditRepo: Repository<DocumentNumberAudit>;
|
||||
let formatRepo: jest.Mocked<{ findOne: jest.Mock }>;
|
||||
|
||||
const mockProject = { id: 1, projectCode: 'LCBP3' };
|
||||
const mockOrg = { id: 1, name: 'Google' };
|
||||
@@ -79,11 +77,17 @@ describe('DocumentNumberingService', () => {
|
||||
},
|
||||
{
|
||||
provide: getRepositoryToken(DocumentNumberAudit),
|
||||
useValue: { save: jest.fn() },
|
||||
useValue: {
|
||||
create: jest.fn().mockReturnValue({ id: 1 }),
|
||||
save: jest.fn().mockResolvedValue({ id: 1 }),
|
||||
},
|
||||
},
|
||||
{
|
||||
provide: getRepositoryToken(DocumentNumberError),
|
||||
useValue: { save: jest.fn() },
|
||||
useValue: {
|
||||
create: jest.fn().mockReturnValue({}),
|
||||
save: jest.fn().mockResolvedValue({}),
|
||||
},
|
||||
},
|
||||
// Mock other dependencies used inside generateNextNumber lookups
|
||||
{
|
||||
@@ -106,16 +110,26 @@ describe('DocumentNumberingService', () => {
|
||||
provide: getRepositoryToken(CorrespondenceSubType),
|
||||
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();
|
||||
|
||||
service = module.get<DocumentNumberingService>(DocumentNumberingService);
|
||||
counterRepo = module.get(getRepositoryToken(DocumentNumberCounter));
|
||||
formatRepo = module.get(getRepositoryToken(DocumentNumberFormat));
|
||||
auditRepo = module.get(getRepositoryToken(DocumentNumberAudit));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
// 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);
|
||||
(disciplineRepo.findOne as jest.Mock).mockResolvedValue(mockDiscipline);
|
||||
(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();
|
||||
|
||||
const result = await service.generateNextNumber(mockContext);
|
||||
|
||||
expect(result).toBe('0001'); // Default padding 4 (see replaceTokens method)
|
||||
expect(counterRepo.save).toHaveBeenCalled();
|
||||
// expect(auditRepo.save).toHaveBeenCalled(); // Disabled in implementation
|
||||
// Service returns object with number and auditId
|
||||
expect(result).toHaveProperty('number');
|
||||
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 orgRepo = module.get(getRepositoryToken(Organization));
|
||||
const typeRepo = module.get(getRepositoryToken(CorrespondenceType));
|
||||
const disciplineRepo = module.get(getRepositoryToken(Discipline));
|
||||
const dataSource = module.get(DataSource);
|
||||
|
||||
(projectRepo.findOne as jest.Mock).mockResolvedValue(mockProject);
|
||||
(orgRepo.findOne as jest.Mock).mockResolvedValue(mockOrg);
|
||||
(typeRepo.findOne as jest.Mock).mockResolvedValue(mockType);
|
||||
(disciplineRepo.findOne as jest.Mock).mockResolvedValue(mockDiscipline);
|
||||
(formatRepo.findOne as jest.Mock).mockResolvedValue({
|
||||
formatTemplate: '{SEQ}',
|
||||
formatTemplate: '{SEQ:4}',
|
||||
resetSequenceYearly: true,
|
||||
});
|
||||
(counterRepo.findOne as jest.Mock).mockResolvedValue({ lastNumber: 1 });
|
||||
|
||||
// Always fail
|
||||
(counterRepo.save as jest.Mock).mockRejectedValue(
|
||||
new OptimisticLockVersionMismatchError('Counter', 1, 2)
|
||||
// Mock transaction to throw error
|
||||
(dataSource.transaction as jest.Mock).mockRejectedValue(
|
||||
new Error('Transaction failed')
|
||||
);
|
||||
|
||||
service.onModuleInit();
|
||||
|
||||
await expect(service.generateNextNumber(mockContext)).rejects.toThrow(
|
||||
InternalServerErrorException
|
||||
Error
|
||||
);
|
||||
expect(counterRepo.save).toHaveBeenCalledTimes(3); // Max retries
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,736 +1,302 @@
|
||||
// File: src/modules/document-numbering/document-numbering.service.ts
|
||||
import {
|
||||
Injectable,
|
||||
OnModuleInit,
|
||||
OnModuleDestroy,
|
||||
InternalServerErrorException,
|
||||
NotFoundException,
|
||||
Logger,
|
||||
} from '@nestjs/common';
|
||||
// backend/src/modules/document-numbering/document-numbering.service.ts
|
||||
|
||||
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import {
|
||||
Repository,
|
||||
EntityManager,
|
||||
OptimisticLockVersionMismatchError,
|
||||
} from 'typeorm';
|
||||
import { Repository, DataSource, IsNull } from 'typeorm';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import Redis from 'ioredis';
|
||||
import Redlock from 'redlock';
|
||||
|
||||
// Entities
|
||||
import { DocumentNumberCounter } from './entities/document-number-counter.entity';
|
||||
import { DocumentNumberFormat } from './entities/document-number-format.entity';
|
||||
import { Project } from '../project/entities/project.entity'; // สมมติ path
|
||||
import { Organization } from '../organization/entities/organization.entity';
|
||||
import { DocumentNumberAudit } from './entities/document-number-audit.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 { Organization } from '../organization/entities/organization.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 {
|
||||
GenerateNumberContext,
|
||||
DecodedTokens,
|
||||
} from './interfaces/document-numbering.interface.js';
|
||||
} from './interfaces/document-numbering.interface';
|
||||
|
||||
@Injectable()
|
||||
export class DocumentNumberingService implements OnModuleInit, OnModuleDestroy {
|
||||
export class DocumentNumberingService implements OnModuleInit {
|
||||
private readonly logger = new Logger(DocumentNumberingService.name);
|
||||
private redisClient!: Redis;
|
||||
private redlock!: Redlock;
|
||||
private redisClient: Redis;
|
||||
private redlock: Redlock;
|
||||
|
||||
constructor(
|
||||
@InjectRepository(DocumentNumberCounter)
|
||||
private counterRepo: Repository<DocumentNumberCounter>,
|
||||
@InjectRepository(DocumentNumberFormat)
|
||||
private formatRepo: Repository<DocumentNumberFormat>,
|
||||
|
||||
// Inject Repositories สำหรับดึง Code มาทำ Token Replacement
|
||||
@InjectRepository(Project) private projectRepo: Repository<Project>,
|
||||
@InjectRepository(Organization) private orgRepo: Repository<Organization>,
|
||||
@InjectRepository(DocumentNumberAudit)
|
||||
private auditRepo: Repository<DocumentNumberAudit>,
|
||||
@InjectRepository(DocumentNumberError)
|
||||
private errorRepo: Repository<DocumentNumberError>,
|
||||
@InjectRepository(Project)
|
||||
private projectRepo: Repository<Project>,
|
||||
@InjectRepository(CorrespondenceType)
|
||||
private typeRepo: Repository<CorrespondenceType>,
|
||||
@InjectRepository(Organization)
|
||||
private orgRepo: Repository<Organization>,
|
||||
@InjectRepository(Discipline)
|
||||
private disciplineRepo: Repository<Discipline>,
|
||||
@InjectRepository(CorrespondenceSubType)
|
||||
private subTypeRepo: Repository<CorrespondenceSubType>,
|
||||
@InjectRepository(DocumentNumberAudit) // [P0-4]
|
||||
private auditRepo: Repository<DocumentNumberAudit>,
|
||||
@InjectRepository(DocumentNumberError) // [P0-4]
|
||||
private errorRepo: Repository<DocumentNumberError>,
|
||||
|
||||
private dataSource: DataSource,
|
||||
private configService: ConfigService
|
||||
) {}
|
||||
|
||||
onModuleInit() {
|
||||
// 1. Setup Redis Connection & Redlock
|
||||
const host = this.configService.get<string>('REDIS_HOST', 'localhost');
|
||||
const port = this.configService.get<number>('REDIS_PORT', 6379);
|
||||
const password = this.configService.get<string>('REDIS_PASSWORD');
|
||||
|
||||
this.redisClient = new Redis({ host, port, password });
|
||||
|
||||
// Config Redlock สำหรับ Distributed Lock
|
||||
this.redlock = new Redlock([this.redisClient], {
|
||||
driftFactor: 0.01,
|
||||
retryCount: 10, // Retry 10 ครั้ง
|
||||
retryDelay: 200, // รอ 200ms ต่อครั้ง
|
||||
retryJitter: 200,
|
||||
this.redisClient = new Redis({
|
||||
host,
|
||||
port,
|
||||
retryStrategy: (times) => Math.min(times * 50, 2000),
|
||||
maxRetriesPerRequest: 3,
|
||||
});
|
||||
|
||||
this.logger.log(
|
||||
`Document Numbering Service initialized (Redis: ${host}:${port})`
|
||||
);
|
||||
this.redisClient.on('error', (err) => {
|
||||
this.logger.error('Redis Client Error', err);
|
||||
});
|
||||
|
||||
this.redlock = new Redlock([this.redisClient], {
|
||||
driftFactor: 0.01,
|
||||
retryCount: 3,
|
||||
retryDelay: 200,
|
||||
retryJitter: 200,
|
||||
});
|
||||
}
|
||||
|
||||
onModuleDestroy() {
|
||||
this.redisClient.disconnect();
|
||||
}
|
||||
async generateNextNumber(
|
||||
ctx: GenerateNumberContext
|
||||
): Promise<{ number: string; auditId: number }> {
|
||||
const currentYear = new Date().getFullYear();
|
||||
|
||||
/**
|
||||
* สร้างเลขที่เอกสารใหม่ (Thread-Safe & Gap-free)
|
||||
*/
|
||||
async generateNextNumber(ctx: GenerateNumberContext): Promise<string> {
|
||||
const year = ctx.year || new Date().getFullYear();
|
||||
const disciplineId = ctx.disciplineId || 0;
|
||||
// 1. Resolve Format & Determine Counter Scope & Reset Rule
|
||||
const { template, counterTypeId, resetSequenceYearly } =
|
||||
await this.resolveFormatAndScope(ctx);
|
||||
const tokens = await this.resolveTokens(ctx, currentYear);
|
||||
|
||||
// 1. Resolve Tokens Outside Lock
|
||||
const tokens = await this.resolveTokens(ctx, year);
|
||||
// 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;
|
||||
|
||||
// 2. Get Format Template WITH META (Padding)
|
||||
const { template, paddingLength } = await this.getFormatTemplateWithMeta(
|
||||
ctx.projectId,
|
||||
ctx.typeId,
|
||||
disciplineId
|
||||
);
|
||||
// 3. Build Lock Key
|
||||
const resourceKey = `counter:${ctx.projectId}:${counterTypeId ?? 'shared'}:${counterYear}`;
|
||||
|
||||
// 3. Resource Key
|
||||
const resourceKey = `doc_num:${ctx.projectId}:${ctx.typeId}:${disciplineId}:${year}`;
|
||||
const lockTtl = 5000;
|
||||
|
||||
let lock;
|
||||
let lock: any;
|
||||
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: {
|
||||
projectId: ctx.projectId,
|
||||
originatorId: ctx.originatorId,
|
||||
recipientOrganizationId: recipientId,
|
||||
typeId: ctx.typeId,
|
||||
subTypeId: subTypeId,
|
||||
rfaTypeId: rfaTypeId,
|
||||
disciplineId: disciplineId,
|
||||
year: year,
|
||||
},
|
||||
});
|
||||
|
||||
if (!counter) {
|
||||
counter = this.counterRepo.create({
|
||||
projectId: ctx.projectId,
|
||||
originatorId: ctx.originatorId,
|
||||
recipientOrganizationId: recipientId,
|
||||
typeId: ctx.typeId,
|
||||
subTypeId: subTypeId,
|
||||
rfaTypeId: rfaTypeId,
|
||||
disciplineId: disciplineId,
|
||||
year: year,
|
||||
lastNumber: 0,
|
||||
});
|
||||
}
|
||||
|
||||
counter.lastNumber += 1;
|
||||
await this.counterRepo.save(counter);
|
||||
|
||||
const generatedNumber = this.replaceTokens(
|
||||
template,
|
||||
tokens,
|
||||
counter.lastNumber,
|
||||
paddingLength // Pass padding from template
|
||||
);
|
||||
|
||||
// Audit skipped for brevity in this block, assumed handled or TBD
|
||||
return generatedNumber;
|
||||
} catch (err) {
|
||||
if (err instanceof OptimisticLockVersionMismatchError) {
|
||||
continue;
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
try {
|
||||
lock = await this.redlock.acquire([`locks:${resourceKey}`], 5000);
|
||||
} catch (e) {
|
||||
this.logger.warn(
|
||||
`Redlock failed for ${resourceKey}, proceeding with DB optimistic lock.`
|
||||
);
|
||||
}
|
||||
throw new InternalServerErrorException('Failed to generate number');
|
||||
} catch (error: any) {
|
||||
// Error logging...
|
||||
|
||||
// 4. Increment Counter (Atomic Transaction)
|
||||
const result = await this.dataSource.transaction(async (manager) => {
|
||||
let counter = await manager.findOne(DocumentNumberCounter, {
|
||||
where: {
|
||||
projectId: ctx.projectId,
|
||||
correspondenceTypeId:
|
||||
counterTypeId === null ? IsNull() : counterTypeId,
|
||||
year: counterYear,
|
||||
},
|
||||
});
|
||||
|
||||
if (!counter) {
|
||||
counter = manager.create(DocumentNumberCounter, {
|
||||
projectId: ctx.projectId,
|
||||
correspondenceTypeId: counterTypeId, // Can be null
|
||||
year: counterYear,
|
||||
lastSequence: 0,
|
||||
});
|
||||
}
|
||||
|
||||
counter.lastSequence += 1;
|
||||
return await manager.save(counter);
|
||||
});
|
||||
|
||||
// 5. Generate Final String
|
||||
const generatedNumber = this.replaceTokens(
|
||||
template,
|
||||
tokens,
|
||||
result.lastSequence
|
||||
);
|
||||
|
||||
// 6. Audit Log
|
||||
const audit = await this.logAudit({
|
||||
generatedNumber,
|
||||
counterKey: resourceKey,
|
||||
templateUsed: template,
|
||||
context: ctx,
|
||||
isSuccess: true,
|
||||
});
|
||||
|
||||
return { number: generatedNumber, auditId: audit.id };
|
||||
} catch (error) {
|
||||
await this.logError(error, ctx, resourceKey);
|
||||
throw error;
|
||||
} finally {
|
||||
if (lock) await lock.release().catch(() => {});
|
||||
if (lock) await lock.release().catch((e) => this.logger.error(e));
|
||||
}
|
||||
}
|
||||
|
||||
async previewNextNumber(
|
||||
ctx: GenerateNumberContext
|
||||
): Promise<{ number: string; isDefaultTemplate: boolean }> {
|
||||
const year = ctx.year || new Date().getFullYear();
|
||||
const disciplineId = ctx.disciplineId || 0;
|
||||
// --- Helper Methods ---
|
||||
|
||||
const tokens = await this.resolveTokens(ctx, year);
|
||||
|
||||
const { template, isDefault, paddingLength } =
|
||||
await this.getFormatTemplateWithMeta(
|
||||
ctx.projectId,
|
||||
ctx.typeId,
|
||||
disciplineId
|
||||
);
|
||||
|
||||
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,
|
||||
},
|
||||
private async resolveFormatAndScope(ctx: GenerateNumberContext): Promise<{
|
||||
template: string;
|
||||
counterTypeId: number | null;
|
||||
resetSequenceYearly: boolean;
|
||||
}> {
|
||||
// A. Try Specific Format
|
||||
const specificFormat = await this.formatRepo.findOne({
|
||||
where: { projectId: ctx.projectId, correspondenceTypeId: ctx.typeId },
|
||||
});
|
||||
|
||||
const nextSeq = (counter?.lastNumber || 0) + 1;
|
||||
if (specificFormat) {
|
||||
return {
|
||||
template: specificFormat.formatTemplate,
|
||||
counterTypeId: ctx.typeId,
|
||||
resetSequenceYearly: specificFormat.resetSequenceYearly,
|
||||
};
|
||||
}
|
||||
|
||||
const generatedNumber = this.replaceTokens(
|
||||
template,
|
||||
tokens,
|
||||
nextSeq,
|
||||
paddingLength
|
||||
);
|
||||
// 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 {
|
||||
number: generatedNumber,
|
||||
isDefaultTemplate: isDefault,
|
||||
template: '{ORG}-{RECIPIENT}-{SEQ:4}-{YEAR:BE}',
|
||||
counterTypeId: null, // Use shared counter
|
||||
resetSequenceYearly: true, // Default fallback behavior
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: ดึงข้อมูล Code ต่างๆ จาก ID เพื่อนำมาแทนที่ใน Template
|
||||
*/
|
||||
private async resolveTokens(
|
||||
ctx: GenerateNumberContext,
|
||||
year: number
|
||||
): Promise<DecodedTokens> {
|
||||
const [project, org, type] = await Promise.all([
|
||||
this.projectRepo.findOne({ where: { id: ctx.projectId } }),
|
||||
this.orgRepo.findOne({ where: { id: ctx.originatorId } }),
|
||||
this.typeRepo.findOne({ where: { id: ctx.typeId } }),
|
||||
]);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
const [project, type, recipientCode, disciplineCode, orgCode] =
|
||||
await Promise.all([
|
||||
this.projectRepo.findOne({
|
||||
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),
|
||||
]);
|
||||
|
||||
return {
|
||||
projectCode: project.projectCode,
|
||||
orgCode: org.organizationCode,
|
||||
typeCode: type.typeCode,
|
||||
disciplineCode,
|
||||
subTypeCode,
|
||||
subTypeNumber,
|
||||
year: yearTh,
|
||||
yearShort: yearTh.slice(-2), // 68
|
||||
recipientCode, // [P1-4]
|
||||
'{PROJECT}': project?.projectCode || 'PROJ',
|
||||
'{TYPE}': type?.typeCode || 'DOC',
|
||||
'{ORG}': orgCode,
|
||||
'{RECIPIENT}': recipientCode,
|
||||
'{DISCIPLINE}': disciplineCode,
|
||||
'{YEAR}': year.toString().substring(2),
|
||||
'{YEAR:BE}': (year + 543).toString().substring(2),
|
||||
'{REV}': '0',
|
||||
};
|
||||
}
|
||||
|
||||
// --- 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(
|
||||
template: string,
|
||||
tokens: DecodedTokens,
|
||||
seq: number,
|
||||
defaultPadding: number = 4
|
||||
sequence: number
|
||||
): string {
|
||||
let result = template;
|
||||
|
||||
const replacements: Record<string, string> = {
|
||||
'{PROJECT}': tokens.projectCode,
|
||||
'{ORG}': tokens.orgCode,
|
||||
'{TYPE}': tokens.typeCode,
|
||||
'{DISCIPLINE}': tokens.disciplineCode,
|
||||
'{SUBTYPE}': tokens.subTypeCode,
|
||||
'{SUBTYPE_NUM}': tokens.subTypeNumber, // [Req 6B] For Transmittal/RFA
|
||||
'{RECIPIENT}': tokens.recipientCode, // [P1-4] Recipient organization
|
||||
'{YEAR}': tokens.year,
|
||||
'{YEAR_SHORT}': tokens.yearShort,
|
||||
};
|
||||
|
||||
// 1. Replace Standard Tokens
|
||||
for (const [key, value] of Object.entries(replacements)) {
|
||||
// ใช้ Global Replace
|
||||
result = result.split(key).join(value);
|
||||
for (const [key, value] of Object.entries(tokens)) {
|
||||
result = result.replace(
|
||||
new RegExp(key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g'),
|
||||
value
|
||||
);
|
||||
}
|
||||
const seqMatch = result.match(/{SEQ:(\d+)}/);
|
||||
if (seqMatch) {
|
||||
const padding = parseInt(seqMatch[1], 10);
|
||||
result = result.replace(
|
||||
seqMatch[0],
|
||||
sequence.toString().padStart(padding, '0')
|
||||
);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* [P0-4] Log successful number generation to audit table
|
||||
*/
|
||||
private async logAudit(
|
||||
auditData: Partial<DocumentNumberAudit>
|
||||
): 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,
|
||||
private async resolveRecipientCode(recipientId?: number): Promise<string> {
|
||||
if (!recipientId) return 'GEN';
|
||||
const org = await this.orgRepo.findOne({
|
||||
where: { id: recipientId },
|
||||
select: ['organizationCode'],
|
||||
});
|
||||
return org ? org.organizationCode : 'GEN';
|
||||
}
|
||||
|
||||
async getErrorLogs(limit = 100): Promise<DocumentNumberError[]> {
|
||||
return this.errorRepo.find({
|
||||
order: { createdAt: 'DESC' },
|
||||
take: limit,
|
||||
private async resolveOrgCode(orgId?: number): Promise<string> {
|
||||
if (!orgId) return 'GEN';
|
||||
const org = await this.orgRepo.findOne({
|
||||
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({
|
||||
where: { id: disciplineId },
|
||||
select: ['code'],
|
||||
});
|
||||
return discipline ? discipline.code : 'GEN';
|
||||
}
|
||||
|
||||
/**
|
||||
* Manual Override: Force set the counter to a specific number.
|
||||
* Useful for aligning with legacy systems or skipping numbers.
|
||||
*/
|
||||
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;
|
||||
private async logAudit(data: any): Promise<DocumentNumberAudit> {
|
||||
const audit = this.auditRepo.create({
|
||||
...data,
|
||||
projectId: data.context.projectId,
|
||||
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
|
||||
);
|
||||
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(),
|
||||
},
|
||||
const errorRecord = this.errorRepo.create({
|
||||
projectId: ctx.projectId,
|
||||
errorType: error.name || 'UnknownError',
|
||||
errorMessage: error.message,
|
||||
stackTrace: error.stack,
|
||||
counterKey: key,
|
||||
inputPayload: JSON.stringify(ctx),
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
const oldNumber = counter.lastNumber;
|
||||
if (newSequence <= oldNumber) {
|
||||
// Warning: Manual override to lower number might cause collisions
|
||||
this.logger.warn(
|
||||
`Manual override to lower sequence: ${oldNumber} -> ${newSequence}`
|
||||
);
|
||||
}
|
||||
|
||||
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(() => {});
|
||||
await this.errorRepo.save(errorRecord);
|
||||
} catch (e) {
|
||||
this.logger.error('Failed to save error log', e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
// 1. Try Rollback Old Number
|
||||
lock = await this.redlock.acquire([resourceKey], lockTtl);
|
||||
|
||||
const recipientId = oldCtx.recipientOrganizationId ?? -1;
|
||||
const subTypeId = oldCtx.subTypeId ?? 0;
|
||||
const rfaTypeId = oldCtx.rfaTypeId ?? 0;
|
||||
|
||||
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)',
|
||||
},
|
||||
});
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// backend/src/modules/document-numbering/entities/document-number-format.entity.ts
|
||||
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
@@ -5,43 +7,46 @@ import {
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
Unique,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { Project } from '../../project/entities/project.entity';
|
||||
// เรายังไม่มี CorrespondenceType Entity เดี๋ยวสร้าง Dummy ไว้ก่อน หรือข้าม Relation ไปก่อนได้
|
||||
// แต่ตามหลักควรมี CorrespondenceType (Master Data)
|
||||
import { CorrespondenceType } from '../../correspondence/entities/correspondence-type.entity';
|
||||
|
||||
@Entity('document_number_formats')
|
||||
@Unique(['projectId', 'correspondenceTypeId']) // 1 Project + 1 Type มีได้แค่ 1 Format
|
||||
@Unique(['projectId', 'correspondenceTypeId'])
|
||||
export class DocumentNumberFormat {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
id: number;
|
||||
|
||||
@Column({ name: 'project_id' })
|
||||
projectId!: number;
|
||||
projectId: number;
|
||||
|
||||
@Column({ name: 'correspondence_type_id' })
|
||||
correspondenceTypeId!: number;
|
||||
@Column({ name: 'correspondence_type_id', nullable: true })
|
||||
correspondenceTypeId: number | null;
|
||||
|
||||
@Column({ name: 'format_template', length: 255 })
|
||||
formatTemplate!: string;
|
||||
@Column({ name: 'format_template', length: 100 })
|
||||
formatTemplate: string;
|
||||
|
||||
@Column({ name: 'discipline_id', default: 0 })
|
||||
disciplineId!: number;
|
||||
@Column({ name: 'description', nullable: true })
|
||||
description: string;
|
||||
|
||||
@Column({ name: 'example_number', length: 100, nullable: true })
|
||||
exampleNumber?: string;
|
||||
// [NEW] Control yearly reset behavior
|
||||
@Column({ name: 'reset_sequence_yearly', default: true })
|
||||
resetSequenceYearly: boolean;
|
||||
|
||||
@Column({ name: 'padding_length', default: 4 })
|
||||
paddingLength!: number;
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt: Date;
|
||||
|
||||
@Column({ name: 'reset_annually', default: true })
|
||||
resetAnnually!: boolean;
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt: Date;
|
||||
|
||||
@Column({ name: 'is_active', default: true })
|
||||
isActive!: boolean;
|
||||
|
||||
// Relation
|
||||
// Relations
|
||||
@ManyToOne(() => Project)
|
||||
@JoinColumn({ name: 'project_id' })
|
||||
project?: Project;
|
||||
project: Project;
|
||||
|
||||
@ManyToOne(() => CorrespondenceType)
|
||||
@JoinColumn({ name: 'correspondence_type_id' })
|
||||
correspondenceType: CorrespondenceType | null;
|
||||
}
|
||||
|
||||
@@ -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?
|
||||
@@ -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?
|
||||
@@ -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?
|
||||
@@ -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?
|
||||
@@ -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?
|
||||
@@ -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?
|
||||
@@ -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?
|
||||
@@ -42,9 +42,9 @@
|
||||
"next": "^16.0.7",
|
||||
"next-auth": "5.0.0-beta.30",
|
||||
"next-themes": "^0.4.6",
|
||||
"react": "^18",
|
||||
"react": "^19.0.0",
|
||||
"react-day-picker": "^9.12.0",
|
||||
"react-dom": "^18",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-dropzone": "^14.3.8",
|
||||
"react-hook-form": "^7.66.1",
|
||||
"reactflow": "^11.11.4",
|
||||
@@ -61,8 +61,8 @@
|
||||
"@testing-library/react": "^16.3.0",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "^18",
|
||||
"@types/react-dom": "^18",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/uuid": "^11.0.0",
|
||||
"@vitejs/plugin-react": "^5.1.2",
|
||||
"autoprefixer": "^10.4.22",
|
||||
|
||||
1087
pnpm-lock.yaml
generated
1087
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -2,10 +2,10 @@
|
||||
|
||||
---
|
||||
title: 'Implementation Guide: Document Numbering System'
|
||||
version: 1.5.1
|
||||
version: 1.6.1
|
||||
status: draft
|
||||
owner: Development Team
|
||||
last_updated: 2025-12-02
|
||||
last_updated: 2025-12-16
|
||||
related:
|
||||
|
||||
- specs/01-requirements/03.11-document-numbering.md
|
||||
@@ -31,6 +31,21 @@ related:
|
||||
### 1.1. Counter Table Schema
|
||||
|
||||
```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 (
|
||||
project_id INT NOT NULL,
|
||||
originator_organization_id INT NOT NULL,
|
||||
@@ -73,14 +88,15 @@ CREATE TABLE document_number_counters (
|
||||
```sql
|
||||
CREATE TABLE document_number_audit (
|
||||
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,
|
||||
counter_key JSON NOT NULL COMMENT 'Counter key used (JSON format)',
|
||||
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),
|
||||
user_agent TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
is_success BOOLEAN DEFAULT TRUE COMMENT 'Track success/failure status',
|
||||
|
||||
-- Performance & Error Tracking
|
||||
retry_count INT DEFAULT 0,
|
||||
@@ -152,7 +168,50 @@ src/modules/document-numbering/
|
||||
└── 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
|
||||
// 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
|
||||
// File: src/modules/document-numbering/services/document-numbering-lock.service.ts
|
||||
|
||||
@@ -122,20 +122,19 @@ LCBP3-DMS ต้องสร้างเลขที่เอกสารอั
|
||||
|
||||
```sql
|
||||
-- Format Templates
|
||||
CREATE TABLE document_number_configs (
|
||||
CREATE TABLE document_number_formats (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
project_id INT NOT NULL,
|
||||
doc_type_id INT NOT NULL COMMENT 'Correspondence, RFA, Transmittal, Drawing',
|
||||
sub_type_id INT DEFAULT 0 COMMENT 'ประเภทย่อย (nullable, use 0 for fallback)',
|
||||
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}',
|
||||
correspondence_type_id INT NULL COMMENT 'Specific Type ID, or NULL for Project Default', -- CHANGED: Allow NULL
|
||||
format_template VARCHAR(100) NOT NULL COMMENT 'e.g. {PROJECT}-{TYPE}-{YEAR}-{SEQ:4}',
|
||||
description TEXT,
|
||||
created_at TIMESTAMP DEFAULT 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 (doc_type_id) REFERENCES document_types(id),
|
||||
UNIQUE KEY unique_config (project_id, doc_type_id, sub_type_id, discipline_id)
|
||||
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE,
|
||||
-- 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';
|
||||
|
||||
-- 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
|
||||
|
||||
### 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
|
||||
|
||||
#### 1. Correspondence (หนังสือราชการ)
|
||||
|
||||
@@ -107,7 +107,7 @@ LCBP3-DMS ต้องเลือก Technology Stack สำหรับพั
|
||||
| Component | Technology | Rationale |
|
||||
| :-------------------- | :------------------ | :------------------------------------- |
|
||||
| **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 |
|
||||
| **Styling** | Tailwind CSS | Utility-first, fast development |
|
||||
| **Component Library** | shadcn/ui | Accessible, customizable, TypeScript |
|
||||
|
||||
@@ -963,9 +963,10 @@ CREATE TABLE contract_drawing_attachments (
|
||||
CREATE TABLE document_number_formats (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
|
||||
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 = ทุกสาขา/ไม่ระบุ)',
|
||||
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',
|
||||
padding_length INT DEFAULT 4 COMMENT 'ความยาวของลำดับเลข (Padding)',
|
||||
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 'วันที่แก้ไขล่าสุด',
|
||||
FOREIGN KEY (project_id) REFERENCES projects (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,
|
||||
correspondence_type_id,
|
||||
discipline_id
|
||||
correspondence_type_id
|
||||
)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user