Files
gongxue-base/apps/server/src/archive/archive.purge.spec.ts

115 lines
4.1 KiB
TypeScript

import { BadRequestException } from '@nestjs/common';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { ArchiveService } from './archive.service';
describe('ArchiveService purge sub-records', () => {
const createService = (overrides?: {
enrollment?: Record<string, unknown>;
examScore?: Record<string, unknown>;
learningRecord?: Record<string, unknown>;
attachment?: Record<string, unknown>;
scoreCount?: number;
}) => {
const enrollmentRepo = {
findOne: jest.fn().mockResolvedValue({
id: 1,
status: 'archived',
...overrides?.enrollment,
}),
delete: jest.fn().mockResolvedValue({ affected: 1 }),
};
const examScoreRepo = {
findOne: jest.fn().mockResolvedValue({
id: 2,
status: 'archived',
...overrides?.examScore,
}),
count: jest.fn().mockResolvedValue(overrides?.scoreCount ?? 0),
delete: jest.fn().mockResolvedValue({ affected: 1 }),
};
const learningRecordRepo = {
findOne: jest.fn().mockResolvedValue({
id: 3,
status: 'archived',
...overrides?.learningRecord,
}),
delete: jest.fn().mockResolvedValue({ affected: 1 }),
};
const attachmentRepo = {
findOne: jest.fn().mockResolvedValue({
id: 4,
status: 'archived',
filePath: 'x.pdf',
...overrides?.attachment,
}),
delete: jest.fn().mockResolvedValue({ affected: 1 }),
};
const service = new ArchiveService(
{} as never,
{} as never,
enrollmentRepo as never,
examScoreRepo as never,
learningRecordRepo as never,
{} as never,
attachmentRepo as never,
{} as never,
{} as never,
);
return { service, enrollmentRepo, examScoreRepo, learningRecordRepo, attachmentRepo };
};
it('rejects non-archived sub-records', async () => {
const { service, enrollmentRepo } = createService({ enrollment: { status: 'active' } });
await expect(service.purgeEnrollment(1)).rejects.toThrow(
new BadRequestException('仅已归档报名记录可以永久删除,请先归档'),
);
expect(enrollmentRepo.delete).not.toHaveBeenCalled();
});
it('rejects enrollments referenced by exam scores', async () => {
const { service, enrollmentRepo } = createService({ scoreCount: 1 });
await expect(service.purgeEnrollment(1)).rejects.toThrow(
new BadRequestException('该报名记录已被考试成绩引用,无法永久删除'),
);
expect(enrollmentRepo.delete).not.toHaveBeenCalled();
});
it('deletes archived enrollment, exam score, and learning record', async () => {
const { service, enrollmentRepo, examScoreRepo, learningRecordRepo } = createService();
await expect(service.purgeEnrollment(1)).resolves.toEqual({
message: '已永久删除报名记录(不可恢复)',
});
await expect(service.purgeExamScore(2)).resolves.toEqual({
message: '已永久删除考试成绩(不可恢复)',
});
await expect(service.purgeLearningRecord(3)).resolves.toEqual({
message: '已永久删除学习记录(不可恢复)',
});
expect(enrollmentRepo.delete).toHaveBeenCalledWith(1);
expect(examScoreRepo.delete).toHaveBeenCalledWith(2);
expect(learningRecordRepo.delete).toHaveBeenCalledWith(3);
});
it('deletes the attachment row and removes the disk file', async () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'archive-purge-'));
process.env.UPLOAD_DIR = tmpDir;
const filePath = 'x.pdf';
const fullPath = path.join(tmpDir, 'archive', filePath);
fs.mkdirSync(path.dirname(fullPath), { recursive: true });
fs.writeFileSync(fullPath, 'data');
try {
const { service, attachmentRepo } = createService({ attachment: { filePath } });
await expect(service.purgeAttachment(4)).resolves.toEqual({
message: '已永久删除附件(不可恢复)',
});
expect(fs.existsSync(fullPath)).toBe(false);
expect(attachmentRepo.delete).toHaveBeenCalledWith(4);
} finally {
fs.rmSync(tmpDir, { recursive: true, force: true });
delete process.env.UPLOAD_DIR;
}
});
});