Skip to content

Commit

Permalink
fix(api): handle answer duplicates in junior process
Browse files Browse the repository at this point in the history
  • Loading branch information
aurelie-crouillebois committed Feb 7, 2025
1 parent 20b8146 commit ac31885
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 6 deletions.
6 changes: 6 additions & 0 deletions api/src/school/domain/models/Mission.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ class Mission {
}
}

getChallengeIndex(activityInfo, challengeId) {
return this.getChallengeIds(activityInfo).findIndex((challengeVersionIds) =>
challengeVersionIds.includes(challengeId),
);
}

get stepCount() {
return this.content.steps.length;
}
Expand Down
7 changes: 4 additions & 3 deletions api/src/school/domain/services/update-current-activity.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function updateCurrentActivity({
if (lastAnswer.result.isOK() || lastActivity.isTutorial) {
const { missionId } = await missionAssessmentRepository.getByAssessmentId(assessmentId);
const mission = await missionRepository.get(missionId);
if (_isActivityFinished(mission, lastActivity, answers)) {
if (_isActivityFinished(mission, lastActivity, lastAnswer)) {
return activityRepository.updateStatus({ activityId: lastActivity.id, status: Activity.status.SUCCEEDED });
}
return lastActivity;
Expand All @@ -26,6 +26,7 @@ export async function updateCurrentActivity({
return activityRepository.updateStatus({ activityId: lastActivity.id, status: Activity.status.SKIPPED });
}

function _isActivityFinished(mission, lastActivity, answers) {
return mission.getChallengeIds(new ActivityInfo(lastActivity)).length === answers.length;
function _isActivityFinished(mission, lastActivity, lastAnswer) {
const challengeIds = mission.getChallengeIds(new ActivityInfo(lastActivity));
return challengeIds.at(-1).includes(lastAnswer.challengeId);
}
6 changes: 4 additions & 2 deletions api/src/school/domain/usecases/get-next-challenge.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ export async function getNextChallenge({
const mission = await missionRepository.get(missionId);
const answers = await activityAnswerRepository.findByActivity(activity.id);

const activityInfo = new ActivityInfo({ level: activity.level, stepIndex: activity.stepIndex });
const previousChallengeIndex = mission.getChallengeIndex(activityInfo, answers.at(-1).challengeId);
const challengeId = mission.getChallengeId({
activityInfo: new ActivityInfo({ level: activity.level, stepIndex: activity.stepIndex }),
challengeIndex: answers.length,
activityInfo,
challengeIndex: previousChallengeIndex + 1,
alternativeVersion: activity.alternativeVersion,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,58 @@ describe('Integration | UseCase | update current activity', function () {
expect(currentActivity.status).equals(Activity.status.STARTED);
});
});
context('when activity is not finished but has answer duplicate', function () {
it('should not update current activity status', async function () {
const { assessmentId, missionId } = databaseBuilder.factory.buildMissionAssessment();
const { id: activityId } = databaseBuilder.factory.buildActivity({
assessmentId,
level: Activity.levels.VALIDATION,
status: Activity.status.STARTED,
stepIndex: 0,
});
databaseBuilder.factory.buildActivityAnswer({
activityId,
challengeId: 'va_challenge_id',
result: AnswerStatus.statuses.OK,
});
// Anwser duplicate
databaseBuilder.factory.buildActivityAnswer({
activityId,
challengeId: 'va_challenge_id',
result: AnswerStatus.statuses.OK,
});

await databaseBuilder.commit();

await mockLearningContent({
missions: [
learningContentBuilder.buildMission({
id: missionId,
content: {
steps: [
{
validationChallenges: [['va_challenge_id'], ['va_next_challenge_id']],
},
],
},
}),
],
});

const currentActivity = await updateCurrentActivity({
assessmentId,
activityRepository,
activityAnswerRepository,
missionAssessmentRepository,
missionRepository,
});

const activities = await knex('activities').where({ assessmentId });
expect(activities).to.have.lengthOf(1);
expect(activities[0].status).to.equal(Activity.status.STARTED);
expect(currentActivity.status).equals(Activity.status.STARTED);
});
});

context('when activity is finished', function () {
it('should update current activity with SUCCEEDED status', async function () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,61 @@ describe('Integration | School | Usecase | get-next-challenge', function () {
expect(updatedAssessment.lastChallengeId).to.equal('second_va_challenge_on_step_2_id');
});
});
context('when last activity is started but has answer duplicate', function () {
it('should return next challenge and update assessment', async function () {
const { assessmentId, missionId } = databaseBuilder.factory.buildMissionAssessment();

const { id: firstStepActivityId } = databaseBuilder.factory.buildActivity({
assessmentId,
level: Activity.levels.VALIDATION,
status: Activity.status.STARTED,
stepIndex: 0,
createdAt: new Date('2022-09-14'),
});
databaseBuilder.factory.buildActivityAnswer({
activityId: firstStepActivityId,
challengeId: 'first_va_challenge_id',
});
// Answer duplicate
databaseBuilder.factory.buildActivityAnswer({
activityId: firstStepActivityId,
challengeId: 'first_va_challenge_id',
});

await databaseBuilder.commit();

await mockLearningContent({
challenges: [learningContentBuilder.buildChallenge({ id: 'second_va_challenge_id', skillId: 'skill_id' })],
skills: [learningContentBuilder.buildSkill({ id: 'skill_id' })],
missions: [
learningContentBuilder.buildMission({
id: missionId,
content: {
steps: [
{
validationChallenges: [['first_va_challenge_id'], ['second_va_challenge_id']],
},
],
},
}),
],
});

const challenge = await getNextChallenge({
assessmentId,
activityRepository,
activityAnswerRepository,
challengeRepository,
assessmentRepository,
missionAssessmentRepository,
missionRepository,
});

const updatedAssessment = await knex('assessments').where({ id: assessmentId }).first();
expect(challenge.id).to.equal('second_va_challenge_id');
expect(challenge).to.be.instanceOf(Challenge);
expect(updatedAssessment.lastChallengeId).to.equal('second_va_challenge_id');
});
});
});
});
26 changes: 25 additions & 1 deletion api/tests/school/unit/domain/models/Mission_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ describe('Unit | Domain | School', function () {
});
});

it('should call challengeRepository#get with challenge id of accurate number in activity', function () {
it('should return challenge id of accurate index in activity', function () {
const mission = domainBuilder.buildMission({
content: {
steps: [
Expand Down Expand Up @@ -210,4 +210,28 @@ describe('Unit | Domain | School', function () {
});
});
});

describe('#getChallengeIndex', function () {
it('should return the index of given challenge in given activity', function () {
const mission = domainBuilder.buildMission({
content: {
steps: [
{
tutorialChallenges: [
['tutorial-challenge-id-1'],
['tutorial-challenge-id-2'],
['tutorial-challenge-id-3'],
],
},
],
},
});
const challengeIndex = mission.getChallengeIndex(
new ActivityInfo({ level: Activity.levels.TUTORIAL, stepIndex: 0 }),
'tutorial-challenge-id-2',
);

expect(challengeIndex).to.equal(1);
});
});
});

0 comments on commit ac31885

Please sign in to comment.