feat(api-v3): Add force-start quest route

This commit is contained in:
Blade Barringer
2016-02-12 08:04:14 -06:00
parent befdb189fc
commit 4360f01936
3 changed files with 182 additions and 1 deletions
+3 -1
View File
@@ -83,5 +83,7 @@
"questAlreadyRejected": "You already rejected the quest invitation.",
"cantCancelActiveQuest": "You can not cancel an active quest, use the abort functionality.",
"onlyLeaderCancelQuest": "Only the group or quest leader can cancel the quest.",
"questInvitationDoesNotExist": "No quest invitation has been sent out yet."
"questInvitationDoesNotExist": "No quest invitation has been sent out yet.",
"questNotPending": "There is no quest to start.",
"questOrGroupLeaderOnlyStartQuest": "Only the quest leader or group leader can force start the quest"
}
@@ -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,
});
});
});
});
+53
View File
@@ -238,6 +238,59 @@ api.rejectQuest = {
},
};
/**
* @api {post} /groups/:groupId/quests/force-start Accept a pending quest
* @apiVersion 3.0.0
* @apiName forceStart
* @apiGroup Group
*
* @apiParam {string} groupId The group _id (or 'party')
*
* @apiSuccess {Object} quest Quest Object
*/
api.forceStart = {
method: 'POST',
url: '/groups/:groupId/quests/force-start',
middlewares: [authWithHeaders(), cron],
async handler (req, res) {
let user = res.locals.user;
req.checkParams('groupId', res.t('groupIdRequired')).notEmpty();
let validationErrors = req.validationErrors();
if (validationErrors) throw validationErrors;
let group = await Group.getGroup({user, groupId: req.params.groupId, fields: 'type quest leader'});
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('questNotPending'));
if (group.quest.active) throw new NotAuthorized(res.t('questAlreadyUnderway'));
if (!(user._id === group.quest.leader || user._id === group.leader)) throw new NotAuthorized(res.t('questOrGroupLeaderOnlyStartQuest'));
group.markModified('quest');
await group.startQuest(user);
let [savedGroup] = await Q.all([
group.save(),
user.save(),
]);
res.respond(200, savedGroup.quest);
analytics.track('quest', {
category: 'behavior',
owner: user._id === group.quest.leader,
response: 'force-start',
gaLabel: 'force-start',
questName: group.quest.key,
uuid: user._id,
});
},
};
/**
* @api {post} /groups/:groupId/quests/cancel Cancels a quest
* @apiVersion 3.0.0