mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 14:47:53 +01:00
fix: Cleanup quest progress for non-members when quest starts
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
|||||||
translate as t,
|
translate as t,
|
||||||
generateUser,
|
generateUser,
|
||||||
} from '../../../../helpers/api-v3-integration.helper';
|
} from '../../../../helpers/api-v3-integration.helper';
|
||||||
|
import Bluebird from 'bluebird';
|
||||||
|
|
||||||
describe('POST /groups/:groupId/quests/accept', () => {
|
describe('POST /groups/:groupId/quests/accept', () => {
|
||||||
const PET_QUEST = 'whale';
|
const PET_QUEST = 'whale';
|
||||||
@@ -115,5 +116,22 @@ describe('POST /groups/:groupId/quests/accept', () => {
|
|||||||
await questingGroup.sync();
|
await questingGroup.sync();
|
||||||
expect(questingGroup.quest.active).to.equal(true);
|
expect(questingGroup.quest.active).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('cleans up user quest data for non-quest members when last member accepts', async () => {
|
||||||
|
let rejectingMember = partyMembers[0];
|
||||||
|
|
||||||
|
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||||
|
await rejectingMember.post(`/groups/${questingGroup._id}/quests/reject`);
|
||||||
|
// quest will start after everyone has accepted
|
||||||
|
await partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||||
|
|
||||||
|
await Bluebird.delay(500);
|
||||||
|
|
||||||
|
await rejectingMember.sync();
|
||||||
|
|
||||||
|
expect(rejectingMember.party.quest.RSVPNeeded).to.eql(false);
|
||||||
|
expect(rejectingMember.party.quest.key).to.not.exist;
|
||||||
|
expect(rejectingMember.party.quest.completed).to.not.exist;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
translate as t,
|
translate as t,
|
||||||
generateUser,
|
generateUser,
|
||||||
} from '../../../../helpers/api-v3-integration.helper';
|
} from '../../../../helpers/api-v3-integration.helper';
|
||||||
|
import Bluebird from 'bluebird';
|
||||||
|
|
||||||
describe('POST /groups/:groupId/quests/force-start', () => {
|
describe('POST /groups/:groupId/quests/force-start', () => {
|
||||||
const PET_QUEST = 'whale';
|
const PET_QUEST = 'whale';
|
||||||
@@ -14,7 +15,7 @@ describe('POST /groups/:groupId/quests/force-start', () => {
|
|||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
let { group, groupLeader, members } = await createAndPopulateGroup({
|
let { group, groupLeader, members } = await createAndPopulateGroup({
|
||||||
groupDetails: { type: 'party', privacy: 'private' },
|
groupDetails: { type: 'party', privacy: 'private' },
|
||||||
members: 2,
|
members: 3,
|
||||||
});
|
});
|
||||||
|
|
||||||
questingGroup = group;
|
questingGroup = group;
|
||||||
@@ -63,8 +64,9 @@ describe('POST /groups/:groupId/quests/force-start', () => {
|
|||||||
it('does not force start for a quest already underway', async () => {
|
it('does not force start for a quest already underway', async () => {
|
||||||
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||||
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
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 partyMembers[1].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||||
|
// quest will start after everyone has accepted
|
||||||
|
await partyMembers[2].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||||
|
|
||||||
await expect(leader.post(`/groups/${questingGroup._id}/quests/force-start`))
|
await expect(leader.post(`/groups/${questingGroup._id}/quests/force-start`))
|
||||||
.to.eventually.be.rejected.and.eql({
|
.to.eventually.be.rejected.and.eql({
|
||||||
@@ -122,5 +124,30 @@ describe('POST /groups/:groupId/quests/force-start', () => {
|
|||||||
[`${leader._id}`]: true,
|
[`${leader._id}`]: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('cleans up user quest data for non-quest members', async () => {
|
||||||
|
let partyMemberThatRejects = partyMembers[1];
|
||||||
|
let partyMemberThatIgnores = partyMembers[2];
|
||||||
|
|
||||||
|
await leader.post(`/groups/${questingGroup._id}/quests/invite/${PET_QUEST}`);
|
||||||
|
await partyMembers[0].post(`/groups/${questingGroup._id}/quests/accept`);
|
||||||
|
await partyMemberThatRejects.post(`/groups/${questingGroup._id}/quests/reject`);
|
||||||
|
|
||||||
|
await leader.post(`/groups/${questingGroup._id}/quests/force-start`);
|
||||||
|
|
||||||
|
await Bluebird.delay(500);
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
partyMemberThatRejects.sync(),
|
||||||
|
partyMemberThatIgnores.sync(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
expect(partyMemberThatRejects.party.quest.RSVPNeeded).to.eql(false);
|
||||||
|
expect(partyMemberThatRejects.party.quest.key).to.not.exist;
|
||||||
|
expect(partyMemberThatRejects.party.quest.completed).to.not.exist;
|
||||||
|
expect(partyMemberThatIgnores.party.quest.RSVPNeeded).to.eql(false);
|
||||||
|
expect(partyMemberThatIgnores.party.quest.key).to.not.exist;
|
||||||
|
expect(partyMemberThatIgnores.party.quest.completed).to.not.exist;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
generateUser,
|
generateUser,
|
||||||
} from '../../../../helpers/api-v3-integration.helper';
|
} from '../../../../helpers/api-v3-integration.helper';
|
||||||
import { v4 as generateUUID } from 'uuid';
|
import { v4 as generateUUID } from 'uuid';
|
||||||
|
import Bluebird from 'bluebird';
|
||||||
|
|
||||||
describe('POST /groups/:groupId/quests/reject', () => {
|
describe('POST /groups/:groupId/quests/reject', () => {
|
||||||
let questingGroup;
|
let questingGroup;
|
||||||
@@ -142,5 +143,26 @@ describe('POST /groups/:groupId/quests/reject', () => {
|
|||||||
|
|
||||||
expect(questingGroup.quest.active).to.be.true;
|
expect(questingGroup.quest.active).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('cleans up user quest data for non-quest members when last member rejects', async () => {
|
||||||
|
let rejectingMember = partyMembers[1];
|
||||||
|
|
||||||
|
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 or rejected
|
||||||
|
await rejectingMember.post(`/groups/${questingGroup._id}/quests/reject`);
|
||||||
|
|
||||||
|
await Bluebird.delay(500);
|
||||||
|
|
||||||
|
await questingGroup.sync();
|
||||||
|
|
||||||
|
expect(questingGroup.quest.active).to.be.true;
|
||||||
|
|
||||||
|
await rejectingMember.sync();
|
||||||
|
|
||||||
|
expect(rejectingMember.party.quest.RSVPNeeded).to.eql(false);
|
||||||
|
expect(rejectingMember.party.quest.key).to.not.exist;
|
||||||
|
expect(rejectingMember.party.quest.completed).to.not.exist;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -301,7 +301,7 @@ api.forceStart = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @api {post} /api/v3/groups/:groupId/quests/cancel Cancels a quest
|
* @api {post} /api/v3/groups/:groupId/quests/cancel Cancels a quest that is not active
|
||||||
* @apiVersion 3.0.0
|
* @apiVersion 3.0.0
|
||||||
* @apiName CancelQuest
|
* @apiName CancelQuest
|
||||||
* @apiGroup Group
|
* @apiGroup Group
|
||||||
|
|||||||
@@ -110,6 +110,27 @@ schema.post('remove', function postRemoveGroup (group) {
|
|||||||
firebase.deleteGroup(group._id);
|
firebase.deleteGroup(group._id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// return a clean object for user.quest
|
||||||
|
function _cleanQuestProgress (merge) {
|
||||||
|
let clean = {
|
||||||
|
key: null,
|
||||||
|
progress: {
|
||||||
|
up: 0,
|
||||||
|
down: 0,
|
||||||
|
collect: {},
|
||||||
|
},
|
||||||
|
completed: null,
|
||||||
|
RSVPNeeded: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (merge) {
|
||||||
|
_.merge(clean, _.omit(merge, 'progress'));
|
||||||
|
if (merge.progress) _.merge(clean.progress, merge.progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
return clean;
|
||||||
|
}
|
||||||
|
|
||||||
schema.statics.getGroup = async function getGroup (options = {}) {
|
schema.statics.getGroup = async function getGroup (options = {}) {
|
||||||
let {user, groupId, fields, optionalMembership = false, populateLeader = false, requireMembership = false} = options;
|
let {user, groupId, fields, optionalMembership = false, populateLeader = false, requireMembership = false} = options;
|
||||||
let query;
|
let query;
|
||||||
@@ -332,10 +353,11 @@ schema.methods.startQuest = async function startQuest (user) {
|
|||||||
this.quest.progress.collect = collected;
|
this.quest.progress.collect = collected;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let nonMembers = Object.keys(_.pick(this.quest.members, (member) => {
|
||||||
|
return !member;
|
||||||
|
}));
|
||||||
|
|
||||||
// Changes quest.members to only include participating members
|
// Changes quest.members to only include participating members
|
||||||
// TODO: is that important? What does it matter if the non-participating members
|
|
||||||
// are still on the object?
|
|
||||||
// TODO: is it important to run clean quest progress on non-members like we did in v2?
|
|
||||||
this.quest.members = _.pick(this.quest.members, _.identity);
|
this.quest.members = _.pick(this.quest.members, _.identity);
|
||||||
let nonUserQuestMembers = _.keys(this.quest.members);
|
let nonUserQuestMembers = _.keys(this.quest.members);
|
||||||
removeFromArray(nonUserQuestMembers, user._id);
|
removeFromArray(nonUserQuestMembers, user._id);
|
||||||
@@ -372,6 +394,16 @@ schema.methods.startQuest = async function startQuest (user) {
|
|||||||
},
|
},
|
||||||
}, { multi: true }).exec();
|
}, { multi: true }).exec();
|
||||||
|
|
||||||
|
// update the users who are not participating
|
||||||
|
// Do not block updates
|
||||||
|
User.update({
|
||||||
|
_id: { $in: nonMembers },
|
||||||
|
}, {
|
||||||
|
$set: {
|
||||||
|
'party.quest': _cleanQuestProgress(),
|
||||||
|
},
|
||||||
|
}, { multi: true }).exec();
|
||||||
|
|
||||||
// send notifications in the background without blocking
|
// send notifications in the background without blocking
|
||||||
User.find(
|
User.find(
|
||||||
{ _id: { $in: nonUserQuestMembers } },
|
{ _id: { $in: nonUserQuestMembers } },
|
||||||
@@ -390,27 +422,6 @@ schema.methods.startQuest = async function startQuest (user) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// return a clean object for user.quest
|
|
||||||
function _cleanQuestProgress (merge) {
|
|
||||||
let clean = {
|
|
||||||
key: null,
|
|
||||||
progress: {
|
|
||||||
up: 0,
|
|
||||||
down: 0,
|
|
||||||
collect: {},
|
|
||||||
},
|
|
||||||
completed: null,
|
|
||||||
RSVPNeeded: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (merge) {
|
|
||||||
_.merge(clean, _.omit(merge, 'progress'));
|
|
||||||
if (merge.progress) _.merge(clean.progress, merge.progress);
|
|
||||||
}
|
|
||||||
|
|
||||||
return clean;
|
|
||||||
}
|
|
||||||
|
|
||||||
schema.statics.cleanQuestProgress = _cleanQuestProgress;
|
schema.statics.cleanQuestProgress = _cleanQuestProgress;
|
||||||
|
|
||||||
// returns a clean object for group.quest
|
// returns a clean object for group.quest
|
||||||
|
|||||||
Reference in New Issue
Block a user