forked from wangziqi/gongxue-base
feat: add exam score management
This commit is contained in:
127
apps/server/src/exams/exams.service.spec.ts
Normal file
127
apps/server/src/exams/exams.service.spec.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { ExamScore } from '../entities';
|
||||
import { ExamsService } from './exams.service';
|
||||
|
||||
function createService(transaction: (run: (manager: any) => Promise<unknown>) => Promise<unknown>) {
|
||||
return new ExamsService(
|
||||
{} as never,
|
||||
{} as never,
|
||||
{} as never,
|
||||
{} as never,
|
||||
{ findOne: jest.fn().mockResolvedValue({ id: 1 }) } as never,
|
||||
{ transaction } as never,
|
||||
);
|
||||
}
|
||||
|
||||
describe('ExamsService', () => {
|
||||
it('creates score rows from the active class roster snapshot', async () => {
|
||||
const members = [
|
||||
{ studentId: 11, status: 'active' },
|
||||
{ studentId: 12, status: 'active' },
|
||||
];
|
||||
const manager = {
|
||||
findOne: jest.fn().mockResolvedValue({ id: 3, isArchived: false }),
|
||||
find: jest.fn().mockResolvedValue(members),
|
||||
create: jest.fn((_entity, value) => value),
|
||||
save: jest.fn(async (entity, value) =>
|
||||
entity.name === 'Exam' ? { ...value, id: 9 } : value,
|
||||
),
|
||||
};
|
||||
const service = createService(async (run) => run(manager));
|
||||
|
||||
await service.create(
|
||||
{
|
||||
examType: '月考',
|
||||
examName: '七月月考',
|
||||
subject: '数学',
|
||||
examDate: '2026-07-21',
|
||||
classId: 3,
|
||||
},
|
||||
1,
|
||||
true,
|
||||
);
|
||||
|
||||
expect(manager.find).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
expect.objectContaining({ where: { classId: 3, status: 'active' } }),
|
||||
);
|
||||
expect(manager.save).toHaveBeenCalledWith(
|
||||
ExamScore,
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ examId: 9, studentId: 11, score: null }),
|
||||
expect.objectContaining({ examId: 9, studentId: 12, score: null }),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects creating an exam for an empty class', async () => {
|
||||
const manager = {
|
||||
findOne: jest.fn().mockResolvedValue({ id: 3, isArchived: false }),
|
||||
find: jest.fn().mockResolvedValue([]),
|
||||
};
|
||||
const service = createService(async (run) => run(manager));
|
||||
|
||||
await expect(
|
||||
service.create(
|
||||
{
|
||||
examType: '月考',
|
||||
examName: '七月月考',
|
||||
subject: '数学',
|
||||
examDate: '2026-07-21',
|
||||
classId: 3,
|
||||
},
|
||||
1,
|
||||
true,
|
||||
),
|
||||
).rejects.toBeInstanceOf(BadRequestException);
|
||||
});
|
||||
|
||||
it('counts zero, ignores empty scores, and uses competition ranking', async () => {
|
||||
const rows = [
|
||||
{ id: 1, score: 90, classAvg: null, rank: null },
|
||||
{ id: 2, score: 90, classAvg: null, rank: null },
|
||||
{ id: 3, score: 60, classAvg: null, rank: null },
|
||||
{ id: 4, score: 0, classAvg: null, rank: null },
|
||||
{ id: 5, score: null, classAvg: null, rank: null },
|
||||
];
|
||||
const manager = {
|
||||
findOne: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ id: 8, classId: 3, status: 'active' })
|
||||
.mockResolvedValueOnce(rows[0])
|
||||
.mockResolvedValueOnce(rows[0]),
|
||||
find: jest.fn().mockResolvedValue(rows),
|
||||
save: jest.fn(async (_entity, value) => value),
|
||||
};
|
||||
const service = createService(async (run) => run(manager));
|
||||
|
||||
await service.updateScore(8, 1, 90, 1, true);
|
||||
|
||||
expect(rows.map((row) => row.rank)).toEqual([1, 1, 3, 4, null]);
|
||||
expect(rows.map((row) => row.classAvg)).toEqual([60, 60, 60, 60, 60]);
|
||||
});
|
||||
|
||||
it('clears a score and recalculates the remaining rows', async () => {
|
||||
const rows = [
|
||||
{ id: 1, score: 90, classAvg: 80, rank: 1 },
|
||||
{ id: 2, score: 70, classAvg: 80, rank: 2 },
|
||||
];
|
||||
const manager = {
|
||||
findOne: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ id: 8, classId: 3, status: 'active' })
|
||||
.mockResolvedValueOnce(rows[0])
|
||||
.mockResolvedValueOnce(rows[0]),
|
||||
find: jest.fn().mockResolvedValue(rows),
|
||||
save: jest.fn(async (_entity, value) => value),
|
||||
};
|
||||
const service = createService(async (run) => run(manager));
|
||||
|
||||
await service.updateScore(8, 1, null, 1, true);
|
||||
|
||||
expect(rows).toEqual([
|
||||
expect.objectContaining({ score: null, classAvg: 70, rank: null }),
|
||||
expect.objectContaining({ score: 70, classAvg: 70, rank: 1 }),
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user