Skip to content

Commit 951d230

Browse files
committed
test: add regression coverage for bannedUsers pagination and private group member access
- rooms.bannedUsers: assert count/offset are honored, pages don't overlap, and the response only carries the projected user fields - groups.*: lock the member-access contract of findPrivateGroupByIdOrName (info/history/close/open as a regular member, non-member rejection, and the subscription open-flag round trip)
1 parent 5144cd8 commit 951d230

2 files changed

Lines changed: 257 additions & 0 deletions

File tree

apps/meteor/tests/end-to-end/api/groups.ts

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,132 @@ describe('[Groups]', () => {
13861386
});
13871387
});
13881388

1389+
describe('group access as a regular member', () => {
1390+
let testGroup: IRoom;
1391+
let member: TestUser<IUser>;
1392+
let memberCredentials: Credentials;
1393+
let outsider: TestUser<IUser>;
1394+
let outsiderCredentials: Credentials;
1395+
const memberGroupName = `member-access-group-${Date.now()}-${Math.random()}`;
1396+
1397+
before(async () => {
1398+
member = await createUser();
1399+
memberCredentials = await login(member.username, password);
1400+
outsider = await createUser();
1401+
outsiderCredentials = await login(outsider.username, password);
1402+
1403+
const result = await createRoom({ type: 'p', name: memberGroupName, members: [member.username] });
1404+
testGroup = result.body.group;
1405+
});
1406+
1407+
after(async () => {
1408+
await deleteRoom({ type: 'p', roomId: testGroup._id });
1409+
await Promise.all([deleteUser(member), deleteUser(outsider)]);
1410+
});
1411+
1412+
it('should return the group info to a regular member', async () => {
1413+
const res = await request
1414+
.get(api('groups.info'))
1415+
.set(memberCredentials)
1416+
.query({
1417+
roomId: testGroup._id,
1418+
})
1419+
.expect('Content-Type', 'application/json')
1420+
.expect(200);
1421+
1422+
expect(res.body).to.have.property('success', true);
1423+
expect(res.body).to.have.nested.property('group._id', testGroup._id);
1424+
expect(res.body).to.have.nested.property('group.name', memberGroupName);
1425+
expect(res.body).to.have.nested.property('group.t', 'p');
1426+
});
1427+
1428+
it('should not return the group info to a non-member', async () => {
1429+
const res = await request
1430+
.get(api('groups.info'))
1431+
.set(outsiderCredentials)
1432+
.query({
1433+
roomId: testGroup._id,
1434+
})
1435+
.expect('Content-Type', 'application/json')
1436+
.expect(400);
1437+
1438+
expect(res.body).to.have.property('success', false);
1439+
expect(res.body).to.have.property('errorType', 'error-room-not-found');
1440+
});
1441+
1442+
it('should return the group history to a regular member', async () => {
1443+
const res = await request
1444+
.get(api('groups.history'))
1445+
.set(memberCredentials)
1446+
.query({
1447+
roomId: testGroup._id,
1448+
})
1449+
.expect('Content-Type', 'application/json')
1450+
.expect(200);
1451+
1452+
expect(res.body).to.have.property('success', true);
1453+
expect(res.body).to.have.property('messages').that.is.an('array');
1454+
});
1455+
1456+
it('should close the group for a regular member', async () => {
1457+
await request
1458+
.post(api('groups.close'))
1459+
.set(memberCredentials)
1460+
.send({
1461+
roomId: testGroup._id,
1462+
})
1463+
.expect('Content-Type', 'application/json')
1464+
.expect(200)
1465+
.expect((res) => {
1466+
expect(res.body).to.have.property('success', true);
1467+
});
1468+
});
1469+
1470+
it('should report the group as already closed on a second close (subscription open flag is read)', async () => {
1471+
await request
1472+
.post(api('groups.close'))
1473+
.set(memberCredentials)
1474+
.send({
1475+
roomId: testGroup._id,
1476+
})
1477+
.expect('Content-Type', 'application/json')
1478+
.expect(400)
1479+
.expect((res) => {
1480+
expect(res.body).to.have.property('success', false);
1481+
expect(res.body).to.have.property('error', `The private group, ${memberGroupName}, is already closed to the sender`);
1482+
});
1483+
});
1484+
1485+
it('should open the group back for a regular member', async () => {
1486+
await request
1487+
.post(api('groups.open'))
1488+
.set(memberCredentials)
1489+
.send({
1490+
roomId: testGroup._id,
1491+
})
1492+
.expect('Content-Type', 'application/json')
1493+
.expect(200)
1494+
.expect((res) => {
1495+
expect(res.body).to.have.property('success', true);
1496+
});
1497+
});
1498+
1499+
it('should report the group as already open on a second open (subscription open flag is read)', async () => {
1500+
await request
1501+
.post(api('groups.open'))
1502+
.set(memberCredentials)
1503+
.send({
1504+
roomId: testGroup._id,
1505+
})
1506+
.expect('Content-Type', 'application/json')
1507+
.expect(400)
1508+
.expect((res) => {
1509+
expect(res.body).to.have.property('success', false);
1510+
expect(res.body).to.have.property('error', `The private group, ${memberGroupName}, is already open for the sender`);
1511+
});
1512+
});
1513+
});
1514+
13891515
describe('/groups.list', () => {
13901516
it('should list the groups the caller is part of', (done) => {
13911517
void request

apps/meteor/tests/end-to-end/api/rooms.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5073,4 +5073,135 @@ describe('[Rooms]', () => {
50735073
});
50745074
});
50755075
});
5076+
5077+
describe('/rooms.bannedUsers', () => {
5078+
let testChannel: IRoom;
5079+
let bannedUsers: TestUser<IUser>[];
5080+
let bannedUserIds: string[];
5081+
5082+
before(async () => {
5083+
const result = await createRoom({ type: 'c', name: `banned-users-list-${Date.now()}-${Math.random()}` });
5084+
testChannel = result.body.channel;
5085+
5086+
bannedUsers = await Promise.all([createUser(), createUser(), createUser()]);
5087+
bannedUserIds = bannedUsers.map((user) => user._id);
5088+
5089+
for (const user of bannedUsers) {
5090+
await request
5091+
.post(api('channels.invite'))
5092+
.set(credentials)
5093+
.send({
5094+
roomId: testChannel._id,
5095+
userId: user._id,
5096+
})
5097+
.expect(200);
5098+
5099+
await request
5100+
.post(api('rooms.banUser'))
5101+
.set(credentials)
5102+
.send({
5103+
roomId: testChannel._id,
5104+
userId: user._id,
5105+
})
5106+
.expect(200);
5107+
}
5108+
});
5109+
5110+
after(async () => {
5111+
await deleteRoom({ type: 'c', roomId: testChannel._id });
5112+
await Promise.all(bannedUsers.map((user) => deleteUser(user)));
5113+
});
5114+
5115+
it('should list every banned user of the room with only the projected fields', async () => {
5116+
const res = await request
5117+
.get(api('rooms.bannedUsers'))
5118+
.set(credentials)
5119+
.query({
5120+
roomId: testChannel._id,
5121+
})
5122+
.expect('Content-Type', 'application/json')
5123+
.expect(200);
5124+
5125+
expect(res.body).to.have.property('success', true);
5126+
expect(res.body).to.have.property('total', bannedUsers.length);
5127+
expect(res.body).to.have.property('count', bannedUsers.length);
5128+
expect(res.body).to.have.property('offset', 0);
5129+
expect(res.body.bannedUsers).to.be.an('array').with.lengthOf(bannedUsers.length);
5130+
expect(res.body.bannedUsers.map((u: IUser) => u._id)).to.have.members(bannedUserIds);
5131+
5132+
res.body.bannedUsers.forEach((user: IUser) => {
5133+
expect(user).to.have.property('_id').that.is.a('string');
5134+
expect(user).to.have.property('username').that.is.a('string');
5135+
expect(Object.keys(user)).to.satisfy(
5136+
(keys: string[]) => keys.every((key) => ['_id', 'username', 'name'].includes(key)),
5137+
'response should only contain the projected fields (_id, username, name)',
5138+
);
5139+
});
5140+
});
5141+
5142+
it('should limit the number of banned users returned when count is provided', async () => {
5143+
const res = await request
5144+
.get(api('rooms.bannedUsers'))
5145+
.set(credentials)
5146+
.query({
5147+
roomId: testChannel._id,
5148+
count: 2,
5149+
})
5150+
.expect('Content-Type', 'application/json')
5151+
.expect(200);
5152+
5153+
expect(res.body).to.have.property('success', true);
5154+
expect(res.body).to.have.property('total', bannedUsers.length);
5155+
expect(res.body).to.have.property('count', 2);
5156+
expect(res.body).to.have.property('offset', 0);
5157+
expect(res.body.bannedUsers).to.be.an('array').with.lengthOf(2);
5158+
});
5159+
5160+
it('should skip banned users when offset is provided', async () => {
5161+
const res = await request
5162+
.get(api('rooms.bannedUsers'))
5163+
.set(credentials)
5164+
.query({
5165+
roomId: testChannel._id,
5166+
offset: 2,
5167+
count: 2,
5168+
})
5169+
.expect('Content-Type', 'application/json')
5170+
.expect(200);
5171+
5172+
expect(res.body).to.have.property('success', true);
5173+
expect(res.body).to.have.property('total', bannedUsers.length);
5174+
expect(res.body).to.have.property('offset', 2);
5175+
expect(res.body.bannedUsers)
5176+
.to.be.an('array')
5177+
.with.lengthOf(bannedUsers.length - 2);
5178+
});
5179+
5180+
it('should paginate through all banned users without overlapping results', async () => {
5181+
const firstPage = await request
5182+
.get(api('rooms.bannedUsers'))
5183+
.set(credentials)
5184+
.query({
5185+
roomId: testChannel._id,
5186+
count: 2,
5187+
})
5188+
.expect(200);
5189+
5190+
const secondPage = await request
5191+
.get(api('rooms.bannedUsers'))
5192+
.set(credentials)
5193+
.query({
5194+
roomId: testChannel._id,
5195+
offset: 2,
5196+
count: 2,
5197+
})
5198+
.expect(200);
5199+
5200+
const firstPageIds = firstPage.body.bannedUsers.map((u: IUser) => u._id);
5201+
const secondPageIds = secondPage.body.bannedUsers.map((u: IUser) => u._id);
5202+
5203+
expect(firstPageIds.filter((id: string) => secondPageIds.includes(id))).to.be.empty;
5204+
expect([...firstPageIds, ...secondPageIds]).to.have.members(bannedUserIds);
5205+
});
5206+
});
50765207
});

0 commit comments

Comments
 (0)