Files
upstream-mirror 30810eecd7
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 2s
snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:21:02 +05:00

2923 lines
90 KiB
TypeScript

import type { Credentials } from '@rocket.chat/api-client';
import { TeamType, type IIntegration, type IMessage, type IRoom, type ITeam, type IUser } from '@rocket.chat/core-typings';
import { assert, expect } from 'chai';
import { after, before, describe, it, beforeEach } from 'mocha';
import { getCredentials, api, request, credentials, apiPrivateChannelName } from '../../data/api-data';
import { pinMessage, starMessage, sendMessage, updateMessage } from '../../data/chat.helper';
import { CI_MAX_ROOMS_PER_GUEST as maxRoomsPerGuest } from '../../data/constants';
import { createGroup, deleteGroup } from '../../data/groups.helper';
import { createIntegration, removeIntegration } from '../../data/integration.helper';
import { updatePermission, updateSetting } from '../../data/permissions.helper';
import { createRoom, deleteRoom } from '../../data/rooms.helper';
import { createTeam, deleteTeam } from '../../data/teams.helper';
import { testFileUploads } from '../../data/uploads.helper';
import { adminUsername, password } from '../../data/user';
import type { TestUser } from '../../data/users.helper';
import { createUser, login, deleteUser } from '../../data/users.helper';
function getRoomInfo(roomId: IRoom['_id']) {
return new Promise<{ group: IRoom }>((resolve) => {
void request
.get(api('groups.info'))
.set(credentials)
.query({
roomId,
})
.end((_err, req) => {
resolve(req.body);
});
});
}
describe('[Groups]', () => {
let group: {
_id: string;
name: string;
};
before((done) => getCredentials(done));
before(async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: apiPrivateChannelName,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group._id');
expect(res.body).to.have.nested.property('group.name', apiPrivateChannelName);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.msgs', 0);
group = {
_id: res.body.group._id,
name: res.body.group.name,
};
});
});
after(async () => {
await request
.post(api('groups.delete'))
.set(credentials)
.send({
roomId: group._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
});
describe('/groups.create', () => {
let guestUser: TestUser<IUser>;
let invitedUser: TestUser<IUser>;
let invitedUserCredentials: Credentials;
let room: IRoom;
let teamId: ITeam['_id'];
before(async () => {
guestUser = await createUser({ roles: ['guest'] });
invitedUser = await createUser();
invitedUserCredentials = await login(invitedUser.username, password);
await updatePermission('create-team', ['admin', 'user']);
const teamCreateRes = await request
.post(api('teams.create'))
.set(credentials)
.send({
name: `team-${Date.now()}`,
type: 0,
members: [invitedUser.username],
});
teamId = teamCreateRes.body.team._id;
await updatePermission('create-team-group', ['owner']);
});
after(async () => {
await deleteUser(guestUser);
await deleteUser(invitedUser);
await updatePermission('create-team-group', ['admin', 'owner', 'moderator']);
});
describe('guest users', () => {
it('should not add guest users to more rooms than defined in the license', async function () {
// TODO this is not the right way to do it. We're doing this way for now just because we have separate CI jobs for EE and CE,
// ideally we should have a single CI job that adds a license and runs both CE and EE tests.
if (!process.env.IS_EE) {
this.skip();
}
const promises = [];
for (let i = 0; i < maxRoomsPerGuest; i++) {
promises.push(
createRoom({
type: 'p',
name: `channel.test.${Date.now()}-${Math.random()}`,
members: [guestUser.username],
}),
);
}
await Promise.all(promises);
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `channel.test.${Date.now()}-${Math.random()}`,
members: [guestUser.username],
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
room = res.body.group;
});
await request
.get(api('groups.members'))
.set(credentials)
.query({
roomId: room._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('members').and.to.be.an('array');
expect(res.body.members).to.have.lengthOf(1);
});
});
});
describe('validate E2E rooms', () => {
before(async () => {
await Promise.all([updateSetting('E2E_Enable', true), updateSetting('E2E_Allow_Unencrypted_Messages', false)]);
});
after(async () => {
await Promise.all([updateSetting('E2E_Enable', false), updateSetting('E2E_Allow_Unencrypted_Messages', true)]);
});
let rid: IRoom['_id'];
it('should create a new encrypted group', async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `encrypted-${apiPrivateChannelName}`,
extraData: {
encrypted: true,
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group.name', `encrypted-${apiPrivateChannelName}`);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.msgs', 0);
expect(res.body).to.have.nested.property('group.encrypted', true);
rid = res.body.group._id;
});
});
it('should send an encrypted message in encrypted group', async () => {
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
text: 'Encrypted Message',
t: 'e2e',
e2e: 'pending',
rid,
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('message');
expect(res.body).to.have.nested.property('message.text', 'Encrypted Message');
expect(res.body).to.have.nested.property('message.t', 'e2e');
expect(res.body).to.have.nested.property('message.e2e', 'pending');
});
});
it('should give an error on sending un-encrypted message in encrypted room', async () => {
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
text: 'Unencrypted Message',
rid,
},
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error').that.is.a('string');
});
});
it('should allow sending un-encrypted messages in encrypted room when setting is enabled', async () => {
await updateSetting('E2E_Allow_Unencrypted_Messages', true);
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
text: 'Unencrypted Message',
rid,
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('message');
expect(res.body).to.have.nested.property('message.text', 'Unencrypted Message');
});
});
});
describe('E2E enabled by default', () => {
before(async () => {
await Promise.all([updateSetting('E2E_Enable', true), updateSetting('E2E_Enabled_Default_PrivateRooms', true)]);
});
after(async () => {
await Promise.all([updateSetting('E2E_Enable', false), updateSetting('E2E_Enabled_Default_PrivateRooms', false)]);
});
it('should create the encrypted room by default', async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `default-encrypted-${apiPrivateChannelName}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group.name', `default-encrypted-${apiPrivateChannelName}`);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.msgs', 0);
expect(res.body).to.have.nested.property('group.encrypted', true);
});
});
});
describe('E2E forced encryption for private rooms', () => {
const createdRoomIds: IRoom['_id'][] = [];
before(async () => {
await Promise.all([updateSetting('E2E_Enable', true), updateSetting('E2E_Force_Encryption_For_Private_Rooms', true)]);
});
after(async () => {
await Promise.all([
updateSetting('E2E_Enable', false),
updateSetting('E2E_Force_Encryption_For_Private_Rooms', false),
...createdRoomIds.map((roomId) => deleteRoom({ type: 'p', roomId })),
]);
});
it('should reject creating a private room with encrypted=false when private room encryption is forced', async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `forced-unencrypted-${apiPrivateChannelName}`,
extraData: {
encrypted: false,
},
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-encrypted-private-rooms-enforced');
});
});
it('should create an encrypted private room when encrypted is omitted and private room encryption is forced', async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `forced-default-${apiPrivateChannelName}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
if (res.body.group?._id) {
createdRoomIds.push(res.body.group._id);
}
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.encrypted', true);
});
});
it('should allow creating a private room with encrypted=true when private room encryption is forced', async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `forced-encrypted-${apiPrivateChannelName}`,
extraData: {
encrypted: true,
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
if (res.body.group?._id) {
createdRoomIds.push(res.body.group._id);
}
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.encrypted', true);
});
});
it('should not enforce encryption on federated private rooms', async () => {
// Federated rooms do not support E2EE, so the forced-encryption policy exempts them.
// Depending on the environment the creation may still fail further down the pipeline
// (federation unavailable), but it must never fail with the forced-encryption error,
// and if it succeeds the room must not have been force-encrypted.
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `forced-federated-${apiPrivateChannelName}`,
extraData: {
broadcast: false,
encrypted: false,
federated: true,
},
})
.expect('Content-Type', 'application/json')
.expect((res) => {
if (res.body.group?._id) {
createdRoomIds.push(res.body.group._id);
}
expect(res.body).to.not.have.property('errorType', 'error-encrypted-private-rooms-enforced');
if (res.body.success) {
expect(res.body).to.not.have.nested.property('group.encrypted', true);
}
});
});
});
it(`should fail when trying to use an existing room's name`, async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: 'general',
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.nested.property('errorType', 'error-duplicate-channel-name');
});
});
it('should successfully create a group in a team', async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `team-group-${Date.now()}`,
extraData: { teamId },
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('group');
expect(res.body.group).to.have.property('teamId', teamId);
});
});
it('should fail creating a group in a team when member does not have the necessary permission', async () => {
await request
.post(api('groups.create'))
.set(invitedUserCredentials)
.send({
name: `team-group-${Date.now()}`,
extraData: { teamId },
})
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-not-allowed');
});
});
});
describe('/groups.info', () => {
let testGroup: IRoom;
const newGroupInfoName = `info-private-channel-test-${Date.now()}`;
before('creating new group...', async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: newGroupInfoName,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
testGroup = res.body.group;
});
});
after('deleting group...', async () => {
await request
.post(api('groups.delete'))
.set(credentials)
.send({
roomName: newGroupInfoName,
})
.expect('Content-Type', 'application/json')
.expect(200);
});
it('should return group basic structure', async () => {
await request
.get(api('groups.info'))
.set(credentials)
.query({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group._id');
expect(res.body).to.have.nested.property('group.name', newGroupInfoName);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.msgs', 0);
});
});
let groupMessage: IMessage;
it('sending a message...', async () => {
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
text: 'Sample message',
rid: testGroup._id,
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
groupMessage = res.body.message;
});
});
it('REACTing with last message', async () => {
await request
.post(api('chat.react'))
.set(credentials)
.send({
emoji: ':squid:',
messageId: groupMessage._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
it('STARring last message', async () => {
await request
.post(api('chat.starMessage'))
.set(credentials)
.send({
messageId: groupMessage._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
it('PINning last message', async () => {
await request
.post(api('chat.pinMessage'))
.set(credentials)
.send({
messageId: groupMessage._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
it('should return group structure with "lastMessage" object including pin, reaction and star(should be an array) infos', async () => {
await request
.get(api('groups.info'))
.set(credentials)
.query({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('group').and.to.be.an('object');
const { group } = res.body;
expect(group).to.have.property('lastMessage').and.to.be.an('object');
expect(group.lastMessage).to.have.property('reactions').and.to.be.an('object');
expect(group.lastMessage).to.have.property('pinned').and.to.be.a('boolean');
expect(group.lastMessage).to.have.property('pinnedAt').and.to.be.a('string');
expect(group.lastMessage).to.have.property('pinnedBy').and.to.be.an('object');
expect(group.lastMessage).to.have.property('starred').and.to.be.an('array');
});
});
it('should return all groups messages where the last message of array should have the "star" array with USERS star ONLY', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').and.to.be.an('array');
const messages = res.body.messages as IMessage[];
const lastMessage = messages.filter((message) => message._id === groupMessage._id)[0];
expect(lastMessage).to.have.property('starred').and.to.be.an('array');
expect(lastMessage.starred?.[0]._id).to.be.equal(adminUsername);
});
});
it('should return all groups messages where the last message of array should have the "star" array with USERS star ONLY even requested with count and offset params', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: testGroup._id,
count: 5,
offset: 0,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').and.to.be.an('array');
const messages = res.body.messages as IMessage[];
const lastMessage = messages.filter((message) => message._id === groupMessage._id)[0];
expect(lastMessage).to.have.property('starred').and.to.be.an('array');
expect(lastMessage.starred?.[0]._id).to.be.equal(adminUsername);
});
});
});
describe('/groups.messages', () => {
let testGroup: IRoom;
let firstUser: IUser;
let secondUser: IUser;
let pinnedMessageId: IMessage['_id'];
before(async () => {
testGroup = (await createGroup({ name: `test-group-${Date.now()}` })).body.group;
firstUser = await createUser({ joinDefaultChannels: false });
secondUser = await createUser({ joinDefaultChannels: false });
const messages = [
{
rid: testGroup._id,
msg: `@${firstUser.username} youre being mentioned`,
mentions: [{ username: firstUser.username, _id: firstUser._id, name: firstUser.name }],
},
{
rid: testGroup._id,
msg: `@${secondUser.username} youre being mentioned`,
mentions: [{ username: secondUser.username, _id: secondUser._id, name: secondUser.name }],
},
{
rid: testGroup._id,
msg: `A simple message`,
},
{
rid: testGroup._id,
msg: `A pinned simple message`,
},
];
const [, , starredMessage, pinnedMessage] = await Promise.all(messages.map((message) => sendMessage({ message })));
await Promise.all([
starMessage({ messageId: starredMessage.body.message._id }),
pinMessage({ messageId: pinnedMessage.body.message._id }),
]);
pinnedMessageId = pinnedMessage.body.message._id;
});
after(async () => {
await deleteGroup({ roomName: testGroup.name });
});
it('should return all messages from a group', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({ roomId: testGroup._id })
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').and.to.be.an('array');
expect(res.body.messages).to.have.lengthOf(5);
});
});
it('should return messages that mention a single user', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: testGroup._id,
mentionIds: firstUser._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.have.lengthOf(1);
expect(res.body.messages[0]).to.have.nested.property('mentions').that.is.an('array').and.to.have.lengthOf(1);
expect(res.body.messages[0].mentions[0]).to.have.property('_id', firstUser._id);
expect(res.body).to.have.property('count', 1);
expect(res.body).to.have.property('total', 1);
});
});
it('should return messages that mention multiple users', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: testGroup._id,
mentionIds: `${firstUser._id},${secondUser._id}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.have.lengthOf(2);
expect(res.body).to.have.property('count', 2);
expect(res.body).to.have.property('total', 2);
const mentionIds = res.body.messages.map((message: any) => message.mentions[0]._id);
expect(mentionIds).to.include.members([firstUser._id, secondUser._id]);
});
});
it('should return messages that are starred by a specific user', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: testGroup._id,
starredIds: 'rocketchat.internal.admin.test',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.have.lengthOf(1);
expect(res.body.messages[0]).to.have.nested.property('starred').that.is.an('array').and.to.have.lengthOf(1);
expect(res.body).to.have.property('count', 1);
expect(res.body).to.have.property('total', 1);
});
});
it('should return messages that are pinned', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: testGroup._id,
pinned: true,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.have.lengthOf(1);
expect(res.body.messages[0]).to.have.nested.property('pinned').that.is.an('boolean').and.to.be.true;
expect(res.body.messages[0]).to.have.nested.property('pinnedBy').that.is.an('object');
expect(res.body.messages[0].pinnedBy).to.have.property('_id', 'rocketchat.internal.admin.test');
expect(res.body).to.have.property('count', 1);
expect(res.body).to.have.property('total', 1);
});
});
it('should return all messages from a group using roomName parameter', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({ roomName: testGroup.name })
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').and.to.be.an('array');
expect(res.body.messages).to.have.lengthOf(5);
});
});
it('should return error if both roomId and roomName are provided', async () => {
const thirdGroup = (await createGroup({ name: `test-priority-${Date.now()}` })).body.group;
const secondGroup = (await createGroup({ name: `test-priority-${Date.now()}` })).body.group;
try {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: thirdGroup._id,
roomName: secondGroup.name,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-invalid-params');
});
} finally {
await Promise.all([deleteGroup({ roomName: secondGroup.name }), deleteGroup({ roomName: thirdGroup.name })]);
}
});
describe('_hidden messages behavior when Message_KeepHistory is enabled', async () => {
before(async () => {
await updateSetting('Message_KeepHistory', true);
await pinMessage({ messageId: pinnedMessageId, unpin: true });
});
after(async () => {
await updateSetting('Message_KeepHistory', false);
});
it('should return all messages, without any pinned messages', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({ roomId: testGroup._id })
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').and.to.be.an('array');
expect(res.body.messages).to.have.lengthOf(5);
res.body.messages.forEach((msg: IMessage) => {
expect(msg).to.not.have.property('pinned', true);
expect(msg).to.not.have.property('_hidden');
});
});
});
it('should return no pinned messages', async () => {
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: testGroup._id,
pinned: true,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.have.lengthOf(0);
expect(res.body).to.have.property('count', 0);
expect(res.body).to.have.property('total', 0);
});
});
it('should not return old message when updating a message', async () => {
await updateMessage({ msgId: pinnedMessageId, updatedMessage: 'message was unpinned', roomId: testGroup._id });
await request
.get(api('groups.messages'))
.set(credentials)
.query({ roomId: testGroup._id })
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').and.to.be.an('array');
expect(res.body.messages).to.have.lengthOf(5);
const updatedMessage = res.body.messages.find((msg: IMessage) => msg._id === pinnedMessageId);
expect(updatedMessage).to.have.property('msg', 'message was unpinned');
expect(updatedMessage).to.have.property('editedAt');
res.body.messages.forEach((msg: IMessage) => {
expect(msg).to.not.have.property('_hidden');
});
});
});
});
});
describe('/groups.invite', async () => {
let roomInfo: { group: IRoom };
before(async () => {
roomInfo = await getRoomInfo(group._id);
});
it('should invite user to group', async () => {
await request
.post(api('groups.invite'))
.set(credentials)
.send({
roomId: group._id,
userId: 'rocket.cat',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group._id');
expect(res.body).to.have.nested.property('group.name', apiPrivateChannelName);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.msgs', roomInfo.group.msgs + 1);
});
});
});
describe('/groups.addModerator', () => {
it('should make user a moderator', async () => {
const res = await request
.post(api('groups.addModerator'))
.set(credentials)
.send({
roomId: group._id,
userId: 'rocket.cat',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe('/groups.removeModerator', () => {
it('should remove user from moderator', async () => {
const res = await request
.post(api('groups.removeModerator'))
.set(credentials)
.send({
roomId: group._id,
userId: 'rocket.cat',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe('/groups.addOwner', () => {
it('should add user as owner', async () => {
const res = await request
.post(api('groups.addOwner'))
.set(credentials)
.send({
roomId: group._id,
userId: 'rocket.cat',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe('/groups.removeOwner', () => {
it('should remove user from owner', async () => {
const res = await request
.post(api('groups.removeOwner'))
.set(credentials)
.send({
roomId: group._id,
userId: 'rocket.cat',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe('/groups.addLeader', () => {
it('should add user as leader', async () => {
const res = await request
.post(api('groups.addLeader'))
.set(credentials)
.send({
roomId: group._id,
userId: 'rocket.cat',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.a.property('success', true);
});
});
describe('/groups.removeLeader', () => {
it('should remove user from leader', async () => {
const res = await request
.post(api('groups.removeLeader'))
.set(credentials)
.send({
roomId: group._id,
userId: 'rocket.cat',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe('/groups.kick', () => {
let testUserModerator: TestUser<IUser>;
let credsModerator: Credentials;
let testUserOwner: TestUser<IUser>;
let credsOwner: Credentials;
let testUserMember: TestUser<IUser>;
let groupTest: IRoom;
const inviteUser = async (userId: IUser['_id']) => {
await request
.post(api('groups.invite'))
.set(credsOwner)
.send({
roomId: groupTest._id,
userId,
})
.expect('Content-Type', 'application/json')
.expect(200);
};
before(async () => {
// had to do them in serie because calling them with Promise.all was failing some times
testUserModerator = await createUser();
testUserOwner = await createUser();
testUserMember = await createUser();
credsModerator = await login(testUserModerator.username, password);
credsOwner = await login(testUserOwner.username, password);
await request
.post(api('groups.create'))
.set(credsOwner)
.send({
name: `kick-test-group-${Date.now()}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group._id');
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.msgs', 0);
groupTest = res.body.group;
});
await inviteUser(testUserModerator._id);
await request
.post(api('groups.addModerator'))
.set(credsOwner)
.send({
roomId: groupTest._id,
userId: testUserModerator._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
});
after(async () => {
await Promise.all([
request
.post(api('groups.delete'))
.set(credsOwner)
.send({
roomId: groupTest._id,
})
.expect('Content-Type', 'application/json')
.expect(200),
updatePermission('kick-user-from-any-p-room', []),
updatePermission('remove-user', ['admin', 'owner', 'moderator']),
deleteUser(testUserModerator),
deleteUser(testUserOwner),
deleteUser(testUserMember),
]);
});
it("should return an error when user is not a member of the group and doesn't have permission", async () => {
await updatePermission('kick-user-from-any-p-room', []);
await request
.post(api('groups.kick'))
.set(credentials)
.send({
roomId: groupTest._id,
userId: testUserMember._id,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-room-not-found');
});
});
it('should allow a moderator to remove user from group', async () => {
await inviteUser(testUserMember._id);
await request
.post(api('groups.kick'))
.set(credsModerator)
.send({
roomId: groupTest._id,
userId: testUserMember._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
});
it('should allow an owner to remove user from group', async () => {
await inviteUser(testUserMember._id);
await request
.post(api('groups.kick'))
.set(credsOwner)
.send({
roomId: groupTest._id,
userId: testUserMember._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
});
it('should kick user from group if not a member of the room but has the required permission', async () => {
await updatePermission('kick-user-from-any-p-room', ['admin']);
await inviteUser(testUserMember._id);
await request
.post(api('groups.kick'))
.set(credentials)
.send({
roomId: group._id,
userId: testUserMember._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
});
it("should return an error when the owner doesn't have the required permission", async () => {
await updatePermission('remove-user', ['admin', 'moderator']);
await inviteUser(testUserMember._id);
await request
.post(api('groups.kick'))
.set(credsOwner)
.send({
roomId: groupTest._id,
userId: testUserMember._id,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-not-allowed');
});
});
it('should return an error when trying to kick the last owner from a group', async () => {
await updatePermission('kick-user-from-any-p-room', ['admin']);
await request
.post(api('groups.kick'))
.set(credentials)
.send({
roomId: groupTest._id,
userId: testUserOwner._id,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-you-are-last-owner');
});
});
it('should return an error when trying to kick user that does not exist');
it('should return an error when trying to kick user from a group that does not exist');
it('should return an error when trying to kick user from a group that the user is not in the room');
});
describe('/groups.setDescription', () => {
it('should set the description of the group with a string', async () => {
const res = await request
.post(api('groups.setDescription'))
.set(credentials)
.send({
roomId: group._id,
description: 'this is a description for a channel for api tests',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('description', 'this is a description for a channel for api tests');
});
it('should set the description of the group with an empty string(remove the description)', async () => {
const res = await request
.post(api('groups.setDescription'))
.set(credentials)
.send({
roomId: group._id,
description: '',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('description', '');
});
});
describe('/groups.setTopic', () => {
it('should set the topic of the group with a string', async () => {
const res = await request
.post(api('groups.setTopic'))
.set(credentials)
.send({
roomId: group._id,
topic: 'this is a topic of a channel for api tests',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('topic', 'this is a topic of a channel for api tests');
});
it('should set the topic of the group with an empty string(remove the topic)', async () => {
const res = await request
.post(api('groups.setTopic'))
.set(credentials)
.send({
roomId: group._id,
topic: '',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('topic', '');
});
});
describe('/groups.setPurpose', () => {
it('should set the purpose of the group with a string', async () => {
const res = await request
.post(api('groups.setPurpose'))
.set(credentials)
.send({
roomId: group._id,
purpose: 'this is a purpose of a channel for api tests',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('purpose', 'this is a purpose of a channel for api tests');
});
it('should set the purpose of the group with an empty string(remove the purpose)', async () => {
const res = await request
.post(api('groups.setPurpose'))
.set(credentials)
.send({
roomId: group._id,
purpose: '',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('purpose', '');
});
});
describe('/groups.history', () => {
it('should return groups history when searching by roomId', async () => {
const res = await request
.get(api('groups.history'))
.set(credentials)
.query({
roomId: group._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages');
});
it('should return groups history when searching by roomId even requested with count and offset params', async () => {
const res = await request
.get(api('groups.history'))
.set(credentials)
.query({
roomId: group._id,
count: 5,
offset: 0,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages');
});
describe('inclusive parameter', () => {
let testGroup: IRoom;
let oldestMessage: IMessage;
let middleMessage: IMessage;
let latestMessage: IMessage;
before(async () => {
const groupRes = await request
.post(api('groups.create'))
.set(credentials)
.send({ name: `inclusive-test-group-${Date.now()}` });
testGroup = groupRes.body.group;
// Send messages with small delays to ensure distinct timestamps
const msg1 = await sendMessage({ message: { rid: testGroup._id, msg: 'oldest message' } });
oldestMessage = msg1.body.message;
// Small delay to ensure timestamps are different
await new Promise((resolve) => setTimeout(resolve, 50));
const msg2 = await sendMessage({ message: { rid: testGroup._id, msg: 'middle message' } });
middleMessage = msg2.body.message;
await new Promise((resolve) => setTimeout(resolve, 50));
const msg3 = await sendMessage({ message: { rid: testGroup._id, msg: 'latest message' } });
latestMessage = msg3.body.message;
});
after(async () => {
if (testGroup?._id) {
await request.post(api('groups.delete')).set(credentials).send({ roomId: testGroup._id });
}
});
it('should include boundary messages when inclusive=true', async () => {
const res = await request
.get(api('groups.history'))
.set(credentials)
.query({
roomId: testGroup._id,
oldest: oldestMessage.ts,
latest: latestMessage.ts,
inclusive: 'true',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').that.is.an('array');
const messageIds = res.body.messages.map((m: IMessage) => m._id);
expect(messageIds).to.include(oldestMessage._id, 'oldest message should be included');
expect(messageIds).to.include(latestMessage._id, 'latest message should be included');
});
it('should exclude boundary messages when inclusive=false', async () => {
const res = await request
.get(api('groups.history'))
.set(credentials)
.query({
roomId: testGroup._id,
oldest: oldestMessage.ts,
latest: latestMessage.ts,
inclusive: 'false',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').that.is.an('array');
const messageIds = res.body.messages.map((m: IMessage) => m._id);
expect(messageIds).to.not.include(oldestMessage._id, 'oldest message should be excluded');
expect(messageIds).to.not.include(latestMessage._id, 'latest message should be excluded');
// Middle message should still be included if it exists in the range
expect(messageIds).to.include(middleMessage._id, 'middle message should be included');
});
it('should exclude boundary messages by default (no inclusive param)', async () => {
const res = await request
.get(api('groups.history'))
.set(credentials)
.query({
roomId: testGroup._id,
oldest: oldestMessage.ts,
latest: latestMessage.ts,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').that.is.an('array');
const messageIds = res.body.messages.map((m: IMessage) => m._id);
expect(messageIds).to.not.include(oldestMessage._id, 'oldest message should be excluded by default');
expect(messageIds).to.not.include(latestMessage._id, 'latest message should be excluded by default');
});
});
});
describe('/groups.archive', () => {
it('should archive the group', async () => {
const res = await request
.post(api('groups.archive'))
.set(credentials)
.send({
roomId: group._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe('/groups.unarchive', () => {
it('should unarchive the group', async () => {
const res = await request
.post(api('groups.unarchive'))
.set(credentials)
.send({
roomId: group._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe('/groups.close', () => {
it('should close the group', async () => {
const res = await request
.post(api('groups.close'))
.set(credentials)
.send({
roomId: group._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
it('should return an error when trying to close a private group that is already closed', async () => {
const res = await request
.post(api('groups.close'))
.set(credentials)
.send({
roomName: apiPrivateChannelName,
})
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', `The private group, ${apiPrivateChannelName}, is already closed to the sender`);
});
});
describe('/groups.open', () => {
it('should open the group', async () => {
const res = await request
.post(api('groups.open'))
.set(credentials)
.send({
roomId: group._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe('group access as a regular member', () => {
let testGroup: IRoom;
let member: TestUser<IUser>;
let memberCredentials: Credentials;
let outsider: TestUser<IUser>;
let outsiderCredentials: Credentials;
const memberGroupName = `member-access-group-${Date.now()}-${Math.random()}`;
before(async () => {
member = await createUser();
memberCredentials = await login(member.username, password);
outsider = await createUser();
outsiderCredentials = await login(outsider.username, password);
const result = await createRoom({ type: 'p', name: memberGroupName, members: [member.username] });
testGroup = result.body.group;
});
after(async () => {
await deleteRoom({ type: 'p', roomId: testGroup._id });
await Promise.all([deleteUser(member), deleteUser(outsider)]);
});
it('should return the group info to a regular member', async () => {
const res = await request
.get(api('groups.info'))
.set(memberCredentials)
.query({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group._id', testGroup._id);
expect(res.body).to.have.nested.property('group.name', memberGroupName);
expect(res.body).to.have.nested.property('group.t', 'p');
});
it('should not return the group info to a non-member', async () => {
const res = await request
.get(api('groups.info'))
.set(outsiderCredentials)
.query({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-room-not-found');
});
it('should return the group history to a regular member', async () => {
const res = await request
.get(api('groups.history'))
.set(memberCredentials)
.query({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages').that.is.an('array');
});
it('should close the group for a regular member', async () => {
await request
.post(api('groups.close'))
.set(memberCredentials)
.send({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
it('should report the group as already closed on a second close (subscription open flag is read)', async () => {
await request
.post(api('groups.close'))
.set(memberCredentials)
.send({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', `The private group, ${memberGroupName}, is already closed to the sender`);
});
});
it('should open the group back for a regular member', async () => {
await request
.post(api('groups.open'))
.set(memberCredentials)
.send({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
it('should report the group as already open on a second open (subscription open flag is read)', async () => {
await request
.post(api('groups.open'))
.set(memberCredentials)
.send({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', `The private group, ${memberGroupName}, is already open for the sender`);
});
});
});
describe('/groups.list', () => {
it('should list the groups the caller is part of', async () => {
const res = await request.get(api('groups.list')).set(credentials).expect('Content-Type', 'application/json').expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('count');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('groups').and.to.be.an('array');
});
it('should return a list of zero length if not a member of any group', async () => {
const user = await createUser();
const newCreds = await login(user.username, password);
await request
.get(api('groups.list'))
.set(newCreds)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('count').and.to.equal(0);
expect(res.body).to.have.property('total').and.to.equal(0);
expect(res.body).to.have.property('groups').and.to.be.an('array').and.that.has.lengthOf(0);
});
});
});
describe('/groups.online', () => {
const createUserAndChannel = async (setAsOnline = true) => {
const testUser = await createUser();
const testUserCredentials = await login(testUser.username, password);
if (setAsOnline) {
await request.post(api('users.setStatus')).set(testUserCredentials).send({
message: '',
status: 'online',
});
}
const roomName = `group-test-${Date.now()}`;
const roomResponse = await createRoom({
name: roomName,
type: 'p',
members: [testUser.username],
credentials: testUserCredentials,
});
return {
testUser,
testUserCredentials,
room: roomResponse.body.group,
};
};
it('should return an error if no query', () =>
request
.get(api('groups.online'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Invalid query');
}));
it('should return an error if passing an empty query', () =>
request
.get(api('groups.online'))
.set(credentials)
.query('query={}')
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Invalid query');
}));
it('should return an array with online members', async () => {
const { testUser, testUserCredentials, room } = await createUserAndChannel();
const response = await request.get(api('groups.online')).set(testUserCredentials).query({
_id: room._id,
});
const { body } = response;
const expected = {
_id: testUser._id,
username: testUser.username,
};
expect(body.online).to.deep.include(expected);
});
it('should return an empty array if members are offline', async () => {
const { testUserCredentials, room } = await createUserAndChannel(false);
const response = await request.get(api('groups.online')).set(testUserCredentials).query({
_id: room._id,
});
const { body } = response;
expect(body.online).to.deep.equal([]);
});
it('should return an error if requesting user is not in group', async () => {
const outsider = await createUser();
const outsiderCredentials = await login(outsider.username, password);
const { room } = await createUserAndChannel();
await request
.get(api('groups.online'))
.set(outsiderCredentials)
.query({
_id: room._id,
})
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-not-allowed');
});
});
});
describe('/groups.members', () => {
it('should return group members when searching by roomId', async () => {
const res = await request
.get(api('groups.members'))
.set(credentials)
.query({
roomId: group._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('count');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('members').and.to.be.an('array');
});
it('should return group members when searching by roomId even requested with count and offset params', async () => {
const res = await request
.get(api('groups.members'))
.set(credentials)
.query({
roomId: group._id,
count: 5,
offset: 0,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('count');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('members').and.to.be.an('array');
});
});
describe('/groups.files', async () => {
await testFileUploads('groups.files', 'p');
});
describe('/groups.listAll', () => {
before(async () => {
return updatePermission('view-room-administration', ['admin']);
});
after(async () => {
return updatePermission('view-room-administration', ['admin']);
});
it('should succeed if user has view-room-administration permission', async () => {
await request
.get(api('groups.listAll'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('groups').and.to.be.an('array');
});
});
it('should fail if the user doesnt have view-room-administration permission', async () => {
await updatePermission('view-room-administration', []);
await request
.get(api('groups.listAll'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(403)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'User does not have the permissions required for this action [error-unauthorized]');
});
});
});
describe('/groups.counters', () => {
let room: IRoom;
let user1: IUser;
let user2: IUser;
let user1Creds: { 'X-Auth-Token': string; 'X-User-Id': string };
before(async () => {
// Create two users
user1 = await createUser();
user2 = await createUser();
user1Creds = await login(user1.username, password);
// Create a new public channel with both users as members
room = (
await createRoom({
type: 'p',
name: `counters-test-${Date.now()}`,
members: [user1.username as string, user2.username as string],
})
).body.group;
});
after(async () => {
// Delete room first
await deleteRoom({ type: 'p', roomId: room._id });
// Then delete users
await Promise.all([deleteUser(user1), deleteUser(user2)]);
});
it('should require auth', async () => {
await request
.get(api('groups.counters'))
.expect('Content-Type', 'application/json')
.expect(401)
.expect((res) => {
expect(res.body).to.have.property('status', 'error');
});
});
it('should require a roomId', async () => {
await request
.get(api('groups.counters'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
});
});
it('should return counters for a channel with correct fields', async () => {
await request
.get(api('groups.counters'))
.set(user1Creds)
.query({ roomId: room._id })
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('members').that.is.a('number').and.equals(3);
expect(res.body).to.have.property('unreads').that.is.a('number');
expect(res.body).to.have.property('unreadsFrom');
expect(res.body).to.have.property('msgs').that.is.a('number');
expect(res.body).to.have.property('latest');
expect(res.body).to.have.property('joined', true);
});
});
it('should not include deactivated users in members count', async () => {
// Deactivate the second user
await request.post(api('users.setActiveStatus')).set(credentials).send({ userId: user2._id, activeStatus: false });
const res = await request.get(api('groups.counters')).set(user1Creds).query({ roomId: room._id });
expect(res.status).to.equal(200);
expect(res.body.success).to.be.true;
// Only user1 and admin remain active
expect(res.body.members).to.equal(2);
});
});
describe('/groups.rename', async () => {
let roomInfo: { group: IRoom };
before(async () => {
roomInfo = await getRoomInfo(group._id);
});
it('should return the group rename with an additional message', async () => {
await request
.post(api('groups.rename'))
.set(credentials)
.send({
roomId: group._id,
name: `EDITED${apiPrivateChannelName}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group._id');
expect(res.body).to.have.nested.property('group.name', `EDITED${apiPrivateChannelName}`);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.msgs', roomInfo.group.msgs + 1);
});
});
});
describe('/groups.getIntegrations', () => {
let integrationCreatedByAnUser: IIntegration;
let createdGroup: IRoom;
before(async () => {
const resRoom = await createRoom({ name: `test-integration-group-${Date.now()}`, type: 'p' });
createdGroup = resRoom.body.group;
const user = await createUser();
const userCredentials = await login(user.username, password);
await Promise.all([
updatePermission('manage-incoming-integrations', ['user']),
updatePermission('manage-own-incoming-integrations', ['user']),
]);
integrationCreatedByAnUser = await createIntegration(
{
type: 'webhook-incoming',
name: 'Incoming test',
enabled: true,
alias: 'test',
username: 'rocket.cat',
scriptEnabled: false,
overrideDestinationChannelEnabled: true,
channel: `#${createdGroup.name}`,
},
userCredentials,
);
});
after(async () => {
await removeIntegration(integrationCreatedByAnUser._id, 'incoming');
await Promise.all([
updatePermission('manage-incoming-integrations', ['admin']),
updatePermission('manage-outgoing-integrations', ['admin']),
updatePermission('manage-own-incoming-integrations', ['admin']),
updatePermission('manage-own-outgoing-integrations', ['admin']),
]);
});
it('should return the list of integrations of create group and it should contain the integration created by user when the admin DOES have the permission', async () => {
await updatePermission('manage-incoming-integrations', ['admin']);
await request
.get(api('groups.getIntegrations'))
.set(credentials)
.query({
roomId: createdGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
const integrationCreated = (res.body.integrations as IIntegration[]).find(
(createdIntegration) => createdIntegration._id === integrationCreatedByAnUser._id,
);
assert.isDefined(integrationCreated);
expect(integrationCreated).to.be.an('object');
expect(integrationCreated._id).to.be.equal(integrationCreatedByAnUser._id);
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('total');
});
});
it('should return the list of integrations created by the user only', async () => {
await Promise.all([
updatePermission('manage-own-incoming-integrations', ['admin']),
updatePermission('manage-incoming-integrations', []),
]);
await request
.get(api('groups.getIntegrations'))
.set(credentials)
.query({
roomId: createdGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
const integrationCreated = (res.body.integrations as IIntegration[]).find(
(createdIntegration) => createdIntegration._id === integrationCreatedByAnUser._id,
);
expect(integrationCreated).to.be.equal(undefined);
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('total');
});
});
it('should return unauthorized error when the user does not have any integrations permissions', async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', []),
updatePermission('manage-outgoing-integrations', []),
updatePermission('manage-own-incoming-integrations', []),
updatePermission('manage-own-outgoing-integrations', []),
]);
await request
.get(api('groups.getIntegrations'))
.set(credentials)
.query({
roomId: createdGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(403)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'User does not have the permissions required for this action [error-unauthorized]');
});
});
});
describe('/groups.setReadOnly', () => {
it('should set the group as read only', async () => {
const res = await request
.post(api('groups.setReadOnly'))
.set(credentials)
.send({
roomId: group._id,
readOnly: true,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe.skip('/groups.leave', () => {
it('should allow the user to leave the group', async () => {
const res = await request
.post(api('groups.leave'))
.set(credentials)
.send({
roomId: group._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe('/groups.setAnnouncement', () => {
it('should set the announcement of the group with a string', async () => {
const res = await request
.post(api('groups.setAnnouncement'))
.set(credentials)
.send({
roomId: group._id,
announcement: 'this is an announcement of a group for api tests',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('announcement', 'this is an announcement of a group for api tests');
});
it('should set the announcement of the group with an empty string(remove the announcement)', async () => {
const res = await request
.post(api('groups.setAnnouncement'))
.set(credentials)
.send({
roomId: group._id,
announcement: '',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('announcement', '');
});
});
describe('/groups.setType', () => {
let roomTypeId: IRoom['_id'];
let testRegularGroup: IRoom;
let testTeamGroupForFailure: IRoom;
let testTeamGroupForSuccess: IRoom;
let team: ITeam;
let testUser: TestUser<IUser>;
let testUserCredentials: Credentials;
beforeEach(async () => {
await updatePermission('create-c', ['admin', 'user']);
await updatePermission('create-team-channel', ['admin', 'owner', 'moderator']);
});
before(async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `channel.type.${Date.now()}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
roomTypeId = res.body.group._id;
});
testUser = await createUser();
testUserCredentials = await login(testUser.username, password);
await updatePermission('create-team', ['admin', 'user']);
team = await createTeam(credentials, `team-setType-${Date.now()}`, TeamType.PRIVATE, [testUser.username]);
await request.post(api('groups.addOwner')).set(credentials).send({ roomId: team.roomId, userId: testUser._id });
testRegularGroup = (await createRoom({ type: 'p', name: `regular-setType-${Date.now()}`, credentials: testUserCredentials })).body
.group;
testTeamGroupForFailure = (await createRoom({ type: 'p', name: `teamGrp-fail-${Date.now()}`, extraData: { teamId: team._id } })).body
.group;
testTeamGroupForSuccess = (await createRoom({ type: 'p', name: `teamGrp-success-${Date.now()}`, extraData: { teamId: team._id } }))
.body.group;
await request.post(api('groups.invite')).set(credentials).send({ roomId: testTeamGroupForFailure._id, userId: testUser._id });
await request.post(api('groups.addOwner')).set(credentials).send({ roomId: testTeamGroupForFailure._id, userId: testUser._id });
await request.post(api('groups.invite')).set(credentials).send({ roomId: testTeamGroupForSuccess._id, userId: testUser._id });
await request.post(api('groups.addOwner')).set(credentials).send({ roomId: testTeamGroupForSuccess._id, userId: testUser._id });
});
after(async () => {
await request
.post(api('channels.delete'))
.set(credentials)
.send({ roomId: roomTypeId })
.expect('Content-Type', 'application/json')
.expect(200);
await Promise.all(
['p', 'c'].map(async (type: any) => {
return Promise.all([
deleteRoom({ type, roomId: testRegularGroup._id }),
deleteRoom({ type, roomId: testTeamGroupForFailure._id }),
deleteRoom({ type, roomId: testTeamGroupForSuccess._id }),
]);
}),
);
await deleteTeam(credentials, team.name);
await deleteUser(testUser);
await updatePermission('create-c', ['admin', 'user']);
await updatePermission('create-team-channel', ['admin', 'owner', 'moderator']);
});
it('should change the type of the group to a channel', async () => {
await request
.post(api('groups.setType'))
.set(credentials)
.send({
roomId: roomTypeId,
type: 'c',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group.t', 'c');
});
});
it('should fail to change a regular group to channel when user lacks create-c permission', async () => {
await updatePermission('create-c', ['admin']);
await request
.post(api('groups.setType'))
.set(testUserCredentials)
.send({ roomId: testRegularGroup._id, type: 'c' })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-action-not-allowed');
});
});
it('should fail to change a team main room to channel when user lacks create-c permission', async () => {
await updatePermission('create-c', ['admin']);
await request
.post(api('groups.setType'))
.set(testUserCredentials)
.send({ roomId: team.roomId, type: 'c' })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-action-not-allowed');
});
});
it('should fail to change a team main room to channel when user has create-team-channel but lacks create-c', async () => {
await updatePermission('create-c', ['admin']);
await updatePermission('create-team-channel', ['owner']);
await request
.post(api('groups.setType'))
.set(testUserCredentials)
.send({ roomId: team.roomId, type: 'c' })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-action-not-allowed');
});
});
it('should fail to change a team group to channel when user lacks create-team-channel permission', async () => {
await updatePermission('create-team-channel', []);
await request
.post(api('groups.setType'))
.set(testUserCredentials)
.send({ roomId: testTeamGroupForFailure._id, type: 'c' })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-action-not-allowed');
});
});
it('should succeed changing a team group to channel when user has create-team-channel but not create-c', async () => {
await updatePermission('create-c', ['admin']);
await updatePermission('create-team-channel', ['owner']);
await request
.post(api('groups.setType'))
.set(testUserCredentials)
.send({ roomId: testTeamGroupForSuccess._id, type: 'c' })
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group.t', 'c');
expect(res.body).to.have.nested.property('group.teamId', team._id);
});
});
});
describe('/groups.setCustomFields', () => {
let cfchannel: IRoom;
let groupWithoutCustomFields: IRoom;
before('create group with customFields', async () => {
const customFields = { field0: 'value0' };
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `channel.cf.${Date.now()}`,
customFields,
})
.expect((res) => {
cfchannel = res.body.group;
});
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `channel.cf.${Date.now()}`,
})
.expect((res) => {
groupWithoutCustomFields = res.body.group;
});
});
after('delete group with customFields', async () => {
await request
.post(api('groups.delete'))
.set(credentials)
.send({
roomName: cfchannel.name,
})
.expect('Content-Type', 'application/json')
.expect(200);
await request
.post(api('groups.delete'))
.set(credentials)
.send({
roomName: groupWithoutCustomFields.name,
})
.expect('Content-Type', 'application/json')
.expect(200);
});
it('get customFields using groups.info', async () => {
const res = await request
.get(api('groups.info'))
.set(credentials)
.query({
roomId: cfchannel._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group.customFields.field0', 'value0');
});
it('change customFields', async () => {
const customFields = { field9: 'value9' };
await request
.post(api('groups.setCustomFields'))
.set(credentials)
.send({
roomId: cfchannel._id,
customFields,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group._id');
expect(res.body).to.have.nested.property('group.name', cfchannel.name);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.customFields.field9', 'value9');
expect(res.body).to.have.not.nested.property('group.customFields.field0', 'value0');
});
});
it('get customFields using groups.info', async () => {
const res = await request
.get(api('groups.info'))
.set(credentials)
.query({
roomId: cfchannel._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group.customFields.field9', 'value9');
});
it('set customFields with one nested field', async () => {
const customFields = { field1: 'value1' };
await request
.post(api('groups.setCustomFields'))
.set(credentials)
.send({
roomId: groupWithoutCustomFields._id,
customFields,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group._id');
expect(res.body).to.have.nested.property('group.name', groupWithoutCustomFields.name);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.customFields.field1', 'value1');
});
});
it('set customFields with multiple nested fields', async () => {
const customFields = { field2: 'value2', field3: 'value3', field4: 'value4' };
await request
.post(api('groups.setCustomFields'))
.set(credentials)
.send({
roomName: groupWithoutCustomFields.name,
customFields,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group._id');
expect(res.body).to.have.nested.property('group.name', groupWithoutCustomFields.name);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.nested.property('group.customFields.field2', 'value2');
expect(res.body).to.have.nested.property('group.customFields.field3', 'value3');
expect(res.body).to.have.nested.property('group.customFields.field4', 'value4');
expect(res.body).to.have.not.nested.property('group.customFields.field1', 'value1');
});
});
it('set customFields to empty object', async () => {
const customFields = {};
await request
.post(api('groups.setCustomFields'))
.set(credentials)
.send({
roomName: groupWithoutCustomFields.name,
customFields,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group._id');
expect(res.body).to.have.nested.property('group.name', groupWithoutCustomFields.name);
expect(res.body).to.have.nested.property('group.t', 'p');
expect(res.body).to.have.not.nested.property('group.customFields.field2', 'value2');
expect(res.body).to.have.not.nested.property('group.customFields.field3', 'value3');
expect(res.body).to.have.not.nested.property('group.customFields.field4', 'value4');
});
});
it('set customFields as a string -> should return 400', (done) => {
const customFields = '';
void request
.post(api('groups.setCustomFields'))
.set(credentials)
.send({
roomName: groupWithoutCustomFields.name,
customFields,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
})
.end(done);
});
});
describe('/groups.delete', () => {
let testGroup: IRoom;
let testTeamGroup: IRoom;
let testModeratorTeamGroup: IRoom;
let invitedUser: TestUser<IUser>;
let moderatorUser: TestUser<IUser>;
let invitedUserCredentials: Credentials;
let moderatorUserCredentials: Credentials;
let teamId: ITeam['_id'];
let teamMainRoomId: IRoom['_id'];
before(async () => {
testGroup = (await createRoom({ name: `group.test.${Date.now()}`, type: 'p' })).body.group;
invitedUser = await createUser();
moderatorUser = await createUser();
invitedUserCredentials = await login(invitedUser.username, password);
moderatorUserCredentials = await login(moderatorUser.username, password);
const teamCreateRes = await request
.post(api('teams.create'))
.set(credentials)
.send({
name: `team-${Date.now()}`,
type: 1,
members: [invitedUser.username, moderatorUser.username],
});
teamId = teamCreateRes.body.team._id;
teamMainRoomId = teamCreateRes.body.team.roomId;
await updatePermission('delete-team-group', ['owner', 'moderator']);
await updatePermission('create-team-group', ['admin', 'owner', 'moderator', 'user']);
const teamGroupResponse = await createRoom({
name: `group.test.${Date.now()}`,
type: 'p',
extraData: { teamId },
credentials: invitedUserCredentials,
});
testTeamGroup = teamGroupResponse.body.group;
await request
.post(api('groups.addModerator'))
.set(credentials)
.send({
userId: moderatorUser._id,
roomId: teamMainRoomId,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
const teamModeratorGroupResponse = await createRoom({
name: `group.test.moderator.${Date.now()}`,
type: 'p',
extraData: { teamId },
credentials: moderatorUserCredentials,
});
testModeratorTeamGroup = teamModeratorGroupResponse.body.group;
});
after(async () => {
await deleteUser(invitedUser);
await deleteUser(moderatorUser);
await updatePermission('create-team-group', ['admin', 'owner', 'moderator']);
await updatePermission('delete-team-group', ['admin', 'owner', 'moderator']);
});
it('should succesfully delete a group', async () => {
await request
.post(api('groups.delete'))
.set(credentials)
.send({
roomName: testGroup.name,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
it(`should fail retrieving a group's info after it's been deleted`, async () => {
await request
.get(api('groups.info'))
.set(credentials)
.query({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-room-not-found');
});
});
it(`should fail deleting a team's group when member does not have the necessary permission in the team`, async () => {
await request
.post(api('groups.delete'))
.set(invitedUserCredentials)
.send({
roomName: testTeamGroup.name,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.a.property('error');
expect(res.body).to.have.a.property('errorType', 'error-not-allowed');
});
});
it(`should fail deleting a team's group when member has the necessary permission in the team, but not in the deleted room`, async () => {
await request
.post(api('groups.delete'))
.set(moderatorUserCredentials)
.send({
roomName: testTeamGroup.name,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.a.property('error');
expect(res.body).to.have.a.property('errorType', 'error-room-not-found');
});
});
it(`should successfully delete a team's group when member has both team and group permissions`, async () => {
await request
.post(api('groups.delete'))
.set(moderatorUserCredentials)
.send({
roomId: testModeratorTeamGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
});
describe('/groups.roles', () => {
let testGroup: IRoom;
before(async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `group.roles.test.${Date.now()}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
testGroup = res.body.group;
});
});
after(async () => {
await request
.post(api('groups.delete'))
.set(credentials)
.send({
roomName: testGroup.name,
})
.expect('Content-Type', 'application/json')
.expect(200);
});
it('/groups.invite', async () => {
const res = await request
.post(api('groups.invite'))
.set(credentials)
.send({
roomId: testGroup._id,
userId: 'rocket.cat',
})
.expect(200);
expect(res.body).to.have.property('success', true);
});
it('/groups.addModerator', async () => {
const res = await request
.post(api('groups.addModerator'))
.set(credentials)
.send({
roomId: testGroup._id,
userId: 'rocket.cat',
})
.expect(200);
expect(res.body).to.have.property('success', true);
});
it('/groups.addLeader', async () => {
const res = await request
.post(api('groups.addLeader'))
.set(credentials)
.send({
roomId: testGroup._id,
userId: 'rocket.cat',
})
.expect(200);
expect(res.body).to.have.property('success', true);
});
it('should return an array of roles <-> user relationships in a private group', async () => {
const res = await request
.get(api('groups.roles'))
.set(credentials)
.query({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.a.property('success', true);
expect(res.body).to.have.a.property('roles').that.is.an('array').that.has.lengthOf(2);
expect(res.body.roles[0]).to.have.a.property('_id').that.is.a('string');
expect(res.body.roles[0]).to.have.a.property('rid').that.is.equal(testGroup._id);
expect(res.body.roles[0]).to.have.a.property('roles').that.is.an('array').that.includes('moderator', 'leader');
expect(res.body.roles[0]).to.have.a.property('u').that.is.an('object');
expect(res.body.roles[0].u).to.have.a.property('_id').that.is.a('string');
expect(res.body.roles[0].u).to.have.a.property('username').that.is.a('string');
expect(res.body.roles[1]).to.have.a.property('_id').that.is.a('string');
expect(res.body.roles[1]).to.have.a.property('rid').that.is.equal(testGroup._id);
expect(res.body.roles[1]).to.have.a.property('roles').that.is.an('array').that.includes('owner');
expect(res.body.roles[1]).to.have.a.property('u').that.is.an('object');
expect(res.body.roles[1].u).to.have.a.property('_id').that.is.a('string');
expect(res.body.roles[1].u).to.have.a.property('username').that.is.a('string');
});
});
describe('/groups.moderators', () => {
let testGroup: IRoom;
before(async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `group.roles.test.${Date.now()}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
testGroup = res.body.group;
});
});
after(async () => {
await request
.post(api('groups.delete'))
.set(credentials)
.send({
roomName: testGroup.name,
})
.expect('Content-Type', 'application/json')
.expect(200);
});
it('/groups.invite', async () => {
const res = await request
.post(api('groups.invite'))
.set(credentials)
.send({
roomId: testGroup._id,
userId: 'rocket.cat',
})
.expect(200);
expect(res.body).to.have.property('success', true);
});
it('/groups.addModerator', async () => {
const res = await request
.post(api('groups.addModerator'))
.set(credentials)
.send({
roomId: testGroup._id,
userId: 'rocket.cat',
})
.expect(200);
expect(res.body).to.have.property('success', true);
});
it('should return an array of moderators with rocket.cat as a moderator', async () => {
const res = await request
.get(api('groups.moderators'))
.set(credentials)
.query({
roomId: testGroup._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.a.property('success', true);
expect(res.body).to.have.a.property('moderators').that.is.an('array').that.has.lengthOf(1);
expect(res.body.moderators[0].username).to.be.equal('rocket.cat');
});
});
describe('/groups.setEncrypted', () => {
let testGroup: IRoom;
before(async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({
name: `group.encrypted.test.${Date.now()}`,
})
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('group._id');
testGroup = res.body.group;
});
await updateSetting('E2E_Enable', true);
});
after(async () => {
await updateSetting('E2E_Enable', false);
await request
.post(api('groups.delete'))
.set(credentials)
.send({
roomName: testGroup.name,
})
.expect('Content-Type', 'application/json')
.expect(200);
});
it('should return an error when passing no boolean param', async () => {
const res = await request
.post(api('groups.setEncrypted'))
.set(credentials)
.send({
roomId: testGroup._id,
encrypted: 'no-boolean',
})
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'must be boolean');
});
it('should set group as encrypted correctly and return the new data', async () => {
const res = await request
.post(api('groups.setEncrypted'))
.set(credentials)
.send({
roomId: testGroup._id,
encrypted: true,
})
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('group');
expect(res.body.group).to.have.property('_id', testGroup._id);
expect(res.body.group).to.have.property('encrypted', true);
});
it('should return the updated room encrypted', async () => {
const roomInfo = await getRoomInfo(testGroup._id);
expect(roomInfo).to.have.a.property('success', true);
expect(roomInfo.group).to.have.a.property('_id', testGroup._id);
expect(roomInfo.group).to.have.a.property('encrypted', true);
});
it('should set group as unencrypted correctly and return the new data', async () => {
const res = await request
.post(api('groups.setEncrypted'))
.set(credentials)
.send({
roomId: testGroup._id,
encrypted: false,
})
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('group');
expect(res.body.group).to.have.property('_id', testGroup._id);
expect(res.body.group).to.have.property('encrypted', false);
});
it('should return the updated room unencrypted', async () => {
const roomInfo = await getRoomInfo(testGroup._id);
expect(roomInfo).to.have.a.property('success', true);
expect(roomInfo.group).to.have.a.property('_id', testGroup._id);
expect(roomInfo.group).to.have.a.property('encrypted', false);
});
});
describe('/groups.convertToTeam', () => {
let newGroup: IRoom;
before(async () => {
await request
.post(api('groups.create'))
.set(credentials)
.send({ name: `group-${Date.now()}` })
.expect(200)
.expect((response) => {
newGroup = response.body.group;
});
});
after(() => {
assert.isDefined(newGroup.name);
return Promise.all([deleteTeam(credentials, newGroup.name), updatePermission('create-team', ['admin', 'user'])]);
});
it('should fail to convert group if lacking edit-room permission', async () => {
await updatePermission('create-team', []);
await updatePermission('edit-room', ['admin']);
const res = await request.post(api('groups.convertToTeam')).set(credentials).send({ roomId: newGroup._id }).expect(403);
expect(res.body).to.have.a.property('success', false);
});
it('should fail to convert group if lacking create-team permission', async () => {
await updatePermission('create-team', ['admin']);
await updatePermission('edit-room', []);
const res = await request.post(api('groups.convertToTeam')).set(credentials).send({ roomId: newGroup._id }).expect(403);
expect(res.body).to.have.a.property('success', false);
});
it('should successfully convert a group to a team', async () => {
await updatePermission('create-team', ['admin']);
await updatePermission('edit-room', ['admin']);
const res = await request.post(api('groups.convertToTeam')).set(credentials).send({ roomId: newGroup._id }).expect(200);
expect(res.body).to.have.a.property('success', true);
});
it('should fail to convert group without the required parameters', (done) => {
void request.post(api('groups.convertToTeam')).set(credentials).send({}).expect(400).end(done);
});
it("should fail to convert group if it's already taken", async () => {
const res = await request.post(api('groups.convertToTeam')).set(credentials).send({ roomId: newGroup._id }).expect(400);
expect(res.body).to.have.a.property('success', false);
});
});
describe("Setting: 'Use Real Name': true", () => {
let realNameGroup: IRoom;
before(async () => {
await updateSetting('UI_Use_Real_Name', true);
await request
.post(api('groups.create'))
.set(credentials)
.send({ name: `group-${Date.now()}` })
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
realNameGroup = res.body.group;
});
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
text: 'Sample message',
rid: realNameGroup._id,
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
after(async () => {
await updateSetting('UI_Use_Real_Name', false);
await request
.post(api('groups.delete'))
.set(credentials)
.send({ roomId: realNameGroup._id })
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
it('should return the last message user real name', async () => {
const res = await request
.get(api('groups.info'))
.query({
roomId: realNameGroup._id,
})
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
const { group } = res.body;
expect(group._id).to.be.equal(realNameGroup._id);
expect(group).to.have.nested.property('lastMessage.u.name', 'RocketChat Internal Admin Test');
});
});
});