mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-19 15:48:04 +01:00
Merge pull request #6565 from HabitRPG/sabrecat/quest-accept
WIP: Quests for API v3
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
});
|
||||
300
test/api/v3/unit/models/group.test.js
Normal file
300
test/api/v3/unit/models/group.test.js
Normal file
@@ -0,0 +1,300 @@
|
||||
import { sleep } from '../../../../helpers/api-unit.helper';
|
||||
import { model as Group } from '../../../../../website/src/models/group';
|
||||
import { model as User } from '../../../../../website/src/models/user';
|
||||
import { quests as questScrolls } from '../../../../../common/script/content';
|
||||
import * as email from '../../../../../website/src/libs/api-v3/email';
|
||||
|
||||
describe('Group Model', () => {
|
||||
context('Instance Methods', () => {
|
||||
describe('#startQuest', () => {
|
||||
let party, questLeader, participatingMember, nonParticipatingMember, undecidedMember;
|
||||
|
||||
beforeEach(async () => {
|
||||
sandbox.stub(email, 'sendTxn');
|
||||
|
||||
party = new Group({
|
||||
name: 'test party',
|
||||
type: 'party',
|
||||
privacy: 'private',
|
||||
});
|
||||
|
||||
questLeader = new User({
|
||||
party: { _id: party._id },
|
||||
items: {
|
||||
quests: {
|
||||
whale: 1,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
party.leader = questLeader._id;
|
||||
|
||||
participatingMember = new User({
|
||||
party: { _id: party._id },
|
||||
});
|
||||
nonParticipatingMember = new User({
|
||||
party: { _id: party._id },
|
||||
});
|
||||
undecidedMember = new User({
|
||||
party: { _id: party._id },
|
||||
});
|
||||
|
||||
await Promise.all([
|
||||
party.save(),
|
||||
questLeader.save(),
|
||||
participatingMember.save(),
|
||||
nonParticipatingMember.save(),
|
||||
undecidedMember.save(),
|
||||
]);
|
||||
});
|
||||
|
||||
context('Failure Conditions', () => {
|
||||
it('throws an error if group is not a party', async () => {
|
||||
let guild = new Group({
|
||||
type: 'guild',
|
||||
});
|
||||
|
||||
await expect(guild.startQuest(participatingMember)).to.eventually.be.rejected;
|
||||
});
|
||||
|
||||
it('throws an error if party is not on a quest', async () => {
|
||||
await expect(party.startQuest(participatingMember)).to.eventually.be.rejected;
|
||||
});
|
||||
|
||||
it('throws an error if quest is already active', async () => {
|
||||
party.quest.key = 'whale';
|
||||
party.quest.active = true;
|
||||
|
||||
await expect(party.startQuest(participatingMember)).to.eventually.be.rejected;
|
||||
});
|
||||
});
|
||||
|
||||
context('Successes', () => {
|
||||
beforeEach(() => {
|
||||
party.quest.key = 'whale';
|
||||
party.quest.active = false;
|
||||
party.quest.leader = questLeader._id;
|
||||
party.quest.members = { };
|
||||
party.quest.members[questLeader._id] = true;
|
||||
party.quest.members[participatingMember._id] = true;
|
||||
party.quest.members[nonParticipatingMember._id] = false;
|
||||
party.quest.members[undecidedMember._id] = null;
|
||||
});
|
||||
|
||||
it('activates quest', () => {
|
||||
party.startQuest(participatingMember);
|
||||
|
||||
expect(party.quest.active).to.eql(true);
|
||||
});
|
||||
|
||||
it('sets up boss quest', () => {
|
||||
let bossQuest = questScrolls.whale;
|
||||
party.quest.key = bossQuest.key;
|
||||
|
||||
party.startQuest(participatingMember);
|
||||
|
||||
expect(party.quest.progress.hp).to.eql(bossQuest.boss.hp);
|
||||
});
|
||||
|
||||
it('sets up rage meter for rage boss quest', () => {
|
||||
let rageBossQuest = questScrolls.trex_undead;
|
||||
party.quest.key = rageBossQuest.key;
|
||||
|
||||
party.startQuest(participatingMember);
|
||||
|
||||
expect(party.quest.progress.rage).to.eql(0);
|
||||
});
|
||||
|
||||
it('sets up collection quest', () => {
|
||||
let collectionQuest = questScrolls.vice2;
|
||||
party.quest.key = collectionQuest.key;
|
||||
party.startQuest(participatingMember);
|
||||
|
||||
expect(party.quest.progress.collect).to.eql({
|
||||
lightCrystal: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('sets up collection quest with multiple items', () => {
|
||||
let collectionQuest = questScrolls.evilsanta2;
|
||||
party.quest.key = collectionQuest.key;
|
||||
party.startQuest(participatingMember);
|
||||
|
||||
expect(party.quest.progress.collect).to.eql({
|
||||
tracks: 0,
|
||||
branches: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('prunes non-participating members from quest members object', () => {
|
||||
party.startQuest(participatingMember);
|
||||
|
||||
let expectedQuestMembers = {};
|
||||
expectedQuestMembers[questLeader._id] = true;
|
||||
expectedQuestMembers[participatingMember._id] = true;
|
||||
|
||||
expect(party.quest.members).to.eql(expectedQuestMembers);
|
||||
});
|
||||
|
||||
it('applies updates to user object directly if user is participating', async () => {
|
||||
await party.startQuest(participatingMember);
|
||||
|
||||
expect(participatingMember.party.quest.key).to.eql('whale');
|
||||
expect(participatingMember.party.quest.progress.down).to.eql(0);
|
||||
expect(participatingMember.party.quest.collect).to.eql({});
|
||||
expect(participatingMember.party.quest.completed).to.eql(null);
|
||||
});
|
||||
|
||||
it('applies updates to other participating members', async () => {
|
||||
await party.startQuest(nonParticipatingMember);
|
||||
|
||||
questLeader = await User.findById(questLeader._id);
|
||||
participatingMember = await User.findById(participatingMember._id);
|
||||
|
||||
expect(participatingMember.party.quest.key).to.eql('whale');
|
||||
expect(participatingMember.party.quest.progress.down).to.eql(0);
|
||||
expect(participatingMember.party.quest.progress.collect).to.eql({});
|
||||
expect(participatingMember.party.quest.completed).to.eql(null);
|
||||
|
||||
expect(questLeader.party.quest.key).to.eql('whale');
|
||||
expect(questLeader.party.quest.progress.down).to.eql(0);
|
||||
expect(questLeader.party.quest.progress.collect).to.eql({});
|
||||
expect(questLeader.party.quest.completed).to.eql(null);
|
||||
});
|
||||
|
||||
it('does not apply updates to nonparticipating members', async () => {
|
||||
await party.startQuest(participatingMember);
|
||||
|
||||
nonParticipatingMember = await User.findById(nonParticipatingMember ._id);
|
||||
undecidedMember = await User.findById(undecidedMember._id);
|
||||
|
||||
expect(nonParticipatingMember.party.quest.key).to.not.eql('whale');
|
||||
expect(undecidedMember.party.quest.key).to.not.eql('whale');
|
||||
});
|
||||
|
||||
it('sends email to participating members that quest has started', async () => {
|
||||
participatingMember.preferences.emailNotifications.questStarted = true;
|
||||
questLeader.preferences.emailNotifications.questStarted = true;
|
||||
await Promise.all([
|
||||
participatingMember.save(),
|
||||
questLeader.save(),
|
||||
]);
|
||||
|
||||
await party.startQuest(nonParticipatingMember);
|
||||
|
||||
await sleep(0.5);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
|
||||
let memberIds = _.pluck(email.sendTxn.args[0][0], '_id');
|
||||
let typeOfEmail = email.sendTxn.args[0][1];
|
||||
|
||||
expect(memberIds).to.have.a.lengthOf(2);
|
||||
expect(memberIds).to.include(participatingMember._id);
|
||||
expect(memberIds).to.include(questLeader._id);
|
||||
expect(typeOfEmail).to.eql('quest-started');
|
||||
});
|
||||
|
||||
it('sends email only to members who have not opted out', async () => {
|
||||
participatingMember.preferences.emailNotifications.questStarted = false;
|
||||
questLeader.preferences.emailNotifications.questStarted = true;
|
||||
await Promise.all([
|
||||
participatingMember.save(),
|
||||
questLeader.save(),
|
||||
]);
|
||||
|
||||
await party.startQuest(nonParticipatingMember);
|
||||
|
||||
await sleep(0.5);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
|
||||
let memberIds = _.pluck(email.sendTxn.args[0][0], '_id');
|
||||
|
||||
expect(memberIds).to.have.a.lengthOf(1);
|
||||
expect(memberIds).to.not.include(participatingMember._id);
|
||||
expect(memberIds).to.include(questLeader._id);
|
||||
});
|
||||
|
||||
it('does not send email to initiating member', async () => {
|
||||
participatingMember.preferences.emailNotifications.questStarted = true;
|
||||
questLeader.preferences.emailNotifications.questStarted = true;
|
||||
await Promise.all([
|
||||
participatingMember.save(),
|
||||
questLeader.save(),
|
||||
]);
|
||||
|
||||
await party.startQuest(participatingMember);
|
||||
|
||||
await sleep(0.5);
|
||||
|
||||
expect(email.sendTxn).to.be.calledOnce;
|
||||
|
||||
let memberIds = _.pluck(email.sendTxn.args[0][0], '_id');
|
||||
|
||||
expect(memberIds).to.have.a.lengthOf(1);
|
||||
expect(memberIds).to.not.include(participatingMember._id);
|
||||
expect(memberIds).to.include(questLeader._id);
|
||||
});
|
||||
|
||||
it('updates participting members (not including user)', async () => {
|
||||
sandbox.spy(User, 'update');
|
||||
|
||||
await party.startQuest(nonParticipatingMember);
|
||||
|
||||
let members = [questLeader._id, participatingMember._id];
|
||||
|
||||
expect(User.update).to.be.calledWith(
|
||||
{ _id: { $in: members } },
|
||||
{
|
||||
$set: {
|
||||
'party.quest.key': 'whale',
|
||||
'party.quest.progress.down': 0,
|
||||
'party.quest.collect': {},
|
||||
'party.quest.completed': null,
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('updates non-user quest leader and decrements quest scroll', async () => {
|
||||
sandbox.spy(User, 'update');
|
||||
|
||||
await party.startQuest(participatingMember);
|
||||
|
||||
expect(User.update).to.be.calledWith(
|
||||
{ _id: questLeader._id },
|
||||
{
|
||||
$inc: {
|
||||
'items.quests.whale': -1,
|
||||
},
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('modifies the participating initiating user directly', async () => {
|
||||
await party.startQuest(participatingMember);
|
||||
|
||||
let userQuest = participatingMember.party.quest;
|
||||
|
||||
expect(userQuest.key).to.eql('whale');
|
||||
expect(userQuest.progress.down).to.eql(0);
|
||||
expect(userQuest.collect).to.eql({});
|
||||
expect(userQuest.completed).to.eql(null);
|
||||
});
|
||||
|
||||
it('does not modify user if not participating', async () => {
|
||||
await party.startQuest(nonParticipatingMember);
|
||||
|
||||
expect(nonParticipatingMember.party.quest.key).to.not.eql('whale');
|
||||
});
|
||||
|
||||
it('removes the quest directly if initiating user is the quest leader', async () => {
|
||||
await party.startQuest(questLeader);
|
||||
|
||||
expect(questLeader.items.quests.whale).to.eql(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user