diff --git a/test/api/v3/integration/groups/POST-groups_groupId_leave.js b/test/api/v3/integration/groups/POST-groups_groupId_leave.js index 59031300ba..3e13634bd8 100644 --- a/test/api/v3/integration/groups/POST-groups_groupId_leave.js +++ b/test/api/v3/integration/groups/POST-groups_groupId_leave.js @@ -3,6 +3,8 @@ import { checkExistence, createAndPopulateGroup, sleep, + generateUser, + translate as t, } from '../../../../helpers/api-v3-integration.helper'; import { each, @@ -20,6 +22,7 @@ describe('POST /groups/:groupId/leave', () => { let groupToLeave; let leader; let member; + let memberCount; beforeEach(async () => { let { group, groupLeader, members } = await createAndPopulateGroup({ @@ -30,6 +33,16 @@ describe('POST /groups/:groupId/leave', () => { groupToLeave = group; leader = groupLeader; member = members[0]; + memberCount = group.memberCount; + }); + + it('prevents non members from leaving', async () => { + let user = await generateUser(); + await expect(user.post(`/groups/${groupToLeave._id}/leave`)).to.eventually.be.rejected.and.eql({ + code: 404, + error: 'NotFound', + message: t('groupNotFound'), + }); }); it(`lets user leave a ${groupType}`, async () => { @@ -39,14 +52,16 @@ describe('POST /groups/:groupId/leave', () => { expect(userThatLeftGroup.guilds).to.be.empty; expect(userThatLeftGroup.party._id).to.not.exist; + await groupToLeave.sync(); + expect(groupToLeave.memberCount).to.equal(memberCount - 1); }); it(`sets a new group leader when leader leaves a ${groupType}`, async () => { await leader.post(`/groups/${groupToLeave._id}/leave`); - let groupToLeaveWithNewLeader = await member.get(`/groups/${groupToLeave._id}`); - - expect(groupToLeaveWithNewLeader.leader._id).to.equal(member._id); + await groupToLeave.sync(); + expect(groupToLeave.memberCount).to.equal(memberCount - 1); + expect(groupToLeave.leader).to.equal(member._id); }); context('With challenges', () => { diff --git a/test/api/v3/integration/user/auth/GET-logout.test.js b/test/api/v3/integration/user/auth/GET-logout.test.js new file mode 100644 index 0000000000..731c523bde --- /dev/null +++ b/test/api/v3/integration/user/auth/GET-logout.test.js @@ -0,0 +1,3 @@ +describe('GET /user/auth/logout', () => { + // TODO Test manually +}); diff --git a/test/api/v3/integration/user/auth/POST-firebase.test.js b/test/api/v3/integration/user/auth/POST-firebase.test.js new file mode 100644 index 0000000000..7ebd5a20cb --- /dev/null +++ b/test/api/v3/integration/user/auth/POST-firebase.test.js @@ -0,0 +1,18 @@ +import { + generateUser, +} from '../../../../../helpers/api-integration/v3'; +import moment from 'moment'; + +describe('POST /user/auth/firebase', () => { + let user; + + before(async () => { + user = await generateUser(); + }); + + it('returns a Firebase token', async () => { + let {token, expires} = await user.post('/user/auth/firebase'); + expect(moment(expires).isValid()).to.be.true; + expect(token).to.be.a('string'); + }); +}); diff --git a/test/helpers/api-integration/v3/object-generators.js b/test/helpers/api-integration/v3/object-generators.js index 57f6e2163b..edd27f30ba 100644 --- a/test/helpers/api-integration/v3/object-generators.js +++ b/test/helpers/api-integration/v3/object-generators.js @@ -83,7 +83,7 @@ export async function createAndPopulateGroup (settings = {}) { }) ); - group.update({ memberCount: numberOfMembers + 1}); + await group.update({ memberCount: numberOfMembers + 1}); let invitees = await Q.all( times(numberOfInvites, () => { diff --git a/website/src/controllers/api-v3/groups.js b/website/src/controllers/api-v3/groups.js index 0c92220c48..54fd971f7d 100644 --- a/website/src/controllers/api-v3/groups.js +++ b/website/src/controllers/api-v3/groups.js @@ -332,7 +332,7 @@ api.leaveGroup = { let validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId: req.params.groupId, fields: '-chat'}); // Do not fetch chat + let group = await Group.getGroup({user, groupId: req.params.groupId, fields: '-chat', requireMembership: true}); if (!group) throw new NotFound(res.t('groupNotFound')); // During quests, checke wheter user can leave @@ -590,7 +590,7 @@ api.inviteToGroup = { let validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; - let group = await Group.getGroup({user, groupId: req.params.groupId, fields: '-chat'}); // Do not fetch chat TODO other fields too? + let group = await Group.getGroup({user, groupId: req.params.groupId, fields: '-chat'}); if (!group) throw new NotFound(res.t('groupNotFound')); let uuids = req.body.uuids; diff --git a/website/src/models/group.js b/website/src/models/group.js index 58d01ef720..1620ed020c 100644 --- a/website/src/models/group.js +++ b/website/src/models/group.js @@ -121,16 +121,24 @@ schema.post('remove', function postRemoveGroup (group) { firebase.deleteGroup(group._id); }); -schema.statics.getGroup = function getGroup (options = {}) { - let {user, groupId, fields, optionalMembership = false, populateLeader = false} = options; +schema.statics.getGroup = async function getGroup (options = {}) { + let {user, groupId, fields, optionalMembership = false, populateLeader = false, requireMembership = false} = options; let query; + let isParty = groupId === 'party' || user.party._id === groupId; + let isGuild = user.guilds.indexOf(groupId) !== -1; + + // When requireMembership is true check that user is member even in public guild + if (requireMembership && !isParty && !isGuild) { + return null; + } + // When optionalMembership is true it's not required for the user to be a member of the group - if (groupId === 'party' || user.party._id === groupId) { + if (isParty) { query = {type: 'party', _id: user.party._id}; } else if (optionalMembership === true) { query = {_id: groupId}; - } else if (user.guilds.indexOf(groupId) !== -1) { + } else if (isGuild) { query = {type: 'guild', _id: groupId}; } else { query = {type: 'guild', privacy: 'public', _id: groupId}; @@ -139,7 +147,8 @@ schema.statics.getGroup = function getGroup (options = {}) { let mQuery = this.findOne(query); if (fields) mQuery.select(fields); if (populateLeader === true) mQuery.populate('leader', nameFields); - return mQuery.exec(); + let group = await mQuery.exec(); + return group; }; // When converting to json remove chat messages with more than 1 flag and remove all flags info @@ -591,13 +600,16 @@ schema.methods.leave = async function leaveGroup (user, keep = 'keep-all') { // otherwise just remove a member TODO create User.methods.removeFromGroup? if (group.type === 'guild') { - promises.push(User.update({_id: user._id}, {$pull: {guilds: group._id } }).exec()); + promises.push(User.update({_id: user._id}, {$pull: {guilds: group._id}}).exec()); } else { - promises.push(User.update({_id: user._id}, {$set: {party: {} } }).exec()); + promises.push(User.update({_id: user._id}, {$set: {party: {}}}).exec()); } // If the leader is leaving (or if the leader previously left, and this wasn't accounted for) - let update = { memberCount: group.memberCount - 1 }; + let update = { + $inc: {memberCount: -1}, + }; + if (group.leader === user._id) { let query = group.type === 'party' ? {'party._id': group._id} : {guilds: group._id}; query._id = {$ne: user._id};