mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-04-12 10:00:39 -04:00
feat(api): add prerequisites to env exam (#56731)
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
@@ -338,7 +338,8 @@ export const examAttemptSansSubmissionTimeInMS: Static<
|
||||
export const exam: EnvExam = {
|
||||
id: examId,
|
||||
config,
|
||||
questionSets
|
||||
questionSets,
|
||||
prerequisites: ['67112fe1c994faa2c26d0b1d']
|
||||
};
|
||||
|
||||
export async function seedEnvExam() {
|
||||
|
||||
@@ -151,11 +151,13 @@ model user {
|
||||
/// An exam for the Exam Environment App as designed by the examiners
|
||||
model EnvExam {
|
||||
/// Globally unique exam id
|
||||
id String @id @default(auto()) @map("_id") @db.ObjectId
|
||||
id String @id @default(auto()) @map("_id") @db.ObjectId
|
||||
/// All questions for a given exam
|
||||
questionSets EnvQuestionSet[]
|
||||
questionSets EnvQuestionSet[]
|
||||
/// Configuration for exam metadata
|
||||
config EnvConfig
|
||||
config EnvConfig
|
||||
/// ObjectIds for required challenges/blocks to take the exam
|
||||
prerequisites String[] @db.ObjectId
|
||||
|
||||
// Relations
|
||||
generatedExams EnvGeneratedExam[]
|
||||
|
||||
@@ -240,6 +240,15 @@ describe('/exam-environment/', () => {
|
||||
describe('POST /exam-environment/generated-exam', () => {
|
||||
afterEach(async () => {
|
||||
await fastifyTestInstance.prisma.envExamAttempt.deleteMany();
|
||||
// Add prerequisite id to user completed challenge
|
||||
await fastifyTestInstance.prisma.user.update({
|
||||
where: { id: defaultUserId },
|
||||
data: {
|
||||
completedChallenges: [
|
||||
{ id: mock.exam.prerequisites.at(0)!, completedDate: Date.now() }
|
||||
]
|
||||
}
|
||||
});
|
||||
await mock.seedEnvExam();
|
||||
});
|
||||
|
||||
@@ -262,8 +271,30 @@ describe('/exam-environment/', () => {
|
||||
expect(res.status).toBe(404);
|
||||
});
|
||||
|
||||
xit('should return an error if the exam prerequisites are not met', async () => {
|
||||
// TODO: Waiting on prerequisites
|
||||
it('should return an error if the exam prerequisites are not met', async () => {
|
||||
await fastifyTestInstance.prisma.user.update({
|
||||
where: { id: defaultUserId },
|
||||
data: {
|
||||
completedChallenges: []
|
||||
}
|
||||
});
|
||||
|
||||
const body: Static<typeof examEnvironmentPostExamGeneratedExam.body> = {
|
||||
examId: mock.exam.id
|
||||
};
|
||||
const res = await superPost('/exam-environment/exam/generated-exam')
|
||||
.send(body)
|
||||
.set(
|
||||
'exam-environment-authorization-token',
|
||||
examEnvironmentAuthorizationToken
|
||||
);
|
||||
|
||||
expect(res.body).toStrictEqual({
|
||||
code: 'FCC_EINVAL_EXAM_ENVIRONMENT_PREREQUISITES',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
message: expect.any(String)
|
||||
});
|
||||
expect(res.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should return an error if the exam has been attempted in the last 24 hours', async () => {
|
||||
|
||||
@@ -170,7 +170,7 @@ async function postExamGeneratedExamHandler(
|
||||
|
||||
// Check user has completed prerequisites
|
||||
const user = req.user!;
|
||||
const isExamPrerequisitesMet = checkPrerequisites(user, true);
|
||||
const isExamPrerequisitesMet = checkPrerequisites(user, exam.prerequisites);
|
||||
|
||||
if (!isExamPrerequisitesMet) {
|
||||
void reply.code(403);
|
||||
@@ -582,12 +582,13 @@ async function getExams(
|
||||
const exams = await this.prisma.envExam.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
config: true
|
||||
config: true,
|
||||
prerequisites: true
|
||||
}
|
||||
});
|
||||
|
||||
const availableExams = exams.map(exam => {
|
||||
const isExamPrerequisitesMet = checkPrerequisites(user, true);
|
||||
const isExamPrerequisitesMet = checkPrerequisites(user, exam.prerequisites);
|
||||
|
||||
return {
|
||||
id: exam.id,
|
||||
|
||||
@@ -3,6 +3,7 @@ import { exam, examAttempt, generatedExam } from '../../../__mocks__/env-exam';
|
||||
import * as schemas from '../schemas';
|
||||
import {
|
||||
checkAttemptAgainstGeneratedExam,
|
||||
checkPrerequisites,
|
||||
constructUserExam,
|
||||
generateExam,
|
||||
userAttemptToDatabaseAttemptQuestionSets,
|
||||
@@ -56,9 +57,27 @@ describe('Exam Environment', () => {
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
xdescribe('checkPrequisites()', () => {
|
||||
// TODO: Awaiting implementation
|
||||
|
||||
describe('checkPrequisites()', () => {
|
||||
it("should return true if all items in the second argument exist in the first argument's `.completedChallenges[].id`", () => {
|
||||
const user = {
|
||||
completedChallenges: [{ id: '1' }, { id: '2' }]
|
||||
};
|
||||
const prerequisites = ['1', '2'];
|
||||
|
||||
expect(checkPrerequisites(user, prerequisites)).toBe(true);
|
||||
});
|
||||
|
||||
it("should return false if any items in the second argument do not exist in the first argument's `.completedChallenges[].id`", () => {
|
||||
const user = {
|
||||
completedChallenges: [{ id: '2' }]
|
||||
};
|
||||
const prerequisites = ['1', '2'];
|
||||
|
||||
expect(checkPrerequisites(user, prerequisites)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructUserExam()', () => {
|
||||
it('should not provide the answers', () => {
|
||||
const userExam = constructUserExam(generatedExam, exam);
|
||||
|
||||
@@ -7,22 +7,33 @@ import {
|
||||
EnvGeneratedExam,
|
||||
EnvMultipleChoiceQuestion,
|
||||
EnvQuestionSet,
|
||||
EnvQuestionSetAttempt,
|
||||
user
|
||||
EnvQuestionSetAttempt
|
||||
} from '@prisma/client';
|
||||
import { type Static } from '@fastify/type-provider-typebox';
|
||||
import * as schemas from '../schemas';
|
||||
|
||||
/**
|
||||
* Checks if all exam prerequisites have been met by the user.
|
||||
*
|
||||
* TODO: This will be done by getting the challenges required from the curriculum.
|
||||
*/
|
||||
export function checkPrerequisites(_user: user, _prerequisites: unknown) {
|
||||
return true;
|
||||
interface CompletedChallengeId {
|
||||
completedChallenges: {
|
||||
id: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export type UserExam = Omit<EnvExam, 'questionSets' | 'config' | 'id'> & {
|
||||
/**
|
||||
* Checks if all exam prerequisites have been met by the user.
|
||||
*/
|
||||
export function checkPrerequisites(
|
||||
user: CompletedChallengeId,
|
||||
prerequisites: EnvExam['prerequisites']
|
||||
) {
|
||||
return prerequisites.every(p =>
|
||||
user.completedChallenges.some(c => c.id === p)
|
||||
);
|
||||
}
|
||||
|
||||
export type UserExam = Omit<
|
||||
EnvExam,
|
||||
'questionSets' | 'config' | 'id' | 'prerequisites'
|
||||
> & {
|
||||
config: Omit<EnvExam['config'], 'tags' | 'questionSets'>;
|
||||
questionSets: (Omit<EnvQuestionSet, 'questions'> & {
|
||||
questions: (Omit<
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
---
|
||||
id: 645147516c245de4d11eb7ba
|
||||
title: Front End Development Certification Exam
|
||||
challengeType: 17
|
||||
challengeType: 24
|
||||
dashedName: front-end-development-certification-exam
|
||||
---
|
||||
|
||||
# --description--
|
||||
|
||||
Here are some rules:
|
||||
|
||||
- click start
|
||||
Start your exam in the exam environment app.
|
||||
|
||||
# --instructions--
|
||||
|
||||
|
||||
Reference in New Issue
Block a user