mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-19 07:37:25 +01:00
feat: Add startQuest method on group model
This commit is contained in:
134
test/api/v3/unit/models/group.test.js
Normal file
134
test/api/v3/unit/models/group.test.js
Normal file
@@ -0,0 +1,134 @@
|
||||
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';
|
||||
|
||||
describe('Group Model', () => {
|
||||
context('Instance Methods', () => {
|
||||
let party;
|
||||
|
||||
beforeEach(() => {
|
||||
party = new Group({
|
||||
type: 'party',
|
||||
});
|
||||
});
|
||||
|
||||
describe('#startQuest', () => {
|
||||
context('Failure Conditions', () => {
|
||||
it('throws an error if group is not a party', () => {
|
||||
let guild = new Group({
|
||||
type: 'guild',
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
guild.startQuest();
|
||||
}).to.throw('Must be a party to use this method');
|
||||
});
|
||||
|
||||
it('throws an error if party is not on a quest', () => {
|
||||
expect(() => {
|
||||
party.startQuest();
|
||||
}).to.throw('Party does not have a pending quest');
|
||||
});
|
||||
|
||||
it('throws an error if quest is already active', () => {
|
||||
party.quest.key = 'whale';
|
||||
party.quest.active = true;
|
||||
|
||||
expect(() => {
|
||||
party.startQuest();
|
||||
}).to.throw('Quest is already active');
|
||||
});
|
||||
});
|
||||
|
||||
context('Successes', () => {
|
||||
beforeEach(() => {
|
||||
party.quest.key = 'whale';
|
||||
party.quest.active = false;
|
||||
party.quest.leader = 'quest-leader';
|
||||
party.quest.members = {
|
||||
'quest-leader': true,
|
||||
'participating-member': true,
|
||||
'non-participating-member': false,
|
||||
'undecided-member': null,
|
||||
};
|
||||
|
||||
sandbox.stub(User, 'update').returns({ exec: sandbox.spy() });
|
||||
});
|
||||
|
||||
it('activates quest', () => {
|
||||
party.startQuest();
|
||||
|
||||
expect(party.quest.active).to.eql(true);
|
||||
});
|
||||
|
||||
it('sets up boss quest', () => {
|
||||
let bossQuest = questScrolls.whale;
|
||||
party.quest.key = bossQuest.key;
|
||||
|
||||
party.startQuest();
|
||||
|
||||
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();
|
||||
|
||||
expect(party.quest.progress.rage).to.eql(0);
|
||||
});
|
||||
|
||||
it('sets up collection quest', () => {
|
||||
let collectionQuest = questScrolls.vice2;
|
||||
party.quest.key = collectionQuest.key;
|
||||
party.startQuest();
|
||||
|
||||
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();
|
||||
|
||||
expect(party.quest.progress.collect).to.eql({
|
||||
tracks: 0,
|
||||
branches: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it('updates quest object for participating members', () => {
|
||||
party.startQuest();
|
||||
|
||||
expect(User.update).to.be.calledTwice;
|
||||
expect(User.update).to.not.be.calledWith({ _id: 'non-participating-member' });
|
||||
expect(User.update).to.not.be.calledWith({ _id: 'undecided-member' });
|
||||
expect(User.update).to.be.calledWith(
|
||||
{ _id: 'participating-member' },
|
||||
sinon.match({ $set: { 'party.quest.key': 'whale' }}),
|
||||
);
|
||||
expect(User.update).to.be.calledWith(
|
||||
{ _id: 'quest-leader' },
|
||||
sinon.match({ $set: { 'party.quest.key': 'whale' }}),
|
||||
);
|
||||
});
|
||||
|
||||
it('removes quest scroll from quest leader', () => {
|
||||
party.startQuest();
|
||||
|
||||
expect(User.update).to.be.calledWith(
|
||||
{ _id: 'quest-leader' },
|
||||
sinon.match({ $inc: { 'items.quests.whale': -1 }}),
|
||||
);
|
||||
});
|
||||
|
||||
it('sends email to participating members that quest has started');
|
||||
|
||||
it('sends email only to members who have not opted out');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -8,8 +8,10 @@ import _ from 'lodash';
|
||||
import { model as Challenge} from './challenge';
|
||||
import validator from 'validator';
|
||||
import { removeFromArray } from '../libs/api-v3/collectionManipulators';
|
||||
import { BadRequest } from '../libs/api-v3/errors';
|
||||
import * as firebase from '../libs/api-v2/firebase';
|
||||
import baseModel from '../libs/api-v3/baseModel';
|
||||
import { quests as questScrolls } from '../../../common/script/content';
|
||||
import Q from 'q';
|
||||
import nconf from 'nconf';
|
||||
|
||||
@@ -168,6 +170,64 @@ schema.methods.isMember = function isGroupMember (user) {
|
||||
}
|
||||
};
|
||||
|
||||
schema.methods.startQuest = function startQuest () {
|
||||
if (this.type !== 'party') throw new BadRequest('Must be a party to use this method');
|
||||
if (!this.quest.key) throw new BadRequest('Party does not have a pending quest');
|
||||
if (this.quest.active) throw new BadRequest('Quest is already active');
|
||||
|
||||
let quest = questScrolls[this.quest.key];
|
||||
let collected = {};
|
||||
if (quest.collect) {
|
||||
collected = _.transform(quest.collect, (result, n, itemToCollect) => {
|
||||
result[itemToCollect] = 0;
|
||||
});
|
||||
}
|
||||
|
||||
let backgroundOperations = [];
|
||||
|
||||
this.markModified('quest');
|
||||
this.quest.active = true;
|
||||
if (quest.boss) {
|
||||
this.quest.progress.hp = quest.boss.hp;
|
||||
if (quest.boss.rage) this.quest.progress.rage = 0;
|
||||
} else if (quest.collect) {
|
||||
this.quest.progress.collect = collected;
|
||||
}
|
||||
|
||||
_.each(this.quest.members, (participating, memberId) => {
|
||||
if (!participating) return;
|
||||
|
||||
let update = {
|
||||
$set: {
|
||||
// Do *not* reset party.quest.progress.up
|
||||
// See https://github.com/HabitRPG/habitrpg/issues/2168#issuecomment-31556322
|
||||
'party.quest.key': this.quest.key,
|
||||
'party.quest.progress.down': 0,
|
||||
'party.quest.collect': collected,
|
||||
'party.quest.completed': null,
|
||||
},
|
||||
$inc: { _v: 1 },
|
||||
};
|
||||
|
||||
if (this.quest.leader === memberId) {
|
||||
update.$inc[`items.quests.${this.quest.key}`] = -1;
|
||||
}
|
||||
|
||||
backgroundOperations.push(User.update({ _id: memberId }, update).exec());
|
||||
});
|
||||
|
||||
// TODO Add emails to users that quest has started to background ops
|
||||
|
||||
// These operations should run in the background
|
||||
// and not hold up the quest routes from resolving
|
||||
// TODO: What here?
|
||||
// Q.all(backgroundOperations).then(() => {
|
||||
// }).catch(err => {
|
||||
// TODO: How to handle errors?
|
||||
// IE, user deleted their account?
|
||||
// });
|
||||
};
|
||||
|
||||
export function chatDefaults (msg, user) {
|
||||
let message = {
|
||||
id: shared.uuid(),
|
||||
|
||||
Reference in New Issue
Block a user