Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 2s
5401 lines
171 KiB
TypeScript
5401 lines
171 KiB
TypeScript
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 { retry } from './helpers/retry';
|
|
import { sleep } from '../../../lib/utils/sleep';
|
|
import { getCredentials, api, request, credentials, apiUrl } from '../../data/api-data';
|
|
import { followMessage, sendMessage, sendSimpleMessage, deleteMessage, updateMessage } from '../../data/chat.helper';
|
|
import { imgURL } from '../../data/interactions';
|
|
import { mockServerHealthy, mockServerReset, mockServerSet } from '../../data/mock-server.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'] };
|
|
let protectedChannel: IRoom;
|
|
|
|
before((done) => getCredentials(done));
|
|
|
|
before(async () => {
|
|
testChannel = (await createRoom({ type: 'c', name: `chat.api-test-${Date.now()}` })).body.channel;
|
|
protectedChannel = (await createRoom({ type: 'c', name: `chat.api-protected-test-${Date.now()}` })).body.channel;
|
|
|
|
await request
|
|
.post(api('rooms.saveRoomSettings'))
|
|
.set(credentials)
|
|
.send({
|
|
rid: protectedChannel._id,
|
|
joinCode: 'super-secret-password',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
});
|
|
|
|
after(async () => {
|
|
await deleteRoom({ type: 'c', roomId: testChannel._id });
|
|
await deleteRoom({ type: 'c', roomId: protectedChannel._id });
|
|
});
|
|
|
|
describe('/chat.postMessage', () => {
|
|
it('should throw an error when at least one of required parameters(channel, roomId) is not sent', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
});
|
|
|
|
it('should throw an error when it has some properties with the wrong type(attachments.title_link_download, attachments.fields, message_link)', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
it('should throw an error when the properties (attachments.fields.title) is missing', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
it('should throw an error when the properties (attachments.fields.value) is missing', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
it('attachment.fields should work fine when value and title are provided', async () => {
|
|
const res = await 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.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');
|
|
});
|
|
|
|
it('should allow forwarding a message into the same password protected room', async () => {
|
|
const postResponse = await request
|
|
.post(api('chat.postMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
roomId: [protectedChannel._id],
|
|
text: 'Message to be forwarded',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(postResponse.body).to.have.property('success', true);
|
|
const originalMessageId = postResponse.body.message._id as IMessage['_id'];
|
|
|
|
const forwardResponse = await request
|
|
.post(api('chat.postMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
roomId: [protectedChannel._id],
|
|
text: `[](http://localhost:3000/channel/${protectedChannel.name}?msg=${originalMessageId}`,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(forwardResponse.body).to.have.property('success', true);
|
|
expect(forwardResponse.body).to.have.nested.property('message.rid', protectedChannel._id);
|
|
});
|
|
|
|
it('should return statusCode 200 when postMessage successfully', async () => {
|
|
const res = await 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.body).to.have.property('success', true);
|
|
expect(res.body).to.have.nested.property('message.msg', 'Sample message');
|
|
message = { _id: res.body.message._id };
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'error-message-size-exceeded');
|
|
});
|
|
|
|
it('should return an error if text parameter in the first attachment surpasses the maximum allowed size', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'error-message-size-exceeded');
|
|
});
|
|
|
|
it('should return an error if text parameter in any of the attachments surpasses the maximum allowed size', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'error-message-size-exceeded');
|
|
});
|
|
|
|
it('should pass if any text parameter length does not surpasses the maximum allowed size', async () => {
|
|
const res = await 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.body).to.have.property('success', true);
|
|
expect(res.body).to.have.nested.property('message.msg', 'Sample');
|
|
});
|
|
});
|
|
|
|
describe('Archived rooms', () => {
|
|
let archivedChannel: IRoom;
|
|
|
|
before(async () => {
|
|
archivedChannel = (await createRoom({ type: 'c', name: `chat.api-archived-post-test-${Date.now()}` })).body.channel;
|
|
await request.post(api('channels.archive')).set(credentials).send({ roomId: archivedChannel._id });
|
|
});
|
|
|
|
after(async () => {
|
|
await request.post(api('channels.unarchive')).set(credentials).send({ roomId: archivedChannel._id });
|
|
await deleteRoom({ type: 'c', roomId: archivedChannel._id });
|
|
});
|
|
|
|
it('should fail to post a message to an archived room', async () => {
|
|
const res = await request
|
|
.post(api('chat.postMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
roomId: archivedChannel._id,
|
|
text: 'This message should not be posted',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'room_is_archived');
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('/chat.getMessage', () => {
|
|
it('should retrieve the message successfully', async () => {
|
|
const res = await request
|
|
.get(api('chat.getMessage'))
|
|
.set(credentials)
|
|
.query({
|
|
msgId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.nested.property('message._id', message._id);
|
|
});
|
|
});
|
|
|
|
describe('/chat.sendMessage', () => {
|
|
it("should throw an error when the required param 'rid' is not sent", async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', "The 'rid' property on the message object is missing.");
|
|
});
|
|
|
|
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)', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
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('Archived rooms', () => {
|
|
let archivedChannel: IRoom;
|
|
|
|
before(async () => {
|
|
archivedChannel = (await createRoom({ type: 'c', name: `chat.api-archived-test-${Date.now()}` })).body.channel;
|
|
await request.post(api('channels.archive')).set(credentials).send({ roomId: archivedChannel._id });
|
|
});
|
|
|
|
after(async () => {
|
|
await request.post(api('channels.unarchive')).set(credentials).send({ roomId: archivedChannel._id });
|
|
await deleteRoom({ type: 'c', roomId: archivedChannel._id });
|
|
});
|
|
|
|
it('should fail to send a message to an archived room', async () => {
|
|
const res = await request
|
|
.post(api('chat.sendMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
message: {
|
|
rid: archivedChannel._id,
|
|
msg: 'This message should not be sent',
|
|
},
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'room_is_archived');
|
|
});
|
|
});
|
|
|
|
describe('oembed', () => {
|
|
// the TEST_MODE oembed provider (server/services/messages/lib/oembed/providers.ts) resolves /video/ URLs through GET /oembed on the mock-server
|
|
const mockVideoUrl = (id: string) => `http://mock-server.dev:8080/video/${id}`;
|
|
const mockPageUrl = (id: string) => `http://mock-server.dev:8080/page/${id}`;
|
|
const mockOembedEndpoint = { method: 'GET', path: '/oembed' };
|
|
// the whole oembed pipeline runs against the local mock-server, so a fixed wait suffices — exceeding it means a CI perf problem worth surfacing
|
|
const oembedProcessingMs = 500;
|
|
|
|
const youtubeOembedResponse = {
|
|
title: 'Rocket.Chat Demo',
|
|
author_name: 'Rocket.Chat',
|
|
author_url: 'https://www.youtube.com/@RocketChatApp',
|
|
type: 'video',
|
|
height: 150,
|
|
width: 200,
|
|
version: '1.0',
|
|
provider_name: 'YouTube',
|
|
provider_url: 'https://www.youtube.com/',
|
|
thumbnail_height: 360,
|
|
thumbnail_width: 480,
|
|
thumbnail_url: 'https://i.ytimg.com/vi/T2v29gK8fP4/hqdefault.jpg',
|
|
html: '<iframe width="200" height="150" src="https://www.youtube.com/embed/T2v29gK8fP4?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen title="Rocket.Chat Demo"></iframe>',
|
|
};
|
|
|
|
const spotifyOembedResponse = {
|
|
html: '<iframe style="border-radius: 12px" width="100%" height="152" title="Spotify Embed: Never Gonna Give You Up" frameborder="0" allowfullscreen allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy" src="https://open.spotify.com/embed/track/4cOdK2wGLETKBW3PvgPWqT?utm_source=oembed"></iframe>',
|
|
iframe_url: 'https://open.spotify.com/embed/track/4cOdK2wGLETKBW3PvgPWqT?utm_source=oembed',
|
|
width: 456,
|
|
height: 152,
|
|
version: '1.0',
|
|
provider_name: 'Spotify',
|
|
provider_url: 'https://spotify.com',
|
|
type: 'rich',
|
|
title: 'Never Gonna Give You Up',
|
|
thumbnail_url: 'https://image-cdn-ak.spotifycdn.com/image/ab67616d00001e0215ebbedaacef61af244262a8',
|
|
thumbnail_width: 300,
|
|
thumbnail_height: 300,
|
|
};
|
|
|
|
let ytEmbedMsgId: IMessage['_id'];
|
|
let imgUrlMsgId: IMessage['_id'];
|
|
|
|
before(async () => {
|
|
const healthy = await mockServerHealthy();
|
|
expect(healthy, 'mock-server is not reachable — ensure it is running').to.be.true;
|
|
await mockServerReset();
|
|
});
|
|
|
|
// programmed responses queue per key and an infinite (times: 0) entry never expires, so each test starts from a clean mock
|
|
afterEach(() => mockServerReset());
|
|
|
|
before(() =>
|
|
Promise.all([
|
|
updateSetting('API_EmbedIgnoredHosts', ''),
|
|
updateSetting('API_EmbedSafePorts', '80, 443, 3000, 8080'),
|
|
updateSetting('SSRF_Allowlist', '127.0.0.1:3000, mock-server.dev'),
|
|
]),
|
|
);
|
|
|
|
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'),
|
|
updateSetting('SSRF_Allowlist', ''),
|
|
]),
|
|
);
|
|
|
|
before(async () => {
|
|
await mockServerSet(mockOembedEndpoint.method, mockOembedEndpoint.path, youtubeOembedResponse);
|
|
const ytEmbedMsgPayload = {
|
|
_id: `id-${Date.now()}`,
|
|
rid: testChannel._id,
|
|
msg: mockVideoUrl('embed-max-width'),
|
|
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://127.0.0.1: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', async () => {
|
|
await retry(
|
|
'Oembed is generated async thats why the retry is required',
|
|
async () => {
|
|
await 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"');
|
|
});
|
|
},
|
|
{
|
|
delayMs: 100,
|
|
retries: 5,
|
|
},
|
|
);
|
|
});
|
|
|
|
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: mockVideoUrl('empty-preview-urls'),
|
|
},
|
|
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 () => {
|
|
const chosenUrl = mockPageUrl('chosen-preview');
|
|
await mockServerSet('GET', '/page/chosen-preview', '<html><head><title>Chosen Preview Page</title></head><body></body></html>');
|
|
|
|
let msgId;
|
|
await request
|
|
.post(api('chat.sendMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
message: {
|
|
rid: testChannel._id,
|
|
msg: `${mockVideoUrl('not-chosen-preview')} ${chosenUrl}`,
|
|
},
|
|
previewUrls: [chosenUrl],
|
|
})
|
|
.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 sleep(oembedProcessingMs);
|
|
|
|
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').to.have.property('pageTitle', 'Chosen Preview Page');
|
|
});
|
|
});
|
|
|
|
it('should not generate previews if the message contains more than five external URL', async () => {
|
|
let msgId;
|
|
const urls = [
|
|
mockVideoUrl('over-limit-1'),
|
|
mockVideoUrl('over-limit-2'),
|
|
mockVideoUrl('over-limit-3'),
|
|
mockVideoUrl('over-limit-4'),
|
|
mockVideoUrl('over-limit-5'),
|
|
mockVideoUrl('over-limit-6'),
|
|
];
|
|
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;
|
|
});
|
|
});
|
|
});
|
|
|
|
it('should embed the metadata of a rich oembed provider response', async () => {
|
|
await mockServerSet(mockOembedEndpoint.method, mockOembedEndpoint.path, spotifyOembedResponse);
|
|
const msg = mockVideoUrl('rich-provider');
|
|
|
|
let msgId;
|
|
await request
|
|
.post(api('chat.sendMessage'))
|
|
.set(credentials)
|
|
.send({ message: { rid: testChannel._id, msg } })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
msgId = res.body.message._id;
|
|
});
|
|
|
|
await sleep(oembedProcessingMs);
|
|
|
|
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);
|
|
|
|
const { meta } = res.body.message.urls[0];
|
|
expect(meta).to.have.property('oembedUrl', msg);
|
|
expect(meta).to.have.property('oembedTitle', 'Never Gonna Give You Up');
|
|
expect(meta).to.have.property('oembedType', 'rich');
|
|
expect(meta).to.have.property('oembedProviderName', 'Spotify');
|
|
expect(meta).to.have.property('oembedThumbnailUrl', spotifyOembedResponse.thumbnail_url);
|
|
expect(meta).to.have.property('oembedHtml').to.have.string('<iframe style="max-width: 100%;width:400px;height:225px"');
|
|
expect(meta, 'non-string oembed values must be dropped').to.not.have.any.keys(
|
|
'oembedWidth',
|
|
'oembedHeight',
|
|
'oembedThumbnailWidth',
|
|
'oembedThumbnailHeight',
|
|
);
|
|
});
|
|
});
|
|
|
|
it('should embed only the oembed source url when the provider response has no string values', async () => {
|
|
await mockServerSet(mockOembedEndpoint.method, mockOembedEndpoint.path, { status: 404, ok: false });
|
|
const msg = mockVideoUrl('no-string-values');
|
|
|
|
let msgId;
|
|
await request
|
|
.post(api('chat.sendMessage'))
|
|
.set(credentials)
|
|
.send({ message: { rid: testChannel._id, msg } })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
msgId = res.body.message._id;
|
|
});
|
|
|
|
await sleep(oembedProcessingMs);
|
|
|
|
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').that.deep.equals({ oembedUrl: msg });
|
|
});
|
|
});
|
|
|
|
it('should not embed metadata when the oembed provider responds with an error', async () => {
|
|
await mockServerSet(mockOembedEndpoint.method, mockOembedEndpoint.path, {}, 500);
|
|
|
|
let msgId;
|
|
await request
|
|
.post(api('chat.sendMessage'))
|
|
.set(credentials)
|
|
.send({ message: { rid: testChannel._id, msg: mockVideoUrl('error-response') } })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
msgId = res.body.message._id;
|
|
});
|
|
|
|
await sleep(oembedProcessingMs);
|
|
|
|
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').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', async () => {
|
|
const res = await request
|
|
.post(api('channels.create'))
|
|
.set(credentials)
|
|
.send({
|
|
name: `readonlychannel${+new Date()}`,
|
|
readOnly: true,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
readOnlyChannel = res.body.channel;
|
|
});
|
|
it('should send a message when the user is the owner of a readonly channel', async () => {
|
|
const res = await request
|
|
.post(api('chat.sendMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
message: {
|
|
rid: readOnlyChannel._id,
|
|
msg: 'Sample message',
|
|
},
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('message').and.to.be.an('object');
|
|
});
|
|
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', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'Not enough permission');
|
|
});
|
|
|
|
it('should fail if user does not have the message-impersonate permission and tries to send message with avatar param', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'Not enough permission');
|
|
});
|
|
|
|
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' });
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
// TODO: Auto-close unclosed markdown code blocks on backend - Remove in 9.0.0
|
|
// In 9.0.0, this behavior is handled entirely on the client side and should no longer be done on the backend.
|
|
it('should auto-close an unclosed code block when sending a message', async () => {
|
|
const unclosedMsg = '```\nsome code';
|
|
const expectedMsg = '```\nsome code\n```';
|
|
await request
|
|
.post(api('chat.sendMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
message: {
|
|
rid: testChannel._id,
|
|
msg: unclosedMsg,
|
|
},
|
|
})
|
|
.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', expectedMsg);
|
|
});
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', 'Only encrypted messages can have content updated.');
|
|
});
|
|
|
|
it('should update a message successfully', async () => {
|
|
const res = 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.body).to.have.property('success', true);
|
|
expect(res.body).to.have.nested.property('message.msg', 'This message was edited via API');
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await request
|
|
.post(api('chat.delete'))
|
|
.set(credentials)
|
|
.send({
|
|
roomId: testChannel._id,
|
|
msgId,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
it('sending message as another user...', async () => {
|
|
const res = await request
|
|
.post(api('chat.sendMessage'))
|
|
.set(userCredentials)
|
|
.send({
|
|
message: {
|
|
rid: testChannel._id,
|
|
msg: 'Sample message',
|
|
},
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
msgId = res.body.message._id;
|
|
});
|
|
it('should delete a message successfully when the user deletes a message send by another user', async () => {
|
|
const res = await request
|
|
.post(api('chat.delete'))
|
|
.set(credentials)
|
|
.send({
|
|
roomId: testChannel._id,
|
|
msgId,
|
|
asUser: true,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
|
|
describe('when deleting by fileId', () => {
|
|
const uploadFile = async (): Promise<string> => {
|
|
const { body } = await request
|
|
.post(api(`rooms.media/${testChannel._id}`))
|
|
.set(credentials)
|
|
.attach('file', imgURL)
|
|
.expect(200);
|
|
expect(body).to.have.property('success', true);
|
|
return body.file._id;
|
|
};
|
|
|
|
it('should delete the message associated with the provided fileId', async () => {
|
|
const fileId = await uploadFile();
|
|
|
|
let fileMsgId: string | undefined;
|
|
await request
|
|
.post(api(`rooms.mediaConfirm/${testChannel._id}/${fileId}`))
|
|
.set(credentials)
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
fileMsgId = res.body.message._id;
|
|
});
|
|
|
|
await request
|
|
.post(api('chat.delete'))
|
|
.set(credentials)
|
|
.send({ fileId })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
|
|
await request
|
|
.get(api('chat.getMessage'))
|
|
.set(credentials)
|
|
.query({ msgId: fileMsgId })
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
});
|
|
|
|
it('should fail when the uploaded file has no associated message', async () => {
|
|
const fileId = await uploadFile();
|
|
|
|
await request
|
|
.post(api('chat.delete'))
|
|
.set(credentials)
|
|
.send({ fileId })
|
|
.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 file id: "${fileId}".`);
|
|
});
|
|
});
|
|
|
|
it('should fail when neither msgId/roomId nor fileId is provided', async () => {
|
|
await request
|
|
.post(api('chat.delete'))
|
|
.set(credentials)
|
|
.send({})
|
|
.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');
|
|
});
|
|
});
|
|
});
|
|
|
|
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('in read-only rooms with unmuted users', () => {
|
|
let readOnlyChannel: IRoom;
|
|
let unmutedUser: TestUser<IUser>;
|
|
let unmutedUserCredentials: Credentials;
|
|
let notUnmutedUser: TestUser<IUser>;
|
|
let notUnmutedUserCredentials: Credentials;
|
|
|
|
before(async () => {
|
|
unmutedUser = await createUser();
|
|
unmutedUserCredentials = await login(unmutedUser.username, password);
|
|
|
|
notUnmutedUser = await createUser();
|
|
notUnmutedUserCredentials = await login(notUnmutedUser.username, password);
|
|
|
|
const channelResult = await request
|
|
.post(api('channels.create'))
|
|
.set(credentials)
|
|
.send({
|
|
name: `readonly-delete-test-${Date.now()}`,
|
|
readOnly: true,
|
|
});
|
|
readOnlyChannel = channelResult.body.channel;
|
|
|
|
await request.post(api('channels.invite')).set(credentials).send({
|
|
roomId: readOnlyChannel._id,
|
|
userId: unmutedUser._id,
|
|
});
|
|
await request.post(api('channels.invite')).set(credentials).send({
|
|
roomId: readOnlyChannel._id,
|
|
userId: notUnmutedUser._id,
|
|
});
|
|
|
|
await request.post(api('rooms.unmuteUser')).set(credentials).send({
|
|
roomId: readOnlyChannel._id,
|
|
username: unmutedUser.username,
|
|
});
|
|
|
|
await updatePermission('delete-message', ['user']);
|
|
});
|
|
|
|
after(async () => {
|
|
await Promise.all([
|
|
readOnlyChannel && deleteRoom({ type: 'c', roomId: readOnlyChannel._id }),
|
|
unmutedUser && deleteUser(unmutedUser),
|
|
notUnmutedUser && deleteUser(notUnmutedUser),
|
|
updatePermission('delete-message', ['admin', 'owner', 'moderator']),
|
|
]);
|
|
});
|
|
|
|
it('should allow unmuted user to delete message from another user in read-only room', async () => {
|
|
const messageResult = await request
|
|
.post(api('chat.sendMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
message: {
|
|
rid: readOnlyChannel._id,
|
|
msg: 'Message to be deleted by unmuted user',
|
|
},
|
|
});
|
|
|
|
const deleteMsgId = messageResult.body.message._id;
|
|
|
|
await request
|
|
.post(api('chat.delete'))
|
|
.set(unmutedUserCredentials)
|
|
.send({
|
|
roomId: readOnlyChannel._id,
|
|
msgId: deleteMsgId,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
});
|
|
|
|
it('should NOT allow non-unmuted user to delete message in read-only room (regression test)', async () => {
|
|
const messageResult = await request
|
|
.post(api('chat.sendMessage'))
|
|
.set(unmutedUserCredentials)
|
|
.send({
|
|
message: {
|
|
rid: readOnlyChannel._id,
|
|
msg: 'Message from unmuted user',
|
|
},
|
|
});
|
|
|
|
const deleteMsgId = messageResult.body.message._id;
|
|
|
|
await request
|
|
.post(api('chat.delete'))
|
|
.set(notUnmutedUserCredentials)
|
|
.send({
|
|
roomId: readOnlyChannel._id,
|
|
msgId: deleteMsgId,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error', "You can't delete messages because the room is readonly.");
|
|
});
|
|
});
|
|
|
|
it('should NOT allow unmuted user without delete-message permission to delete message', async () => {
|
|
await updatePermission('delete-message', []);
|
|
|
|
const messageResult = await request
|
|
.post(api('chat.sendMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
message: {
|
|
rid: readOnlyChannel._id,
|
|
msg: 'Message that unmuted user without permission cannot delete',
|
|
},
|
|
});
|
|
|
|
const deleteMsgId = messageResult.body.message._id;
|
|
|
|
await request
|
|
.post(api('chat.delete'))
|
|
.set(unmutedUserCredentials)
|
|
.send({
|
|
roomId: readOnlyChannel._id,
|
|
msgId: deleteMsgId,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
|
|
await updatePermission('delete-message', ['user']);
|
|
});
|
|
});
|
|
});
|
|
|
|
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');
|
|
await sendMessage('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!');
|
|
});
|
|
|
|
it('should return a list of messages when execute successfully', async () => {
|
|
const res = await request
|
|
.get(api('chat.search'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId: testChannel._id,
|
|
searchText: 'msg1',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('messages');
|
|
});
|
|
it('should return a list of messages(length=1) when is provided "count" query parameter execute successfully', async () => {
|
|
const res = await request
|
|
.get(api('chat.search'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId: testChannel._id,
|
|
searchText: 'msg1',
|
|
count: 1,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('messages');
|
|
expect(res.body.messages.length).to.equal(1);
|
|
});
|
|
it('should return a list of messages(length=3) when is provided "count" and "offset" query parameters are executed successfully', async () => {
|
|
const res = await 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.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('messages');
|
|
expect(res.body.messages.length).to.equal(3);
|
|
});
|
|
|
|
it('should return a empty list of messages when is provided a huge offset value', async () => {
|
|
const res = await 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.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('messages');
|
|
expect(res.body.messages.length).to.equal(0);
|
|
});
|
|
|
|
it('should return an empty array of messages with status 200 if the regexp starts with an invalid quantifier', async () => {
|
|
await request
|
|
.get(api('chat.search'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId: testChannel._id,
|
|
searchText: '/*test/',
|
|
})
|
|
.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.deep.equal([]);
|
|
});
|
|
});
|
|
|
|
it('should return an empty array of messages with status 200 if the regexp has an unclosed parenthesis', async () => {
|
|
await request
|
|
.get(api('chat.search'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId: testChannel._id,
|
|
searchText: '/(test/',
|
|
})
|
|
.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.deep.equal([]);
|
|
});
|
|
});
|
|
|
|
it('should return an empty array of messages with status 200 if the regexp has an unclosed character class', async () => {
|
|
await request
|
|
.get(api('chat.search'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId: testChannel._id,
|
|
searchText: '/[a-z/',
|
|
})
|
|
.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.deep.equal([]);
|
|
});
|
|
});
|
|
|
|
it('should not cause catastrophic backtracking when using a malicious regexp', async () => {
|
|
await request
|
|
.get(api('chat.search'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId: testChannel._id,
|
|
searchText: '/(a+)+$/',
|
|
})
|
|
.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.deep.equal([]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('[/chat.react]', () => {
|
|
it("should return statusCode: 200 and success when try unreact a message that's no reacted yet", async () => {
|
|
const res = await request
|
|
.post(api('chat.react'))
|
|
.set(credentials)
|
|
.send({
|
|
emoji: ':squid:',
|
|
messageId: message._id,
|
|
shouldReact: false,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
it('should react a message successfully', async () => {
|
|
const res = await request
|
|
.post(api('chat.react'))
|
|
.set(credentials)
|
|
.send({
|
|
emoji: 'smile',
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
|
|
it('should return statusCode: 200 when the emoji is valid', async () => {
|
|
const res = await request
|
|
.post(api('chat.react'))
|
|
.set(credentials)
|
|
.send({
|
|
emoji: ':squid:',
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
it("should return statusCode: 200 and success when try react a message that's already reacted", async () => {
|
|
const res = await request
|
|
.post(api('chat.react'))
|
|
.set(credentials)
|
|
.send({
|
|
emoji: ':squid:',
|
|
messageId: message._id,
|
|
shouldReact: true,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
it('should return statusCode: 200 when unreact a message with flag, shouldReact: false', async () => {
|
|
const res = await request
|
|
.post(api('chat.react'))
|
|
.set(credentials)
|
|
.send({
|
|
emoji: ':squid:',
|
|
messageId: message._id,
|
|
shouldReact: false,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
it('should return statusCode: 200 when react a message with flag, shouldReact: true', async () => {
|
|
const res = await request
|
|
.post(api('chat.react'))
|
|
.set(credentials)
|
|
.send({
|
|
emoji: ':squid:',
|
|
messageId: message._id,
|
|
shouldReact: true,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
it('should return statusCode: 200 when the emoji is valid and has no colons', async () => {
|
|
const res = await request
|
|
.post(api('chat.react'))
|
|
.set(credentials)
|
|
.send({
|
|
emoji: 'bee',
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
it('should return statusCode: 200 for reaction property when the emoji is valid', async () => {
|
|
const res = await request
|
|
.post(api('chat.react'))
|
|
.set(credentials)
|
|
.send({
|
|
reaction: 'ant',
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await request
|
|
.post(api('chat.reportMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
description: 'test',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
});
|
|
|
|
describe('when an error occurs', () => {
|
|
it('should return statusCode 400 and an error', async () => {
|
|
const res = await request
|
|
.post(api('chat.reportMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await 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.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);
|
|
});
|
|
it('should return a list of deleted messages when the user sets count query parameter', async () => {
|
|
const res = await 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.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);
|
|
});
|
|
it('should return a list of deleted messages when the user sets count and offset query parameters', async () => {
|
|
const res = await 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.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);
|
|
});
|
|
});
|
|
|
|
describe('when an error occurs', () => {
|
|
it('should return statusCode 400', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
});
|
|
it('should return statusCode 400 and an error when "since" is not provided', async () => {
|
|
const res = await request
|
|
.get(api('chat.getDeletedMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId,
|
|
count: 1,
|
|
offset: 0,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
it('should return statusCode 400 and an error when "since" is provided but it is invalid ISODate', async () => {
|
|
const res = await request
|
|
.get(api('chat.getDeletedMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId,
|
|
since: 'InvalidaDate',
|
|
count: 1,
|
|
offset: 0,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
await updateSetting('Message_AllowPinning', false);
|
|
|
|
const res = await request
|
|
.post(api('chat.pinMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
it('should return an error when pinMessage is allowed in server but user dont have permission', async () => {
|
|
await updateSetting('Message_AllowPinning', true);
|
|
|
|
await updatePermission('pin-message', []);
|
|
|
|
const res = await request
|
|
.post(api('chat.pinMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
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', async () => {
|
|
await updatePermission('pin-message', ['admin']);
|
|
|
|
const res = await request
|
|
.post(api('chat.pinMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.not.have.property('error');
|
|
});
|
|
|
|
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', async () => {
|
|
await updateSetting('Message_AllowPinning', false);
|
|
|
|
const res = await request
|
|
.post(api('chat.unPinMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
it('should return an error when pinMessage is allowed in server but users dont have permission', async () => {
|
|
await updateSetting('Message_AllowPinning', true);
|
|
|
|
await updatePermission('pin-message', []);
|
|
|
|
const res = await request
|
|
.post(api('chat.unPinMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
it('should unpin Message successfully', async () => {
|
|
await updatePermission('pin-message', ['admin']);
|
|
|
|
const res = await request
|
|
.post(api('chat.unPinMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.not.have.property('error');
|
|
});
|
|
});
|
|
|
|
describe('[/chat.unStarMessage]', () => {
|
|
after(() => updateSetting('Message_AllowStarring', true));
|
|
|
|
it('should return an error when starMessage is not allowed in this server', async () => {
|
|
await updateSetting('Message_AllowStarring', false);
|
|
|
|
const res = await request
|
|
.post(api('chat.unStarMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
it('should unstar Message successfully', async () => {
|
|
await updateSetting('Message_AllowStarring', true);
|
|
|
|
const res = await request
|
|
.post(api('chat.unStarMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.not.have.property('error');
|
|
});
|
|
});
|
|
|
|
describe('[/chat.starMessage]', () => {
|
|
after(() => updateSetting('Message_AllowStarring', true));
|
|
|
|
it('should return an error when starMessage is not allowed in this server', async () => {
|
|
await updateSetting('Message_AllowStarring', false);
|
|
|
|
const res = await request
|
|
.post(api('chat.starMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('error');
|
|
});
|
|
|
|
it('should star Message successfully', async () => {
|
|
await updateSetting('Message_AllowStarring', true);
|
|
|
|
const res = await request
|
|
.post(api('chat.starMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
messageId: message._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.not.have.property('error');
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await request
|
|
.get(api('chat.getPinnedMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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);
|
|
});
|
|
it('should return a list of pinned messages when the user sets count query parameter', async () => {
|
|
const res = await request
|
|
.get(api('chat.getPinnedMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId,
|
|
count: 1,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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);
|
|
});
|
|
it('should return a list of pinned messages when the user sets count and offset query parameters', async () => {
|
|
const res = await request
|
|
.get(api('chat.getPinnedMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId,
|
|
count: 1,
|
|
offset: 0,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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);
|
|
});
|
|
});
|
|
|
|
describe('when an error occurs', () => {
|
|
it('should return statusCode 400 and an error when "roomId" is not provided', async () => {
|
|
const res = await request
|
|
.get(api('chat.getPinnedMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
count: 1,
|
|
offset: 0,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await request
|
|
.get(api('chat.getMentionedMessages'))
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body.errorType).to.be.equal('error-invalid-params');
|
|
});
|
|
|
|
it('should return an error when the roomId is invalid', async () => {
|
|
const res = await request
|
|
.get(api('chat.getMentionedMessages'))
|
|
.query({ roomId: 'invalid-room' })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body.error).to.be.equal('error-not-allowed');
|
|
});
|
|
|
|
it('should return the mentioned messages', async () => {
|
|
const res = await request
|
|
.get(api('chat.getMentionedMessages'))
|
|
.query({ roomId: testChannel._id })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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');
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await request.get(api('chat.getStarredMessages')).set(credentials).expect('Content-Type', 'application/json').expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body.errorType).to.be.equal('error-invalid-params');
|
|
});
|
|
|
|
it('should return an error when the roomId is invalid', async () => {
|
|
const res = await request
|
|
.get(api('chat.getStarredMessages'))
|
|
.query({ roomId: 'invalid-room' })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body.error).to.be.equal('error-not-allowed');
|
|
});
|
|
|
|
it('should return the starred messages', async () => {
|
|
const res = await request
|
|
.get(api('chat.getStarredMessages'))
|
|
.query({ roomId: testChannel._id })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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');
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await request.get(api('chat.getDiscussions')).set(credentials).expect('Content-Type', 'application/json').expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
|
|
it('should return an error when the roomId is invalid', async () => {
|
|
const res = await request
|
|
.get(api('chat.getDiscussions'))
|
|
.query({ roomId: 'invalid-room' })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body.error).to.be.equal('error-not-allowed');
|
|
});
|
|
|
|
it('should return the discussions of a room', async () => {
|
|
const res = await request
|
|
.get(api('chat.getDiscussions'))
|
|
.query({ roomId: testChannel._id })
|
|
.set(credentials)
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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');
|
|
});
|
|
it('should return the discussions of a room even requested with count and offset params', async () => {
|
|
const res = await request
|
|
.get(api('chat.getDiscussions'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId: testChannel._id,
|
|
count: 5,
|
|
offset: 0,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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');
|
|
});
|
|
|
|
function filterDiscussionsByText(text: string) {
|
|
it(`should return the room's discussion list filtered by the text '${text}'`, async () => {
|
|
const res = await request
|
|
.get(api('chat.getDiscussions'))
|
|
.set(credentials)
|
|
.query({
|
|
roomId: testChannel._id,
|
|
text,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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);
|
|
});
|
|
|
|
it(`should return the room's discussion list filtered by the text '${text}' even requested with count and offset params`, async () => {
|
|
const res = await 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.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);
|
|
});
|
|
}
|
|
|
|
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', async () => {
|
|
const res = await request.get(api('chat.syncMessages')).set(credentials).expect('Content-Type', 'application/json').expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body.errorType).to.be.equal('error-invalid-params');
|
|
expect(res.body.error).to.include(`must have required property 'roomId'`);
|
|
});
|
|
|
|
it('should return an error when the neither "lastUpdate" or "type" parameter is sent', async () => {
|
|
const res = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: testChannel._id })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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');
|
|
});
|
|
|
|
it('should return an error when the "lastUpdate" parameter is invalid', async () => {
|
|
const res = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: 'invalid-room', lastUpdate: 'invalid-date' })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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');
|
|
});
|
|
|
|
it('should return an error when user provides an invalid roomId', async () => {
|
|
const res = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: 'invalid-room', lastUpdate: new Date().toISOString() })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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');
|
|
});
|
|
|
|
it('should return an error when the "type" parameter is not supported', async () => {
|
|
const res = await 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.body).to.have.property('success', false);
|
|
expect(res.body.errorType).to.be.equal('error-invalid-params');
|
|
expect(res.body.error).to.include('must be equal to one of the allowed values');
|
|
});
|
|
|
|
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', async () => {
|
|
const res = await 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.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');
|
|
});
|
|
|
|
it('should return an error when both "next" or "previous" and "lastUpdate" are sent', async () => {
|
|
const res = await 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.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');
|
|
});
|
|
|
|
it('should return an error when neither "type" or "lastUpdate" are sent', async () => {
|
|
const res = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: testChannel._id })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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');
|
|
});
|
|
|
|
it('should return an empty response when there are no messages to sync', async () => {
|
|
const res = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: testChannel._id, lastUpdate: new Date().toISOString() })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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);
|
|
});
|
|
|
|
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 an error when the "fromTs" parameter is invalid', async () => {
|
|
const res = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: testChannel._id, lastUpdate: new Date().toISOString(), fromTs: 'invalid-date' })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body.errorType).to.be.equal('error-invalid-params');
|
|
});
|
|
|
|
it('should return an error when "fromTs" is sent along with cursor pagination', async () => {
|
|
const res = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: testChannel._id, type: 'UPDATED', next: `${Date.now()}`, fromTs: new Date().toISOString() })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body.errorType).to.be.equal('error-fromTs-requires-lastUpdate');
|
|
expect(res.body.error).to.include('The "fromTs" parameter can only be used together with "lastUpdate"');
|
|
});
|
|
|
|
it('should return an error when "fromTs" is sent without "lastUpdate"', async () => {
|
|
const res = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: testChannel._id, type: 'UPDATED', fromTs: new Date().toISOString() })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body.errorType).to.be.equal('error-fromTs-requires-lastUpdate');
|
|
});
|
|
|
|
describe('"fromTs" parameter', () => {
|
|
let olderMessage: IMessage;
|
|
let newerMessage: IMessage;
|
|
let lastUpdate: string;
|
|
|
|
// the server filters with `ts: { $gte: fromTs }`, so a boundary taken from the newer message only
|
|
// excludes the older one when the two land on distinct milliseconds
|
|
const sendBoundaryMessages = async (olderText: string, newerText: string): Promise<[IMessage, IMessage]> => {
|
|
const older: IMessage = (await sendSimpleMessage({ roomId: testChannel._id, text: olderText })).body.message;
|
|
await sleep(5);
|
|
const newer: IMessage = (await sendSimpleMessage({ roomId: testChannel._id, text: newerText })).body.message;
|
|
|
|
expect(new Date(newer.ts).getTime()).to.be.greaterThan(new Date(older.ts).getTime());
|
|
|
|
return [older, newer];
|
|
};
|
|
|
|
before(async () => {
|
|
[olderMessage, newerMessage] = await sendBoundaryMessages('Older Message', 'Newer Message');
|
|
|
|
// every edit below happens after this point, so both messages are candidates on `_updatedAt`
|
|
lastUpdate = new Date().toISOString();
|
|
|
|
await updateMessage({ roomId: testChannel._id, msgId: olderMessage._id, updatedMessage: 'Older Message edited' });
|
|
await updateMessage({ roomId: testChannel._id, msgId: newerMessage._id, updatedMessage: 'Newer Message edited' });
|
|
});
|
|
|
|
after(() =>
|
|
Promise.all([
|
|
deleteMessage({ roomId: testChannel._id, msgId: olderMessage._id }),
|
|
deleteMessage({ roomId: testChannel._id, msgId: newerMessage._id }),
|
|
]),
|
|
);
|
|
|
|
it('should return every edited message when "fromTs" is not provided', async () => {
|
|
const response = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: testChannel._id, lastUpdate })
|
|
.expect(200);
|
|
|
|
expect(response.body.result.updated).to.have.lengthOf(2);
|
|
expect(response.body.result.updated.map((msg: IMessage) => msg._id)).to.have.members([olderMessage._id, newerMessage._id]);
|
|
});
|
|
|
|
it('should omit messages older than "fromTs" while still returning the ones inside the window', async () => {
|
|
const response = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: testChannel._id, lastUpdate, fromTs: newerMessage.ts })
|
|
.expect(200);
|
|
|
|
expect(response.body.result.updated).to.have.lengthOf(1);
|
|
expect(response.body.result.updated[0]._id).to.be.equal(newerMessage._id);
|
|
expect(response.body.result.updated[0].msg).to.be.equal('Newer Message edited');
|
|
});
|
|
|
|
it('should omit deletions of messages older than "fromTs"', async () => {
|
|
const [droppedMessage, windowStart] = await sendBoundaryMessages('To Be Deleted', 'Window Start');
|
|
|
|
const deletionLastUpdate = new Date().toISOString();
|
|
await deleteMessage({ roomId: testChannel._id, msgId: droppedMessage._id });
|
|
|
|
const scoped = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: testChannel._id, lastUpdate: deletionLastUpdate, fromTs: windowStart.ts })
|
|
.expect(200);
|
|
|
|
expect(scoped.body.result.deleted).to.have.lengthOf(0);
|
|
|
|
const unscoped = await request
|
|
.get(api('chat.syncMessages'))
|
|
.set(credentials)
|
|
.query({ roomId: testChannel._id, lastUpdate: deletionLastUpdate })
|
|
.expect(200);
|
|
|
|
expect(unscoped.body.result.deleted).to.have.lengthOf(1);
|
|
expect(unscoped.body.result.deleted[0]._id).to.be.equal(droppedMessage._id);
|
|
|
|
await deleteMessage({ roomId: testChannel._id, msgId: windowStart._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', async () => {
|
|
await updateSetting('Threads_enabled', false);
|
|
|
|
const res = await request
|
|
.get(api('chat.getThreadsList'))
|
|
.set(credentials)
|
|
.query({
|
|
rid: testChannel._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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]');
|
|
});
|
|
|
|
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", async () => {
|
|
await updatePermission('view-c-room', ['admin', 'user']);
|
|
|
|
const res = await request
|
|
.get(api('chat.getThreadsList'))
|
|
.set(credentials)
|
|
.query({
|
|
rid: testChannel._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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 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', 'error-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', 'error-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", async () => {
|
|
await updatePermission('view-c-room', ['admin', 'user']);
|
|
|
|
const res = await request
|
|
.get(api('chat.getThreadsList'))
|
|
.set(credentials)
|
|
.query({
|
|
rid: testChannel._id,
|
|
count: 5,
|
|
offset: 0,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body).to.have.property('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);
|
|
});
|
|
|
|
function filterThreadsByText(text: string) {
|
|
it(`should return the room's thread list filtered by the text '${text}'`, async () => {
|
|
const res = await request
|
|
.get(api('chat.getThreadsList'))
|
|
.set(credentials)
|
|
.query({
|
|
rid: testChannel._id,
|
|
text,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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 filtered by the text '${text}' even requested with count and offset params`, async () => {
|
|
const res = await 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.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);
|
|
});
|
|
}
|
|
|
|
messageWords.forEach((text) => {
|
|
filterThreadsByText(text);
|
|
});
|
|
|
|
it('should return an empty thread list', async () => {
|
|
await updatePermission('view-c-room', ['admin', 'user']);
|
|
|
|
const res = await request
|
|
.get(api('chat.getThreadsList'))
|
|
.set(credentials)
|
|
.query({
|
|
rid: testChannel._id,
|
|
text: 'missing',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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);
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
await updateSetting('Threads_enabled', false);
|
|
|
|
const res = await request
|
|
.get(api('chat.getThreadsList'))
|
|
.set(credentials)
|
|
.query({
|
|
rid: testChannel._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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]');
|
|
});
|
|
|
|
it('should return an error when the required param "rid" is missing', async () => {
|
|
await updateSetting('Threads_enabled', true);
|
|
|
|
const res = await request
|
|
.get(api('chat.syncThreadsList'))
|
|
.set(credentials)
|
|
.query({})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('errorType', 'error-invalid-params');
|
|
});
|
|
|
|
it('should return an error when the required param "updatedSince" is missing', async () => {
|
|
await updateSetting('Threads_enabled', true);
|
|
|
|
const res = await request
|
|
.get(api('chat.syncThreadsList'))
|
|
.set(credentials)
|
|
.query({
|
|
rid: testChannel._id,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('errorType', 'error-invalid-params');
|
|
});
|
|
|
|
it('should return an error when the param "updatedSince" is an invalid date', async () => {
|
|
await updateSetting('Threads_enabled', true);
|
|
|
|
const res = await request
|
|
.get(api('chat.syncThreadsList'))
|
|
.set(credentials)
|
|
.query({
|
|
rid: testChannel._id,
|
|
updatedSince: 'invalid-date',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('errorType', 'error-invalid-params');
|
|
});
|
|
|
|
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", async () => {
|
|
await updatePermission('view-c-room', ['admin', 'user']);
|
|
|
|
const res = await 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.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);
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
await updateSetting('Threads_enabled', false);
|
|
|
|
const res = await request
|
|
.get(api('chat.getThreadMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
tmid: threadMessage.tmid,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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]');
|
|
});
|
|
|
|
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", async () => {
|
|
await updatePermission('view-c-room', ['admin', 'user']);
|
|
|
|
const res = await request
|
|
.get(api('chat.getThreadMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
tmid: threadMessage.tmid,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
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);
|
|
});
|
|
|
|
describe('Message_KeepHistory', () => {
|
|
describe('when enabled', () => {
|
|
let secondThreadMessage: IThreadMessage;
|
|
|
|
before(async () => {
|
|
await updateSetting('Message_KeepHistory', true);
|
|
await updateMessage({
|
|
msgId: threadMessage._id,
|
|
updatedMessage: 'Edited thread message',
|
|
roomId: testChannel._id,
|
|
});
|
|
secondThreadMessage = (
|
|
await sendSimpleMessage({
|
|
roomId: testChannel._id,
|
|
text: 'Second thread message',
|
|
tmid: createdThreadMessage._id,
|
|
})
|
|
).body.message;
|
|
});
|
|
after(() => updateSetting('Message_KeepHistory', false));
|
|
|
|
it('should exclude hidden message edit history from offset pagination', async () => {
|
|
const res = await request
|
|
.get(api('chat.getThreadMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
tmid: threadMessage.tmid,
|
|
count: 1,
|
|
offset: 1,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.include({ success: true, total: 2, count: 1, offset: 1 });
|
|
expect(res.body.messages).to.have.lengthOf(1);
|
|
expect(res.body.messages[0]).to.have.property('_id', secondThreadMessage._id);
|
|
});
|
|
|
|
it('should exclude hidden message edit history when resolving an aroundId offset', async () => {
|
|
const res = await request
|
|
.get(api('chat.getThreadMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
tmid: threadMessage.tmid,
|
|
aroundId: secondThreadMessage._id,
|
|
count: 2,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.include({ success: true, total: 2, count: 2, offset: 0 });
|
|
expect(res.body.messages.map(({ _id }: IMessage) => _id)).to.deep.equal([threadMessage._id, secondThreadMessage._id]);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('[/chat.readThread]', () => {
|
|
let testChannel: IRoom;
|
|
let parentMessage: IMessage;
|
|
let replier: TestUser<IUser>;
|
|
let replierCredentials: Credentials;
|
|
|
|
before(async () => {
|
|
testChannel = (await createRoom({ type: 'c', name: `channel.test.readThread.${Date.now()}` })).body.channel;
|
|
parentMessage = (await sendSimpleMessage({ roomId: testChannel._id, text: 'Message to create thread' })).body.message;
|
|
|
|
replier = await createUser();
|
|
replierCredentials = await login(replier.username, password);
|
|
await addUserToRoom({ usernames: [replier.username], rid: testChannel._id });
|
|
});
|
|
|
|
after(() =>
|
|
Promise.all([updateSetting('Threads_enabled', true), deleteRoom({ type: 'c', roomId: testChannel._id }), deleteUser(replier)]),
|
|
);
|
|
|
|
it('should fail when threads are disabled', async () => {
|
|
await updateSetting('Threads_enabled', false);
|
|
|
|
try {
|
|
await request
|
|
.post(api('chat.readThread'))
|
|
.set(credentials)
|
|
.send({ tmid: parentMessage._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');
|
|
});
|
|
} finally {
|
|
await updateSetting('Threads_enabled', true);
|
|
}
|
|
});
|
|
|
|
it('should fail when tmid does not match any message', async () => {
|
|
await request
|
|
.post(api('chat.readThread'))
|
|
.set(credentials)
|
|
.send({ tmid: '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');
|
|
});
|
|
});
|
|
|
|
it('should fail when tmid is missing', async () => {
|
|
await request
|
|
.post(api('chat.readThread'))
|
|
.set(credentials)
|
|
.send({})
|
|
.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 clear the unread thread flag of the caller subscription', async () => {
|
|
await sendSimpleMessage({
|
|
roomId: testChannel._id,
|
|
text: 'Thread reply',
|
|
tmid: parentMessage._id,
|
|
userCredentials: replierCredentials,
|
|
});
|
|
|
|
await retry('caller subscription has the thread as unread', async () => {
|
|
const subscription = await getSubscriptionByRoomId(testChannel._id);
|
|
expect(subscription.tunread).to.include(parentMessage._id);
|
|
});
|
|
|
|
await request
|
|
.post(api('chat.readThread'))
|
|
.set(credentials)
|
|
.send({ tmid: parentMessage._id })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200)
|
|
.expect((res) => {
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
|
|
const subscription = await getSubscriptionByRoomId(testChannel._id);
|
|
expect(subscription.tunread ?? []).to.not.include(parentMessage._id);
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
await updateSetting('Threads_enabled', false);
|
|
|
|
const res = await request
|
|
.get(api('chat.syncThreadMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
tmid: threadMessage.tmid,
|
|
updatedSince: new Date().toISOString(),
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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]');
|
|
});
|
|
|
|
it('should return an error when the required param "tmid" is missing', async () => {
|
|
await updateSetting('Threads_enabled', true);
|
|
|
|
const res = await request
|
|
.get(api('chat.syncThreadMessages'))
|
|
.set(credentials)
|
|
.query({})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('errorType', 'error-invalid-params');
|
|
});
|
|
|
|
it('should return an error when the required param "updatedSince" is missing', async () => {
|
|
await updateSetting('Threads_enabled', true);
|
|
|
|
const res = await request
|
|
.get(api('chat.syncThreadMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
tmid: threadMessage.tmid,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('errorType', 'error-invalid-params');
|
|
});
|
|
|
|
it('should return an error when the param "updatedSince" is an invalid date', async () => {
|
|
await updateSetting('Threads_enabled', true);
|
|
|
|
const res = await request
|
|
.get(api('chat.syncThreadMessages'))
|
|
.set(credentials)
|
|
.query({
|
|
tmid: threadMessage.tmid,
|
|
updatedSince: 'invalid-date',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
expect(res.body).to.have.property('errorType', 'error-invalid-params');
|
|
});
|
|
|
|
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", async () => {
|
|
await updatePermission('view-c-room', ['admin', 'user']);
|
|
|
|
const res = await 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.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);
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
await updateSetting('Threads_enabled', false);
|
|
|
|
const res = await request
|
|
.post(api('chat.followMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
mid: threadMessage.tmid,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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]');
|
|
});
|
|
|
|
it('should return an error when the message does not exist', async () => {
|
|
await updateSetting('Threads_enabled', true);
|
|
|
|
const res = await request
|
|
.post(api('chat.followMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
mid: 'invalid-message-id',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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]');
|
|
});
|
|
|
|
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', async () => {
|
|
await updatePermission('view-c-room', ['admin', 'user']);
|
|
|
|
const res = await request
|
|
.post(api('chat.followMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
mid: threadMessage.tmid,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
});
|
|
|
|
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', async () => {
|
|
await updateSetting('Threads_enabled', false);
|
|
|
|
const res = await request
|
|
.post(api('chat.unfollowMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
mid: threadMessage.tmid,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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]');
|
|
});
|
|
|
|
it('should return an error when the message does not exist', async () => {
|
|
await updateSetting('Threads_enabled', true);
|
|
|
|
const res = await request
|
|
.post(api('chat.unfollowMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
mid: 'invalid-message-id',
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(400);
|
|
|
|
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]');
|
|
});
|
|
|
|
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', async () => {
|
|
await updatePermission('view-c-room', ['admin', 'user']);
|
|
|
|
const res = await request
|
|
.post(api('chat.unfollowMessage'))
|
|
.set(credentials)
|
|
.send({
|
|
mid: threadMessage.tmid,
|
|
})
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
});
|
|
});
|
|
|
|
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');
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('[/chat.getMessages]', () => {
|
|
let firstRoom: IRoom;
|
|
let secondRoom: IRoom;
|
|
let privateRoom: IRoom;
|
|
let firstMessageId: IMessage['_id'];
|
|
let secondMessageId: IMessage['_id'];
|
|
let privateMessageId: IMessage['_id'];
|
|
let outsider: TestUser<IUser>;
|
|
let outsiderCredentials: Credentials;
|
|
|
|
before(async () => {
|
|
firstRoom = (await createRoom({ type: 'c', name: `chat.getMessages-first-${Date.now()}` })).body.channel;
|
|
secondRoom = (await createRoom({ type: 'c', name: `chat.getMessages-second-${Date.now()}` })).body.channel;
|
|
privateRoom = (await createRoom({ type: 'p', name: `chat.getMessages-private-${Date.now()}` })).body.group;
|
|
|
|
const [first, second, priv] = await Promise.all([
|
|
sendMessage({ message: { rid: firstRoom._id, msg: 'first room message' } }),
|
|
sendMessage({ message: { rid: secondRoom._id, msg: 'second room message' } }),
|
|
sendMessage({ message: { rid: privateRoom._id, msg: 'private room message' } }),
|
|
]);
|
|
|
|
firstMessageId = first.body.message._id;
|
|
secondMessageId = second.body.message._id;
|
|
privateMessageId = priv.body.message._id;
|
|
|
|
outsider = await createUser();
|
|
outsiderCredentials = await login(outsider.username, password);
|
|
});
|
|
|
|
after(async () => {
|
|
await Promise.all([
|
|
deleteRoom({ type: 'c', roomId: firstRoom._id }),
|
|
deleteRoom({ type: 'c', roomId: secondRoom._id }),
|
|
deleteRoom({ type: 'p', roomId: privateRoom._id }),
|
|
deleteUser(outsider),
|
|
]);
|
|
});
|
|
|
|
it('should return the requested messages', async () => {
|
|
const res = await request
|
|
.post(api('chat.getMessages'))
|
|
.set(credentials)
|
|
.send({ messageIds: [firstMessageId] })
|
|
.expect('Content-Type', 'application/json')
|
|
.expect(200);
|
|
|
|
expect(res.body).to.have.property('success', true);
|
|
expect(res.body.messages).to.have.lengthOf(1);
|
|
expect(res.body.messages[0]).to.have.property('_id', firstMessageId);
|
|
expect(res.body.messages[0]).to.have.property('msg', 'first room message');
|
|
});
|
|
|
|
it('should resolve a batch spanning several rooms in a single request', async () => {
|
|
const res = await request
|
|
.post(api('chat.getMessages'))
|
|
.set(credentials)
|
|
.send({ messageIds: [firstMessageId, secondMessageId] })
|
|
.expect(200);
|
|
|
|
expect(res.body.messages.map((message: IMessage) => message._id)).to.have.members([firstMessageId, secondMessageId]);
|
|
});
|
|
|
|
it('should omit ids that do not resolve to a message', async () => {
|
|
const res = await request
|
|
.post(api('chat.getMessages'))
|
|
.set(credentials)
|
|
.send({ messageIds: [firstMessageId, 'does-not-exist'] })
|
|
.expect(200);
|
|
|
|
expect(res.body.messages.map((message: IMessage) => message._id)).to.deep.equal([firstMessageId]);
|
|
});
|
|
|
|
it('should reject the whole batch when any message belongs to an unreadable room', async () => {
|
|
const res = await request
|
|
.post(api('chat.getMessages'))
|
|
.set(outsiderCredentials)
|
|
.send({ messageIds: [firstMessageId, privateMessageId] })
|
|
.expect(403);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
|
|
it('should resolve the readable subset for the same user when the unreadable id is dropped', async () => {
|
|
const res = await request
|
|
.post(api('chat.getMessages'))
|
|
.set(outsiderCredentials)
|
|
.send({ messageIds: [firstMessageId] })
|
|
.expect(200);
|
|
|
|
expect(res.body.messages).to.have.lengthOf(1);
|
|
});
|
|
|
|
it('should fail when messageIds is empty', async () => {
|
|
const res = await request.post(api('chat.getMessages')).set(credentials).send({ messageIds: [] }).expect(400);
|
|
|
|
expect(res.body).to.have.property('success', false);
|
|
});
|
|
|
|
it('should fail when unauthenticated', async () => {
|
|
await request
|
|
.post(api('chat.getMessages'))
|
|
.send({ messageIds: [firstMessageId] })
|
|
.expect(401);
|
|
});
|
|
});
|
|
});
|