diff --git a/common/locales/en/api-v3.json b/common/locales/en/api-v3.json index cdb6718327..a2495529e7 100644 --- a/common/locales/en/api-v3.json +++ b/common/locales/en/api-v3.json @@ -73,5 +73,6 @@ "questNotFound": "Quest \"<%= key %>\" not found.", "questNotOwned": "You don't own that quest scroll.", "questLevelTooHigh": "You must be Level <%= level %> to begin this quest.", - "questAlreadyUnderway": "Your party is already on a quest. Try again when the current quest has ended." + "questAlreadyUnderway": "Your party is already on a quest. Try again when the current quest has ended.", + "questAlreadyAccepted": "You already accepted the quest invitation." } diff --git a/test/api/v3/integration/quests/POST-groups_groupId_quests_accept.test.js b/test/api/v3/integration/quests/POST-groups_groupId_quests_accept.test.js index e218d7e3d9..665a185279 100644 --- a/test/api/v3/integration/quests/POST-groups_groupId_quests_accept.test.js +++ b/test/api/v3/integration/quests/POST-groups_groupId_quests_accept.test.js @@ -1,27 +1,37 @@ import { createAndPopulateGroup, translate as t, + generateUser, } from '../../../../helpers/api-v3-integration.helper'; -describe.skip('POST /groups/:groupId/quests/accept', () => { +describe('POST /groups/:groupId/quests/accept', () => { + const PET_QUEST = 'whale'; + let questingGroup; let leader; - let member; + let partyMembers; + let user; beforeEach(async () => { + user = await generateUser(); + let { group, groupLeader, members } = await createAndPopulateGroup({ groupDetails: { type: 'party', privacy: 'private' }, - members: 1, + members: 2, }); questingGroup = group; leader = groupLeader; - member = members[0]; + partyMembers = members; + + await leader.update({ + [`items.quests.${PET_QUEST}`]: 1, + }); }); context('failure conditions', () => { it('does not accept quest without an invite', async () => { - await expect(leader.post(`/groups/${questingGroup._id}/quests/accept`, {})) + await expect(leader.post(`/groups/${questingGroup._id}/quests/accept`)) .to.eventually.be.rejected.and.eql({ code: 404, error: 'NotFound', @@ -30,25 +40,80 @@ describe.skip('POST /groups/:groupId/quests/accept', () => { }); it('does not accept quest for a group in which user is not a member', async () => { - await expect(member.post(`/groups/${questingGroup._id}/quests/accept`, {})) + await expect(user.post(`/groups/${questingGroup._id}/quests/accept`)) .to.eventually.be.rejected.and.eql({ code: 404, error: 'NotFound', - message: t('questInviteNotFound'), + message: t('groupNotFound'), + }); + }); + + it('does not accept quest for a guild', async () => { + let { group: guild, groupLeader: guildLeader } = await createAndPopulateGroup({ + groupDetails: { type: 'guild', privacy: 'private' }, + }); + + await expect(guildLeader.post(`/groups/${guild._id}/quests/accept`)) + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('guildQuestsNotSupported'), + }); + }); + + it('does not accept invite twice', async () => { + await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`); + await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`); + + await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`)) + .to.eventually.be.rejected.and.eql({ + code: 400, + error: 'BadRequest', + message: t('questAlreadyAccepted'), + }); + }); + + it('does not accept invite for a quest already underway', async () => { + await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`); + await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`); + // quest will start after everyone has accepted + await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`); + + await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`)) + .to.eventually.be.rejected.and.eql({ + code: 401, + error: 'NotAuthorized', + message: t('questAlreadyUnderway'), }); }); }); + context('successfully accepting a quest invitation', () => { - it('joins a quest from an invitation', () => { + it('joins a quest from an invitation', async () => { + await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`); + await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`); + await Promise.all([partyMembers[0].sync(), questingGroup.sync()]); + expect(leader.party.quest.RSVPNeeded).to.equal(false); + expect(questingGroup.quest.members[partyMembers[0]._id]); }); - it('does not begin the quest if pending invitations remain', () => { + it('does not begin the quest if pending invitations remain', async () => { + await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`); + await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`); + await questingGroup.sync(); + expect(questingGroup.quest.active).to.equal(false); }); - it('begins the quest if accepting the last pending invite', () => { + it('begins the quest if accepting the last pending invite', async () => { + await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`); + await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`); + // quest will start after everyone has accepted + await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`); + await questingGroup.sync(); + expect(questingGroup.quest.active).to.equal(true); }); }); }); diff --git a/website/src/controllers/api-v3/quests.js b/website/src/controllers/api-v3/quests.js index 13584b139c..162954b7dd 100644 --- a/website/src/controllers/api-v3/quests.js +++ b/website/src/controllers/api-v3/quests.js @@ -12,6 +12,7 @@ import { import { NotFound, NotAuthorized, + BadRequest, } from '../../libs/api-v3/errors'; import { getUserInfo, @@ -151,7 +152,10 @@ api.acceptQuest = { if (!group) throw new NotFound(res.t('groupNotFound')); if (group.type !== 'party') throw new NotAuthorized(res.t('guildQuestsNotSupported')); if (!group.quest.key) throw new NotFound(res.t('questInviteNotFound')); + if (group.quest.active) throw new NotAuthorized(res.t('questAlreadyUnderway')); + if (group.quest.members[user._id]) throw new BadRequest(res.t('questAlreadyAccepted')); + group.markModified('quest'); group.quest.members[user._id] = true; user.party.quest.RSVPNeeded = false;