diff --git a/api/db/seeds/data/team-prescription/build-quests.js b/api/db/seeds/data/team-prescription/build-quests.js index cedad40a15d..a21e3b162b7 100644 --- a/api/db/seeds/data/team-prescription/build-quests.js +++ b/api/db/seeds/data/team-prescription/build-quests.js @@ -36,42 +36,69 @@ const USERS = [ }, ]; const ORGANIZATION = { name: 'attestation', type: 'SCO', isManagingStudents: true }; -const CAMPAIGN = { code: 'ATESTTEST', multipleSendings: true }; +const CAMPAIGN = [ + { code: 'ATTEST001', multipleSendings: true, name: 'campagne attestation 1' }, + { code: 'ATTEST002', multipleSendings: true, name: 'campagne attestation 2' }, + { code: 'ATTEST003', multipleSendings: true, name: 'campagne attestation 3' }, +]; -const TUBES = [ - { - id: 'tube2e715GxaaWzNK6', - level: 2, - }, - { - id: 'recs1vdbHxX8X55G9', - level: 2, - }, - { - id: 'reccqGUKgzIOK8f9U', - level: 2, - }, - { - id: 'recBbCIEKgrQi7eb6', - level: 2, - }, - { - id: 'recpe7Y8Wq2D56q6I', - level: 2, - }, - { - id: 'recPOjwrHFhM21yGE', - level: 2, - }, +const TARGET_PROFILE_TUBES = [ + [ + { + id: 'tube2e715GxaaWzNK6', + level: 2, + }, + { + id: 'recs1vdbHxX8X55G9', + level: 2, + }, + { + id: 'reccqGUKgzIOK8f9U', + level: 2, + }, + { + id: 'recBbCIEKgrQi7eb6', + level: 2, + }, + { + id: 'recpe7Y8Wq2D56q6I', + level: 2, + }, + ], + [ + { + id: 'tube2e715GxaaWzNK6', + level: 2, + }, + { + id: 'recs1vdbHxX8X55G9', + level: 2, + }, + { + id: 'reccqGUKgzIOK8f9U', + level: 2, + }, + ], + [ + { + id: 'recBbCIEKgrQi7eb6', + level: 2, + }, + { + id: 'recpe7Y8Wq2D56q6I', + level: 2, + }, + { + id: 'recPOjwrHFhM21yGE', + level: 2, + }, + ], ]; -const SKILLS = [ - 'skill2wQfMYrOHlL6HI', - 'skill1QAVccgLO16Rx8', - 'skillX5Rpk2rucNfnF', - 'skill1aj7jVAKrVgUye', - 'reczOCGv8pz976Acl', - 'skill2mIMdudcltFsaz', +const CAMPAIGN_SKILLS = [ + ['skill2wQfMYrOHlL6HI', 'skill1QAVccgLO16Rx8', 'skillX5Rpk2rucNfnF', 'skill1aj7jVAKrVgUye', 'reczOCGv8pz976Acl'], + ['skill2wQfMYrOHlL6HI', 'skill1QAVccgLO16Rx8', 'skillX5Rpk2rucNfnF'], + ['skill1aj7jVAKrVgUye', 'reczOCGv8pz976Acl', 'skill2mIMdudcltFsaz'], ]; const buildUsers = (databaseBuilder) => USERS.map((user) => databaseBuilder.factory.buildUser.withRawPassword(user)); @@ -86,20 +113,38 @@ const buildOrganizationLearners = (databaseBuilder, organization, users) => }), ); -const buildCampaignParticipations = (databaseBuilder, campaignId, users) => - users.map(({ user, organizationLearner, status, sharedAt }) => - databaseBuilder.factory.buildCampaignParticipation({ +const buildCampaignParticipations = (databaseBuilder, users) => + users.map(async ({ user, organizationLearner, status, sharedAt }) => { + const stages = await databaseBuilder.knex('stages').where({ targetProfileId: TARGET_PROFILE_BADGES_STAGES_ID }); + const stageZero = stages.find((stage) => stage.level === 0 || stage.threshold === 0); + + const { id: participationId } = databaseBuilder.factory.buildCampaignParticipation({ userId: user.id, - campaignId, + campaignId: user.campaignId, masteryRate: 1, organizationLearnerId: organizationLearner.id, status, sharedAt, - }), - ); + }); + databaseBuilder.factory.buildAssessment({ + userId: user.id, + type: Assessment.types.CAMPAIGN, + campaignParticipationId: participationId, + }); + + databaseBuilder.factory.buildStageAcquisition({ + stageId: stageZero.id, + userId: user.id, + campaignParticipationId: participationId, + }); + }); -const buildQuest = (databaseBuilder, rewardId, targetProfileId) => { - const questEligibilityRequirements = [ +const buildSixthGradeQuests = ( + databaseBuilder, + rewardId, + [firstTargetProfile, secondTargetProfile, thirdTargetProfile], +) => { + const firstQuestRequirement = [ { type: 'organization', data: { @@ -118,17 +163,16 @@ const buildQuest = (databaseBuilder, rewardId, targetProfileId) => { { type: 'campaignParticipations', data: { - targetProfileIds: [targetProfileId], + targetProfileIds: [firstTargetProfile.id], }, comparison: COMPARISON.ALL, }, ]; - - const questSuccessRequirements = [ + const firstQuestSuccessRequirements = [ { type: 'skill', data: { - ids: SKILLS, + ids: CAMPAIGN_SKILLS[0], threshold: 50, }, }, @@ -137,58 +181,90 @@ const buildQuest = (databaseBuilder, rewardId, targetProfileId) => { databaseBuilder.factory.buildQuest({ rewardType: REWARD_TYPES.ATTESTATION, rewardId, - eligibilityRequirements: questEligibilityRequirements, - successRequirements: questSuccessRequirements, + eligibilityRequirements: firstQuestRequirement, + successRequirements: firstQuestSuccessRequirements, }); -}; -const buildFirstStages = async ( - databaseBuilder, - successUser, - successParticipation, - failedUser, - failedParticipation, - pendingUser, - pendingParticipation, -) => { - const stages = await databaseBuilder.knex('stages').where({ targetProfileId: TARGET_PROFILE_BADGES_STAGES_ID }); + const secondQuestEligibilityRequirements = [ + { + type: 'organization', + data: { + type: 'SCO', + }, + comparison: COMPARISON.ALL, + }, + { + type: 'organization', + data: { + isManagingStudents: true, + tags: [AEFE_TAG.name], + }, + comparison: COMPARISON.ONE_OF, + }, + { + type: 'campaignParticipations', + data: { + targetProfileIds: [secondTargetProfile.id, thirdTargetProfile.id], + }, + comparison: COMPARISON.ALL, + }, + ]; - const stageZero = stages.find((stage) => stage.level === 0 || stage.threshold === 0); + const secondQuestSuccessRequirements = [ + { + type: 'skill', + data: { + ids: [CAMPAIGN_SKILLS[1], CAMPAIGN_SKILLS[2]].flat(), + threshold: 50, + }, + }, + ]; - databaseBuilder.factory.buildStageAcquisition({ - stageId: stageZero.id, - userId: successUser.id, - campaignParticipationId: successParticipation.id, - }); - databaseBuilder.factory.buildStageAcquisition({ - stageId: stageZero.id, - userId: failedUser.id, - campaignParticipationId: failedParticipation.id, - }); - databaseBuilder.factory.buildStageAcquisition({ - stageId: stageZero.id, - userId: pendingUser.id, - campaignParticipationId: pendingParticipation.id, + databaseBuilder.factory.buildQuest({ + rewardType: REWARD_TYPES.ATTESTATION, + rewardId, + eligibilityRequirements: secondQuestEligibilityRequirements, + successRequirements: secondQuestSuccessRequirements, }); }; -const buildTargetProfile = (databaseBuilder, organization) => { - const targetProfile = databaseBuilder.factory.buildTargetProfile({ - description: 'parcours attestation 6 eme', - name: 'parcours attestation 6 eme', - ownerOrganizationId: organization.id, +const buildTargetProfiles = (databaseBuilder, organization) => + TARGET_PROFILE_TUBES.map((tubes, index) => { + const targetProfile = databaseBuilder.factory.buildTargetProfile({ + description: `parcours attestation 6 eme numero ${index + 1}`, + name: `parcours attestation 6 eme numero ${index + 1}`, + ownerOrganizationId: organization.id, + }); + + tubes.map(({ tubeId, level }) => + databaseBuilder.factory.buildTargetProfileTube({ + targetProfileId: targetProfile.id, + tubeId, + level, + }), + ); + + return targetProfile; }); - TUBES.map(({ tubeId, level }) => - databaseBuilder.factory.buildTargetProfileTube({ +const buildCampaigns = (databaseBuilder, organization, targetProfiles) => + targetProfiles.map((targetProfile, index) => { + const { id: campaignId } = databaseBuilder.factory.buildCampaign({ + ...CAMPAIGN[index], targetProfileId: targetProfile.id, - tubeId, - level, - }), - ); + organizationId: organization.id, + title: `Attestation 6ème ${index + 1}`, + }); - return targetProfile; -}; + CAMPAIGN_SKILLS[index].map((skillId) => + databaseBuilder.factory.buildCampaignSkill({ + campaignId, + skillId, + }), + ); + + return campaignId; + }); export const buildQuests = async (databaseBuilder) => { // Create USERS @@ -235,77 +311,53 @@ export const buildQuests = async (databaseBuilder) => { ] = buildOrganizationLearners(databaseBuilder, organization, organizationLearnersData); // Create target profile - const targetProfile = buildTargetProfile(databaseBuilder, organization); + + const targetProfiles = buildTargetProfiles(databaseBuilder, organization); // Create campaigns - const { id: campaignId } = databaseBuilder.factory.buildCampaign({ - ...CAMPAIGN, - targetProfileId: targetProfile.id, - organizationId: organization.id, - }); - SKILLS.map((skillId) => - databaseBuilder.factory.buildCampaignSkill({ - campaignId, - skillId, - }), - ); + const campaigns = buildCampaigns(databaseBuilder, organization, targetProfiles); // Create campaignParticipations - const [successParticipation, successSharedParticipation, failedParticipation, pendingParticipation] = - buildCampaignParticipations(databaseBuilder, campaignId, [ - { - user: successUser, - organizationLearner: successOrganizationLearner, - sharedAt: null, - status: CampaignParticipationStatuses.TO_SHARE, - }, - { - user: successSharedUser, - organizationLearner: successSharedOrganizationLearner, - }, - { - user: failedUser, - organizationLearner: failedOrganizationLearner, - }, - { - user: pendingUser, - organizationLearner: pendingOrganizationLearner, - }, - ]); - // Create assessments - databaseBuilder.factory.buildAssessment({ - userId: successUser.id, - type: Assessment.types.CAMPAIGN, - campaignParticipationId: successParticipation.id, - }); - databaseBuilder.factory.buildAssessment({ - userId: successSharedUser.id, - type: Assessment.types.CAMPAIGN, - campaignParticipationId: successSharedParticipation.id, - }); - databaseBuilder.factory.buildAssessment({ - userId: failedUser.id, - type: Assessment.types.CAMPAIGN, - campaignParticipationId: failedParticipation.id, - }); - databaseBuilder.factory.buildAssessment({ - userId: pendingUser.id, - type: Assessment.types.CAMPAIGN, - campaignParticipationId: pendingParticipation.id, - }); - - // Create first stage - await buildFirstStages( - databaseBuilder, - successUser, - successParticipation, - failedUser, - failedParticipation, - pendingUser, - pendingParticipation, - ); + buildCampaignParticipations(databaseBuilder, [ + { + user: successUser, + campaignId: campaigns[0].id, + organizationLearner: successOrganizationLearner, + sharedAt: null, + status: CampaignParticipationStatuses.TO_SHARE, + }, + { + user: successUser, + campaignId: campaigns[1].id, + organizationLearner: successOrganizationLearner, + sharedAt: null, + status: CampaignParticipationStatuses.TO_SHARE, + }, + { + user: successUser, + campaignId: campaigns[2].id, + organizationLearner: successOrganizationLearner, + sharedAt: null, + status: CampaignParticipationStatuses.TO_SHARE, + }, + { + user: successSharedUser, + campaignId: campaigns[0].id, + organizationLearner: successSharedOrganizationLearner, + }, + { + user: failedUser, + campaignId: campaigns[0].id, + organizationLearner: failedOrganizationLearner, + }, + { + user: pendingUser, + campaignId: campaigns[0].id, + organizationLearner: pendingOrganizationLearner, + }, + ]); // Create attestation quest const { id: rewardId } = databaseBuilder.factory.buildAttestation({ @@ -313,8 +365,8 @@ export const buildQuests = async (databaseBuilder) => { key: ATTESTATIONS.SIXTH_GRADE, }); - // Create quest - buildQuest(databaseBuilder, rewardId, targetProfile.id); + // Create quests + buildSixthGradeQuests(databaseBuilder, rewardId, targetProfiles); // Create reward for success user databaseBuilder.factory.buildProfileReward({ diff --git a/api/src/profile/domain/models/Campaign.js b/api/src/profile/domain/models/Campaign.js index 77690c91436..92e40149741 100644 --- a/api/src/profile/domain/models/Campaign.js +++ b/api/src/profile/domain/models/Campaign.js @@ -1,6 +1,7 @@ export class Campaign { - constructor({ id, organizationId }) { + constructor({ id, organizationId, targetProfileId }) { this.id = id; this.organizationId = organizationId; + this.targetProfileId = targetProfileId; } } diff --git a/api/src/profile/infrastructure/repositories/campaign-participation-repository.js b/api/src/profile/infrastructure/repositories/campaign-participation-repository.js index 0a4e0931c4c..012a480240a 100644 --- a/api/src/profile/infrastructure/repositories/campaign-participation-repository.js +++ b/api/src/profile/infrastructure/repositories/campaign-participation-repository.js @@ -4,7 +4,7 @@ import { Campaign } from '../../domain/models/Campaign.js'; export async function getCampaignByParticipationId({ campaignParticipationId }) { const knexConnection = DomainTransaction.getConnection(); const campaign = await knexConnection('campaign-participations') - .select('campaigns.id', 'campaigns.organizationId') + .select('campaigns.id', 'campaigns.organizationId', 'campaigns.targetProfileId') .innerJoin('campaigns', 'campaigns.id', 'campaign-participations.campaignId') .where({ 'campaign-participations.id': campaignParticipationId }) .first(); diff --git a/api/src/quest/domain/models/Eligibility.js b/api/src/quest/domain/models/Eligibility.js index a759948bcd6..481fbdfde4f 100644 --- a/api/src/quest/domain/models/Eligibility.js +++ b/api/src/quest/domain/models/Eligibility.js @@ -15,6 +15,10 @@ export class Eligibility { }; } + set campaignParticipations(campaignParticipations) { + this.#campaignParticipations = campaignParticipations; + } + hasCampaignParticipation(campaignParticipationId) { return Boolean( this.#campaignParticipations.find( @@ -23,6 +27,14 @@ export class Eligibility { ); } + hasCampaignParticipationForTargetProfileId(targetProfileId) { + return Boolean( + this.#campaignParticipations.find( + (campaignParticipation) => campaignParticipation.targetProfileId === targetProfileId, + ), + ); + } + getTargetProfileForCampaignParticipation(campaignParticipationId) { const campaignParticipation = this.#campaignParticipations.find( (campaignParticipation) => campaignParticipation.id === campaignParticipationId, diff --git a/api/src/quest/domain/usecases/get-quest-results-for-campaign-participation.js b/api/src/quest/domain/usecases/get-quest-results-for-campaign-participation.js index daa3eb1d964..a70e78f920e 100644 --- a/api/src/quest/domain/usecases/get-quest-results-for-campaign-participation.js +++ b/api/src/quest/domain/usecases/get-quest-results-for-campaign-participation.js @@ -1,9 +1,73 @@ +const getEligibilityForThisCampaignParticipation = async (eligibilityRepository, userId, campaignParticipationId) => { + const eligibilities = await eligibilityRepository.find({ userId }); + return eligibilities.find((e) => e.hasCampaignParticipation(campaignParticipationId)); +}; + +const getTargetProfileRequirementsPerQuest = (quests) => + quests + .map((quest) => { + const campaignParticipationsRequirement = quest.eligibilityRequirements.find( + (requirement) => requirement.type === 'campaignParticipations', + ); + if (campaignParticipationsRequirement && campaignParticipationsRequirement.data.targetProfileIds) + return campaignParticipationsRequirement.data.targetProfileIds; + }) + .filter(Boolean); + +/** + * This function retrieves the target profiles we should use for the current participation. + * It first retrieves the target profile for the current campaign participation. + * Then it retrieves the target profile requirements for each quest. + * It filters the target profile requirements to only keep the ones that contain the target profile for the current participation. + * It checks if the user has participated in campaigns linked to all the target profiles present in the quest requirements. + * If the user has participated in campaigns linked to all the target profiles present in the quest requirements, it returns the target profile requirements containing the target profile for the current participation. + * If not, it returns the target profile for the current participation. + * + * @param campaignParticipationRepository + * @param {number} campaignParticipationId + * @param {[Quest]} quests + * @param {Eligibility} eligibility + * @returns {Promise<[number]>} + */ +const getTargetProfilesForThisCampaignParticipation = async ({ + campaignParticipationRepository, + campaignParticipationId, + quests, + eligibility, +}) => { + const { targetProfileId: targetProfileForThisParticipation } = + await campaignParticipationRepository.getCampaignByParticipationId({ + campaignParticipationId, + }); + + const targetProfileRequirementsPerQuest = getTargetProfileRequirementsPerQuest(quests); + + const targetProfileRequirementsContainingTargetProfileForCurrentParticipation = + targetProfileRequirementsPerQuest.filter((targetProfileIds) => + targetProfileIds.includes(targetProfileForThisParticipation), + ); + + const targetProfileRequirementContainingTargetProfileForCurrentParticipationWithParticipationForEveryTargetProfile = + targetProfileRequirementsContainingTargetProfileForCurrentParticipation.find((targetProfileRequirement) => + targetProfileRequirement.every((targetProfileId) => + eligibility.hasCampaignParticipationForTargetProfileId(targetProfileId), + ), + ); + + return ( + targetProfileRequirementContainingTargetProfileForCurrentParticipationWithParticipationForEveryTargetProfile ?? [ + targetProfileForThisParticipation, + ] + ); +}; + export const getQuestResultsForCampaignParticipation = async ({ userId, campaignParticipationId, questRepository, eligibilityRepository, rewardRepository, + campaignParticipationRepository, }) => { const quests = await questRepository.findAll(); @@ -11,19 +75,25 @@ export const getQuestResultsForCampaignParticipation = async ({ return []; } - const eligibilities = await eligibilityRepository.find({ userId }); - const eligibility = eligibilities.find((e) => e.hasCampaignParticipation(campaignParticipationId)); + const eligibility = await getEligibilityForThisCampaignParticipation( + eligibilityRepository, + userId, + campaignParticipationId, + ); if (!eligibility) return []; - /* - This effectively overrides the existing campaignParticipations property with a new getter that always returns the updated targetProfileIds array based on the provided campaignParticipationId. - We can't just reassign the getter with the new value, because the getter will still be called and the new value would be ignored - */ - Object.defineProperty(eligibility, 'campaignParticipations', { - get: () => ({ targetProfileIds: [eligibility.getTargetProfileForCampaignParticipation(campaignParticipationId)] }), + const targetProfileIdsForThisCampaignParticipation = await getTargetProfilesForThisCampaignParticipation({ + campaignParticipationRepository, + campaignParticipationId, + quests, + eligibility, }); + eligibility.campaignParticipations = targetProfileIdsForThisCampaignParticipation.map((targetProfileId) => ({ + targetProfileId, + })); + const questResults = []; for (const quest of quests) { const isEligibleForQuest = quest.isEligible(eligibility); diff --git a/api/src/quest/domain/usecases/index.js b/api/src/quest/domain/usecases/index.js index f4708a7f30c..00e94f59c3b 100644 --- a/api/src/quest/domain/usecases/index.js +++ b/api/src/quest/domain/usecases/index.js @@ -1,6 +1,7 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; +import * as campaignParticipationRepository from '../../../profile/infrastructure/repositories/campaign-participation-repository.js'; import { injectDependencies } from '../../../shared/infrastructure/utils/dependency-injection.js'; import { importNamedExportsFromDirectory } from '../../../shared/infrastructure/utils/import-named-exports-from-directory.js'; import { repositories } from '../../infrastructure/repositories/index.js'; @@ -17,6 +18,7 @@ const dependencies = { rewardRepository: repositories.rewardRepository, successRepository: repositories.successRepository, questRepository, + campaignParticipationRepository, }; const usecases = injectDependencies(usecasesWithoutInjectedDependencies, dependencies); diff --git a/api/tests/profile/integration/infrastructure/repositories/campaign-participation-repository_test.js b/api/tests/profile/integration/infrastructure/repositories/campaign-participation-repository_test.js index ad97caee781..a8458f8abd7 100644 --- a/api/tests/profile/integration/infrastructure/repositories/campaign-participation-repository_test.js +++ b/api/tests/profile/integration/infrastructure/repositories/campaign-participation-repository_test.js @@ -19,6 +19,7 @@ describe('Profile | Integration | Infrastructure | Repository | campaign-partici expect(result).to.be.an.instanceOf(Campaign); expect(result.id).to.equal(campaign.id); expect(result.organizationId).to.equal(campaign.organizationId); + expect(result.targetProfileId).to.equal(campaign.targetProfileId); }); it('return null if campaignParticipation does not exist', async function () { diff --git a/api/tests/quest/integration/domain/usecases/get-quest-results-for-campaign-participation_test.js b/api/tests/quest/integration/domain/usecases/get-quest-results-for-campaign-participation_test.js index 1acde3d74f4..ac240e9df3e 100644 --- a/api/tests/quest/integration/domain/usecases/get-quest-results-for-campaign-participation_test.js +++ b/api/tests/quest/integration/domain/usecases/get-quest-results-for-campaign-participation_test.js @@ -4,6 +4,167 @@ import { usecases } from '../../../../../src/quest/domain/usecases/index.js'; import { databaseBuilder, expect } from '../../../../test-helper.js'; describe('Quest | Integration | Domain | Usecases | getQuestResultsForCampaignParticipation', function () { + describe('when there are multiple target profiles in the quest requirements', function () { + it('should get quest results for campaign participation belonging to one of the target profiles', async function () { + const organizationId = databaseBuilder.factory.buildOrganization({ type: 'SCO' }).id; + const { id: organizationLearnerId, userId } = databaseBuilder.factory.buildOrganizationLearner({ + organizationId, + }); + + // build target profiles + + const firstTargetProfile = databaseBuilder.factory.buildTargetProfile({ + ownerOrganizationId: organizationId, + }); + const secondTargetProfile = databaseBuilder.factory.buildTargetProfile({ + ownerOrganizationId: organizationId, + }); + + // build campaigns + + const firstCampaign = databaseBuilder.factory.buildCampaign({ + organizationId, + targetProfileId: firstTargetProfile.id, + }); + + const secondCampaign = databaseBuilder.factory.buildCampaign({ + organizationId, + targetProfileId: secondTargetProfile.id, + }); + + // build campaign participations + + databaseBuilder.factory.buildCampaignParticipation({ + organizationLearnerId, + campaignId: firstCampaign.id, + userId, + }); + + const { id: secondCampaignParticipationId } = databaseBuilder.factory.buildCampaignParticipation({ + organizationLearnerId, + campaignId: secondCampaign.id, + userId, + }); + + const rewardId = databaseBuilder.factory.buildAttestation().id; + const questId = databaseBuilder.factory.buildQuest({ + rewardType: 'attestations', + rewardId, + eligibilityRequirements: [ + { + type: 'organization', + data: { + type: 'SCO', + }, + comparison: COMPARISON.ALL, + }, + { + type: 'campaignParticipations', + data: { + targetProfileIds: [firstTargetProfile.id, secondTargetProfile.id], + }, + comparison: COMPARISON.ALL, + }, + ], + successRequirements: [], + }).id; + + await databaseBuilder.commit(); + + const result = await usecases.getQuestResultsForCampaignParticipation({ + userId, + campaignParticipationId: secondCampaignParticipationId, + }); + + expect(result[0]).to.be.instanceOf(QuestResult); + expect(result[0].id).to.equal(questId); + expect(result[0].reward.id).to.equal(rewardId); + }); + + it('should not return quest results for campaign participation if user has not participated to campaigns linked to all profiles target in quest requirement', async function () { + const organizationId = databaseBuilder.factory.buildOrganization({ type: 'SCO' }).id; + const { id: organizationLearnerId, userId } = databaseBuilder.factory.buildOrganizationLearner({ + organizationId, + }); + + // build target profiles + + const firstTargetProfile = databaseBuilder.factory.buildTargetProfile({ + ownerOrganizationId: organizationId, + }); + const secondTargetProfile = databaseBuilder.factory.buildTargetProfile({ + ownerOrganizationId: organizationId, + }); + const thirdTargetProfile = databaseBuilder.factory.buildTargetProfile({ + ownerOrganizationId: organizationId, + }); + + // build campaigns + + const firstCampaign = databaseBuilder.factory.buildCampaign({ + organizationId, + targetProfileId: firstTargetProfile.id, + }); + + const secondCampaign = databaseBuilder.factory.buildCampaign({ + organizationId, + targetProfileId: secondTargetProfile.id, + }); + + databaseBuilder.factory.buildCampaign({ + organizationId, + targetProfileId: thirdTargetProfile.id, + }); + + // build campaign participations + + databaseBuilder.factory.buildCampaignParticipation({ + organizationLearnerId, + campaignId: firstCampaign.id, + userId, + }); + + const { id: secondCampaignParticipationId } = databaseBuilder.factory.buildCampaignParticipation({ + organizationLearnerId, + campaignId: secondCampaign.id, + userId, + }); + + const rewardId = databaseBuilder.factory.buildAttestation().id; + + databaseBuilder.factory.buildQuest({ + rewardType: 'attestations', + rewardId, + eligibilityRequirements: [ + { + type: 'organization', + data: { + type: 'SCO', + }, + comparison: COMPARISON.ALL, + }, + { + type: 'campaignParticipations', + data: { + targetProfileIds: [firstTargetProfile.id, secondTargetProfile.id, thirdTargetProfile.id], + }, + comparison: COMPARISON.ALL, + }, + ], + successRequirements: [], + }).id; + + await databaseBuilder.commit(); + + const result = await usecases.getQuestResultsForCampaignParticipation({ + userId, + campaignParticipationId: secondCampaignParticipationId, + }); + + expect(result).to.be.empty; + }); + }); + it('should get quest results for campaign participation', async function () { const organizationId = databaseBuilder.factory.buildOrganization({ type: 'SCO' }).id; const { id: organizationLearnerId, userId } = databaseBuilder.factory.buildOrganizationLearner({ organizationId }); @@ -33,6 +194,7 @@ describe('Quest | Integration | Domain | Usecases | getQuestResultsForCampaignPa expect(result[0]).to.be.instanceOf(QuestResult); expect(result[0].id).to.equal(questId); + expect(result[0].obtained).to.equal(false); expect(result[0].reward.id).to.equal(rewardId); }); diff --git a/api/tests/quest/unit/domain/models/Eligibility_test.js b/api/tests/quest/unit/domain/models/Eligibility_test.js index 21e03d22da6..694aaf079c7 100644 --- a/api/tests/quest/unit/domain/models/Eligibility_test.js +++ b/api/tests/quest/unit/domain/models/Eligibility_test.js @@ -79,4 +79,36 @@ describe('Quest | Unit | Domain | Models | Eligibility ', function () { expect(result).to.be.null; }); }); + + describe('#hasCampaignParticipationForTargetProfileId', function () { + it('should return true if has campaign participation for given target profile', function () { + // given + const campaignParticipations = [ + { id: 1, targetProfileId: 10 }, + { id: 2, targetProfileId: 20 }, + ]; + const eligiblity = new Eligibility({ campaignParticipations }); + + // when + const result = eligiblity.hasCampaignParticipationForTargetProfileId(10); + + // then + expect(result).to.be.true; + }); + + it('should return false if there are no campaign participation for given target profile', function () { + // given + const campaignParticipations = [ + { id: 1, targetProfileId: 10 }, + { id: 2, targetProfileId: 20 }, + ]; + const eligiblity = new Eligibility({ campaignParticipations }); + + // when + const result = eligiblity.hasCampaignParticipationForTargetProfileId(1); + + // then + expect(result).to.be.false; + }); + }); }); diff --git a/api/tests/quest/unit/domain/usecases/get-quest-results-for-campaign-participation_test.js b/api/tests/quest/unit/domain/usecases/get-quest-results-for-campaign-participation_test.js index ce0fd7b8610..afea35d67af 100644 --- a/api/tests/quest/unit/domain/usecases/get-quest-results-for-campaign-participation_test.js +++ b/api/tests/quest/unit/domain/usecases/get-quest-results-for-campaign-participation_test.js @@ -4,6 +4,8 @@ import { getQuestResultsForCampaignParticipation } from '../../../../../src/ques import { expect, sinon } from '../../../../test-helper.js'; describe('Quest | Unit | Domain | Usecases | getQuestResultsForCampaignParticipation', function () { + let campaignParticipationRepository; + let questRepository, eligibilityRepository, rewardRepository, campaignParticipationId, userId; beforeEach(function () { @@ -11,6 +13,7 @@ describe('Quest | Unit | Domain | Usecases | getQuestResultsForCampaignParticipa campaignParticipationId = 2; questRepository = { findAll: sinon.stub() }; eligibilityRepository = { find: sinon.stub() }; + campaignParticipationRepository = { getCampaignByParticipationId: sinon.stub() }; rewardRepository = { getByQuestAndUserId: sinon.stub() }; }); @@ -56,6 +59,7 @@ describe('Quest | Unit | Domain | Usecases | getQuestResultsForCampaignParticipa userId, questRepository, eligibilityRepository, + campaignParticipationRepository, rewardRepository, }); @@ -77,6 +81,9 @@ describe('Quest | Unit | Domain | Usecases | getQuestResultsForCampaignParticipa rewardId: 20, }), ]); + campaignParticipationRepository.getCampaignByParticipationId + .withArgs({ campaignParticipationId }) + .resolves([{ targetProfileId: 40 }]); eligibilityRepository.find.withArgs({ userId }).resolves([ new Eligibility({ @@ -90,6 +97,7 @@ describe('Quest | Unit | Domain | Usecases | getQuestResultsForCampaignParticipa userId, questRepository, eligibilityRepository, + campaignParticipationRepository, rewardRepository, });