Merge pull request #6565 from HabitRPG/sabrecat/quest-accept

WIP: Quests for API v3
This commit is contained in:
Matteo Pagliazzi
2016-02-12 20:56:19 +01:00
23 changed files with 2260 additions and 451 deletions

View File

@@ -0,0 +1,119 @@
import {
createAndPopulateGroup,
translate as t,
generateUser,
} from '../../../../helpers/api-v3-integration.helper';
describe('POST /groups/:groupId/quests/accept', () => {
const PET_QUEST = 'whale';
let questingGroup;
let leader;
let partyMembers;
let user;
beforeEach(async () => {
user = await generateUser();
let { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: { type: 'party', privacy: 'private' },
members: 2,
});
questingGroup = group;
leader = groupLeader;
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`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('questInviteNotFound'),
});
});
it('does not accept quest for a group in which user is not a member', async () => {
await expect(user.post(`/groups/${questingGroup._id}/quests/accept`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
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', 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', 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', 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);
});
});
});

View File

@@ -0,0 +1,126 @@
import {
createAndPopulateGroup,
translate as t,
generateUser,
} from '../../../../helpers/api-v3-integration.helper';
describe('POST /groups/:groupId/quests/force-start', () => {
const PET_QUEST = 'whale';
let questingGroup;
let leader;
let partyMembers;
beforeEach(async () => {
let { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: { type: 'party', privacy: 'private' },
members: 2,
});
questingGroup = group;
leader = groupLeader;
partyMembers = members;
await leader.update({
[`items.quests.${PET_QUEST}`]: 1,
});
});
context('failure conditions', () => {
it('does not force start a quest for a group in which user is not a member', async () => {
let nonMember = await generateUser();
await expect(nonMember.post(`/groups/${questingGroup._id}/quests/force-start`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('does not force start 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/force-start`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('guildQuestsNotSupported'),
});
});
it('does not force start for a party without a pending quest', async () => {
await expect(leader.post(`/groups/${questingGroup._id}/quests/force-start`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('questNotPending'),
});
});
it('does not force start 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(leader.post(`/groups/${questingGroup._id}/quests/force-start`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('questAlreadyUnderway'),
});
});
it('does not allow non-quest leader or non-group leader to force start a quest', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/force-start`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('questOrGroupLeaderOnlyStartQuest'),
});
});
});
context('successfully force starting a quest', () => {
it('allows quest leader to force start quest', async () => {
let questLeader = partyMembers[0];
await questLeader.update({[`items.quests.${PET_QUEST}`]: 1});
await questLeader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await questLeader.post(`/groups/${questingGroup._id}/quests/force-start`);
await questingGroup.sync();
expect(questingGroup.quest.active).to.eql(true);
});
it('allows group leader to force start quest', async () => {
let questLeader = partyMembers[0];
await questLeader.update({[`items.quests.${PET_QUEST}`]: 1});
await questLeader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await leader.post(`/groups/${questingGroup._id}/quests/force-start`);
await questingGroup.sync();
expect(questingGroup.quest.active).to.eql(true);
});
it('sends back the quest object', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
let quest = await leader.post(`/groups/${questingGroup._id}/quests/force-start`);
expect(quest.active).to.eql(true);
expect(quest.key).to.eql(PET_QUEST);
expect(quest.members).to.eql({
[`${leader._id}`]: true,
});
});
});
});

View File

@@ -0,0 +1,192 @@
import {
createAndPopulateGroup,
translate as t,
sleep,
} from '../../../../helpers/api-v3-integration.helper';
import { v4 as generateUUID } from 'uuid';
import { quests as questScrolls } from '../../../../../common/script/content';
describe('POST /groups/:groupId/quests/invite/:questKey', () => {
let questingGroup;
let leader;
let member;
const PET_QUEST = 'whale';
beforeEach(async () => {
let { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: { type: 'party', privacy: 'private' },
members: 1,
});
questingGroup = group;
leader = groupLeader;
member = members[0];
});
context('failure conditions', () => {
it('does not issue invites with an invalid group ID', async () => {
await expect(leader.post(`/groups/${generateUUID()}/quests/invite/${PET_QUEST}`)).to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('does not issue invites for a group in which user is not a member', async () => {
let { group } = await createAndPopulateGroup({
groupDetails: { type: 'party', privacy: 'private' },
members: 1,
});
let alternateGroup = group;
await expect(leader.post(`/groups/${alternateGroup._id}/quests/invite/${PET_QUEST}`)).to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('does not issue invites for Guilds', async () => {
let { group } = await createAndPopulateGroup({
groupDetails: { type: 'guild', privacy: 'public' },
members: 1,
});
let alternateGroup = group;
await expect(leader.post(`/groups/${alternateGroup._id}/quests/invite/${PET_QUEST}`)).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('guildQuestsNotSupported'),
});
});
it('does not issue invites with an invalid quest key', async () => {
const FAKE_QUEST = 'herkimer';
await expect(leader.post(`/groups/${questingGroup._id}/quests/invite/${FAKE_QUEST}`)).to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('questNotFound', {key: FAKE_QUEST}),
});
});
it('does not issue invites for a quest the user does not own', async () => {
await expect(leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`)).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('questNotOwned'),
});
});
it('does not issue invites if the user is of insufficient Level', async () => {
const LEVELED_QUEST = 'atom1';
const LEVELED_QUEST_REQ = questScrolls[LEVELED_QUEST].lvl;
const leaderUpdate = {};
leaderUpdate[`items.quests.${LEVELED_QUEST}`] = 1;
leaderUpdate['stats.lvl'] = LEVELED_QUEST_REQ - 1;
await leader.update(leaderUpdate);
await expect(leader.post(`/groups/${questingGroup._id}/quests/invite/${LEVELED_QUEST}`)).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('questLevelTooHigh', {level: LEVELED_QUEST_REQ}),
});
});
it('does not issue invites if a quest is already underway', async () => {
const QUEST_IN_PROGRESS = 'atom1';
const leaderUpdate = {};
leaderUpdate[`items.quests.${PET_QUEST}`] = 1;
await leader.update(leaderUpdate);
await questingGroup.update({ 'quest.key': QUEST_IN_PROGRESS });
await expect(leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`)).to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('questAlreadyUnderway'),
});
});
});
context('successfully issuing a quest invitation', () => {
beforeEach(async () => {
const memberUpdate = {};
memberUpdate[`items.quests.${PET_QUEST}`] = 1;
await Promise.all([
leader.update(memberUpdate),
member.update(memberUpdate),
]);
});
it('adds quest details to group object', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await questingGroup.sync();
let quest = questingGroup.quest;
expect(quest.key).to.eql(PET_QUEST);
expect(quest.active).to.eql(false);
expect(quest.leader).to.eql(leader._id);
expect(quest.members).to.have.property(leader._id, true);
expect(quest.members).to.have.property(member._id, null);
expect(quest).to.have.property('progress');
});
it('adds quest details to user objects', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await sleep(0.1); // member updates happen in the background
await Promise.all([
leader.sync(),
member.sync(),
]);
expect(leader.party.quest.key).to.eql(PET_QUEST);
expect(member.party.quest.key).to.eql(PET_QUEST);
expect(leader.party.quest.RSVPNeeded).to.eql(false);
expect(member.party.quest.RSVPNeeded).to.eql(true);
});
it('sends back the quest object', async () => {
let inviteResponse = await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
expect(inviteResponse.key).to.eql(PET_QUEST);
expect(inviteResponse.active).to.eql(false);
expect(inviteResponse.leader).to.eql(leader._id);
expect(inviteResponse.members).to.have.property(leader._id, true);
expect(inviteResponse.members).to.have.property(member._id, null);
expect(inviteResponse).to.have.property('progress');
});
it('allows non-party-leader party members to send invites', async () => {
let inviteResponse = await member.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await questingGroup.sync();
expect(inviteResponse.key).to.eql(PET_QUEST);
expect(questingGroup.quest.key).to.eql(PET_QUEST);
});
it('starts quest automatically if user is in a solo party', async () => {
let leaderDetails = { balance: 10 };
leaderDetails[`items.quests.${PET_QUEST}`] = 1;
let { group, groupLeader } = await createAndPopulateGroup({
groupDetails: { type: 'party', privacy: 'private' },
leaderDetails,
});
await groupLeader.post(`/groups/${group._id}/quests/invite/${PET_QUEST}`);
await group.sync();
expect(group.quest.active).to.eql(true);
});
});
});

View File

@@ -0,0 +1,126 @@
import {
createAndPopulateGroup,
translate as t,
generateUser,
} from '../../../../helpers/api-v3-integration.helper';
import { v4 as generateUUID } from 'uuid';
describe('POST /groups/:groupId/quests/abort', () => {
let questingGroup;
let partyMembers;
let user;
let leader;
const PET_QUEST = 'whale';
beforeEach(async () => {
let { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: { type: 'party', privacy: 'private' },
members: 2,
});
questingGroup = group;
leader = groupLeader;
partyMembers = members;
await leader.update({
[`items.quests.${PET_QUEST}`]: 1,
});
user = await generateUser();
});
context('failure conditions', () => {
it('returns an error when group is not found', async () => {
await expect(partyMembers[0].post(`/groups/${generateUUID()}/quests/abort`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('returns an error for a group in which user is not a member', async () => {
await expect(user.post(`/groups/${questingGroup._id}/quests/abort`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('returns an error when group is a guild', async () => {
let { group: guild, groupLeader: guildLeader } = await createAndPopulateGroup({
groupDetails: { type: 'guild', privacy: 'private' },
});
await expect(guildLeader.post(`/groups/${guild._id}/quests/abort`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('guildQuestsNotSupported'),
});
});
it('returns an error when quest is not active', async () => {
await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/abort`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('noActiveQuestToAbort'),
});
});
it('returns an error when non quest leader attempts to abort', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/abort`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('onlyLeaderAbortQuest'),
});
});
});
it('aborts a quest', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
let res = await leader.post(`/groups/${questingGroup._id}/quests/abort`);
await Promise.all([
leader.sync(),
questingGroup.sync(),
partyMembers[0].sync(),
partyMembers[1].sync(),
]);
let cleanUserQuestObj = {
key: null,
progress: {
up: 0,
down: 0,
collect: {},
},
completed: null,
RSVPNeeded: false,
};
expect(leader.party.quest).to.eql(cleanUserQuestObj);
expect(partyMembers[0].party.quest).to.eql(cleanUserQuestObj);
expect(partyMembers[1].party.quest).to.eql(cleanUserQuestObj);
expect(leader.items.quests[PET_QUEST]).to.equal(1);
expect(questingGroup.quest).to.deep.equal(res);
expect(questingGroup.quest).to.eql({
key: null,
active: false,
leader: null,
progress: {
collect: {},
},
members: {},
});
});
});

View File

@@ -0,0 +1,138 @@
import {
createAndPopulateGroup,
translate as t,
generateUser,
} from '../../../../helpers/api-v3-integration.helper';
import { v4 as generateUUID } from 'uuid';
describe('POST /groups/:groupId/quests/cancel', () => {
let questingGroup;
let partyMembers;
let user;
let leader;
const PET_QUEST = 'whale';
beforeEach(async () => {
let { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: { type: 'party', privacy: 'private' },
members: 2,
});
questingGroup = group;
leader = groupLeader;
partyMembers = members;
await leader.update({
[`items.quests.${PET_QUEST}`]: 1,
});
user = await generateUser();
});
context('failure conditions', () => {
it('returns an error when group is not found', async () => {
await expect(partyMembers[0].post(`/groups/${generateUUID()}/quests/cancel`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('does not reject quest for a group in which user is not a member', async () => {
await expect(user.post(`/groups/${questingGroup._id}/quests/cancel`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('returns an error when group is a guild', async () => {
let { group: guild, groupLeader: guildLeader } = await createAndPopulateGroup({
groupDetails: { type: 'guild', privacy: 'private' },
});
await expect(guildLeader.post(`/groups/${guild._id}/quests/cancel`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('guildQuestsNotSupported'),
});
});
it('returns an error when group is not on a quest', async () => {
await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/cancel`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('questInvitationDoesNotExist'),
});
});
it('only the leader can cancel the quest', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/cancel`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('onlyLeaderCancelQuest'),
});
});
it('does not cancel 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(leader.post(`/groups/${questingGroup._id}/quests/cancel`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('cantCancelActiveQuest'),
});
});
});
it('cancels a quest', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
let res = await leader.post(`/groups/${questingGroup._id}/quests/cancel`);
await Promise.all([
leader.sync(),
partyMembers[0].sync(),
partyMembers[1].sync(),
questingGroup.sync(),
]);
let clean = {
key: null,
progress: {
up: 0,
down: 0,
collect: {},
},
completed: null,
RSVPNeeded: false,
};
expect(leader.party.quest).to.eql(clean);
expect(partyMembers[1].party.quest).to.eql(clean);
expect(partyMembers[0].party.quest).to.eql(clean);
expect(res).to.eql(questingGroup.quest);
expect(questingGroup.quest).to.eql({
key: null,
active: false,
leader: null,
progress: {
collect: {},
},
members: {},
});
});
});

View File

@@ -0,0 +1,124 @@
import {
createAndPopulateGroup,
translate as t,
generateUser,
} from '../../../../helpers/api-v3-integration.helper';
import { v4 as generateUUID } from 'uuid';
describe('POST /groups/:groupId/quests/leave', () => {
let questingGroup;
let partyMembers;
let user;
let leader;
const PET_QUEST = 'whale';
beforeEach(async () => {
let { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: { type: 'party', privacy: 'private' },
members: 2,
});
questingGroup = group;
leader = groupLeader;
partyMembers = members;
await leader.update({
[`items.quests.${PET_QUEST}`]: 1,
});
user = await generateUser();
});
context('failure conditions', () => {
it('returns an error when group is not found', async () => {
await expect(partyMembers[0].post(`/groups/${generateUUID()}/quests/leave`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('returns an error for a group in which user is not a member', async () => {
await expect(user.post(`/groups/${questingGroup._id}/quests/leave`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('returns an error when group is a guild', async () => {
let { group: guild, groupLeader: guildLeader } = await createAndPopulateGroup({
groupDetails: { type: 'guild', privacy: 'private' },
});
await expect(guildLeader.post(`/groups/${guild._id}/quests/leave`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('guildQuestsNotSupported'),
});
});
it('returns an error when quest is not active', async () => {
await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/leave`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('noActiveQuestToLeave'),
});
});
it('returns an error when quest leader attempts to leave', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
await expect(leader.post(`/groups/${questingGroup._id}/quests/leave`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('questLeaderCannotLeaveQuest'),
});
});
it('returns an error when non quest member attempts to leave', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/reject`);
await expect(partyMembers[1].post(`/groups/${questingGroup._id}/quests/leave`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('notPartOfQuest'),
});
});
});
it('leaves a quest', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
let leaveResult = await partyMembers[0].post(`/groups/${questingGroup._id}/quests/leave`);
await Promise.all([
partyMembers[0].sync(),
questingGroup.sync(),
]);
expect(partyMembers[0].party.quest).to.eql({
key: null,
progress: {
up: 0,
down: 0,
collect: {},
},
completed: null,
RSVPNeeded: false,
});
expect(questingGroup.quest).to.deep.equal(leaveResult);
expect(questingGroup.quest.members[partyMembers[0]._id]).to.be.false;
});
});

View File

@@ -0,0 +1,146 @@
import {
createAndPopulateGroup,
translate as t,
generateUser,
} from '../../../../helpers/api-v3-integration.helper';
import { v4 as generateUUID } from 'uuid';
describe('POST /groups/:groupId/quests/reject', () => {
let questingGroup;
let partyMembers;
let user;
let leader;
const PET_QUEST = 'whale';
beforeEach(async () => {
let { group, groupLeader, members } = await createAndPopulateGroup({
groupDetails: { type: 'party', privacy: 'private' },
members: 2,
});
questingGroup = group;
leader = groupLeader;
partyMembers = members;
await leader.update({
[`items.quests.${PET_QUEST}`]: 1,
});
user = await generateUser();
});
context('failure conditions', () => {
it('returns an error when group is not found', async () => {
await expect(partyMembers[0].post(`/groups/${generateUUID()}/quests/reject`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('does not accept quest for a group in which user is not a member', async () => {
await expect(user.post(`/groups/${questingGroup._id}/quests/accept`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('groupNotFound'),
});
});
it('returns an error when group is a guild', async () => {
let { group: guild, groupLeader: guildLeader } = await createAndPopulateGroup({
groupDetails: { type: 'guild', privacy: 'private' },
});
await expect(guildLeader.post(`/groups/${guild._id}/quests/reject`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('guildQuestsNotSupported'),
});
});
it('returns an error when group is not on a quest', async () => {
await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`))
.to.eventually.be.rejected.and.eql({
code: 404,
error: 'NotFound',
message: t('questInvitationDoesNotExist'),
});
});
it('return an error when an user rejects an invite twice', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`);
await expect(partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('questAlreadyRejected'),
});
});
it('return an error when an user rejects an invite already accepted', 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/reject`))
.to.eventually.be.rejected.and.eql({
code: 400,
error: 'BadRequest',
message: t('questAlreadyAccepted'),
});
});
it('does not reject 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/reject`))
.to.eventually.be.rejected.and.eql({
code: 401,
error: 'NotAuthorized',
message: t('questAlreadyUnderway'),
});
});
});
context('successfully quest rejection', () => {
let cleanUserQuestObj = {
key: null,
progress: {
up: 0,
down: 0,
collect: {},
},
completed: null,
RSVPNeeded: false,
};
it('rejects a quest invitation', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
let res = await partyMembers[0].post(`/groups/${questingGroup._id}/quests/reject`);
await partyMembers[0].sync();
await questingGroup.sync();
expect(partyMembers[0].party.quest).to.eql(cleanUserQuestObj);
expect(questingGroup.quest.members[partyMembers[0]._id]).to.be.false;
expect(questingGroup.quest.active).to.be.false;
expect(res).to.eql(questingGroup.quest);
});
it('starts the quest when the last user reject', async () => {
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/reject`);
await questingGroup.sync();
expect(questingGroup.quest.active).to.be.true;
});
});
});