feat(api): add version to exam collections (#61644)

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Shaun Hamilton
2025-08-12 11:39:07 +02:00
committed by GitHub
parent efd1f3bb2c
commit 6adc98a426
5 changed files with 144 additions and 8 deletions
+6 -3
View File
@@ -248,7 +248,8 @@ export const generatedExam: ExamEnvironmentGeneratedExam = {
}
]
}
]
],
version: 1
};
export const examAttempt: ExamEnvironmentExamAttempt = {
@@ -293,7 +294,8 @@ export const examAttempt: ExamEnvironmentExamAttempt = {
}
],
startTimeInMS: Date.now(),
userId: defaultUserId
userId: defaultUserId,
version: 1
};
export const examAttemptSansSubmissionTimeInMS: Static<
@@ -340,7 +342,8 @@ export const exam: ExamEnvironmentExam = {
config,
questionSets,
prerequisites: ['67112fe1c994faa2c26d0b1d'],
deprecated: false
deprecated: false,
version: 1
};
export async function seedEnvExam() {
+12
View File
@@ -182,6 +182,9 @@ model ExamEnvironmentExam {
prerequisites String[] @db.ObjectId
/// If `deprecated`, the exam should no longer be considered for users
deprecated Boolean
/// Version of the record
/// The default must be incremented by 1, if anything in the schema changes
version Int @default(1)
// Relations
generatedExams ExamEnvironmentGeneratedExam[]
@@ -313,6 +316,9 @@ model ExamEnvironmentExamAttempt {
questionSets ExamEnvironmentQuestionSetAttempt[]
/// Time exam was started as milliseconds since epoch
startTimeInMS Int
/// Version of the record
/// The default must be incremented by 1, if anything in the schema changes
version Int @default(1)
// Relations
user user @relation(fields: [userId], references: [id], onDelete: Cascade)
@@ -347,6 +353,9 @@ model ExamEnvironmentGeneratedExam {
questionSets ExamEnvironmentGeneratedQuestionSet[]
/// If `deprecated`, the generation should not longer be considered for users
deprecated Boolean
/// Version of the record
/// The default must be incremented by 1, if anything in the schema changes
version Int @default(1)
// Relations
exam ExamEnvironmentExam @relation(fields: [examId], references: [id], onDelete: Cascade)
@@ -549,6 +558,9 @@ model ExamEnvironmentExamModeration {
/// Date the exam attempt was added to the moderation queue
submissionDate DateTime @default(now()) @db.Date
/// Version of the record
/// The default must be incremented by 1, if anything in the schema changes
version Int @default(1)
// Relations
examAttempt ExamEnvironmentExamAttempt @relation(fields: [examAttemptId], references: [id], onDelete: Cascade)
@@ -512,7 +512,9 @@ describe('/exam-environment/', () => {
generatedExamId: generatedExam!.id,
questionSets: [],
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
startTimeInMS: expect.any(Number)
startTimeInMS: expect.any(Number),
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
version: expect.any(Number)
});
});
@@ -1,11 +1,16 @@
import { ExamEnvironmentAnswer } from '@prisma/client';
import {
ExamEnvironmentAnswer,
ExamEnvironmentQuestionType
} from '@prisma/client';
import { type Static } from '@fastify/type-provider-typebox';
import {
exam,
examAttempt,
generatedExam
generatedExam,
oid
} from '../../../__mocks__/exam-environment-exam';
import * as schemas from '../schemas';
import { setupServer } from '../../../jest.utils';
import {
checkAttemptAgainstGeneratedExam,
checkPrerequisites,
@@ -408,3 +413,117 @@ describe('Exam Environment', () => {
});
});
});
describe('Exam Environment Schema', () => {
setupServer();
describe('ExamEnvironmentExam', () => {
afterAll(async () => {
await fastifyTestInstance.prisma.examEnvironmentExam.deleteMany({});
});
it("If this test fails and you've deliberately altered the schema, then increment the `version` field by 1", async () => {
const configQuestionSets = [
{
numberOfCorrectAnswers: 0,
numberOfIncorrectAnswers: 0,
numberOfQuestions: 0,
numberOfSet: 0,
type: ExamEnvironmentQuestionType.MultipleChoice
}
];
const tags = [
{
group: [''],
numberOfQuestions: 0
}
];
const config = {
name: '',
note: '',
passingPercent: 0.0,
questionSets: configQuestionSets,
retakeTimeInMS: 0,
tags,
totalTimeInMS: 0
};
const questions = [
{
answers: [
{
id: oid(),
isCorrect: false,
text: ''
}
],
audio: { captions: '', url: '' },
deprecated: false,
id: oid(),
tags: [''],
text: ''
}
];
const questionSets = [
{
context: '',
id: oid(),
questions,
type: ExamEnvironmentQuestionType.MultipleChoice
}
];
const data = {
config,
deprecated: false,
prerequisites: [oid()],
questionSets
};
await fastifyTestInstance.prisma.examEnvironmentExam.create({
data
});
});
});
describe('ExamEnvironmentGeneratedExam', () => {
afterAll(async () => {
await fastifyTestInstance.prisma.examEnvironmentGeneratedExam.deleteMany(
{}
);
});
it("If this test fails and you've deliberately altered the schema, then increment the `version` field by 1", async () => {
await fastifyTestInstance.prisma.examEnvironmentGeneratedExam.create({
data: {
deprecated: false,
examId: oid(),
questionSets: [
{ id: oid(), questions: [{ answers: [oid()], id: oid() }] }
]
}
});
});
});
describe('ExamEnvironmentExamAttempt', () => {
afterAll(async () => {
await fastifyTestInstance.prisma.examEnvironmentExamAttempt.deleteMany(
{}
);
});
it("If this test fails and you've deliberately altered the schema, then increment the `version` field by 1", async () => {
await fastifyTestInstance.prisma.examEnvironmentExamAttempt.create({
data: {
examId: oid(),
generatedExamId: oid(),
questionSets: [
{
id: oid(),
questions: [
{ answers: [oid()], id: oid(), submissionTimeInMS: 0 }
]
}
],
startTimeInMS: 0,
userId: oid()
}
});
});
});
});
@@ -41,7 +41,7 @@ export function checkPrerequisites(
export type UserExam = Omit<
ExamEnvironmentExam,
'questionSets' | 'config' | 'id' | 'prerequisites' | 'deprecated'
'questionSets' | 'config' | 'id' | 'prerequisites' | 'deprecated' | 'version'
> & {
config: Omit<ExamEnvironmentExam['config'], 'tags' | 'questionSets'>;
questionSets: (Omit<ExamEnvironmentQuestionSet, 'questions'> & {
@@ -280,7 +280,7 @@ export function userAttemptToDatabaseAttemptQuestionSets(
*/
export function generateExam(
exam: ExamEnvironmentExam
): Omit<ExamEnvironmentGeneratedExam, 'id'> {
): Omit<ExamEnvironmentGeneratedExam, 'id' | 'version'> {
const examCopy = structuredClone(exam);
const TIMEOUT_IN_MS = 5_000;