Skip to content

Commit 133da0b

Browse files
fix(team): await default-room assignments when adding team members (#38701)
1 parent dad0dba commit 133da0b

File tree

2 files changed

+122
-6
lines changed

2 files changed

+122
-6
lines changed

apps/meteor/server/services/team/service.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -998,13 +998,11 @@ export class TeamService extends ServiceClassInternal implements ITeamService {
998998
const defaultRooms = await Rooms.findDefaultRoomsForTeam(teamId).toArray();
999999
const users = await Users.findActiveByIds(members.map((member) => member.userId)).toArray();
10001000

1001-
defaultRooms.map(async (room) => {
1001+
for (const room of defaultRooms) {
10021002
// at this point, users are already part of the team so we won't check for membership
1003-
for await (const user of users) {
1004-
// add each user to the default room
1005-
await addUserToRoom(room._id, user, inviter, { skipSystemMessage: false });
1006-
}
1007-
});
1003+
// eslint-disable-next-line no-await-in-loop
1004+
await Promise.all(users.map((user) => addUserToRoom(room._id, user, inviter, { skipSystemMessage: false })));
1005+
}
10081006
}
10091007

10101008
async deleteById(teamId: string): Promise<boolean> {
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { expect } from 'chai';
2+
import { beforeEach, describe, it } from 'mocha';
3+
import proxyquire from 'proxyquire';
4+
import sinon from 'sinon';
5+
6+
const Rooms = {
7+
findDefaultRoomsForTeam: sinon.stub(),
8+
};
9+
10+
const Users = {
11+
findActiveByIds: sinon.stub(),
12+
};
13+
14+
const addUserToRoom = sinon.stub();
15+
16+
const { TeamService } = proxyquire.noCallThru().load('../../../../../server/services/team/service', {
17+
'@rocket.chat/core-services': {
18+
Room: {},
19+
Authorization: {},
20+
Message: {},
21+
ServiceClassInternal: class {},
22+
api: {},
23+
},
24+
'@rocket.chat/models': {
25+
Team: {},
26+
Rooms,
27+
Subscriptions: {},
28+
Users,
29+
TeamMember: {},
30+
},
31+
'@rocket.chat/string-helpers': {
32+
escapeRegExp: (value: string) => value,
33+
},
34+
'../../../app/channel-settings/server': {
35+
saveRoomName: sinon.stub(),
36+
},
37+
'../../../app/channel-settings/server/functions/saveRoomType': {
38+
saveRoomType: sinon.stub(),
39+
},
40+
'../../../app/lib/server/functions/addUserToRoom': {
41+
addUserToRoom,
42+
},
43+
'../../../app/lib/server/functions/checkUsernameAvailability': {
44+
checkUsernameAvailability: sinon.stub(),
45+
},
46+
'../../../app/lib/server/functions/getRoomsWithSingleOwner': {
47+
getSubscribedRoomsForUserWithDetails: sinon.stub(),
48+
},
49+
'../../../app/lib/server/functions/removeUserFromRoom': {
50+
removeUserFromRoom: sinon.stub(),
51+
},
52+
'../../../app/lib/server/lib/notifyListener': {
53+
notifyOnSubscriptionChangedByRoomIdAndUserId: sinon.stub(),
54+
notifyOnRoomChangedById: sinon.stub(),
55+
},
56+
'../../../app/settings/server': {
57+
settings: { get: sinon.stub() },
58+
},
59+
});
60+
61+
const service = new TeamService();
62+
63+
describe('Team service', () => {
64+
beforeEach(() => {
65+
addUserToRoom.reset();
66+
Rooms.findDefaultRoomsForTeam.reset();
67+
Users.findActiveByIds.reset();
68+
});
69+
70+
it('should wait for default room membership operations to finish', async function () {
71+
this.timeout(15000);
72+
73+
addUserToRoom.onFirstCall().resolves(true);
74+
addUserToRoom.onSecondCall().returns(
75+
new Promise<void>((resolve) => {
76+
setTimeout(() => {
77+
resolve();
78+
}, 20);
79+
}),
80+
);
81+
82+
Rooms.findDefaultRoomsForTeam.returns({
83+
toArray: () => Promise.resolve([{ _id: 'default-room' }]),
84+
});
85+
Users.findActiveByIds.returns({
86+
toArray: () =>
87+
Promise.resolve([
88+
{ _id: 'user-1', username: 'user-1' },
89+
{ _id: 'user-2', username: 'user-2' },
90+
]),
91+
});
92+
93+
await service.addMembersToDefaultRooms({ _id: 'inviter', username: 'inviter' }, 'team-id', [
94+
{ userId: 'user-1' },
95+
{ userId: 'user-2' },
96+
]);
97+
98+
expect(addUserToRoom.callCount).to.equal(2);
99+
});
100+
101+
it('should propagate errors from default room membership operations', async function () {
102+
this.timeout(15000);
103+
104+
addUserToRoom.rejects(new Error('room-add-failed'));
105+
Rooms.findDefaultRoomsForTeam.returns({
106+
toArray: () => Promise.resolve([{ _id: 'default-room' }]),
107+
});
108+
Users.findActiveByIds.returns({
109+
toArray: () => Promise.resolve([{ _id: 'user-1', username: 'user-1' }]),
110+
});
111+
112+
await expect(
113+
service.addMembersToDefaultRooms({ _id: 'inviter', username: 'inviter' }, 'team-id', [{ userId: 'user-1' }]),
114+
).to.be.rejectedWith('room-add-failed');
115+
116+
expect(addUserToRoom.callCount).to.equal(1);
117+
});
118+
});

0 commit comments

Comments
 (0)