Files
upstream-rocketchat/apps/meteor/tests/end-to-end/api/incoming-integrations.ts
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

1732 lines
57 KiB
TypeScript

import type { Credentials } from '@rocket.chat/api-client';
import { TeamType } from '@rocket.chat/core-typings';
import type { AtLeast, IIntegration, IMessage, IRoom, ITeam, IUser } from '@rocket.chat/core-typings';
import { Random } from '@rocket.chat/random';
import { assert, expect } from 'chai';
import { after, before, describe, it } from 'mocha';
import { getCredentials, api, request, credentials } from '../../data/api-data';
import { createIntegration, removeIntegration } from '../../data/integration.helper';
import { addPermissions, updatePermission, updateSetting } from '../../data/permissions.helper';
import { assignRoleToUser } from '../../data/roles.helper';
import { createRoom, deleteRoom } from '../../data/rooms.helper';
import { createTeam, deleteTeam } from '../../data/teams.helper';
import { adminUsername, password } from '../../data/user';
import type { TestUser } from '../../data/users.helper';
import { createUser, deleteUser, login, removeRoleFromUser } from '../../data/users.helper';
describe('[Incoming Integrations]', () => {
let integration: IIntegration;
let integrationCreatedByAnUser: IIntegration;
let user: TestUser<IUser>;
let userCredentials: Credentials;
let channel: IRoom;
let testChannelName: string;
before((done) => getCredentials(done));
before(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', []),
updatePermission('manage-own-incoming-integrations', []),
updatePermission('manage-own-outgoing-integrations', []),
updatePermission('manage-outgoing-integrations', []),
]);
testChannelName = `channel.test.${Date.now()}-${Math.random()}`;
channel = (await createRoom({ type: 'c', name: testChannelName })).body.channel;
});
after(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', ['admin']),
updatePermission('manage-own-incoming-integrations', ['admin']),
updatePermission('manage-own-outgoing-integrations', ['admin']),
updatePermission('manage-outgoing-integrations', ['admin']),
deleteRoom({ type: 'c', roomId: channel._id }),
deleteUser(user),
]);
});
describe('[/integrations.create]', () => {
describe('Permission checks', () => {
before(async () => {
await Promise.all([updatePermission('manage-incoming-integrations', []), updatePermission('manage-own-incoming-integrations', [])]);
});
after(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', ['admin']),
updatePermission('manage-own-incoming-integrations', ['admin']),
]);
});
it('should return an error when the user DOES NOT have the permission "manage-incoming-integrations" to add an incoming integration', async () => {
const res = await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test',
enabled: true,
alias: 'test',
username: 'rocket.cat',
scriptEnabled: false,
overrideDestinationChannelEnabled: true,
channel: '#general',
})
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'not_authorized');
});
it('should return an error when the user DOES NOT have the permission "manage-own-incoming-integrations" to add an incoming integration', async () => {
const res = await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test',
enabled: true,
alias: 'test',
username: 'rocket.cat',
scriptEnabled: false,
overrideDestinationChannelEnabled: true,
channel: '#general',
})
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'not_authorized');
});
});
describe('With manage-incoming-integrations permission', () => {
let tempIntegrationId: IIntegration['_id'];
before(async () => {
await updatePermission('manage-incoming-integrations', ['admin']);
});
after(async () => {
if (tempIntegrationId) {
await removeIntegration(tempIntegrationId, 'incoming');
}
});
it('should add the integration successfully when the user ONLY has the permission "manage-incoming-integrations" to add an incoming integration', async () => {
const res = await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test',
enabled: true,
alias: 'test',
username: 'rocket.cat',
scriptEnabled: false,
overrideDestinationChannelEnabled: false,
channel: '#general',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration').and.to.be.an('object');
tempIntegrationId = res.body.integration._id;
});
it('should set overrideDestinationChannelEnabled setting to false when it is not provided', async () => {
const res = await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test',
enabled: true,
alias: 'test',
username: 'rocket.cat',
scriptEnabled: false,
channel: '#general',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration').and.to.be.an('object');
expect(res.body.integration).to.have.property('overrideDestinationChannelEnabled', false);
const integrationId = res.body.integration._id;
await removeIntegration(integrationId, 'incoming');
});
});
describe('With manage-own-incoming-integrations permission', () => {
before(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', []),
updatePermission('manage-own-incoming-integrations', ['admin']),
]);
});
after(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', ['admin']),
updatePermission('manage-own-incoming-integrations', ['admin']),
]);
});
it('should add the integration successfully when the user ONLY has the permission "manage-own-incoming-integrations" to add an incoming integration', async () => {
const res = await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test 2',
enabled: true,
alias: 'test2',
username: 'rocket.cat',
scriptEnabled: false,
overrideDestinationChannelEnabled: false,
channel: '#general',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration').and.to.be.an('object');
integration = res.body.integration;
});
});
describe('With both manage-own-incoming-integrations and manage-incoming-integrations', () => {
it('should not allow users without a role with the message-impersonate permission to be assigned to new incoming integrations', async () => {
await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test',
enabled: true,
alias: 'test',
username: adminUsername,
scriptEnabled: false,
overrideDestinationChannelEnabled: true,
channel: '#general',
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property(
'error',
"User selected for the incoming integration lacks the 'message-impersonate' permission. [error-user-lacks-message-impersonate-permission]",
);
});
});
});
describe('Incoming Integration execution', () => {
it('should return an error when the user sends an invalid type of integration', async () => {
const res = await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming-invalid',
name: 'Incoming test',
enabled: true,
alias: 'test',
username: 'rocket.cat',
scriptEnabled: false,
overrideDestinationChannelEnabled: true,
channel: '#general',
})
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Invalid integration type.');
});
it('should execute the incoming integration', async () => {
await request
.post(`/hooks/${integration._id}/${integration.token}`)
.send({
text: 'Example message',
})
.expect(200);
});
it("should return an error when sending 'channel' field telling its configuration is disabled", async () => {
const res = await request
.post(`/hooks/${integration._id}/${integration.token}`)
.send({
text: 'Example message',
channel: [testChannelName],
})
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'overriding destination channel is disabled for this integration');
});
it("should return an error when sending 'roomId' field telling its configuration is disabled", async () => {
const res = await request
.post(`/hooks/${integration._id}/${integration.token}`)
.send({
text: 'Example message',
roomId: channel._id,
})
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'overriding destination channel is disabled for this integration');
});
it('should send a message for a channel that is specified in the webhooks configuration', (done) => {
const successfulMessage = `Message sent successfully at #${Date.now()}`;
void request
.post(`/hooks/${integration._id}/${integration.token}`)
.send({
text: successfulMessage,
})
.expect(200)
.end(() => {
return request
.get(api('channels.messages'))
.set(credentials)
.query({
roomId: 'GENERAL',
})
.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 as IMessage[]).find((m) => m.msg === successfulMessage)).to.be.true;
})
.end(done);
});
});
it('should send a message for a channel that is not specified in the webhooks configuration', async () => {
await request
.put(api('integrations.update'))
.set(credentials)
.send({
type: 'webhook-incoming',
overrideDestinationChannelEnabled: true,
integrationId: integration._id,
username: 'rocket.cat',
channel: '#general',
scriptEnabled: true,
enabled: true,
name: integration.name,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration');
expect(res.body.integration.overrideDestinationChannelEnabled).to.be.equal(true);
});
const successfulMessage = `Message sent successfully at #${Date.now()}`;
await request
.post(`/hooks/${integration._id}/${integration.token}`)
.send({
text: successfulMessage,
channel: [testChannelName],
})
.expect(200);
return request
.get(api('channels.messages'))
.set(credentials)
.query({
roomId: channel._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 as IMessage[]).find((m) => m.msg === successfulMessage)).to.be.true;
});
});
it('should send a message if the payload is a application/x-www-form-urlencoded JSON', async () => {
const payload = { msg: `Message as x-www-form-urlencoded JSON sent successfully at #${Date.now()}` };
await request
.post(`/hooks/${integration._id}/${integration.token}`)
.set('Content-Type', 'application/x-www-form-urlencoded')
.send(`payload=${JSON.stringify(payload)}`)
.expect(200);
const messagesResult = await request
.get(api('channels.messages'))
.set(credentials)
.query({
roomId: 'GENERAL',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(messagesResult.body).to.have.property('success', true);
expect(messagesResult.body).to.have.property('messages').and.to.be.an('array');
expect(!!(messagesResult.body.messages as IMessage[]).find((m) => m.msg === payload.msg)).to.be.true;
});
describe('When the user does not have a role with the message-impersonate permission', () => {
before(async () => {
await addPermissions({
'message-impersonate': ['user'],
});
});
after(async () => {
await addPermissions({
'message-impersonate': ['bot', 'app'],
});
});
it('should return an error when executing incoming integration', async () => {
await request
.post(`/hooks/${integration._id}/${integration.token}`)
.send({
text: 'Example message',
})
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'error-user-lacks-message-impersonate-permission');
});
});
});
});
describe('Script integration tests', () => {
let withScript: IIntegration;
let withScriptDefaultContentType: IIntegration;
let withSkipTranspile: IIntegration;
const sloppyModeScript =
'const buildMessage = (obj) => {\n' +
' \n' +
' const template = `[#VALUE](${ obj.test })`;\n' +
' \n' +
' return {\n' +
' text: template\n' +
' };\n' +
' };\n' +
' \n' +
' class Script {\n' +
' process_incoming_request({ request }) {\n' +
' msg = buildMessage(request.content);\n' +
' \n' +
' return {\n' +
' content:{\n' +
' text: msg.text\n' +
' }\n' +
' };\n' +
' }\n' +
' }\n' +
' \n';
before(async () => {
await updatePermission('manage-incoming-integrations', ['admin']);
const res1 = await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test with script',
enabled: true,
alias: 'test',
username: 'rocket.cat',
scriptEnabled: true,
overrideDestinationChannelEnabled: false,
channel: '#general',
script: `
class Script {
process_incoming_request({ request }) {
return {
content:{
text: request.content.text
}
};
}
}
`,
})
.expect(200);
withScript = res1.body.integration;
const res2 = await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test with script and default content-type',
enabled: true,
alias: 'test',
username: 'rocket.cat',
scriptEnabled: true,
overrideDestinationChannelEnabled: false,
channel: '#general',
script: sloppyModeScript,
})
.expect(200);
withScriptDefaultContentType = res2.body.integration;
// Same script but with skipTranspile: true — no Babel, class methods
// run in strict mode so `msg = buildMessage(...)` throws ReferenceError.
const res3 = await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test with skipTranspile',
enabled: true,
alias: 'test',
username: 'rocket.cat',
scriptEnabled: true,
skipTranspile: true,
overrideDestinationChannelEnabled: false,
channel: '#general',
script: sloppyModeScript,
})
.expect(200);
withSkipTranspile = res3.body.integration;
});
after(async () => {
await Promise.all([
removeIntegration(withScript._id, 'incoming'),
removeIntegration(withScriptDefaultContentType._id, 'incoming'),
removeIntegration(withSkipTranspile._id, 'incoming'),
]);
});
it('should send a message if the payload is a application/x-www-form-urlencoded JSON AND the integration has a valid script', async () => {
const payload = { text: `Message as x-www-form-urlencoded JSON sent successfully at #${Date.now()}` };
await request
.post(`/hooks/${withScript._id}/${withScript.token}`)
.set('Content-Type', 'application/x-www-form-urlencoded')
.send(`payload=${JSON.stringify(payload)}`)
.expect(200);
const messagesResult = await request
.get(api('channels.messages'))
.set(credentials)
.query({
roomId: 'GENERAL',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(messagesResult.body).to.have.property('success', true);
expect(messagesResult.body).to.have.property('messages').and.to.be.an('array');
expect(!!(messagesResult.body.messages as IMessage[]).find((m) => m.msg === payload.text)).to.be.true;
});
it('should send a message if the payload is application/json and the integration has a valid script', async () => {
const payload = { test: 'test' };
await request
.post(`/hooks/${withScriptDefaultContentType._id}/${withScriptDefaultContentType.token}`)
.set('Content-Type', 'application/json')
.send(JSON.stringify(payload))
.expect(200);
const messagesResult = await request
.get(api('channels.messages'))
.set(credentials)
.query({
roomId: 'GENERAL',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(messagesResult.body).to.have.property('success', true);
expect(messagesResult.body).to.have.property('messages').and.to.be.an('array');
expect(!!(messagesResult.body.messages as IMessage[]).find((m) => m.msg === '[#VALUE](test)')).to.be.true;
});
it('should create the skipTranspile integration with scriptCompiled and no scriptError', () => {
expect(withSkipTranspile).to.have.property('scriptCompiled');
expect(withSkipTranspile).to.not.have.property('scriptError');
expect(withSkipTranspile).to.have.property('skipTranspile', true);
});
it('should fail to execute the same sloppy-mode script when skipTranspile is true', async () => {
const payload = { test: 'test' };
await request
.post(`/hooks/${withSkipTranspile._id}/${withSkipTranspile.token}`)
.set('Content-Type', 'application/json')
.send(JSON.stringify(payload))
.expect(400);
});
});
describe('With manage-own-incoming-integrations permission', () => {
let integrationId: string;
before(async () => {
await updatePermission('manage-own-incoming-integrations', ['admin']);
});
after(async () => {
if (integrationId) {
await removeIntegration(integrationId, 'incoming');
}
});
it('should return scriptCompiled and no scriptError', async () => {
const res = await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test',
enabled: true,
alias: 'test',
username: 'rocket.cat',
scriptEnabled: true,
scriptEngine: 'isolated-vm',
channel: '#general',
script: `
class Script {
process_incoming_request({ request }) {
return {
content:{
text: request.content.text
}
};
}
}
`,
})
.expect(200);
expect(res.body.integration).to.have.property('scriptCompiled');
expect(res.body.integration).to.not.have.property('scriptError');
integrationId = res.body.integration._id;
});
});
});
describe('[/integrations.history]', () => {
before(async () => {
await Promise.all([updatePermission('manage-incoming-integrations', []), updatePermission('manage-own-incoming-integrations', [])]);
});
after(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', ['admin']),
updatePermission('manage-own-incoming-integrations', ['admin']),
]);
});
it('should return an error when trying to get history of incoming integrations if user does NOT have enough permissions', async () => {
const res = await request
.get(api('integrations.history'))
.set(credentials)
.query({
id: integration._id,
})
.expect('Content-Type', 'application/json')
.expect(403);
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('[/integrations.list]', () => {
before((done) => {
void createUser().then((createdUser) => {
user = createdUser;
void login(user.username, password).then((credentials) => {
userCredentials = credentials;
void updatePermission('manage-incoming-integrations', ['user']).then(() => {
void createIntegration(
{
type: 'webhook-incoming',
name: 'Incoming test',
enabled: true,
alias: 'test',
username: 'rocket.cat',
scriptEnabled: false,
overrideDestinationChannelEnabled: true,
channel: '#general',
},
userCredentials,
).then((integration) => {
integrationCreatedByAnUser = integration;
done();
});
});
});
});
});
it('should return the list of incoming integrations', async () => {
const res = await request.get(api('integrations.list')).set(credentials).expect('Content-Type', 'application/json').expect(200);
expect(res.body).to.have.property('success', true);
const integrationCreatedByAdmin = (res.body.integrations as IIntegration[]).find(
(createdIntegration) => createdIntegration._id === integration._id,
);
assert.isDefined(integrationCreatedByAdmin);
expect(integrationCreatedByAdmin).to.be.an('object');
expect(integrationCreatedByAdmin._id).to.be.equal(integration._id);
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('items');
expect(res.body).to.have.property('total');
});
describe('With manage-own-incoming-integrations permission', () => {
before(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', []),
updatePermission('manage-own-incoming-integrations', ['user']),
]);
});
after(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', ['admin']),
updatePermission('manage-own-incoming-integrations', ['admin']),
]);
});
it('should return the list of integrations created by the user only', async () => {
const res = await request.get(api('integrations.list')).set(userCredentials).expect('Content-Type', 'application/json').expect(200);
expect(res.body).to.have.property('success', true);
const integrationCreatedByAdmin = (res.body.integrations as IIntegration[]).find(
(createdIntegration) => createdIntegration._id === integration._id,
);
expect(integrationCreatedByAdmin).to.be.equal(undefined);
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('items');
expect(res.body).to.have.property('total');
});
});
describe('Without any permissions', () => {
before(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', []),
updatePermission('manage-own-incoming-integrations', []),
updatePermission('manage-outgoing-integrations', []),
updatePermission('manage-own-outgoing-integrations', []),
]);
});
after(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', ['admin']),
updatePermission('manage-own-incoming-integrations', ['admin']),
updatePermission('manage-outgoing-integrations', ['admin']),
updatePermission('manage-own-outgoing-integrations', ['admin']),
]);
});
it('should return unauthorized error when the user does not have any integrations permissions', async () => {
await request
.get(api('integrations.list'))
.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('[/integrations.get]', () => {
describe('Invalid params', () => {
it('should return an error when the required "integrationId" query parameters is not sent', async () => {
const res = await request.get(api('integrations.get')).set(credentials).expect('Content-Type', 'application/json').expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', `must have required property 'integrationId'`);
});
it('should return an error when the user sends an invalid integration', async () => {
const res = await request
.get(api('integrations.get'))
.query({ integrationId: 'invalid' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'The integration does not exists.');
});
});
describe('Without permissions', () => {
before(async () => {
await Promise.all([updatePermission('manage-incoming-integrations', []), updatePermission('manage-own-incoming-integrations', [])]);
});
after(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', ['admin']),
updatePermission('manage-own-incoming-integrations', ['admin']),
]);
});
it('should return an error when the user DOES NOT have the permission "manage-incoming-integrations" to get an incoming integration', async () => {
const res = await request
.get(api('integrations.get'))
.query({ integrationId: integration._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'not-authorized');
});
it('should return an error when the user DOES NOT have the permission "manage-incoming-integrations" to get an incoming integration created by another user', async () => {
const res = await request
.get(api('integrations.get'))
.query({ integrationId: integrationCreatedByAnUser._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'not-authorized');
});
});
describe('With manage-incoming-integrations permission', () => {
before(async () => {
await updatePermission('manage-incoming-integrations', ['admin']);
});
it('should return the integration successfully', async () => {
const res = await request
.get(api('integrations.get'))
.query({ integrationId: integration._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration');
expect(res.body.integration._id).to.be.equal(integration._id);
});
});
describe('With manage-own-incoming-integrations permission', () => {
before(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', []),
updatePermission('manage-own-incoming-integrations', ['user']),
]);
});
after(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', ['admin']),
updatePermission('manage-own-incoming-integrations', ['admin']),
]);
});
it('should return the integration successfully when the user is able to see only your own integrations', async () => {
const res = await request
.get(api('integrations.get'))
.query({ integrationId: integrationCreatedByAnUser._id })
.set(userCredentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration');
expect(res.body.integration._id).to.be.equal(integrationCreatedByAnUser._id);
});
});
});
describe('[/integrations.update]', () => {
let senderUser: IUser;
let sendUserCredentials: Credentials;
before(async () => {
await addPermissions({
'message-impersonate': ['user'],
});
senderUser = await createUser();
sendUserCredentials = await login(senderUser.username, password);
});
after(async () => {
await addPermissions({
'message-impersonate': ['bot', 'app'],
});
await deleteUser(senderUser);
});
it('should update an integration by id and return the new data', async () => {
const res = await request
.put(api('integrations.update'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test updated',
enabled: true,
alias: 'test updated',
username: senderUser.username,
scriptEnabled: true,
overrideDestinationChannelEnabled: true,
channel: '#general',
integrationId: integration._id,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration');
expect(res.body.integration._id).to.be.equal(integration._id);
expect(res.body.integration.name).to.be.equal('Incoming test updated');
expect(res.body.integration.alias).to.be.equal('test updated');
integration = res.body.integration;
});
it('should not allow users without a role with the message-impersonate permission to be assigned to existing incoming integrations', async () => {
await request
.put(api('integrations.update'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test update attempt',
enabled: true,
alias: 'test update attempt',
username: 'rocket.cat',
scriptEnabled: true,
overrideDestinationChannelEnabled: true,
channel: '#general',
integrationId: integration._id,
})
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property(
'error',
"User selected for the incoming integration lacks the 'message-impersonate' permission. [error-user-lacks-message-impersonate-permission]",
);
});
});
it('should have integration updated on subsequent gets', async () => {
const res = await request
.get(api('integrations.get'))
.query({ integrationId: integration._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration');
expect(res.body.integration._id).to.be.equal(integration._id);
expect(res.body.integration.name).to.be.equal('Incoming test updated');
expect(res.body.integration.alias).to.be.equal('test updated');
});
it("should update an integration's username and associated userId correctly and return the new data", async () => {
await request
.put(api('integrations.update'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test updated x2',
enabled: true,
alias: 'test updated x2',
username: senderUser.username,
scriptEnabled: true,
overrideDestinationChannelEnabled: true,
channel: '#general',
integrationId: integration._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration');
expect(res.body.integration._id).to.be.equal(integration._id);
expect(res.body.integration.name).to.be.equal('Incoming test updated x2');
expect(res.body.integration.alias).to.be.equal('test updated x2');
expect(res.body.integration.username).to.be.equal(senderUser.username);
expect(res.body.integration.userId).to.be.equal(sendUserCredentials['X-User-Id']);
integration = res.body.integration;
});
});
it('should send messages to the channel under the updated username', async () => {
const successfulMessage = `Message sent successfully at #${Random.id()}`;
await request
.post(`/hooks/${integration._id}/${integration.token}`)
.send({
text: successfulMessage,
})
.expect(200);
await request
.get(api('channels.messages'))
.set(credentials)
.query({
roomId: 'GENERAL',
})
.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 message = (res.body.messages as IMessage[]).find((m) => m.msg === successfulMessage);
expect(message?.u).have.property('username', senderUser.username);
});
});
});
describe('[/integrations.remove]', () => {
describe('Without permissions', () => {
before(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', []),
updatePermission('manage-own-incoming-integrations', []),
updatePermission('manage-outgoing-integrations', []),
updatePermission('manage-own-outgoing-integrations', []),
]);
});
after(async () => {
await Promise.all([
updatePermission('manage-incoming-integrations', ['admin']),
updatePermission('manage-own-incoming-integrations', ['admin']),
updatePermission('manage-outgoing-integrations', ['admin']),
updatePermission('manage-own-outgoing-integrations', ['admin']),
]);
});
it('should return an error when the user DOES NOT have the permission "manage-incoming-integrations" to remove an incoming integration', async () => {
const res = await request
.post(api('integrations.remove'))
.set(credentials)
.send({
integrationId: integration._id,
type: 'webhook-incoming',
})
.expect('Content-Type', 'application/json')
.expect(403);
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]');
});
it('should return an error when the user DOES NOT have the permission "manage-own-incoming-integrations" to remove an incoming integration', async () => {
const res = await request
.post(api('integrations.remove'))
.set(credentials)
.send({
integrationId: integration._id,
type: 'webhook-incoming',
})
.expect('Content-Type', 'application/json')
.expect(403);
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('Invalid params', () => {
before(async () => {
await updatePermission('manage-own-incoming-integrations', ['admin']);
});
it('should return an error when the user sends an invalid type of integration', async () => {
const res = await request
.post(api('integrations.remove'))
.set(credentials)
.send({
integrationId: integration._id,
type: 'invalid-type',
})
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error').include(`must match exactly one schema in oneOf`);
});
});
describe('With manage-incoming-integrations permission', () => {
before(async () => {
await updatePermission('manage-incoming-integrations', ['admin']);
});
it('should remove the integration successfully when the user at least one of the necessary permission to remove an incoming integration', async () => {
const res = await request
.post(api('integrations.remove'))
.set(credentials)
.send({
integrationId: integration._id,
type: integration.type,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
describe('Normal user with manage-own-incoming-integrations', () => {
before(async () => {
await updatePermission('manage-own-incoming-integrations', ['user']);
});
after(async () => {
await updatePermission('manage-own-incoming-integrations', ['admin']);
});
it('the normal user should remove the integration successfully when the user have the "manage-own-incoming-integrations" to remove an incoming integration', async () => {
const res = await request
.post(api('integrations.remove'))
.set(userCredentials)
.send({
integrationId: integrationCreatedByAnUser._id,
type: integrationCreatedByAnUser.type,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
});
});
});
describe('Additional Tests for Message Delivery, Permissions, and Authentication', () => {
let nonMemberUser: IUser;
let privateTeam: ITeam;
let publicChannelInPrivateTeam: IRoom;
let privateRoom: IRoom;
let publicRoom: IRoom;
let integration2: IIntegration;
let integration3: IIntegration;
let integration4: IIntegration;
let integrationMixed1: IIntegration;
let integrationMixed2: IIntegration;
before(async () => {
nonMemberUser = await createUser({ username: `g_${Random.id()}`, roles: ['bot'] });
privateTeam = await createTeam(credentials, `private.team.${Random.id()}`, TeamType.PRIVATE);
await assignRoleToUser({ username: adminUsername, roleId: 'bot' });
const [publicInPrivateResponse, privateRoomResponse, publicRoomResponse] = await Promise.all([
createRoom({
type: 'c',
name: `teamPrivate.publicChannel.${Random.id()}`,
credentials,
extraData: {
teamId: privateTeam._id,
},
}),
createRoom({
type: 'p',
name: `privateChannel.${Random.id()}`,
credentials,
}),
createRoom({
type: 'c',
name: `publicChannel.${Random.id()}`,
credentials,
}),
]);
publicChannelInPrivateTeam = publicInPrivateResponse.body.channel;
privateRoom = privateRoomResponse.body.group;
publicRoom = publicRoomResponse.body.channel;
await updatePermission('manage-incoming-integrations', ['admin']);
await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test 2 - Sending Messages',
enabled: true,
alias: 'Incoming test 2 - Sending Messages',
username: nonMemberUser.username as string,
scriptEnabled: false,
overrideDestinationChannelEnabled: false,
channel: `#${privateRoom.fname}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration').and.to.be.an('object');
integration2 = res.body.integration;
});
await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test 3 - Sending Messages',
enabled: true,
alias: 'Incoming test 3 - Sending Messages',
username: nonMemberUser.username as string,
scriptEnabled: false,
overrideDestinationChannelEnabled: false,
channel: `#${publicChannelInPrivateTeam.fname}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration').and.to.be.an('object');
integration3 = res.body.integration;
});
await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test 4 - Sending Messages',
enabled: true,
alias: 'Incoming test 4 - Sending Messages',
username: nonMemberUser.username as string,
scriptEnabled: false,
overrideDestinationChannelEnabled: false,
channel: `#${publicRoom.fname}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration').and.to.be.an('object');
integration4 = res.body.integration;
});
await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test Mixed - Sending Messages',
enabled: true,
alias: 'Incoming test Mixed - Sending Messages',
username: nonMemberUser.username as string,
scriptEnabled: false,
overrideDestinationChannelEnabled: false,
channel: `#${publicRoom.fname}, #${privateRoom.fname}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration').and.to.be.an('object');
integrationMixed1 = res.body.integration;
});
await request
.post(api('integrations.create'))
.set(credentials)
.send({
type: 'webhook-incoming',
name: 'Incoming test Mixed - Sending Messages',
enabled: true,
alias: 'Incoming test Mixed - Sending Messages',
username: adminUsername,
scriptEnabled: false,
overrideDestinationChannelEnabled: false,
channel: `#${publicRoom.fname}, #${privateRoom.fname}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('integration').and.to.be.an('object');
integrationMixed2 = res.body.integration;
});
});
after(async () => {
await Promise.all(
[publicRoom, privateRoom, publicChannelInPrivateTeam].map((room) => deleteRoom({ type: room.t as 'c' | 'p', roomId: room._id })),
);
await deleteTeam(credentials, privateTeam.name);
await deleteUser(nonMemberUser);
await Promise.all([
...[integration2, integration3, integration4, integrationMixed1, integrationMixed2].map((integration) =>
request.post(api('integrations.remove')).set(credentials).send({
integrationId: integration._id,
type: integration.type,
}),
),
updatePermission('manage-incoming-integrations', ['admin']),
]);
await removeRoleFromUser(adminUsername, 'bot');
});
it('should not send a message in public room if token is invalid', async () => {
const successfulMessage = `Message sent successfully at #${Random.id()}`;
await request
.post(`/hooks/${integration4._id}/invalid-token`)
.send({
text: successfulMessage,
})
.expect(500)
.expect((res) => {
expect(res.text).to.be.equal('Internal Server Error');
});
await request
.get(api('channels.messages'))
.set(credentials)
.query({
roomId: publicRoom._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 as IMessage[]).find((m) => m.msg === successfulMessage)).to.be.undefined;
});
});
it('should not send a message in private room if token is invalid', async () => {
const successfulMessage = `Message sent successfully at #${Random.id()}`;
await request
.post(`/hooks/${integration2._id}/invalid-token`)
.send({
text: successfulMessage,
})
.expect(500)
.expect((res) => {
expect(res.text).to.be.equal('Internal Server Error');
});
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: privateRoom._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 as IMessage[]).find((m) => m.msg === successfulMessage)).to.be.undefined;
});
});
it('should not send a message to a private rooms on behalf of a non member', async () => {
const successfulMessage = `Message sent successfully at #${Random.id()}`;
await request
.post(`/hooks/${integration2._id}/${integration2.token}`)
.send({
text: successfulMessage,
})
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('error', 'error-not-allowed');
});
await request
.get(api('groups.messages'))
.set(credentials)
.query({
roomId: privateRoom._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 as IMessage[]).find((m) => m.msg === successfulMessage)).to.be.undefined;
});
});
it('should not add non member to private rooms when sending message', async () => {
const successfulMessage = `Message sent successfully at #${Random.id()}`;
await request
.post(`/hooks/${integration2._id}/${integration2.token}`)
.send({
text: successfulMessage,
})
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('error', 'error-not-allowed');
});
await request
.get(api('groups.members'))
.set(credentials)
.query({
roomId: privateRoom._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 as AtLeast<IUser, '_id'>[]).find((m) => m._id === nonMemberUser._id)).to.be.undefined;
});
});
it('should not send a message to public channel of a private team on behalf of a non team member', async () => {
const successfulMessage = `Message sent successfully at #${Random.id()}`;
await request
.post(`/hooks/${integration3._id}/${integration3.token}`)
.send({
text: successfulMessage,
})
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('error', 'error-not-allowed');
});
await request
.get(api('channels.messages'))
.set(credentials)
.query({
roomId: publicChannelInPrivateTeam._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 as IMessage[]).find((m) => m.msg === successfulMessage)).to.be.undefined;
});
});
it('should not add non team member to the public channel in a private team when sending message', async () => {
const successfulMessage = `Message sent successfully at #${Random.id()}`;
await request
.post(`/hooks/${integration3._id}/${integration3.token}`)
.send({
text: successfulMessage,
})
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('error', 'error-not-allowed');
});
await request
.get(api('channels.members'))
.set(credentials)
.query({
roomId: publicChannelInPrivateTeam._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 as AtLeast<IUser, '_id'>[]).find((m) => m._id === nonMemberUser._id)).to.be.undefined;
});
});
it('should send messages from non-members to public rooms and add them as room members', async () => {
const successfulMessage = `Message sent successfully at #${Random.id()}`;
await request
.post(`/hooks/${integration4._id}/${integration4.token}`)
.send({
text: successfulMessage,
})
.expect(200);
await request
.get(api('channels.messages'))
.set(credentials)
.query({
roomId: publicRoom._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 as IMessage[]).find((m) => m.msg === successfulMessage)).not.to.be.undefined;
});
await request
.get(api('channels.members'))
.set(credentials)
.query({
roomId: publicRoom._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 as AtLeast<IUser, '_id'>[]).find((m) => m._id === nonMemberUser._id)).not.to.be.undefined;
});
});
describe('Message Settings', async () => {
const maxSize = 5000;
before(() => updateSetting('Message_MaxAllowedSize', maxSize));
after(() => updateSetting('Message_MaxAllowedSize', maxSize));
it('should not send a message if message size is greater than the Message_MaxAllowedSize', async () => {
const largeMesssage = Array.from({ length: maxSize + 1 })
.map(() => 'A')
.join('');
await request
.post(`/hooks/${integration4._id}/${integration4.token}`)
.send({
text: largeMesssage,
})
.expect(400)
.expect((res) => {
expect(res.body.error).to.be.equal('error-message-size-exceeded');
});
await request
.get(api('channels.messages'))
.set(credentials)
.query({
roomId: publicRoom._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 as IMessage[]).find((m) => m.msg === largeMesssage)).to.be.undefined;
});
});
it('should send a message if message size is less than the Message_MaxAllowedSize', async () => {
const smallerMessage = Array.from({ length: maxSize - 1 })
.map(() => 'A')
.join('');
await request
.post(`/hooks/${integration4._id}/${integration4.token}`)
.send({
text: smallerMessage,
})
.expect(200)
.expect((res) => {
expect(res.body.success).to.be.equal(true);
});
await request
.get(api('channels.messages'))
.set(credentials)
.query({
roomId: publicRoom._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 as IMessage[]).find((m) => m.msg === smallerMessage)).to.not.be.undefined;
});
});
});
describe('Multiple channels delivery', () => {
it('should not return separate responses when separateResponse is not provided', async () => {
const testMsg = `msg-${Random.id()}`;
await request
.post(`/hooks/${integrationMixed1._id}/${integrationMixed1.token}`)
.send({
text: testMsg,
})
.expect(400)
.expect((res) => {
expect(res.body).to.not.have.property('responses');
});
});
it('should not return separate responses when separateResponse = false', async () => {
const testMsg = `msg-${Random.id()}`;
await request
.post(`/hooks/${integrationMixed1._id}/${integrationMixed1.token}`)
.send({
text: testMsg,
separateResponse: false,
})
.expect(400)
.expect((res) => {
expect(res.body).to.not.have.property('responses');
});
});
it('should return separate responses when separateResponse = true', async () => {
const testMsg = `msg-${Random.id()}`;
await request
.post(`/hooks/${integrationMixed1._id}/${integrationMixed1.token}`)
.send({
text: testMsg,
separateResponse: true,
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.responses).to.be.an('array').and.to.have.lengthOf(2);
const publicResponse = res.body.responses.find((r: any) => r.channel === `#${publicRoom.fname}`);
const privateResponse = res.body.responses.find((r: any) => r.channel === `#${privateRoom.fname}`);
expect(publicResponse).not.to.have.property('error');
expect(privateResponse).to.have.property('error');
});
});
it('should not deliver to any channel if one fails when separateResponse is not provided', async () => {
const testMsg = `msg-${Random.id()}`;
await request
.post(`/hooks/${integrationMixed1._id}/${integrationMixed1.token}`)
.send({
text: testMsg,
})
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('error');
});
await request
.get(api('groups.messages'))
.set(credentials)
.query({ roomId: privateRoom._id })
.expect(200)
.expect((res) => {
expect((res.body.messages as IMessage[]).find((m) => m.msg === testMsg)).to.be.undefined;
});
await request
.get(api('channels.messages'))
.set(credentials)
.query({ roomId: publicRoom._id })
.expect(200)
.expect((res) => {
expect((res.body.messages as IMessage[]).find((m) => m.msg === testMsg)).to.be.undefined;
});
});
it('should not deliver to any channel if one fails when separateResponse = false', async () => {
const testMsg = `msg-${Random.id()}`;
await request
.post(`/hooks/${integrationMixed1._id}/${integrationMixed1.token}`)
.send({
text: testMsg,
separateResponse: false,
})
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('error');
});
await request
.get(api('groups.messages'))
.set(credentials)
.query({ roomId: privateRoom._id })
.expect(200)
.expect((res) => {
expect((res.body.messages as IMessage[]).find((m) => m.msg === testMsg)).to.be.undefined;
});
await request
.get(api('channels.messages'))
.set(credentials)
.query({ roomId: publicRoom._id })
.expect(200)
.expect((res) => {
expect((res.body.messages as IMessage[]).find((m) => m.msg === testMsg)).to.be.undefined;
});
});
it('should send message to only public channel when separateResponse = true', async () => {
const testMsg = `msg-${Random.id()}`;
await request
.post(`/hooks/${integrationMixed1._id}/${integrationMixed1.token}`)
.send({
text: testMsg,
separateResponse: true,
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
await request
.get(api('groups.messages'))
.set(credentials)
.query({ roomId: privateRoom._id })
.expect(200)
.expect((res) => {
expect((res.body.messages as IMessage[]).find((m) => m.msg === testMsg)).to.be.undefined;
});
await request
.get(api('channels.messages'))
.set(credentials)
.query({ roomId: publicRoom._id })
.expect(200)
.expect((res) => {
const found = (res.body.messages as IMessage[]).filter((m) => m.msg === testMsg);
expect(found).to.have.lengthOf(1);
});
});
it('should send messages to all channels when message could be delivered to all channels', async () => {
const testMsg = `msg-${Random.id()}`;
await request
.post(`/hooks/${integrationMixed2._id}/${integrationMixed2.token}`)
.send({
text: testMsg,
separateResponse: true,
})
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.responses).to.be.an('array').and.to.have.lengthOf(2);
const publicResponse = res.body.responses.find((r: any) => r.channel === `#${publicRoom.fname}`);
const privateResponse = res.body.responses.find((r: any) => r.channel === `#${privateRoom.fname}`);
expect(publicResponse).not.to.have.property('error');
expect(privateResponse).not.to.have.property('error');
});
await request
.get(api('groups.messages'))
.set(credentials)
.query({ roomId: privateRoom._id })
.expect(200)
.expect((res) => {
const found = (res.body.messages as IMessage[]).filter((m) => m.msg === testMsg);
expect(found).to.have.lengthOf(1);
});
await request
.get(api('channels.messages'))
.set(credentials)
.query({ roomId: publicRoom._id })
.expect(200)
.expect((res) => {
const found = (res.body.messages as IMessage[]).filter((m) => m.msg === testMsg);
expect(found).to.have.lengthOf(1);
});
});
});
});
});