mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 22:27:26 +01:00
497 lines
12 KiB
JavaScript
497 lines
12 KiB
JavaScript
var sinon = require('sinon');
|
|
var chai = require("chai");
|
|
chai.use(require("sinon-chai"));
|
|
var expect = chai.expect;
|
|
|
|
var Bluebird = require('bluebird');
|
|
var Group = require('../../../website/server/models/group').model;
|
|
var groupsController = require('../../../website/server/controllers/api-v2/groups');
|
|
|
|
describe('Groups Controller', function() {
|
|
var utils = require('../../../website/server/libs/api-v2/utils');
|
|
|
|
describe('#invite', function() {
|
|
var res, req, user, group;
|
|
|
|
beforeEach(function() {
|
|
group = {
|
|
_id: 'group-id',
|
|
name: 'group-name',
|
|
type: 'party',
|
|
members: [
|
|
'user-id',
|
|
'another-user'
|
|
],
|
|
save: sinon.stub().yields(),
|
|
markModified: sinon.spy()
|
|
};
|
|
|
|
user = {
|
|
_id: 'user-id',
|
|
name: 'inviter',
|
|
email: 'inviter@example.com',
|
|
save: sinon.stub().yields(),
|
|
markModified: sinon.spy()
|
|
};
|
|
|
|
res = {
|
|
locals: {
|
|
group: group,
|
|
user: user
|
|
},
|
|
json: sinon.stub(),
|
|
sendStatus: sinon.stub()
|
|
};
|
|
|
|
req = {
|
|
body: {}
|
|
};
|
|
});
|
|
|
|
context('uuids', function() {
|
|
beforeEach(function() {
|
|
req.body.uuids = ['invited-user'];
|
|
});
|
|
|
|
it('returns 400 if user not found');
|
|
|
|
it('returns a 400 if user is already in the group');
|
|
|
|
it('retuns 400 if user was already invited to that group');
|
|
|
|
it('returns 400 if user is already pending an invitation');
|
|
|
|
it('returns 400 is user is already in another party');
|
|
|
|
it('emails invited user');
|
|
|
|
it('does not email invited user if email preference is set to false');
|
|
});
|
|
|
|
context('emails', function() {
|
|
var EmailUnsubscription = require('../../../website/server/models/emailUnsubscription').model;
|
|
var execStub, selectStub;
|
|
|
|
beforeEach(function() {
|
|
sinon.stub(utils, 'encrypt').returns('http://link.com');
|
|
sinon.stub(utils, 'getUserInfo').returns({
|
|
name: user.name,
|
|
email: user.email
|
|
});
|
|
execStub = sinon.stub();
|
|
selectStub = sinon.stub().returns({
|
|
exec: execStub
|
|
});
|
|
sinon.stub(User, 'findOne').returns({
|
|
select: selectStub
|
|
});
|
|
sinon.stub(EmailUnsubscription, 'findOne');
|
|
sinon.stub(utils, 'txnEmail');
|
|
|
|
req.body.emails = [{email: 'user@example.com', name: 'user'}];
|
|
});
|
|
|
|
afterEach(function() {
|
|
User.findOne.restore();
|
|
EmailUnsubscription.findOne.restore();
|
|
utils.encrypt.restore();
|
|
utils.getUserInfo.restore();
|
|
utils.txnEmail.restore();
|
|
});
|
|
|
|
it('emails user with invite', function() {
|
|
execStub.yields(null, null);
|
|
EmailUnsubscription.findOne.yields(null, null);
|
|
|
|
groupsController.invite(req, res);
|
|
|
|
expect(utils.txnEmail).to.be.calledOnce;
|
|
expect(utils.txnEmail).to.be.calledWith(
|
|
{ email: 'user@example.com', name: 'user' },
|
|
'invite-friend',
|
|
[
|
|
{ name: 'LINK', content: '?partyInvite=http://link.com' },
|
|
{ name: 'INVITER', content: 'inviter' }
|
|
]
|
|
);
|
|
});
|
|
|
|
it('does not email user if user is on unsubscribe list', function() {
|
|
EmailUnsubscription.findOne.yields(null, {_id: 'on-list'});
|
|
|
|
expect(utils.txnEmail).to.not.be.called;
|
|
});
|
|
|
|
it('checks if a user with provided email already exists');
|
|
});
|
|
|
|
context('others', function() {
|
|
it ('returns a 400 error', function() {
|
|
groupsController.invite(req, res);
|
|
|
|
expect(res.json).to.be.calledOnce;
|
|
expect(res.json).to.be.calledWith(
|
|
400,
|
|
{ err: 'Can invite only by email or uuid' }
|
|
);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#leave', function() {
|
|
var res, req, user, group;
|
|
|
|
beforeEach(function() {
|
|
group = {
|
|
_id: 'group-id',
|
|
type: 'party',
|
|
members: [
|
|
'user-id',
|
|
'another-user'
|
|
],
|
|
save: sinon.stub().yields(),
|
|
leave: sinon.stub().yields(),
|
|
markModified: sinon.spy()
|
|
};
|
|
|
|
user = {
|
|
_id: 'user-id',
|
|
save: sinon.stub().yields(),
|
|
markModified: sinon.spy()
|
|
};
|
|
|
|
res = {
|
|
locals: {
|
|
group: group,
|
|
user: user
|
|
},
|
|
json: sinon.stub(),
|
|
sendStatus: sinon.stub()
|
|
};
|
|
|
|
req = {
|
|
query: { keep: 'keep' }
|
|
};
|
|
});
|
|
|
|
context('party', function() {
|
|
beforeEach(function() {
|
|
group.type = 'party';
|
|
});
|
|
|
|
it('prevents user from leaving party if quest is active and part of the active members list', function() {
|
|
group.quest = {
|
|
active: true,
|
|
members: {
|
|
another_user: true,
|
|
yet_another_user: null,
|
|
'user-id': true
|
|
}
|
|
};
|
|
|
|
groupsController.leave(req, res);
|
|
|
|
expect(group.leave).to.not.be.called;
|
|
expect(res.json).to.be.calledOnce;
|
|
expect(res.json).to.be.calledWith(403, 'You cannot leave party during an active quest. Please leave the quest first.');
|
|
});
|
|
|
|
it('prevents quest leader from leaving a party if they have started a quest', function() {
|
|
group.quest = {
|
|
active: false,
|
|
leader: 'user-id'
|
|
};
|
|
|
|
groupsController.leave(req, res);
|
|
|
|
expect(group.leave).to.not.be.called;
|
|
expect(res.json).to.be.calledOnce;
|
|
expect(res.json).to.be.calledWith(403, 'You cannot leave your party when you have started a quest. Abort the quest first.');
|
|
});
|
|
|
|
it('leaves party if quest is not active', function() {
|
|
group.quest = {
|
|
active: false,
|
|
members: {
|
|
another_user: true,
|
|
yet_another_user: null,
|
|
'user-id': null
|
|
}
|
|
};
|
|
|
|
groupsController.leave(req, res);
|
|
|
|
expect(group.leave).to.be.calledOnce;
|
|
expect(res.json).to.not.be.called;
|
|
});
|
|
|
|
it('leaves party if quest is active, but user is not part of quest', function() {
|
|
group.quest = {
|
|
active: true,
|
|
members: {
|
|
another_user: true,
|
|
yet_another_user: null,
|
|
'user-id': null
|
|
}
|
|
};
|
|
|
|
groupsController.leave(req, res);
|
|
|
|
expect(group.leave).to.be.calledOnce;
|
|
expect(res.json).to.not.be.called;
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#questLeave', function() {
|
|
var res, req, group, user, saveSpy;
|
|
|
|
beforeEach(function() {
|
|
sinon.stub(Q, 'all').returns({
|
|
done: sinon.stub().yields()
|
|
});
|
|
group = {
|
|
_id: 'group-id',
|
|
type: 'party',
|
|
quest: {
|
|
leader : 'another-user',
|
|
active: true,
|
|
members: {
|
|
'user-id': true,
|
|
'another-user': true
|
|
},
|
|
key : 'vice1',
|
|
progress : {
|
|
hp : 364,
|
|
collect : {}
|
|
}
|
|
},
|
|
save: sinon.stub().yields(),
|
|
markModified: sinon.spy()
|
|
};
|
|
|
|
user = {
|
|
_id: 'user-id',
|
|
party : {
|
|
quest : {
|
|
key : 'vice1',
|
|
progress : {
|
|
up : 50,
|
|
down : 0,
|
|
collect : {}
|
|
},
|
|
completed : null,
|
|
RSVPNeeded : false
|
|
}
|
|
},
|
|
save: sinon.stub().yields(),
|
|
markModified: sinon.spy()
|
|
};
|
|
|
|
res = {
|
|
locals: {
|
|
group: group,
|
|
user: user
|
|
},
|
|
json: sinon.stub(),
|
|
sendStatus: sinon.stub()
|
|
};
|
|
|
|
req = { };
|
|
});
|
|
|
|
afterEach(function() {
|
|
Promise.all.restore();
|
|
});
|
|
|
|
context('error conditions', function() {
|
|
it('errors if quest is not active', function() {
|
|
group.quest.active = false;
|
|
|
|
groupsController.questLeave(req, res);
|
|
|
|
expect(res.json).to.be.calledOnce;
|
|
expect(res.json).to.be.calledWith(
|
|
404,
|
|
{ err: 'No active quest to leave' }
|
|
);
|
|
});
|
|
|
|
it('errors if user is not part of quest', function() {
|
|
delete group.quest.members[user._id];
|
|
|
|
groupsController.questLeave(req, res);
|
|
|
|
expect(res.json).to.be.calledOnce;
|
|
expect(res.json).to.be.calledWith(
|
|
403,
|
|
{ err: 'You are not part of the quest' }
|
|
);
|
|
});
|
|
|
|
it('does not allow quest leader to leave quest', function() {
|
|
group.quest.leader = 'user-id';
|
|
|
|
groupsController.questLeave(req, res);
|
|
|
|
expect(res.json).to.be.calledOnce;
|
|
expect(res.json).to.be.calledWith(
|
|
403,
|
|
{ err: 'Quest leader cannot leave quest' }
|
|
);
|
|
});
|
|
|
|
it('sends 500 if group cannot save', function() {
|
|
Promise.all.returns({
|
|
done: sinon.stub().callsArgWith(1, {err: 'save error'})
|
|
});
|
|
var nextSpy = sinon.spy();
|
|
|
|
groupsController.questLeave(req, res, nextSpy);
|
|
|
|
expect(res.json).to.not.be.called;
|
|
expect(nextSpy).to.be.calledOnce;
|
|
expect(nextSpy).to.be.calledWith({err: 'save error'});
|
|
});
|
|
});
|
|
|
|
context('success', function() {
|
|
it('removes user from quest', function() {
|
|
expect(group.quest.members[user._id]).to.exist;
|
|
|
|
groupsController.questLeave(req, res);
|
|
|
|
expect(group.quest.members[user._id]).to.not.exist;
|
|
});
|
|
|
|
it('scrubs quest data from user', function() {
|
|
user.party.quest.progress = {
|
|
up: 100,
|
|
down: 32,
|
|
collect: {
|
|
foo: 12,
|
|
bar: 4
|
|
}
|
|
};
|
|
|
|
groupsController.questLeave(req, res);
|
|
|
|
expect(user.party.quest.key).to.not.exist;
|
|
expect(user.party.quest.progress).to.eql({
|
|
up: 0,
|
|
down: 0,
|
|
collect: 0,
|
|
});
|
|
});
|
|
|
|
it('sends back 204 on success', function() {
|
|
groupsController.questLeave(req, res);
|
|
|
|
expect(res.sendStatus).to.be.calledOnce;
|
|
expect(res.sendStatus).to.be.calledWith(204);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('#removeMember', function() {
|
|
var req, res, group, user;
|
|
|
|
beforeEach(function() {
|
|
user = { _id: 'user-id' };
|
|
group = {
|
|
_id: 'group-id',
|
|
leader: 'user-id',
|
|
members: ['user-id', 'member-to-boot', 'another-user']
|
|
}
|
|
res = {
|
|
locals: {
|
|
user: user,
|
|
group: group
|
|
},
|
|
sendStatus: sinon.stub()
|
|
};
|
|
req = {
|
|
query: {
|
|
uuid: 'member-to-boot'
|
|
}
|
|
};
|
|
|
|
sinon.stub(Group, 'update');
|
|
sinon.stub(User, 'update');
|
|
sinon.stub(User, 'findById');
|
|
});
|
|
|
|
afterEach(function() {
|
|
Group.update.restore();
|
|
User.update.restore();
|
|
User.findById.restore();
|
|
});
|
|
|
|
context('quest behavior', function() {
|
|
it('removes quest from party if booted member was quest leader', function() {
|
|
group.quest = {
|
|
leader: 'member-to-boot',
|
|
active: true,
|
|
members: {
|
|
'user-id': true,
|
|
'leader-id': true,
|
|
'member-to-boot': true
|
|
},
|
|
key: 'whale'
|
|
}
|
|
|
|
groupsController.removeMember(req, res);
|
|
|
|
expect(Group.update).to.be.calledOnce;
|
|
expect(Group.update).to.be.calledWith(
|
|
{ _id: 'group-id'},
|
|
{
|
|
'$inc': { memberCount: -1 },
|
|
'$pull': { members: 'member-to-boot' },
|
|
'$set': { quest: {key: null, leader: null} }
|
|
}
|
|
);
|
|
});
|
|
|
|
it('returns quest scroll to booted member if booted member was leader of quest', function() {
|
|
Group.update.yields();
|
|
var bootedMember = {
|
|
_id: 'member-to-boot',
|
|
apiToken: 'api',
|
|
preferences: {
|
|
emailNotifications: {
|
|
kickedGroup: false
|
|
}
|
|
}
|
|
};
|
|
User.findById.yields(null, bootedMember);
|
|
User.update.returns({
|
|
exec: sinon.stub()
|
|
});
|
|
|
|
group.quest = {
|
|
leader: 'member-to-boot',
|
|
active: true,
|
|
members: {
|
|
'user-id': true,
|
|
'leader-id': true,
|
|
'member-to-boot': true
|
|
},
|
|
key: 'whale'
|
|
}
|
|
|
|
groupsController.removeMember(req, res);
|
|
|
|
expect(User.update).to.be.calledOnce;
|
|
expect(User.update).to.be.calledWith(
|
|
{ _id: 'member-to-boot', apiToken: 'api' },
|
|
{
|
|
'$unset': { 'newMessages.group-id': ''},
|
|
'$inc': { 'items.quests.whale': 1 }
|
|
}
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|