The communications platform that puts data protection first.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
Rocket.Chat/apps/meteor/tests/end-to-end/api/chat.ts

4661 lines
143 KiB

import type { Credentials } from '@rocket.chat/api-client';
import type { IMessage, IRoom, ISubscription, IThreadMessage, IUser } from '@rocket.chat/core-typings';
import { Random } from '@rocket.chat/random';
import { expect } from 'chai';
import { after, before, beforeEach, describe, it } from 'mocha';
import type { Response } from 'supertest';
import { getCredentials, api, request, credentials, apiUrl } from '../../data/api-data';
import { followMessage, sendSimpleMessage, deleteMessage } from '../../data/chat.helper';
import { updatePermission, updateSetting } from '../../data/permissions.helper';
import { addUserToRoom, createRoom, deleteRoom, getSubscriptionByRoomId } from '../../data/rooms.helper';
import { password } from '../../data/user';
import type { TestUser } from '../../data/users.helper';
import { createUser, deleteUser, login } from '../../data/users.helper';
const pinMessage = ({ msgId }: { msgId: IMessage['_id'] }) => {
if (!msgId) {
throw new Error('"msgId" is required in "pinMessage" test helper');
}
return request.post(api('chat.pinMessage')).set(credentials).send({
messageId: msgId,
});
};
describe('[Chat]', () => {
let testChannel: IRoom;
let message: { _id: IMessage['_id'] };
before((done) => getCredentials(done));
before(async () => {
testChannel = (await createRoom({ type: 'c', name: `chat.api-test-${Date.now()}` })).body.channel;
});
after(() => deleteRoom({ type: 'c', roomId: testChannel._id }));
describe('/chat.postMessage', () => {
it('should throw an error when at least one of required parameters(channel, roomId) is not sent', (done) => {
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
})
.end(done);
});
it('should throw an error when it has some properties with the wrong type(attachments.title_link_download, attachments.fields, message_link)', (done) => {
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
alias: 'Gruggy',
text: 'Sample message',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 12,
collapsed: false,
author_name: 'Bradley Hilton',
author_link: 'https://rocket.chat/',
author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
title: 'Attachment Example',
title_link: 'https://youtube.com',
title_link_download: 'https://youtube.com',
image_url: 'http://res.guggy.com/logo_128.png',
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
video_url: 'http://www.w3schools.com/tags/movie.mp4',
fields: '',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
})
.end(done);
});
describe('should throw an error when the sensitive properties contain malicious XSS values', () => {
it('attachment.message_link', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
alias: 'Gruggy',
text: 'Sample message',
avatar: 'http://res.guggy.com/logo_128.png',
emoji: ':smirk:',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 'javascript:alert("xss")',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.author_link', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
author_link: 'javascript:alert("xss")',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.title_link', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
title: 'Attachment Example',
title_link: 'javascript:alert("xss")',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.action.url', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
title: 'Attachment Example',
title_link: 'https://youtube.com',
actions: [
{
type: 'button',
text: 'Text',
url: 'javascript:alert("xss")',
},
],
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('message.avatar', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
emoji: ':smirk:',
avatar: 'javascript:alert("xss")',
alias: 'Gruggy',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
title: 'Attachment Example',
title_link: 'https://youtube.com',
actions: [
{
type: 'button',
text: 'Text',
url: 'https://youtube.com',
},
],
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.action.image_url', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
title: 'Attachment Example',
title_link: 'https://youtube.com',
actions: [
{
type: 'button',
text: 'Text',
url: 'http://res.guggy.com/logo_128.png',
image_url: 'javascript:alert("xss")',
},
],
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.thumb_url', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
emoji: ':smirk:',
alias: 'Gruggy',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
title: 'Attachment Example',
thumb_url: 'javascript:alert("xss")',
title_link: 'http://res.guggy.com/logo_128.png',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.author_icon', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
emoji: ':smirk:',
alias: 'Gruggy',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
title: 'Attachment Example',
author_icon: 'javascript:alert("xss")',
title_link: 'http://res.guggy.com/logo_128.png',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.image_url', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
title: 'Attachment Example',
image_url: 'javascript:alert("xss")',
title_link: 'http://res.guggy.com/logo_128.png',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.audio_url', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
title: 'Attachment Example',
audio_url: 'javascript:alert("xss")',
title_link: 'http://res.guggy.com/logo_128.png',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.video_url', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
alias: 'Gruggy',
text: 'Sample message',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
title: 'Attachment Example',
video_url: 'javascript:alert("xss")',
title_link: 'http://res.guggy.com/logo_128.png',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
});
it('should throw an error when the properties (attachments.fields.title, attachments.fields.value) are with the wrong type', (done) => {
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
emoji: ':smirk:',
alias: 'Gruggy',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 'https://google.com',
collapsed: false,
author_name: 'Bradley Hilton',
author_link: 'https://rocket.chat/',
author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
title: 'Attachment Example',
title_link: 'https://youtube.com',
title_link_download: true,
image_url: 'http://res.guggy.com/logo_128.png',
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
video_url: 'http://www.w3schools.com/tags/movie.mp4',
fields: [
{
short: true,
title: 12,
value: false,
},
],
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
})
.end(done);
});
it('should throw an error when the properties (attachments.fields.title) is missing', (done) => {
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
emoji: ':smirk:',
alias: 'Gruggy',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 'https://google.com',
collapsed: false,
author_name: 'Bradley Hilton',
author_link: 'https://rocket.chat/',
author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
title: 'Attachment Example',
title_link: 'https://youtube.com',
title_link_download: true,
image_url: 'http://res.guggy.com/logo_128.png',
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
video_url: 'http://www.w3schools.com/tags/movie.mp4',
fields: [
{
short: true,
value: 'This is attachment field value',
},
],
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
})
.end(done);
});
it('should throw an error when the properties (attachments.fields.value) is missing', (done) => {
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
emoji: ':smirk:',
alias: 'Gruggy',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 'https://google.com',
collapsed: false,
author_name: 'Bradley Hilton',
author_link: 'https://rocket.chat/',
author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
title: 'Attachment Example',
title_link: 'https://youtube.com',
title_link_download: true,
image_url: 'http://res.guggy.com/logo_128.png',
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
video_url: 'http://www.w3schools.com/tags/movie.mp4',
fields: [
{
short: true,
title: 'This is attachment field title',
},
],
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
})
.end(done);
});
it('attachment.fields should work fine when value and title are provided', (done) => {
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
attachments: [
{
text: 'This is attachment field',
color: '#764FA5',
fields: [{ short: true, value: 'This is value', title: 'This is title' }],
},
],
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.not.have.property('error');
expect(res.body).to.have.nested.property('message.msg', 'Sample message');
expect(res.body).to.have.nested.property('message.attachments').to.be.an('array');
expect(res.body).to.have.nested.property('message.attachments[0].fields').to.be.an('array');
expect(res.body).to.have.nested.property('message.attachments[0].fields[0].short', true);
expect(res.body).to.have.nested.property('message.attachments[0].fields[0].value', 'This is value');
expect(res.body).to.have.nested.property('message.attachments[0].fields[0].title', 'This is title');
})
.end(done);
});
it('should return statusCode 200 when postMessage successfully', (done) => {
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
emoji: ':smirk:',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 'https://google.com',
collapsed: false,
author_name: 'Bradley Hilton',
author_link: 'https://rocket.chat/',
author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
title: 'Attachment Example',
title_link: 'https://youtube.com',
title_link_download: true,
image_url: 'http://res.guggy.com/logo_128.png',
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
video_url: 'http://www.w3schools.com/tags/movie.mp4',
fields: [
{
short: true,
title: 'Test',
value: 'Testing out something or other',
},
{
short: true,
title: 'Another Test',
value: '[Link](https://google.com/) something and this and that.',
},
],
},
],
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', 'Sample message');
message = { _id: res.body.message._id };
})
.end(done);
});
it('should not parse urls when parseUrls=false is provided', async () => {
return request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: apiUrl,
parseUrls: false,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', apiUrl);
expect(res.body.message).to.not.have.property('urls');
});
});
it('should parse urls when parseUrls=true is provided', async () => {
return request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: apiUrl,
parseUrls: 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('message.msg', apiUrl);
expect(res.body.message).to.have.property('urls');
});
});
it('should parse urls when parseUrls is not provided', async () => {
return request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: apiUrl,
parseUrls: undefined,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', apiUrl);
expect(res.body.message).to.have.property('urls');
});
});
describe('text message allowed size', () => {
before(async () => {
await updateSetting('Message_MaxAllowedSize', 10);
});
after(async () => {
await updateSetting('Message_MaxAllowedSize', 5000);
});
it('should return an error if text parameter surpasses the maximum allowed size', (done) => {
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: '#general',
text: 'Text to test max limit allowed',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'error-message-size-exceeded');
})
.end(done);
});
it('should return an error if text parameter in the first attachment surpasses the maximum allowed size', (done) => {
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Yay!',
emoji: ':smirk:',
attachments: [
{
color: '#ff0000',
text: 'Text to test max limit allowed',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 'https://google.com',
collapsed: false,
author_name: 'Bradley Hilton',
author_link: 'https://rocket.chat/',
author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
title: 'Attachment Example',
title_link: 'https://youtube.com',
title_link_download: true,
image_url: 'http://res.guggy.com/logo_128.png',
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
video_url: 'http://www.w3schools.com/tags/movie.mp4',
fields: [],
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'error-message-size-exceeded');
})
.end(done);
});
it('should return an error if text parameter in any of the attachments surpasses the maximum allowed size', (done) => {
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Yay!',
emoji: ':smirk:',
attachments: [
{
color: '#ff0000',
text: 'Yay!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 'https://google.com',
collapsed: false,
author_name: 'Bradley Hilton',
author_link: 'https://rocket.chat/',
author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
title: 'Attachment Example',
title_link: 'https://youtube.com',
title_link_download: true,
image_url: 'http://res.guggy.com/logo_128.png',
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
video_url: 'http://www.w3schools.com/tags/movie.mp4',
fields: [],
},
{
color: '#ff0000',
text: 'Text to large to test max limit allowed',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 'https://google.com',
collapsed: false,
author_name: 'Bradley Hilton',
author_link: 'https://rocket.chat/',
author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
title: 'Attachment Example',
title_link: 'https://youtube.com',
title_link_download: true,
image_url: 'http://res.guggy.com/logo_128.png',
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
video_url: 'http://www.w3schools.com/tags/movie.mp4',
fields: [],
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'error-message-size-exceeded');
})
.end(done);
});
it('should pass if any text parameter length does not surpasses the maximum allowed size', (done) => {
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample',
emoji: ':smirk:',
attachments: [
{
color: '#ff0000',
text: 'Sample',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 'https://google.com',
collapsed: false,
author_name: 'Bradley Hilton',
author_link: 'https://rocket.chat/',
author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
title: 'Attachment Example',
title_link: 'https://youtube.com',
title_link_download: true,
image_url: 'http://res.guggy.com/logo_128.png',
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
video_url: 'http://www.w3schools.com/tags/movie.mp4',
fields: [
{
short: true,
title: 'Test',
value: 'Testing out something or other',
},
{
short: true,
title: 'Another Test',
value: '[Link](https://google.com/) something and this and that.',
},
],
},
],
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', 'Sample');
})
.end(done);
});
});
});
describe('/chat.getMessage', () => {
it('should retrieve the message successfully', (done) => {
void request
.get(api('chat.getMessage'))
.set(credentials)
.query({
msgId: message._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('message._id', message._id);
})
.end(done);
});
});
describe('/chat.sendMessage', () => {
it("should throw an error when the required param 'rid' is not sent", (done) => {
void request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
},
})
.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 'rid' property on the message object is missing.");
})
.end(done);
});
describe('should throw an error when the sensitive properties contain malicious XSS values', () => {
it('attachment.message_link', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
alias: 'Gruggy',
text: 'Sample message',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 'javascript:alert("xss")',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.author_link', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
author_link: 'javascript:alert("xss")',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.title_link', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
title: 'Attachment Example',
title_link: 'javascript:alert("xss")',
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
it('attachment.action.url', () =>
void request
.post(api('chat.postMessage'))
.set(credentials)
.send({
channel: testChannel.name,
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
title: 'Attachment Example',
title_link: 'https://youtube.com',
actions: [
{
type: 'button',
text: 'Text',
url: 'javascript:alert("xss")',
},
],
},
],
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
}));
});
it('should throw an error when it has some properties with the wrong type(attachments.title_link_download, attachments.fields, message_link)', (done) => {
void request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
channel: testChannel.name,
text: 'Sample message',
alias: 'Gruggy',
emoji: ':smirk:',
avatar: 'http://res.guggy.com/logo_128.png',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 12,
collapsed: false,
author_name: 'Bradley Hilton',
author_link: 'https://rocket.chat/',
author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
title: 'Attachment Example',
title_link: 'https://youtube.com',
title_link_download: 'https://youtube.com',
image_url: 'http://res.guggy.com/logo_128.png',
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
video_url: 'http://www.w3schools.com/tags/movie.mp4',
fields: '',
},
],
},
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
})
.end(done);
});
it('should send a message successfully', (done) => {
message._id = `id-${Date.now()}`;
void request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
_id: message._id,
rid: testChannel._id,
msg: 'Sample message',
emoji: ':smirk:',
attachments: [
{
color: '#ff0000',
text: 'Yay for gruggy!',
ts: '2016-12-09T16:53:06.761Z',
thumb_url: 'http://res.guggy.com/logo_128.png',
message_link: 'https://google.com',
collapsed: false,
author_name: 'Bradley Hilton',
author_link: 'https://rocket.chat/',
author_icon: 'https://avatars.githubusercontent.com/u/850391?v=3',
title: 'Attachment Example',
title_link: 'https://youtube.com',
title_link_download: true,
image_url: 'http://res.guggy.com/logo_128.png',
audio_url: 'http://www.w3schools.com/tags/horse.mp3',
video_url: 'http://www.w3schools.com/tags/movie.mp4',
fields: [
{
short: true,
title: 'Test',
value: 'Testing out something or other',
},
{
short: true,
title: 'Another Test',
value: '[Link](https://google.com/) something and this and that.',
},
],
},
],
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', 'Sample message');
})
.end(done);
});
describe('Bad words filter', () => {
before(() =>
Promise.all([updateSetting('Message_AllowBadWordsFilter', true), updateSetting('Message_BadWordsFilterList', 'badword,badword2')]),
);
after(() => Promise.all([updateSetting('Message_AllowBadWordsFilter', false), updateSetting('Message_BadWordsFilterList', '')]));
it('should censor bad words on send', async () => {
const badMessage = {
_id: Random.id(),
rid: testChannel._id,
msg: 'This message has badword badword2',
};
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({ message: badMessage })
.expect(200)
.expect('Content-Type', 'application/json')
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('message');
const { message } = res.body;
expect(message).to.have.property('msg', 'This message has ******* ********');
expect(message).to.have.property('md').to.be.an('array').that.has.lengthOf(1);
const para = message.md[0];
expect(para).to.have.property('value').to.be.an('array').that.has.lengthOf(1);
const text = para.value[0];
expect(text).to.have.property('value', 'This message has ******* ********');
});
});
});
describe('oembed', () => {
let ytEmbedMsgId: IMessage['_id'];
let imgUrlMsgId: IMessage['_id'];
before(() => Promise.all([updateSetting('API_EmbedIgnoredHosts', ''), updateSetting('API_EmbedSafePorts', '80, 443, 3000')]));
after(() =>
Promise.all([
updateSetting('API_EmbedIgnoredHosts', 'localhost, 127.0.0.1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16'),
updateSetting('API_EmbedSafePorts', '80, 443'),
]),
);
before(async () => {
const ytEmbedMsgPayload = {
_id: `id-${Date.now()}`,
rid: testChannel._id,
msg: 'https://www.youtube.com/watch?v=T2v29gK8fP4',
emoji: ':smirk:',
};
const ytPostResponse = await request.post(api('chat.sendMessage')).set(credentials).send({ message: ytEmbedMsgPayload });
ytEmbedMsgId = ytPostResponse.body.message._id;
});
before(async () => {
const imgUrlMsgPayload = {
_id: `id-${Date.now()}1`,
rid: testChannel._id,
msg: 'http://localhost:3000/images/logo/logo.png',
emoji: ':smirk:',
};
const imgUrlResponse = await request.post(api('chat.sendMessage')).set(credentials).send({ message: imgUrlMsgPayload });
imgUrlMsgId = imgUrlResponse.body.message._id;
});
it('should have an iframe oembed with style max-width', (done) => {
setTimeout(() => {
void request
.get(api('chat.getMessage'))
.set(credentials)
.query({
msgId: ytEmbedMsgId,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('message').to.have.property('urls').to.be.an('array').that.is.not.empty;
expect(res.body.message.urls[0])
.to.have.property('meta')
.to.have.property('oembedHtml')
.to.have.string('<iframe style="max-width: 100%;width:400px;height:225px"');
})
.end(done);
}, 1000);
});
it('should embed an image preview if message has an image url', (done) => {
setTimeout(() => {
void request
.get(api('chat.getMessage'))
.set(credentials)
.query({
msgId: imgUrlMsgId,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('message').to.have.property('urls').to.be.an('array').that.is.not.empty;
expect(res.body.message.urls[0]).to.have.property('headers').to.have.property('contentType', 'image/png');
})
.end(done);
}, 200);
});
it('should not generate previews if an empty array of URL to preview is provided', async () => {
let msgId;
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: testChannel._id,
msg: 'https://www.youtube.com/watch?v=T2v29gK8fP4',
},
previewUrls: [],
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('message').to.have.property('urls').to.be.an('array').that.is.not.empty;
expect(res.body.message.urls[0]).to.have.property('ignoreParse', true);
msgId = res.body.message._id;
});
await request
.get(api('chat.getMessage'))
.set(credentials)
.query({
msgId,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('message').to.have.property('urls').to.be.an('array').that.has.lengthOf(1);
expect(res.body.message.urls[0]).to.have.property('meta').to.deep.equals({});
});
});
it('should generate previews of chosen URL when the previewUrls array is provided', async () => {
let msgId;
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: testChannel._id,
msg: 'https://www.youtube.com/watch?v=T2v29gK8fP4 https://www.rocket.chat/',
},
previewUrls: ['https://www.rocket.chat/'],
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('message').to.have.property('urls').to.be.an('array').that.has.lengthOf(2);
expect(res.body.message.urls[0]).to.have.property('ignoreParse', true);
expect(res.body.message.urls[1]).to.not.have.property('ignoreParse');
msgId = res.body.message._id;
});
await request
.get(api('chat.getMessage'))
.set(credentials)
.query({
msgId,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('message').to.have.property('urls').to.be.an('array').that.has.lengthOf(2);
expect(res.body.message.urls[0]).to.have.property('meta').that.is.an('object').that.is.empty;
expect(res.body.message.urls[1]).to.have.property('meta').that.is.an('object').that.is.not.empty;
});
});
it('should not generate previews if the message contains more than five external URL', async () => {
let msgId;
const urls = [
'https://www.youtube.com/watch?v=no050HN4ojo',
'https://www.youtube.com/watch?v=9iaSd13mqXA',
'https://www.youtube.com/watch?v=MW_qsbgt1KQ',
'https://www.youtube.com/watch?v=hLF1XwH5rd4',
'https://www.youtube.com/watch?v=Eo-F9hRBbTk',
'https://www.youtube.com/watch?v=08ms3W7adFI',
];
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: testChannel._id,
msg: urls.join(' '),
},
previewUrls: urls,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('message').to.have.property('urls').to.be.an('array').that.has.lengthOf(urls.length);
msgId = res.body.message._id;
});
await request
.get(api('chat.getMessage'))
.set(credentials)
.query({
msgId,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('message').to.have.property('urls').to.be.an('array').that.has.lengthOf(urls.length);
(res.body.message as IMessage).urls?.forEach((url) => {
expect(url).to.not.have.property('ignoreParse');
expect(url).to.have.property('meta').that.is.an('object').that.is.empty;
});
});
});
});
describe('Read only channel', () => {
let readOnlyChannel: IRoom;
let userCredentials: Credentials;
let user: TestUser<IUser>;
before(async () => {
user = await createUser();
userCredentials = await login(user.username, password);
});
after(async () =>
Promise.all([
deleteRoom({ type: 'c', roomId: readOnlyChannel._id }),
deleteUser(user),
updatePermission('post-readonly', ['admin', 'owner', 'moderator']),
]),
);
it('Creating a read-only channel', (done) => {
void request
.post(api('channels.create'))
.set(credentials)
.send({
name: `readonlychannel${+new Date()}`,
readOnly: true,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
readOnlyChannel = res.body.channel;
})
.end(done);
});
it('should send a message when the user is the owner of a readonly channel', (done) => {
void request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: readOnlyChannel._id,
msg: 'Sample message',
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('message').and.to.be.an('object');
})
.end(done);
});
it('Inviting regular user to read-only channel', (done) => {
void request
.post(api('channels.invite'))
.set(credentials)
.send({
roomId: readOnlyChannel._id,
userId: user._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(() => {
done();
});
});
it('should fail to send message when the user lacks permission', (done) => {
void request
.post(api('chat.sendMessage'))
.set(userCredentials)
.send({
message: {
rid: readOnlyChannel._id,
msg: 'Sample blocked message',
},
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
})
.end(done);
});
it('should send a message when the user has permission to send messages on readonly channels', async () => {
await updatePermission('post-readonly', ['user']);
await request
.post(api('chat.sendMessage'))
.set(userCredentials)
.send({
message: {
rid: readOnlyChannel._id,
msg: 'Sample message overwriting readonly status',
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('message').and.to.be.an('object');
});
});
});
it('should fail if user does not have the message-impersonate permission and tries to send message with alias param', (done) => {
void request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: testChannel._id,
msg: 'Sample message',
alias: 'Gruggy',
},
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Not enough permission');
})
.end(done);
});
it('should fail if user does not have the message-impersonate permission and tries to send message with avatar param', (done) => {
void request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: testChannel._id,
msg: 'Sample message',
avatar: 'http://site.com/logo.png',
},
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Not enough permission');
})
.end(done);
});
it('should fail if message is a system message', () => {
const msgId = Random.id();
return request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
_id: msgId,
rid: 'GENERAL',
msg: 'xss',
t: 'subscription-role-added',
role: '<h1>XSS<iframe srcdoc=\'<script src="/file-upload/664b3f90c4d3e60470c5e34a/js.js"></script>\'></iframe>',
},
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
});
});
describe('customFields', () => {
async function testMessageSending({
customFields,
testCb,
statusCode,
}: {
customFields?: Record<string, unknown>;
testCb: (res: Response) => any;
statusCode: number;
}) {
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: testChannel._id,
msg: 'Sample message',
customFields,
},
})
.expect('Content-Type', 'application/json')
.expect(statusCode)
.expect(testCb);
await request
.post(api('chat.postMessage'))
.set(credentials)
.send({
roomId: testChannel._id,
text: 'Sample message',
customFields,
})
.expect('Content-Type', 'application/json')
.expect(statusCode)
.expect(testCb);
}
describe('when disabled', () => {
it('should not allow sending custom fields', async () => {
await testMessageSending({
customFields: {
field1: 'value1',
},
statusCode: 400,
testCb: (res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Custom fields not enabled');
},
});
});
it('should not allow update custom fields', async () => {
const res = await sendSimpleMessage({ roomId: testChannel._id });
const msgId = res.body.message._id;
await request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId,
text: 'Sample message Updated',
customFields: {
field1: 'value1',
},
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Custom fields not enabled');
});
});
});
describe('when enabled', () => {
before(async () => {
await updateSetting('Message_CustomFields_Enabled', true);
await updateSetting(
'Message_CustomFields',
JSON.stringify({
properties: {
priority: {
type: 'string',
nullable: false,
enum: ['low', 'medium', 'high'],
},
},
required: ['priority'],
}),
);
});
after(async () => {
await updateSetting('Message_CustomFields_Enabled', false);
});
it('should allow not sending custom fields', async () => {
await testMessageSending({
statusCode: 200,
testCb: (res) => {
expect(res.body).to.have.property('success', true);
},
});
});
it('should not allow sending empty custom fields', async () => {
await testMessageSending({
customFields: {},
statusCode: 400,
testCb: (res) => {
expect(res.body).to.have.property('success', false);
},
});
});
it('should not allow sending wrong custom fields', async () => {
await testMessageSending({
customFields: {
field1: 'value1',
},
statusCode: 400,
testCb: (res) => {
expect(res.body).to.have.property('success', false);
},
});
});
it('should allow sending correct custom fields', async () => {
await testMessageSending({
customFields: {
priority: 'low',
},
statusCode: 200,
testCb: (res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.message).to.have.property('customFields').to.deep.equal({ priority: 'low' });
},
});
});
it('should allow not sending custom fields on update', async () => {
const res = await sendSimpleMessage({ roomId: testChannel._id });
const msgId = res.body.message._id;
await request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId,
text: 'Sample message Updated',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
});
});
it('should not allow update empty custom fields', async () => {
const res = await sendSimpleMessage({ roomId: testChannel._id });
const msgId = res.body.message._id;
await request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId,
text: 'Sample message Updated',
customFields: {},
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
});
});
it('should not allow update wrong custom fields', async () => {
const res = await sendSimpleMessage({ roomId: testChannel._id });
const msgId = res.body.message._id;
await request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId,
text: 'Sample message Updated',
customFields: {
field1: 'value1',
},
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
});
});
it('should allow update correct custom fields', async () => {
const res = await sendSimpleMessage({ roomId: testChannel._id });
const msgId = res.body.message._id;
await request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId,
text: 'Sample message Updated',
customFields: {
priority: 'low',
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.message).to.have.property('customFields').to.deep.equal({ priority: 'low' });
});
});
});
});
});
describe('/chat.update', () => {
const siteUrl = process.env.SITE_URL || process.env.TEST_API_URL || 'http://localhost:3000';
let simpleMessageId: IMessage['_id'];
before('should send simple message in room', async () => {
await updateSetting('Message_CustomFields_Enabled', true);
await updateSetting('Message_CustomFields', JSON.stringify({ properties: { test: { type: 'string' } } }));
const res = await sendSimpleMessage({ roomId: 'GENERAL' });
simpleMessageId = res.body.message._id;
});
after(async () => {
await updateSetting('Message_CustomFields_Enabled', false);
await updateSetting('Message_CustomFields', '');
});
it('should fail updating a message if no room id is provided', () => {
return request
.post(api('chat.update'))
.set(credentials)
.send({
msgId: message._id,
text: 'This message was edited via API',
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'invalid-params');
});
});
it('should fail updating a message if no message id is provided', () => {
return request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
text: 'This message was edited via API',
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'invalid-params');
});
});
it('should fail updating a message if no text is provided', () => {
return request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: message._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', 'invalid-params');
});
});
it('should fail updating a message if an invalid message id is provided', () => {
return request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: 'invalid-id',
text: 'This message was edited via API',
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'No message found with the id of "invalid-id".');
});
});
it('should fail updating a message if it is not in the provided room', () => {
return request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: 'invalid-room',
msgId: message._id,
text: 'This message was edited via API',
})
.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 room id provided does not match where the message is from.');
});
});
it('should fail updating a message with "content" if it is not encrypted', (done) => {
void request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: message._id,
content: {
algorithm: 'rc.v1.aes-sha2',
ciphertext: 'U2FsdGVkX1+u3j0u2+oXg4o3kw5y4t7D9sdfsdff==',
},
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'Only encrypted messages can have content updated.');
})
.end(done);
});
it('should update a message successfully', (done) => {
void request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: message._id,
text: 'This message was edited via API',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', 'This message was edited via API');
})
.end(done);
});
it('should add quote attachments to a message', async () => {
const quotedMsgLink = `${siteUrl}/channel/general?msg=${message._id}`;
void request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: message._id,
text: `Testing quotes ${quotedMsgLink}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', `Testing quotes ${quotedMsgLink}`);
expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(1);
expect(res.body.message.attachments[0]).to.have.property('message_link', quotedMsgLink);
});
});
it('should replace a quote attachment in a message', async () => {
const quotedMsgLink = `${siteUrl}/channel/general?msg=${simpleMessageId}`;
void request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: message._id,
text: `Testing quotes ${quotedMsgLink}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', `Testing quotes ${quotedMsgLink}`);
expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(1);
expect(res.body.message.attachments[0]).to.have.property('message_link', quotedMsgLink);
});
});
it('should add multiple quote attachments in a single message', async () => {
const quotedMsgLink = `${siteUrl}/channel/general?msg=${simpleMessageId}`;
const newQuotedMsgLink = `${siteUrl}/channel/general?msg=${message._id}`;
void request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: message._id,
text: `${newQuotedMsgLink} Testing quotes ${quotedMsgLink}`,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', `Testing quotes ${quotedMsgLink}`);
expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(2);
expect(res.body.message.attachments[0]).to.have.property('message_link', newQuotedMsgLink);
expect(res.body.message.attachments[1]).to.have.property('message_link', quotedMsgLink);
});
});
it('should erase old quote attachments when updating a message', async () => {
await request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: message._id,
text: 'This message was edited via API',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', 'This message was edited via API');
expect(res.body.message).to.have.property('attachments').that.is.an('array').that.has.lengthOf(0);
});
});
it('should do nothing if the message text hasnt changed and theres no custom fields', async () => {
await request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: message._id,
text: 'This message was edited via API',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', 'This message was edited via API');
expect(res.body).to.not.have.nested.property('message.customFields');
});
});
it('should update message custom fields along with msg', async () => {
await request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: message._id,
text: 'This message was edited via API 2',
customFields: { test: 'test' },
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', 'This message was edited via API 2');
expect(res.body.message).to.have.property('customFields').that.is.an('object').that.deep.equals({ test: 'test' });
});
});
it('should update message custom fields without changes to msg', async () => {
await request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: message._id,
text: 'This message was edited via API 2',
customFields: { test: 'test 2' },
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.nested.property('message.msg', 'This message was edited via API 2');
expect(res.body.message).to.have.property('customFields').that.is.an('object').that.deep.equals({ test: 'test 2' });
});
});
describe('Bad words filter', () => {
before(() =>
Promise.all([updateSetting('Message_AllowBadWordsFilter', true), updateSetting('Message_BadWordsFilterList', 'badword,badword2')]),
);
after(() => Promise.all([updateSetting('Message_AllowBadWordsFilter', false), updateSetting('Message_BadWordsFilterList', '')]));
it('should censor bad words on update', async () => {
await request
.post(api('chat.update'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: message._id,
text: 'This message has badword badword2',
})
.expect(200)
.expect('Content-Type', 'application/json')
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('message');
const { message } = res.body;
expect(message).to.have.property('msg', 'This message has ******* ********');
expect(message).to.have.property('md').to.be.an('array').that.has.lengthOf(1);
const para = message.md[0];
expect(para).to.have.property('value').to.be.an('array').that.has.lengthOf(1);
const text = para.value[0];
expect(text).to.have.property('value', 'This message has ******* ********');
});
});
});
});
describe('[/chat.delete]', () => {
let msgId: IMessage['_id'];
let user: TestUser<IUser>;
let userCredentials: Credentials;
before(async () => {
user = await createUser();
userCredentials = await login(user.username, password);
});
after(() => deleteUser(user));
beforeEach((done) => {
void request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: testChannel._id,
msg: 'Sample message',
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
msgId = res.body.message._id;
})
.end(done);
});
it('should fail deleting a message if no message id is provided', async () => {
return request
.post(api('chat.delete'))
.set(credentials)
.send({
roomId: testChannel._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', 'invalid-params');
});
});
it('should fail deleting a message if no room id is provided', async () => {
return request
.post(api('chat.delete'))
.set(credentials)
.send({
msgId,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'invalid-params');
});
});
it('should fail deleting a message if it is not in the provided room', async () => {
return request
.post(api('chat.delete'))
.set(credentials)
.send({
roomId: 'invalid-room',
msgId,
})
.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 room id provided does not match where the message is from.');
});
});
it('should fail deleting a message if an invalid id is provided', async () => {
return request
.post(api('chat.delete'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId: 'invalid-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', `No message found with the id of "invalid-id".`);
});
});
it('should delete a message successfully', (done) => {
void request
.post(api('chat.delete'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
it('sending message as another user...', (done) => {
void request
.post(api('chat.sendMessage'))
.set(userCredentials)
.send({
message: {
rid: testChannel._id,
msg: 'Sample message',
},
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
msgId = res.body.message._id;
})
.end(done);
});
it('should delete a message successfully when the user deletes a message send by another user', (done) => {
void request
.post(api('chat.delete'))
.set(credentials)
.send({
roomId: testChannel._id,
msgId,
asUser: true,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
describe('when deleting a thread message', () => {
let otherUser: TestUser<IUser>;
let otherUserCredentials: Credentials;
let parentThreadId: IMessage['_id'];
before(async () => {
const username = `user${+new Date()}`;
otherUser = await createUser({ username });
otherUserCredentials = await login(otherUser.username, password);
parentThreadId = (await sendSimpleMessage({ roomId: testChannel._id })).body.message._id;
await addUserToRoom({ rid: testChannel._id, usernames: [otherUser.username] });
});
after(() => Promise.all([deleteUser(otherUser), deleteMessage({ msgId: parentThreadId, roomId: testChannel._id })]));
const expectNoUnreadThreadMessages = (s: ISubscription) => {
expect(s).to.have.property('tunread');
expect(s.tunread).to.be.an('array');
expect(s.tunread).to.deep.equal([]);
};
it('should reset the unread counter if the message was removed', async () => {
const { body } = await sendSimpleMessage({ roomId: testChannel._id, tmid: parentThreadId, userCredentials: otherUserCredentials });
const childrenMessageId = body.message._id;
await followMessage({ msgId: parentThreadId, requestCredentials: otherUserCredentials });
await deleteMessage({ msgId: childrenMessageId, roomId: testChannel._id });
const userWhoCreatedTheThreadSubscription = await getSubscriptionByRoomId(testChannel._id);
expectNoUnreadThreadMessages(userWhoCreatedTheThreadSubscription);
});
it('should reset the unread counter of users who followed the thread', async () => {
const { body } = await sendSimpleMessage({ roomId: testChannel._id, tmid: parentThreadId });
const childrenMessageId = body.message._id;
await followMessage({ msgId: parentThreadId, requestCredentials: otherUserCredentials });
await deleteMessage({ msgId: childrenMessageId, roomId: testChannel._id });
const userWhoWasFollowingTheThreadSubscription = await getSubscriptionByRoomId(testChannel._id, otherUserCredentials);
expectNoUnreadThreadMessages(userWhoWasFollowingTheThreadSubscription);
});
});
});
describe('/chat.search', () => {
before(async () => {
const sendMessage = (text: string) =>
request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: testChannel._id,
msg: text,
},
});
await sendMessage('msg1');
await sendMessage('msg1');
await sendMessage('msg1');
await sendMessage('msg1');
await sendMessage('msg1');
});
it('should return a list of messages when execute successfully', (done) => {
void request
.get(api('chat.search'))
.set(credentials)
.query({
roomId: testChannel._id,
searchText: 'msg1',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages');
})
.end(done);
});
it('should return a list of messages(length=1) when is provided "count" query parameter execute successfully', (done) => {
void request
.get(api('chat.search'))
.set(credentials)
.query({
roomId: testChannel._id,
searchText: 'msg1',
count: 1,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages');
expect(res.body.messages.length).to.equal(1);
})
.end(done);
});
it('should return a list of messages(length=3) when is provided "count" and "offset" query parameters are executed successfully', (done) => {
void request
.get(api('chat.search'))
.set(credentials)
.query({
roomId: testChannel._id,
searchText: 'msg1',
offset: 1,
count: 3,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages');
expect(res.body.messages.length).to.equal(3);
})
.end(done);
});
it('should return a empty list of messages when is provided a huge offset value', (done) => {
void request
.get(api('chat.search'))
.set(credentials)
.query({
roomId: testChannel._id,
searchText: 'msg1',
offset: 9999,
count: 3,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('messages');
expect(res.body.messages.length).to.equal(0);
})
.end(done);
});
});
describe('[/chat.react]', () => {
it("should return statusCode: 200 and success when try unreact a message that's no reacted yet", (done) => {
void request
.post(api('chat.react'))
.set(credentials)
.send({
emoji: ':squid:',
messageId: message._id,
shouldReact: false,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
it('should react a message successfully', (done) => {
void request
.post(api('chat.react'))
.set(credentials)
.send({
emoji: 'smile',
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
it('should return statusCode: 200 when the emoji is valid', (done) => {
void request
.post(api('chat.react'))
.set(credentials)
.send({
emoji: ':squid:',
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
it("should return statusCode: 200 and success when try react a message that's already reacted", (done) => {
void request
.post(api('chat.react'))
.set(credentials)
.send({
emoji: ':squid:',
messageId: message._id,
shouldReact: true,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
it('should return statusCode: 200 when unreact a message with flag, shouldReact: false', (done) => {
void request
.post(api('chat.react'))
.set(credentials)
.send({
emoji: ':squid:',
messageId: message._id,
shouldReact: false,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
it('should return statusCode: 200 when react a message with flag, shouldReact: true', (done) => {
void request
.post(api('chat.react'))
.set(credentials)
.send({
emoji: ':squid:',
messageId: message._id,
shouldReact: true,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
it('should return statusCode: 200 when the emoji is valid and has no colons', (done) => {
void request
.post(api('chat.react'))
.set(credentials)
.send({
emoji: 'bee',
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
it('should return statusCode: 200 for reaction property when the emoji is valid', (done) => {
void request
.post(api('chat.react'))
.set(credentials)
.send({
reaction: 'ant',
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
});
describe('[/chat.getMessageReadReceipts]', () => {
const isEnterprise = typeof process.env.IS_EE === 'string' ? process.env.IS_EE === 'true' : !!process.env.IS_EE;
describe('when execute successfully', () => {
it('should return statusCode: 200 and an array of receipts when running EE', function (done) {
if (!isEnterprise) {
this.skip();
}
void request
.get(api(`chat.getMessageReadReceipts`))
.set(credentials)
.query({
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('receipts').and.to.be.an('array');
expect(res.body).to.have.property('success', true);
})
.end(done);
});
});
describe('when an error occurs', () => {
it('should throw error-action-not-allowed error when not running EE', function (done) {
// 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 (isEnterprise) {
this.skip();
}
void request
.get(api(`chat.getMessageReadReceipts`))
.set(credentials)
.query({
messageId: message._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', 'This is an enterprise feature [error-action-not-allowed]');
expect(res.body).to.have.property('errorType', 'error-action-not-allowed');
})
.end(done);
});
it('should return statusCode: 400 and an error when no messageId is provided', function (done) {
if (!isEnterprise) {
this.skip();
}
void request
.get(api('chat.getMessageReadReceipts'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).not.have.property('receipts');
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
})
.end(done);
});
});
});
describe('[/chat.reportMessage]', () => {
describe('when execute successfully', () => {
it('should return the statusCode 200', (done) => {
void request
.post(api('chat.reportMessage'))
.set(credentials)
.send({
messageId: message._id,
description: 'test',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
});
describe('when an error occurs', () => {
it('should return statusCode 400 and an error', (done) => {
void request
.post(api('chat.reportMessage'))
.set(credentials)
.send({
messageId: message._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');
})
.end(done);
});
});
});
describe('[/chat.getDeletedMessages]', () => {
let roomId: IRoom['_id'];
before(async () => {
roomId = (
await createRoom({
type: 'c',
name: `channel.test.${Date.now()}`,
})
).body.channel._id;
const msgId = (await sendSimpleMessage({ roomId })).body.message._id;
await deleteMessage({ roomId, msgId });
});
after(() => deleteRoom({ type: 'c', roomId }));
describe('when execute successfully', () => {
it('should return a list of deleted messages', (done) => {
void request
.get(api('chat.getDeletedMessages'))
.set(credentials)
.query({
roomId,
since: new Date('20 December 2018 17:51 UTC').toISOString(),
})
.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.length).to.be.equal(1);
})
.end(done);
});
it('should return a list of deleted messages when the user sets count query parameter', (done) => {
void request
.get(api('chat.getDeletedMessages'))
.set(credentials)
.query({
roomId,
since: new Date('20 December 2018 17:51 UTC').toISOString(),
count: 1,
})
.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.length).to.be.equal(1);
})
.end(done);
});
it('should return a list of deleted messages when the user sets count and offset query parameters', (done) => {
void request
.get(api('chat.getDeletedMessages'))
.set(credentials)
.query({
roomId,
since: new Date('20 December 2018 17:51 UTC').toISOString(),
count: 1,
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');
expect(res.body.messages.length).to.be.equal(1);
})
.end(done);
});
});
describe('when an error occurs', () => {
it('should return statusCode 400', (done) => {
void request
.get(api('chat.getDeletedMessages'))
.set(credentials)
.query({
since: new Date('20 December 2018 17:51 UTC').toISOString(),
count: 1,
offset: 0,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
})
.end(done);
});
it('should return statusCode 400 and an error when "since" is not provided', (done) => {
void request
.get(api('chat.getDeletedMessages'))
.set(credentials)
.query({
roomId,
count: 1,
offset: 0,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
})
.end(done);
});
it('should return statusCode 400 and an error when "since" is provided but it is invalid ISODate', (done) => {
void request
.get(api('chat.getDeletedMessages'))
.set(credentials)
.query({
roomId,
since: 'InvalidaDate',
count: 1,
offset: 0,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
})
.end(done);
});
});
});
describe('[/chat.pinMessage]', () => {
after(() =>
Promise.all([updateSetting('Message_AllowPinning', true), updatePermission('pin-message', ['owner', 'moderator', 'admin'])]),
);
it('should return an error when pinMessage is not allowed in this server', (done) => {
void updateSetting('Message_AllowPinning', false).then(() => {
void request
.post(api('chat.pinMessage'))
.set(credentials)
.send({
messageId: message._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');
})
.end(done);
});
});
it('should return an error when pinMessage is allowed in server but user dont have permission', (done) => {
void updateSetting('Message_AllowPinning', true).then(() => {
void updatePermission('pin-message', []).then(() => {
void request
.post(api('chat.pinMessage'))
.set(credentials)
.send({
messageId: message._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');
})
.end(done);
});
});
});
it('should return an error when messageId does not exist', async () => {
await request
.post(api('chat.pinMessage'))
.set(credentials)
.send({
messageId: 'test',
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error');
});
});
it('should pin Message successfully', (done) => {
void updatePermission('pin-message', ['admin']).then(() => {
void request
.post(api('chat.pinMessage'))
.set(credentials)
.send({
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.not.have.property('error');
})
.end(done);
});
});
it('should return message when its already pinned', async () => {
await request
.post(api('chat.pinMessage'))
.set(credentials)
.send({
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.not.have.property('error');
});
});
});
describe('[/chat.unPinMessage]', () => {
after(() =>
Promise.all([updateSetting('Message_AllowPinning', true), updatePermission('pin-message', ['owner', 'moderator', 'admin'])]),
);
it('should return an error when pinMessage is not allowed in this server', (done) => {
void updateSetting('Message_AllowPinning', false).then(() => {
void request
.post(api('chat.unPinMessage'))
.set(credentials)
.send({
messageId: message._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');
})
.end(done);
});
});
it('should return an error when pinMessage is allowed in server but users dont have permission', (done) => {
void updateSetting('Message_AllowPinning', true).then(() => {
void updatePermission('pin-message', []).then(() => {
void request
.post(api('chat.unPinMessage'))
.set(credentials)
.send({
messageId: message._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');
})
.end(done);
});
});
});
it('should unpin Message successfully', (done) => {
void updatePermission('pin-message', ['admin']).then(() => {
void request
.post(api('chat.unPinMessage'))
.set(credentials)
.send({
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.not.have.property('error');
})
.end(done);
});
});
});
describe('[/chat.unStarMessage]', () => {
after(() => updateSetting('Message_AllowStarring', true));
it('should return an error when starMessage is not allowed in this server', (done) => {
void updateSetting('Message_AllowStarring', false).then(() => {
void request
.post(api('chat.unStarMessage'))
.set(credentials)
.send({
messageId: message._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');
})
.end(done);
});
});
it('should unstar Message successfully', (done) => {
void updateSetting('Message_AllowStarring', true).then(() => {
void request
.post(api('chat.unStarMessage'))
.set(credentials)
.send({
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.not.have.property('error');
})
.end(done);
});
});
});
describe('[/chat.starMessage]', () => {
after(() => updateSetting('Message_AllowStarring', true));
it('should return an error when starMessage is not allowed in this server', (done) => {
void updateSetting('Message_AllowStarring', false).then(() => {
void request
.post(api('chat.starMessage'))
.set(credentials)
.send({
messageId: message._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');
})
.end(done);
});
});
it('should star Message successfully', (done) => {
void updateSetting('Message_AllowStarring', true).then(() => {
void request
.post(api('chat.starMessage'))
.set(credentials)
.send({
messageId: message._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.not.have.property('error');
})
.end(done);
});
});
});
describe('[/chat.ignoreUser]', () => {
let user: IUser;
let testDM: IRoom & { rid: IRoom['_id'] };
before(async () => {
user = await createUser();
await request
.post(api('im.create'))
.set(credentials)
.send({
username: user.username,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
testDM = res.body.room;
});
});
after(() => Promise.all([deleteRoom({ type: 'd', roomId: testDM.rid }), deleteUser(user)]));
it('should fail if invalid roomId', (done) => {
void request
.get(api('chat.ignoreUser'))
.set(credentials)
.query({
rid: 'invalid',
userId: 'rocket.cat',
})
.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-subscription');
})
.end(() => {
done();
});
});
it('should fail if invalid userId', (done) => {
void request
.get(api('chat.ignoreUser'))
.set(credentials)
.query({
rid: testDM.rid,
userId: 'invalid',
})
.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-subscription');
})
.end(() => {
done();
});
});
it('should successfully ignore user', (done) => {
void request
.get(api('chat.ignoreUser'))
.set(credentials)
.query({
rid: testDM.rid,
userId: user._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(() => {
done();
});
});
it('should successfully unignore user', (done) => {
void request
.get(api('chat.ignoreUser'))
.set(credentials)
.query({
rid: testDM.rid,
userId: user._id,
ignore: false,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(() => {
done();
});
});
});
describe('[/chat.getPinnedMessages]', () => {
let roomId: IRoom['_id'];
before(async () => {
roomId = (
await createRoom({
type: 'c',
name: `channel.test.${Date.now()}`,
})
).body.channel._id;
const msgId = (await sendSimpleMessage({ roomId })).body.message._id;
await pinMessage({ msgId });
});
after(() => deleteRoom({ type: 'c', roomId }));
describe('when execute successfully', () => {
it('should return a list of pinned messages', (done) => {
void request
.get(api('chat.getPinnedMessages'))
.set(credentials)
.query({
roomId,
})
.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.length).to.be.equal(1);
})
.end(done);
});
it('should return a list of pinned messages when the user sets count query parameter', (done) => {
void request
.get(api('chat.getPinnedMessages'))
.set(credentials)
.query({
roomId,
count: 1,
})
.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.length).to.be.equal(1);
})
.end(done);
});
it('should return a list of pinned messages when the user sets count and offset query parameters', (done) => {
void request
.get(api('chat.getPinnedMessages'))
.set(credentials)
.query({
roomId,
count: 1,
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');
expect(res.body.messages.length).to.be.equal(1);
})
.end(done);
});
});
describe('when an error occurs', () => {
it('should return statusCode 400 and an error when "roomId" is not provided', (done) => {
void request
.get(api('chat.getPinnedMessages'))
.set(credentials)
.query({
count: 1,
offset: 0,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
})
.end(done);
});
});
});
describe('[/chat.getMentionedMessages]', () => {
let testChannel: IRoom;
before(async () => {
testChannel = (
await createRoom({
type: 'c',
name: `channel.test.${Date.now()}`,
})
).body.channel;
});
after(() => deleteRoom({ type: 'c', roomId: testChannel._id }));
it('should return an error when the required "roomId" parameter is not sent', (done) => {
void request
.get(api('chat.getMentionedMessages'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.errorType).to.be.equal('invalid-params');
})
.end(done);
});
it('should return an error when the roomId is invalid', (done) => {
void request
.get(api('chat.getMentionedMessages'))
.query({ roomId: 'invalid-room' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('error-not-allowed');
})
.end(done);
});
it('should return the mentioned messages', (done) => {
void request
.get(api('chat.getMentionedMessages'))
.query({ roomId: testChannel._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.be.an('array');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('count');
})
.end(done);
});
});
describe('[/chat.getStarredMessages]', () => {
let testChannel: IRoom;
before(async () => {
testChannel = (
await createRoom({
type: 'c',
name: `channel.test.${Date.now()}`,
})
).body.channel;
});
after(() => deleteRoom({ type: 'c', roomId: testChannel._id }));
it('should return an error when the required "roomId" parameter is not sent', (done) => {
void request
.get(api('chat.getStarredMessages'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.errorType).to.be.equal('invalid-params');
})
.end(done);
});
it('should return an error when the roomId is invalid', (done) => {
void request
.get(api('chat.getStarredMessages'))
.query({ roomId: 'invalid-room' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('error-not-allowed');
})
.end(done);
});
it('should return the starred messages', (done) => {
void request
.get(api('chat.getStarredMessages'))
.query({ roomId: testChannel._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.be.an('array');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('count');
})
.end(done);
});
});
describe('[/chat.getDiscussions]', () => {
const messageText = 'Message to create discussion';
let testChannel: IRoom;
let discussionRoom: IRoom & { rid: IRoom['_id'] };
const messageWords = [
...messageText.split(' '),
...messageText.toUpperCase().split(' '),
...messageText.toLowerCase().split(' '),
messageText,
messageText.charAt(0),
' ',
];
before(async () => {
testChannel = (await createRoom({ type: 'c', name: `channel.test.getDiscussions.${Date.now()}` })).body.channel;
discussionRoom = (
await request
.post(api('rooms.createDiscussion'))
.set(credentials)
.send({
prid: testChannel._id,
t_name: 'Message to create discussion',
})
.expect('Content-Type', 'application/json')
.expect(200)
).body.discussion;
});
after(() => deleteRoom({ type: 'c', roomId: testChannel._id }));
it('should return an error when the required "roomId" parameter is not sent', (done) => {
void request
.get(api('chat.getDiscussions'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
})
.end(done);
});
it('should return an error when the roomId is invalid', (done) => {
void request
.get(api('chat.getDiscussions'))
.query({ roomId: 'invalid-room' })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.error).to.be.equal('error-not-allowed');
})
.end(done);
});
it('should return the discussions of a room', (done) => {
void request
.get(api('chat.getDiscussions'))
.query({ roomId: testChannel._id })
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.messages).to.be.an('array');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('count');
})
.end(done);
});
it('should return the discussions of a room even requested with count and offset params', (done) => {
void request
.get(api('chat.getDiscussions'))
.set(credentials)
.query({
roomId: testChannel._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.messages).to.be.an('array');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('count');
})
.end(done);
});
function filterDiscussionsByText(text: string) {
it(`should return the room's discussion list filtered by the text '${text}'`, (done) => {
void request
.get(api('chat.getDiscussions'))
.set(credentials)
.query({
roomId: testChannel._id,
text,
})
.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).to.have.property('total');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('count');
expect(res.body.messages).to.have.lengthOf(1);
expect(res.body.messages[0].drid).to.be.equal(discussionRoom.rid);
})
.end(done);
});
it(`should return the room's discussion list filtered by the text '${text}' even requested with count and offset params`, (done) => {
void request
.get(api('chat.getDiscussions'))
.set(credentials)
.query({
roomId: testChannel._id,
text,
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');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('count');
expect(res.body.messages).to.have.lengthOf(1);
expect(res.body.messages[0].drid).to.be.equal(discussionRoom.rid);
})
.end(done);
});
}
messageWords.forEach((text) => {
filterDiscussionsByText(text);
});
});
describe('[/chat.syncMessages]', () => {
let testChannel: IRoom;
before(async () => {
testChannel = (await createRoom({ type: 'c', name: `channel.test.syncMessages.${Date.now()}` })).body.channel;
});
after(() => deleteRoom({ type: 'c', roomId: testChannel._id }));
it('should return an error when the required "roomId" parameter is not sent', (done) => {
void request
.get(api('chat.syncMessages'))
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.errorType).to.be.equal('invalid-params');
expect(res.body.error).to.include(`must have required property 'roomId'`);
})
.end(done);
});
it('should return an error when the neither "lastUpdate" or "type" parameter is sent', (done) => {
void request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: testChannel._id })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.errorType).to.be.equal('error-param-required');
expect(res.body.error).to.include('The "type" or "lastUpdate" parameters must be provided');
})
.end(done);
});
it('should return an error when the "lastUpdate" parameter is invalid', (done) => {
void request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: 'invalid-room', lastUpdate: 'invalid-date' })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.errorType).to.be.equal('error-lastUpdate-param-invalid');
expect(res.body.error).to.include('The "lastUpdate" query parameter must be a valid date');
})
.end(done);
});
it('should return an error when user provides an invalid roomId', (done) => {
void request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: 'invalid-room', lastUpdate: new Date().toISOString() })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.errorType).to.be.equal('error-not-allowed');
expect(res.body.error).to.include('Not allowed');
})
.end(done);
});
it('should return an error when the "type" parameter is not supported', (done) => {
void request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: testChannel._id, type: 'invalid-type', next: new Date().toISOString() })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.errorType).to.be.equal('invalid-params');
expect(res.body.error).to.include('must be equal to one of the allowed values');
})
.end(done);
});
it('should return an error when the "next" or "previous" parameter is sent without the "type" parameter', async () => {
const nextResponse = await request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: testChannel._id, next: new Date().toISOString() });
const previousResponse = await request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: testChannel._id, previous: new Date().toISOString() });
expect(nextResponse.statusCode).to.equal(400);
expect(nextResponse.body).to.have.property('success', false);
expect(nextResponse.body.errorType).to.be.equal('error-param-required');
expect(nextResponse.body.error).to.include('The "type" or "lastUpdate" parameters must be provided');
expect(previousResponse.statusCode).to.equal(400);
expect(previousResponse.body).to.have.property('success', false);
expect(previousResponse.body.errorType).to.be.equal('error-param-required');
expect(previousResponse.body.error).to.include('The "type" or "lastUpdate" parameters must be provided');
});
it('should return an error when both "next" and "previous" are sent', (done) => {
void request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: testChannel._id, type: 'UPDATED', next: new Date().toISOString(), previous: new Date().toISOString() })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.errorType).to.be.equal('error-cursor-conflict');
expect(res.body.error).to.include('You cannot provide both "next" and "previous" parameters');
})
.end(done);
});
it('should return an error when both "next" or "previous" and "lastUpdate" are sent', (done) => {
void request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: testChannel._id, type: 'UPDATED', next: new Date().toISOString(), lastUpdate: new Date().toISOString() })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.errorType).to.be.equal('error-cursor-and-lastUpdate-conflict');
expect(res.body.error).to.include('The attributes "next", "previous" and "lastUpdate" cannot be used together');
})
.end(done);
});
it('should return an error when neither "type" or "lastUpdate" are sent', (done) => {
void request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: testChannel._id })
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.errorType).to.be.equal('error-param-required');
expect(res.body.error).to.include('The "type" or "lastUpdate" parameters must be provided');
})
.end(done);
});
it('should return an empty response when there are no messages to sync', (done) => {
void request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: testChannel._id, lastUpdate: new Date().toISOString() })
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body.result).to.have.property('updated').and.to.be.an('array');
expect(res.body.result).to.have.property('deleted').and.to.be.an('array');
expect(res.body.result.updated).to.have.lengthOf(0);
expect(res.body.result.deleted).to.have.lengthOf(0);
})
.end(done);
});
it('should return all updated and deleted messages since "lastUpdate" parameter date', async () => {
const lastUpdate = new Date().toISOString();
// Create two messages isolated to avoid ts conflict
const firstMessage = await sendSimpleMessage({ roomId: testChannel._id, text: 'First Message' });
const secondMessage = await sendSimpleMessage({ roomId: testChannel._id, text: 'Second Message' });
const response = await request.get(api('chat.syncMessages')).set(credentials).query({ roomId: testChannel._id, lastUpdate });
expect(response.body.result.updated).to.have.lengthOf(2);
expect(response.body.result.updated[0]._id).to.be.equal(secondMessage.body.message._id);
expect(response.body.result.updated[1]._id).to.be.equal(firstMessage.body.message._id);
expect(response.body.result.deleted).to.have.lengthOf(0);
await deleteMessage({ roomId: testChannel._id, msgId: firstMessage.body.message._id });
const responseAfterDelete = await request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: testChannel._id, lastUpdate });
expect(responseAfterDelete.body.result.updated).to.have.lengthOf(1);
expect(responseAfterDelete.body.result.updated[0]._id).to.be.equal(secondMessage.body.message._id);
expect(responseAfterDelete.body.result.deleted).to.have.lengthOf(1);
expect(responseAfterDelete.body.result.deleted[0]._id).to.be.equal(firstMessage.body.message._id);
await deleteMessage({ roomId: testChannel._id, msgId: secondMessage.body.message._id });
});
it('should return all updated messages with a cursor when "type" parameter is "UPDATED"', async () => {
const lastUpdate = new Date();
const firstMessage = await sendSimpleMessage({ roomId: testChannel._id, text: 'First Message' });
const secondMessage = await sendSimpleMessage({ roomId: testChannel._id, text: 'Second Message' });
const thirdMessage = await sendSimpleMessage({ roomId: testChannel._id, text: 'Third Message' });
const response = await request
.get(api('chat.syncMessages'))
.set(credentials)
.query({
roomId: testChannel._id,
next: new Date(lastUpdate).getTime().toString(),
type: 'UPDATED',
count: 2,
});
expect(response.body.result.updated).to.have.lengthOf(2);
expect(response.body.result.updated[0]._id).to.be.equal(firstMessage.body.message._id);
expect(response.body.result.updated[1]._id).to.be.equal(secondMessage.body.message._id);
expect(response.body.result.cursor)
.to.have.property('next')
.and.to.equal(new Date(response.body.result.updated[response.body.result.updated.length - 1]._updatedAt).getTime().toString());
expect(response.body.result.cursor)
.to.have.property('previous')
.and.to.equal(new Date(response.body.result.updated[0]._updatedAt).getTime().toString());
expect(response.body.result.cursor)
.to.have.property('previous')
.and.to.equal(new Date(firstMessage.body.message._updatedAt).getTime().toString());
const responseWithNext = await request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: testChannel._id, next: response.body.result.cursor.next, type: 'UPDATED', count: 2 });
expect(responseWithNext.body.result.updated).to.have.lengthOf(1);
expect(responseWithNext.body.result.updated[0]._id).to.be.equal(thirdMessage.body.message._id);
expect(responseWithNext.body.result.cursor).to.have.property('next').and.to.be.null;
expect(responseWithNext.body.result.cursor)
.to.have.property('previous')
.and.to.equal(new Date(thirdMessage.body.message._updatedAt).getTime().toString());
await Promise.all([
deleteMessage({ roomId: testChannel._id, msgId: firstMessage.body.message._id }),
deleteMessage({ roomId: testChannel._id, msgId: secondMessage.body.message._id }),
deleteMessage({ roomId: testChannel._id, msgId: thirdMessage.body.message._id }),
]);
});
it('should return all deleted messages with a cursor when "type" parameter is "DELETED"', async () => {
const newChannel = (await createRoom({ type: 'c', name: `channel.test.syncMessages.${Date.now()}` })).body.channel;
const lastUpdate = new Date();
const firstMessage = (await sendSimpleMessage({ roomId: newChannel._id, text: 'First Message' })).body.message;
const secondMessage = (await sendSimpleMessage({ roomId: newChannel._id, text: 'Second Message' })).body.message;
const thirdMessage = (await sendSimpleMessage({ roomId: newChannel._id, text: 'Third Message' })).body.message;
const response = await request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: newChannel._id, next: lastUpdate.getTime().toString(), type: 'DELETED', count: 2 });
expect(response.body.result.deleted).to.have.lengthOf(0);
expect(response.body.result.cursor).to.have.property('next').and.to.be.null;
expect(response.body.result.cursor).to.have.property('previous').and.to.be.null;
const firstDeletedMessage = (await deleteMessage({ roomId: newChannel._id, msgId: firstMessage._id })).body.message;
const secondDeletedMessage = (await deleteMessage({ roomId: newChannel._id, msgId: secondMessage._id })).body.message;
const thirdDeletedMessage = (await deleteMessage({ roomId: newChannel._id, msgId: thirdMessage._id })).body.message;
const responseAfterDelete = await request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: newChannel._id, next: lastUpdate.getTime().toString(), type: 'DELETED', count: 2 });
expect(responseAfterDelete.body.result.deleted).to.have.lengthOf(2);
expect(responseAfterDelete.body.result.deleted[0]._id).to.be.equal(firstDeletedMessage._id);
expect(responseAfterDelete.body.result.deleted[1]._id).to.be.equal(secondDeletedMessage._id);
expect(responseAfterDelete.body.result.cursor)
.to.have.property('next')
.and.to.equal(
new Date(responseAfterDelete.body.result.deleted[responseAfterDelete.body.result.deleted.length - 1]._deletedAt)
.getTime()
.toString(),
);
expect(responseAfterDelete.body.result.cursor)
.to.have.property('previous')
.and.to.equal(new Date(responseAfterDelete.body.result.deleted[0]._deletedAt).getTime().toString());
const responseAfterDeleteWithPrevious = await request
.get(api('chat.syncMessages'))
.set(credentials)
.query({ roomId: newChannel._id, next: responseAfterDelete.body.result.cursor.next, type: 'DELETED', count: 2 });
expect(responseAfterDeleteWithPrevious.body.result.deleted).to.have.lengthOf(1);
expect(responseAfterDeleteWithPrevious.body.result.deleted[0]._id).to.be.equal(thirdDeletedMessage._id);
expect(responseAfterDeleteWithPrevious.body.result.cursor).to.have.property('next').and.to.be.null;
expect(responseAfterDeleteWithPrevious.body.result.cursor)
.to.have.property('previous')
.and.to.equal(new Date(responseAfterDeleteWithPrevious.body.result.deleted[0]._deletedAt).getTime().toString());
await deleteRoom({ type: 'c', roomId: newChannel._id });
});
});
});
describe('Threads', () => {
let testThreadChannel: IRoom;
before((done) => getCredentials(done));
before(async () => {
testThreadChannel = (await createRoom({ type: 'c', name: `chat.api-test-${Date.now()}` })).body.channel;
await updatePermission('view-c-room', ['admin', 'user', 'bot', 'app', 'anonymous']);
});
after(() =>
Promise.all([
updateSetting('Threads_enabled', true),
updatePermission('view-c-room', ['admin', 'user', 'bot', 'app', 'anonymous']),
deleteRoom({ type: 'c', roomId: testThreadChannel._id }),
]),
);
describe('[/chat.getThreadsList]', () => {
const messageText = 'Message to create thread';
let testChannel: IRoom;
let threadMessage: IThreadMessage;
let user: TestUser<IUser>;
const messageWords = [
...messageText.split(' '),
...messageText.toUpperCase().split(' '),
...messageText.toLowerCase().split(' '),
messageText,
messageText.charAt(0),
' ',
];
before(async () => {
testChannel = (await createRoom({ type: 'c', name: `channel.test.threads.${Date.now()}` })).body.channel;
const { message } = (
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: testChannel._id,
msg: 'Message to create thread',
},
})
.expect('Content-Type', 'application/json')
.expect(200)
).body;
threadMessage = (
await request
.post(api('chat.sendMessage'))
.set(credentials)
.send({
message: {
rid: testChannel._id,
msg: 'Thread message',
tmid: message._id,
},
})
.expect('Content-Type', 'application/json')
.expect(200)
).body.message;
});
after(() =>
Promise.all([
updateSetting('Threads_enabled', true),
deleteRoom({ type: 'c', roomId: testChannel._id }),
updatePermission('view-c-room', ['admin', 'user', 'bot', 'app', 'anonymous']),
deleteUser(user),
]),
);
it('should return an error for chat.getThreadsList when threads are not allowed in this server', (done) => {
void updateSetting('Threads_enabled', false).then(() => {
void request
.get(api('chat.getThreadsList'))
.set(credentials)
.query({
rid: testChannel._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');
expect(res.body).to.have.property('error', 'Threads Disabled [error-not-allowed]');
})
.end(done);
});
});
it('should return an error when the user is not allowed access the room', (done) => {
void createUser().then((createdUser) => {
user = createdUser;
void login(createdUser.username, password).then((userCredentials) => {
void updateSetting('Threads_enabled', true).then(() => {
void updatePermission('view-c-room', []).then(() => {
void request
.get(api('chat.getThreadsList'))
.set(userCredentials)
.query({
rid: testChannel._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');
expect(res.body).to.have.property('error', 'Not Allowed [error-not-allowed]');
})
.end(done);
});
});
});
});
});
it("should return the room's thread list", (done) => {
void updatePermission('view-c-room', ['admin', 'user']).then(() => {
void request
.get(api('chat.getThreadsList'))
.set(credentials)
.query({
rid: testChannel._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('threads').and.to.be.an('array');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('count');
expect(res.body.threads).to.have.lengthOf(1);
expect(res.body.threads[0]._id).to.be.equal(threadMessage.tmid);
})
.end(done);
});
});
it("should fail returning a room's thread list if no roomId is provided", async () => {
await updatePermission('view-c-room', ['admin', 'user']);
return request
.get(api('chat.getThreadsList'))
.set(credentials)
.query({})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'invalid-params');
});
});
it("should fail returning a room's thread list if an invalid type is provided", async () => {
return request
.get(api('chat.getThreadsList'))
.set(credentials)
.query({
rid: testChannel._id,
type: 'invalid-type',
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'invalid-params');
});
});
it("should return the room's thread list", async () => {
return request
.get(api('chat.getThreadsList'))
.set(credentials)
.query({
rid: testChannel._id,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('threads').and.to.be.an('array');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('count');
expect(res.body.threads).to.have.lengthOf(1);
expect(res.body.threads[0]._id).to.be.equal(threadMessage.tmid);
});
});
it("should return the room's thread list even requested with count and offset params", (done) => {
void updatePermission('view-c-room', ['admin', 'user']).then(() => {
void request
.get(api('chat.getThreadsList'))
.set(credentials)
.query({
rid: testChannel._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('threads').and.to.be.an('array');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('count');
expect(res.body.threads).to.have.lengthOf(1);
expect(res.body.threads[0]._id).to.be.equal(threadMessage.tmid);
})
.end(done);
});
});
function filterThreadsByText(text: string) {
it(`should return the room's thread list filtered by the text '${text}'`, (done) => {
void request
.get(api('chat.getThreadsList'))
.set(credentials)
.query({
rid: testChannel._id,
text,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('threads').and.to.be.an('array');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('count');
expect(res.body.threads).to.have.lengthOf(1);
expect(res.body.threads[0]._id).to.be.equal(threadMessage.tmid);
})
.end(done);
});
it(`should return the room's thread list filtered by the text '${text}' even requested with count and offset params`, (done) => {
void request
.get(api('chat.getThreadsList'))
.set(credentials)
.query({
rid: testChannel._id,
text,
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('threads').and.to.be.an('array');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('count');
expect(res.body.threads).to.have.lengthOf(1);
expect(res.body.threads[0]._id).to.be.equal(threadMessage.tmid);
})
.end(done);
});
}
messageWords.forEach((text) => {
filterThreadsByText(text);
});
it('should return an empty thread list', (done) => {
void updatePermission('view-c-room', ['admin', 'user']).then(() => {
void request
.get(api('chat.getThreadsList'))
.set(credentials)
.query({
rid: testChannel._id,
text: 'missing',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('threads').and.to.be.an('array');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('count');
expect(res.body.threads).to.have.lengthOf(0);
})
.end(done);
});
});
});
describe('[/chat.syncThreadsList]', () => {
let testChannel: IRoom;
let threadMessage: IThreadMessage;
let user: TestUser<IUser>;
before(async () => {
testChannel = (await createRoom({ type: 'c', name: `.threads.sync.${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(() =>
Promise.all([
updateSetting('Threads_enabled', true),
deleteRoom({ type: 'c', roomId: testChannel._id }),
updatePermission('view-c-room', ['admin', 'user', 'bot', 'app', 'anonymous']),
deleteUser(user),
]),
);
it('should return an error for chat.getThreadsList when threads are not allowed in this server', (done) => {
void updateSetting('Threads_enabled', false).then(() => {
void request
.get(api('chat.getThreadsList'))
.set(credentials)
.query({
rid: testChannel._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');
expect(res.body).to.have.property('error', 'Threads Disabled [error-not-allowed]');
})
.end(done);
});
});
it('should return an error when the required param "rid" is missing', (done) => {
void updateSetting('Threads_enabled', true).then(() => {
void request
.get(api('chat.syncThreadsList'))
.set(credentials)
.query({})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'invalid-params');
})
.end(done);
});
});
it('should return an error when the required param "updatedSince" is missing', (done) => {
void updateSetting('Threads_enabled', true).then(() => {
void request
.get(api('chat.syncThreadsList'))
.set(credentials)
.query({
rid: testChannel._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', 'invalid-params');
})
.end(done);
});
});
it('should return an error when the param "updatedSince" is an invalid date', (done) => {
void updateSetting('Threads_enabled', true).then(() => {
void request
.get(api('chat.syncThreadsList'))
.set(credentials)
.query({
rid: testChannel._id,
updatedSince: 'invalid-date',
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'invalid-params');
})
.end(done);
});
});
it('should return an error when the user is not allowed access the room', (done) => {
void createUser().then((createdUser) => {
user = createdUser;
void login(createdUser.username, password).then((userCredentials) => {
void updatePermission('view-c-room', []).then(() => {
void request
.get(api('chat.syncThreadsList'))
.set(userCredentials)
.query({
rid: testChannel._id,
updatedSince: new Date().toISOString(),
})
.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');
expect(res.body).to.have.property('error', 'Not Allowed [error-not-allowed]');
})
.end(done);
});
});
});
});
it("should return the room's thread synced list", (done) => {
void updatePermission('view-c-room', ['admin', 'user']).then(() => {
void request
.get(api('chat.syncThreadsList'))
.set(credentials)
.query({
rid: testChannel._id,
updatedSince: new Date('2019-04-01').toISOString(),
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('threads').and.to.be.an('object');
expect(res.body.threads).to.have.property('update').and.to.be.an('array');
expect(res.body.threads).to.have.property('remove').and.to.be.an('array');
expect(res.body.threads.update).to.have.lengthOf(1);
expect(res.body.threads.remove).to.have.lengthOf(0);
expect(res.body.threads.update[0]._id).to.be.equal(threadMessage.tmid);
})
.end(done);
});
});
});
describe('[/chat.getThreadMessages]', () => {
let testChannel: IRoom;
let threadMessage: IThreadMessage;
let createdThreadMessage: IThreadMessage;
let user: TestUser<IUser>;
before(async () => {
testChannel = (await createRoom({ type: 'c', name: `channel.test.threads.${Date.now()}` })).body.channel;
createdThreadMessage = (
await sendSimpleMessage({
roomId: testChannel._id,
text: 'Message to create thread',
})
).body.message;
threadMessage = (
await sendSimpleMessage({
roomId: testChannel._id,
text: 'Thread Message',
tmid: createdThreadMessage._id,
})
).body.message;
});
after(() =>
Promise.all([
updateSetting('Threads_enabled', true),
deleteRoom({ type: 'c', roomId: testChannel._id }),
updatePermission('view-c-room', ['admin', 'user', 'bot', 'app', 'anonymous']),
deleteUser(user),
]),
);
it('should return an error for chat.getThreadMessages when threads are not allowed in this server', (done) => {
void updateSetting('Threads_enabled', false).then(() => {
void request
.get(api('chat.getThreadMessages'))
.set(credentials)
.query({
tmid: threadMessage.tmid,
})
.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');
expect(res.body).to.have.property('error', 'Threads Disabled [error-not-allowed]');
})
.end(done);
});
});
it('should return an error when the user is not allowed access the room', (done) => {
void createUser().then((createdUser) => {
user = createdUser;
void login(createdUser.username, password).then((userCredentials) => {
void updateSetting('Threads_enabled', true).then(() => {
void updatePermission('view-c-room', []).then(() => {
void request
.get(api('chat.getThreadMessages'))
.set(userCredentials)
.query({
tmid: threadMessage.tmid,
})
.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');
expect(res.body).to.have.property('error', 'Not Allowed [error-not-allowed]');
})
.end(done);
});
});
});
});
});
it("should return the thread's message list", (done) => {
void updatePermission('view-c-room', ['admin', 'user']).then(() => {
void request
.get(api('chat.getThreadMessages'))
.set(credentials)
.query({
tmid: threadMessage.tmid,
})
.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).to.have.property('total').and.to.be.equal(1);
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('count');
expect(res.body.messages).to.have.lengthOf(1);
expect(res.body.messages[0].tmid).to.be.equal(createdThreadMessage._id);
})
.end(done);
});
});
});
describe('[/chat.syncThreadMessages]', () => {
let testChannel: IRoom;
let threadMessage: IThreadMessage;
let createdThreadMessage: IThreadMessage;
let user: TestUser<IUser>;
before(async () => {
testChannel = (await createRoom({ type: 'c', name: `message.threads.${Date.now()}` })).body.channel;
createdThreadMessage = (
await sendSimpleMessage({
roomId: testChannel._id,
text: 'Message to create thread',
})
).body.message;
threadMessage = (
await sendSimpleMessage({
roomId: testChannel._id,
text: 'Thread Message',
tmid: createdThreadMessage._id,
})
).body.message;
});
after(() =>
Promise.all([
updateSetting('Threads_enabled', true),
deleteRoom({ type: 'c', roomId: testChannel._id }),
updatePermission('view-c-room', ['admin', 'user', 'bot', 'app', 'anonymous']),
deleteUser(user),
]),
);
it('should return an error for chat.syncThreadMessages when threads are not allowed in this server', (done) => {
void updateSetting('Threads_enabled', false).then(() => {
void request
.get(api('chat.syncThreadMessages'))
.set(credentials)
.query({
tmid: threadMessage.tmid,
updatedSince: new Date().toISOString(),
})
.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');
expect(res.body).to.have.property('error', 'Threads Disabled [error-not-allowed]');
})
.end(done);
});
});
it('should return an error when the required param "tmid" is missing', (done) => {
void updateSetting('Threads_enabled', true).then(() => {
void request
.get(api('chat.syncThreadMessages'))
.set(credentials)
.query({})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'invalid-params');
})
.end(done);
});
});
it('should return an error when the required param "updatedSince" is missing', (done) => {
void updateSetting('Threads_enabled', true).then(() => {
void request
.get(api('chat.syncThreadMessages'))
.set(credentials)
.query({
tmid: threadMessage.tmid,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'invalid-params');
})
.end(done);
});
});
it('should return an error when the param "updatedSince" is an invalid date', (done) => {
void updateSetting('Threads_enabled', true).then(() => {
void request
.get(api('chat.syncThreadMessages'))
.set(credentials)
.query({
tmid: threadMessage.tmid,
updatedSince: 'invalid-date',
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'invalid-params');
})
.end(done);
});
});
it('should return an error when the user is not allowed access the room', (done) => {
void createUser().then((createdUser) => {
user = createdUser;
void login(createdUser.username, password).then((userCredentials) => {
void updatePermission('view-c-room', []).then(() => {
void request
.get(api('chat.syncThreadMessages'))
.set(userCredentials)
.query({
tmid: threadMessage.tmid,
updatedSince: new Date().toISOString(),
})
.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');
expect(res.body).to.have.property('error', 'Not Allowed [error-not-allowed]');
})
.end(done);
});
});
});
});
it("should return the thread's message list", (done) => {
void updatePermission('view-c-room', ['admin', 'user']).then(() => {
void request
.get(api('chat.syncThreadMessages'))
.set(credentials)
.query({
tmid: threadMessage.tmid,
updatedSince: new Date('2019-04-01').toISOString(),
})
.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('object');
expect(res.body.messages).to.have.property('update').and.to.be.an('array');
expect(res.body.messages).to.have.property('remove').and.to.be.an('array');
expect(res.body.messages.update).to.have.lengthOf(1);
expect(res.body.messages.remove).to.have.lengthOf(0);
expect(res.body.messages.update[0].id).to.be.equal(createdThreadMessage.tmid);
})
.end(done);
});
});
});
describe('[/chat.followMessage]', () => {
let testChannel: IRoom;
let threadMessage: IThreadMessage;
let user: TestUser<IUser>;
before(async () => {
testChannel = (await createRoom({ type: 'c', name: `channel.test.threads.follow${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(() =>
Promise.all([
updateSetting('Threads_enabled', true),
deleteRoom({ type: 'c', roomId: testChannel._id }),
updatePermission('view-c-room', ['admin', 'user', 'bot', 'app', 'anonymous']),
deleteUser(user),
]),
);
it('should return an error for chat.followMessage when threads are not allowed in this server', (done) => {
void updateSetting('Threads_enabled', false).then(() => {
void request
.post(api('chat.followMessage'))
.set(credentials)
.send({
mid: threadMessage.tmid,
})
.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');
expect(res.body).to.have.property('error', 'not-allowed [error-not-allowed]');
})
.end(done);
});
});
it('should return an error when the message does not exist', (done) => {
void updateSetting('Threads_enabled', true).then(() => {
void request
.post(api('chat.followMessage'))
.set(credentials)
.send({
mid: 'invalid-message-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-invalid-message');
expect(res.body).to.have.property('error', 'Invalid message [error-invalid-message]');
})
.end(done);
});
});
it('should return an error when the user is not allowed access the room', (done) => {
void createUser().then((createdUser) => {
user = createdUser;
void login(createdUser.username, password).then((userCredentials) => {
void updatePermission('view-c-room', []).then(() => {
void request
.post(api('chat.followMessage'))
.set(userCredentials)
.send({
mid: threadMessage.tmid,
})
.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');
expect(res.body).to.have.property('error', 'not-allowed [error-not-allowed]');
})
.end(done);
});
});
});
});
it('should return success: true when it execute successfully', (done) => {
void updatePermission('view-c-room', ['admin', 'user']).then(() => {
void request
.post(api('chat.followMessage'))
.set(credentials)
.send({
mid: threadMessage.tmid,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
});
});
describe('[/chat.unfollowMessage]', () => {
let testChannel: IRoom;
let threadMessage: IThreadMessage;
let user: TestUser<IUser>;
before(async () => {
testChannel = (await createRoom({ type: 'c', name: `channel.test.threads.unfollow.${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(() =>
Promise.all([
updateSetting('Threads_enabled', true),
deleteRoom({ type: 'c', roomId: testChannel._id }),
updatePermission('view-c-room', ['admin', 'user', 'bot', 'app', 'anonymous']),
deleteUser(user),
]),
);
it('should return an error for chat.unfollowMessage when threads are not allowed in this server', (done) => {
void updateSetting('Threads_enabled', false).then(() => {
void request
.post(api('chat.unfollowMessage'))
.set(credentials)
.send({
mid: threadMessage.tmid,
})
.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');
expect(res.body).to.have.property('error', 'not-allowed [error-not-allowed]');
})
.end(done);
});
});
it('should return an error when the message does not exist', (done) => {
void updateSetting('Threads_enabled', true).then(() => {
void request
.post(api('chat.unfollowMessage'))
.set(credentials)
.send({
mid: 'invalid-message-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-invalid-message');
expect(res.body).to.have.property('error', 'Invalid message [error-invalid-message]');
})
.end(done);
});
});
it('should return an error when the user is not allowed access the room', (done) => {
void createUser().then((createdUser) => {
user = createdUser;
void login(createdUser.username, password).then((userCredentials) => {
void updatePermission('view-c-room', []).then(() => {
void request
.post(api('chat.unfollowMessage'))
.set(userCredentials)
.send({
mid: threadMessage.tmid,
})
.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');
expect(res.body).to.have.property('error', 'not-allowed [error-not-allowed]');
})
.end(done);
});
});
});
});
it('should return success: true when it execute successfully', (done) => {
void updatePermission('view-c-room', ['admin', 'user']).then(() => {
void request
.post(api('chat.unfollowMessage'))
.set(credentials)
.send({
mid: threadMessage.tmid,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
})
.end(done);
});
});
});
describe('[/chat.getURLPreview]', () => {
const url = 'https://www.youtube.com/watch?v=no050HN4ojo';
it('should return the URL preview with metadata and headers', async () => {
await request
.get(api('chat.getURLPreview'))
.set(credentials)
.query({
roomId: testThreadChannel._id,
url,
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('urlPreview').and.to.be.an('object').that.is.not.empty;
expect(res.body.urlPreview).to.have.property('url', url);
expect(res.body.urlPreview).to.have.property('headers').and.to.be.an('object').that.is.not.empty;
});
});
describe('when an error occurs', () => {
it('should return statusCode 400 and an error when "roomId" is not provided', async () => {
await request
.get(api('chat.getURLPreview'))
.set(credentials)
.query({
url,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
});
});
it('should return statusCode 400 and an error when "url" is not provided', async () => {
await request
.get(api('chat.getURLPreview'))
.set(credentials)
.query({
roomId: testThreadChannel._id,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
});
});
it('should return statusCode 400 and an error when "roomId" is provided but user is not in the room', async () => {
await request
.get(api('chat.getURLPreview'))
.set(credentials)
.query({
roomId: 'undefined',
url,
})
.expect('Content-Type', 'application/json')
.expect(400)
.expect((res) => {
expect(res.body).to.have.property('success', false);
expect(res.body.errorType).to.be.equal('error-not-allowed');
});
});
});
});
});