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 1s
snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
2026-09-16 01:21:02 +05:00

582 lines
17 KiB
TypeScript

import type { Credentials } from '@rocket.chat/api-client';
import type { IRoom, IThreadMessage, IUser } from '@rocket.chat/core-typings';
import { Random } from '@rocket.chat/random';
import { expect } from 'chai';
import { before, describe, it, after } from 'mocha';
import { getCredentials, api, request, credentials } from '../../data/api-data';
import { sendSimpleMessage } from '../../data/chat.helper';
import { createRoom, deleteRoom } from '../../data/rooms.helper';
import { password } from '../../data/user';
import type { TestUser } from '../../data/users.helper';
import { createUser, deleteUser, login } from '../../data/users.helper';
describe('[Commands]', () => {
before((done) => getCredentials(done));
describe('[/commands.get]', () => {
it('should return an error when call the endpoint without "command" required parameter', async () => {
const res = await request.get(api('commands.get')).set(credentials).expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal(`must have required property 'command'`);
});
it('should return an error when call the endpoint with an invalid command', async () => {
const res = await request
.get(api('commands.get'))
.set(credentials)
.query({
command: 'invalid-command',
})
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('There is no command in the system by the name of: invalid-command');
});
it('should return success when parameters are correct', async () => {
const res = await request
.get(api('commands.get'))
.set(credentials)
.query({
command: 'help',
})
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('command');
expect(res.body.command).to.have.property('command');
expect(res.body.command).to.have.property('description');
expect(res.body.command).to.have.property('clientOnly');
expect(res.body.command).to.have.property('providesPreview');
});
});
describe('[/commands.list]', () => {
it('should return a list of commands', async () => {
const res = await request.get(api('commands.list')).set(credentials).expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('count');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('commands').and.to.be.an('array');
});
it('should return a list of commands even requested with count and offset params', async () => {
const res = await request
.get(api('commands.list'))
.set(credentials)
.query({
count: 5,
offset: 0,
})
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('count');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('commands').and.to.be.an('array');
});
});
describe('[/commands.run]', () => {
let testChannel: IRoom;
let threadMessage: IThreadMessage;
before(async () => {
testChannel = (await createRoom({ type: 'c', name: `channel.test.commands.${Date.now()}` })).body.channel;
const { body: { message } = {} } = await sendSimpleMessage({
roomId: testChannel._id,
text: 'Message to create thread',
});
threadMessage = (
await sendSimpleMessage({
roomId: testChannel._id,
text: 'Thread Message',
tmid: message._id,
})
).body.message;
});
after(() => deleteRoom({ type: 'c', roomId: testChannel._id }));
it('should return an error when call the endpoint without "command" required parameter', async () => {
const res = await request.post(api('commands.run')).set(credentials).expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal("must have required property 'command'");
});
it('should coerce non-string "params" to string via ajv coercion', async () => {
const res = await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'help',
roomId: 'GENERAL',
params: true,
})
.expect(200);
expect(res.body).to.have.property('success', true);
});
it('should return an error when call the endpoint without "roomId" required parameter', async () => {
const res = await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'help',
params: 'params',
})
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal("must have required property 'roomId'");
});
it('should coerce non-string "tmid" to string via ajv coercion and fail with invalid thread', async () => {
const res = await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'help',
params: 'params',
roomId: 'GENERAL',
tmid: true,
})
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('Invalid thread.');
});
it('should return an error when call the endpoint with the invalid "command" param', async () => {
const res = await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'invalid-command',
params: 'params',
roomId: 'GENERAL',
})
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('The command provided does not exist (or is disabled).');
});
it('should return an error when call the endpoint with an invalid thread id', async () => {
const res = await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'tableflip',
params: 'params',
roomId: 'GENERAL',
tmid: 'invalid-thread',
})
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('Invalid thread.');
});
it('should return an error when call the endpoint with a valid thread id of wrong channel', async () => {
const res = await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'tableflip',
params: 'params',
roomId: 'GENERAL',
tmid: threadMessage.tmid,
})
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('Invalid thread.');
});
it('should return success when parameters are correct', async () => {
const res = await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'tableflip',
params: 'params',
roomId: threadMessage.rid,
tmid: threadMessage.tmid,
})
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe('Command archive', function () {
describe('unauthorized cases', () => {
let user: TestUser<IUser>;
let credentials: Credentials;
this.beforeAll(async () => {
user = await createUser({
joinDefaultChannels: true,
});
credentials = await login(user.username, password);
});
this.afterAll(async () => {
await deleteUser(user);
});
it('should return an error when the user is not logged in', async () => {
await request
.post(api('commands.run'))
.send({ command: 'archive', roomId: 'GENERAL' })
.expect(401)
.expect((res) => {
expect(res.body).to.have.property('status', 'error');
});
});
it('should return an error when the user has not enough permissions', async () => {
await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'archive',
roomId: 'GENERAL',
})
.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-authorized');
});
});
});
describe('authorized cases', function () {
this.afterAll(async () => {
await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'unarchive',
roomId: 'GENERAL',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
it('should return a success when the user has enough permissions', async () => {
await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'archive',
roomId: 'GENERAL',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
});
});
describe('Command unarchive', function () {
describe('unauthorized cases', () => {
let user: TestUser<IUser>;
let credentials: Credentials;
this.beforeAll(async () => {
user = await createUser({
joinDefaultChannels: true,
});
credentials = await login(user.username, password);
});
this.afterAll(async () => {
await deleteUser(user);
});
it('should return an error when the user is not logged in', async () => {
await request
.post(api('commands.run'))
.send({ command: 'unarchive', roomId: 'GENERAL' })
.expect(401)
.expect((res) => {
expect(res.body).to.have.property('status', 'error');
});
});
it('should return an error when the user has not enough permissions', async () => {
await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'unarchive',
roomId: 'GENERAL',
})
.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-authorized');
});
});
});
describe('authorized cases', () => {
before(async () => {
await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'archive',
roomId: 'GENERAL',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
it('should return a success when the user has enough permissions', async () => {
await request
.post(api('commands.run'))
.set(credentials)
.send({
command: 'unarchive',
roomId: 'GENERAL',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
});
});
describe('Command "invite-all-from"', function () {
let group: IRoom;
let group1: IRoom;
let channel: IRoom;
let user1: TestUser<IUser>;
let user2: TestUser<IUser>;
let user1Credentials: Credentials;
let user2Credentials: Credentials;
this.beforeAll(async () => {
user1 = await createUser();
user2 = await createUser();
[user1Credentials, user2Credentials] = await Promise.all([login(user1.username, password), login(user2.username, password)]);
});
this.beforeAll(async () => {
const [response1, response2, response3] = await Promise.all([
createRoom({ type: 'p', name: `room1-${Date.now()}.${Random.id()}`, credentials: user1Credentials }),
createRoom({ type: 'c', name: `room2-${Date.now()}.${Random.id()}`, credentials: user2Credentials }),
createRoom({ type: 'p', name: `room3-${Date.now()}.${Random.id()}` }),
]);
group = response1.body.group;
channel = response2.body.channel;
group1 = response3.body.group;
});
this.afterAll(async () => {
await Promise.all([
deleteRoom({ type: 'p', roomId: group._id }),
deleteRoom({ type: 'c', roomId: channel._id }),
deleteRoom({ type: 'p', roomId: group1._id }),
]);
await Promise.all([deleteUser(user1), deleteUser(user2)]);
});
it('should not add users from group which is not accessible by current user', async () => {
await request
.post(api('commands.run'))
.set(user2Credentials)
.send({
roomId: channel._id,
command: 'invite-all-from',
params: `#${group.name}`,
triggerId: Random.id(),
})
.expect(200)
.expect(async (res) => {
expect(res.body).to.have.a.property('success', true);
});
await request
.get(api('channels.members'))
.query({ roomId: channel._id })
.set(user2Credentials)
.expect(200)
.expect((res) => {
const isUser1Added = res.body.members.some((member: IUser) => member.username === user1.username);
expect(isUser1Added).to.be.false;
});
});
it('should not add users to a room that is not accessible by the current user', async () => {
await request
.post(api('commands.run'))
.set(user1Credentials)
.send({
roomId: group1._id,
command: 'invite-all-from',
params: `#${group.name}`,
triggerId: Random.id(),
})
.expect(403)
.expect((res) => {
expect(res.body).to.have.a.property('error', 'unauthorized');
});
});
it('should add users from group which is accessible by current user', async () => {
await request
.post(api('groups.invite'))
.set(user1Credentials)
.send({
roomId: group._id,
userId: user2._id,
})
.expect(200);
await request
.post(api('commands.run'))
.set(user2Credentials)
.send({
roomId: channel._id,
command: 'invite-all-from',
params: `#${group.name}`,
triggerId: Random.id(),
})
.expect(200)
.expect(async (res) => {
expect(res.body).to.have.a.property('success', true);
});
await request
.get(api('channels.members'))
.set(user2Credentials)
.query({ roomId: channel._id })
.expect(200)
.expect((res) => {
const isUser1Added = res.body.members.some((member: IUser) => member.username === user1.username);
expect(isUser1Added).to.be.true;
});
});
});
describe('Command "kick"', function () {
let directMessageRoom: IRoom;
let user1: TestUser<IUser>;
let user1Credentials: Credentials;
this.beforeAll(async () => {
user1 = await createUser();
[user1Credentials] = await Promise.all([login(user1.username, password)]);
});
this.beforeAll(async () => {
const [response1] = await Promise.all([
createRoom({ type: 'd', name: `room1-${Date.now()}.${Random.id()}`, username: user1.username }),
]);
directMessageRoom = response1.body.room;
});
this.afterAll(async () => {
await Promise.all([deleteRoom({ type: 'd', roomId: directMessageRoom._id })]);
await Promise.all([deleteUser(user1)]);
});
it('should fail when trying to kick a user from a direct message room', async () => {
const res = await request
.post(api('commands.run'))
.set(user1Credentials)
.send({ command: 'kick', roomId: directMessageRoom._id, params: user1.username })
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error').that.is.a('string');
});
});
describe('Command "leave"', function () {
let directMessageRoom: IRoom;
let user1: TestUser<IUser>;
let user2: TestUser<IUser>;
let user1Credentials: Credentials;
this.beforeAll(async () => {
user1 = await createUser();
user2 = await createUser();
[user1Credentials] = await Promise.all([login(user1.username, password)]);
});
this.beforeAll(async () => {
const [response1] = await Promise.all([
createRoom({ type: 'd', name: `room1-${Date.now()}.${Random.id()}`, username: user1.username }),
]);
directMessageRoom = response1.body.room;
});
this.afterAll(async () => {
await Promise.all([deleteRoom({ type: 'd', roomId: directMessageRoom._id })]);
await Promise.all([deleteUser(user1), deleteUser(user2)]);
});
it('should fail when trying to leave a direct message room', async () => {
const res = await request
.post(api('commands.run'))
.set(user1Credentials)
.send({ command: 'leave', roomId: directMessageRoom._id })
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error').that.is.a('string');
});
});
// TODO: invite never fails
describe.skip('Command "invite"', function () {
let directMessageRoom: IRoom;
let user1: TestUser<IUser>;
let user2: TestUser<IUser>;
let user1Credentials: Credentials;
this.beforeAll(async () => {
user1 = await createUser();
user2 = await createUser();
[user1Credentials] = await Promise.all([login(user1.username, password)]);
});
this.beforeAll(async () => {
const [response1] = await Promise.all([
createRoom({ type: 'd', name: `room1-${Date.now()}.${Random.id()}`, username: user1.username }),
]);
directMessageRoom = response1.body.room;
});
this.afterAll(async () => {
await Promise.all([deleteRoom({ type: 'd', roomId: directMessageRoom._id })]);
await Promise.all([deleteUser(user1), deleteUser(user2)]);
});
it('should fail when trying to invite a user to a direct message room', async () => {
const res = await request
.post(api('commands.run'))
.set(user1Credentials)
.send({ command: 'invite', roomId: directMessageRoom._id, params: 'g1' })
.expect(403);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error').that.is.a('string');
expect(res.body.error).to.equal('unauthorized');
});
});
});