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

796 lines
29 KiB
TypeScript

import type { Credentials } from '@rocket.chat/api-client';
import type { IInstanceStatus, IRoom, ITeam, IUser } from '@rocket.chat/core-typings';
import { TeamType } from '@rocket.chat/core-typings';
import type { IInstance } from '@rocket.chat/rest-typings';
import { AssertionError, expect } from 'chai';
import { after, before, describe, it } from 'mocha';
import { MongoClient } from 'mongodb';
import { getCredentials, api, request, credentials } from '../../data/api-data';
import { updatePermission, updateSetting } from '../../data/permissions.helper';
import { createRoom, deleteRoom } from '../../data/rooms.helper';
import { createTeam, deleteTeam } from '../../data/teams.helper';
import { adminEmail, adminUsername, adminPassword, password } from '../../data/user';
import type { TestUser } from '../../data/users.helper';
import { createUser, deleteUser, login as doLogin } from '../../data/users.helper';
import { IS_EE, URL_MONGODB } from '../../e2e/config/constants';
describe('miscellaneous', () => {
before((done) => getCredentials(done));
describe('API default', () => {
// Required by mobile apps
describe('/info', () => {
let version: string;
it('should return "version", "build", "commit" and "marketplaceApiVersion" when the user is logged in', async () => {
const res = await request.get('/api/info').set(credentials).expect('Content-Type', 'application/json').expect(200);
expect(res.body).to.have.property('version').and.to.be.a('string');
expect(res.body.info).to.have.property('version').and.to.be.a('string');
expect(res.body.info).to.have.property('build').and.to.be.an('object');
expect(res.body.info).to.have.property('commit').and.to.be.an('object');
expect(res.body.info).to.have.property('marketplaceApiVersion').and.to.be.a('string');
version = res.body.info.version;
});
it('should return only "version" and the version should not have patch info when the user is not logged in', async () => {
const res = await request.get('/api/info').expect('Content-Type', 'application/json').expect(200);
expect(res.body).to.have.property('version');
expect(res.body).to.not.have.property('info');
expect(res.body.version).to.be.equal(version.replace(/(\d+\.\d+).*/, '$1'));
});
});
});
it('/login', () => {
expect(credentials).to.have.property('X-Auth-Token').with.lengthOf.at.least(1);
expect(credentials).to.have.property('X-User-Id').with.lengthOf.at.least(1);
});
it('/login (wrapper username)', async () => {
const res = await request
.post(api('login'))
.send({
user: {
username: adminUsername,
},
password: adminPassword,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('status', 'success');
expect(res.body).to.have.property('data').and.to.be.an('object');
expect(res.body.data).to.have.property('userId');
expect(res.body.data).to.have.property('authToken');
expect(res.body.data).to.have.property('me');
});
it('/login (wrapper email)', async () => {
const res = await request
.post(api('login'))
.send({
user: {
email: adminEmail,
},
password: adminPassword,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('status', 'success');
expect(res.body).to.have.property('data').and.to.be.an('object');
expect(res.body.data).to.have.property('userId');
expect(res.body.data).to.have.property('authToken');
expect(res.body.data).to.have.property('me');
});
it('/login by user', async () => {
const res = await request
.post(api('login'))
.send({
user: adminEmail,
password: adminPassword,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('status', 'success');
expect(res.body).to.have.property('data').and.to.be.an('object');
expect(res.body.data).to.have.property('userId');
expect(res.body.data).to.have.property('authToken');
expect(res.body.data).to.have.property('me');
expect(res.body.data.me.services).to.not.have.nested.property('password.bcrypt');
expect(res.body.data.me.services).to.have.nested.property('password.exists', true);
});
it('/login by username', async () => {
const res = await request
.post(api('login'))
.send({
username: adminUsername,
password: adminPassword,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('status', 'success');
expect(res.body).to.have.property('data').and.to.be.an('object');
expect(res.body.data).to.have.property('userId');
expect(res.body.data).to.have.property('authToken');
expect(res.body.data).to.have.property('me');
expect(res.body.data.me.services).to.not.have.nested.property('password.bcrypt');
expect(res.body.data.me.services).to.have.nested.property('password.exists', true);
});
it('/me', async () => {
const user = await createUser();
const userCredentials = await doLogin(user.username, password);
await request
.get(api('me'))
.set(userCredentials)
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
const allUserPreferencesKeys = [
'alsoSendThreadToChannel',
// 'language',
'newRoomNotification',
'newMessageNotification',
'showThreadsInMainChannel',
// 'clockMode',
'useEmojis',
'convertAsciiEmoji',
'saveMobileBandwidth',
'collapseMediaByDefault',
'autoImageLoad',
'emailNotificationMode',
'unreadAlert',
'masterVolume',
'notificationsSoundVolume',
'voipRingerVolume',
'omnichannelTranscriptEmail',
IS_EE ? 'omnichannelTranscriptPDF' : false,
'desktopNotifications',
'pushNotifications',
'enableAutoAway',
// 'highlights',
'desktopNotificationRequireInteraction',
'hideUsernames',
'hideRoles',
'displayAvatars',
'hideFlexTab',
'sendOnEnter',
'idleTimeLimit',
'sidebarShowFavorites',
'sidebarShowUnread',
'themeAppearence',
'sidebarSortby',
'sidebarViewMode',
'sidebarDisplayAvatar',
'sidebarGroupByType',
'sidebarSectionsOrder',
'muteFocusedConversations',
'notifyCalendarEvents',
'enableMobileRinging',
'featuresPreview',
'desktopNotificationVoiceCalls',
].filter((p) => Boolean(p));
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('_id', user._id);
expect(res.body).to.have.property('username', user.username);
expect(res.body).to.have.property('active');
expect(res.body).to.have.property('name');
expect(res.body).to.have.property('roles').and.to.be.an('array');
expect(res.body).to.have.nested.property('emails[0].address', user.emails[0].address);
expect(res.body).to.have.nested.property('settings.preferences').and.to.be.an('object');
expect(res.body.settings.preferences).to.have.all.keys(allUserPreferencesKeys);
expect(res.body).to.have.property('isOAuthUser', false);
expect(res.body.services).to.not.have.nested.property('password.bcrypt');
expect(res.body.services).to.have.nested.property('password.exists', true);
});
await deleteUser(user);
});
describe('/directory', () => {
let user: TestUser<IUser>;
let userWithLeftoverFederationData: TestUser<IUser>;
let testChannel: IRoom;
let testGroup: IRoom;
let normalUserCredentials: Credentials;
const teamName = `new-team-name-${Date.now()}` as const;
let teamCreated: ITeam;
before(async () => {
await updatePermission('create-team', ['admin', 'user']);
user = await createUser();
userWithLeftoverFederationData = await createUser();
normalUserCredentials = await doLogin(user.username, password);
[testChannel, testGroup, teamCreated] = await Promise.all([
createRoom({ name: `channel.test.${Date.now()}`, type: 'c' }).then((res) => res.body.channel),
createRoom({ name: `group.test.${Date.now()}`, type: 'p' }).then((res) => res.body.group),
createTeam(normalUserCredentials, teamName, TeamType.PUBLIC),
]);
const connection = await MongoClient.connect(URL_MONGODB);
const updateResult = await connection
.db()
.collection('users')
.updateOne({ _id: userWithLeftoverFederationData._id as any }, { $set: { federation: { origin: 'example.com' } } });
await connection.close();
expect(updateResult.modifiedCount).to.equal(1);
});
after(async () => {
await Promise.all([
deleteTeam(normalUserCredentials, teamName),
deleteUser(user),
deleteUser(userWithLeftoverFederationData),
deleteRoom({ type: 'c', roomId: testChannel._id }),
deleteRoom({ type: 'p', roomId: testGroup._id }),
updatePermission('create-team', ['admin', 'user']),
]);
});
it('should return an array(result) when search by user and execute successfully', async () => {
const res = await request
.get(api('directory'))
.set(credentials)
.query({
text: user.username,
type: 'users',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('result').and.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');
expect(res.body.result[0]).to.have.property('_id');
expect(res.body.result[0]).to.have.property('createdAt');
expect(res.body.result[0]).to.have.property('username');
expect(res.body.result[0]).to.have.property('emails').and.to.be.an('array');
expect(res.body.result[0]).to.have.property('name');
});
it('should not return the emails field for non admins', async () => {
const res = await request
.get(api('directory'))
.set(normalUserCredentials)
.query({
text: user.username,
type: 'users',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('result').and.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');
expect(res.body.result[0]).to.have.property('_id');
expect(res.body.result[0]).to.have.property('createdAt');
expect(res.body.result[0]).to.have.property('username');
expect(res.body.result[0]).to.not.have.property('emails');
expect(res.body.result[0]).to.have.property('name');
});
it('should find local users that still have leftover federation data', async () => {
const res = await request
.get(api('directory'))
.set(credentials)
.query({
text: userWithLeftoverFederationData.username,
type: 'users',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body.result).to.have.lengthOf(1);
expect(res.body.result[0]).to.have.property('_id', userWithLeftoverFederationData._id);
});
it('should return an array(result) when search by channel and execute successfully', async () => {
const res = await request
.get(api('directory'))
.set(credentials)
.query({
text: testChannel.name,
type: 'channels',
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('count');
expect(res.body).to.have.property('result').and.to.be.an('array');
expect(res.body.result[0]).to.have.property('_id');
expect(res.body.result[0]).to.have.property('name');
expect(res.body.result[0]).to.have.property('usersCount').and.to.be.an('number');
expect(res.body.result[0]).to.have.property('ts');
});
it('should return private group when search by channel and execute successfully', async () => {
await request
.get(api('directory'))
.set(credentials)
.query({
text: testGroup.name,
type: 'channels',
})
.expect('Content-Type', 'application/json')
.expect(200)
.expect((res) => {
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('count');
expect(res.body).to.have.property('result').and.to.be.an('array');
expect(res.body.result[0]).to.have.property('_id', testGroup._id);
expect(res.body.result[0]).to.have.property('t', 'p');
expect(res.body.result[0]).to.have.property('name');
expect(res.body.result[0]).to.have.property('usersCount').and.to.be.an('number');
expect(res.body.result[0]).to.have.property('ts');
});
});
it('should return an array(result) when search by channel with sort params correctly and execute successfully', async () => {
const res = await request
.get(api('directory'))
.set(credentials)
.query({
text: testChannel.name,
type: 'channels',
sort: JSON.stringify({
name: 1,
}),
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('offset');
expect(res.body).to.have.property('total');
expect(res.body).to.have.property('count');
expect(res.body).to.have.property('result').and.to.be.an('array');
expect(res.body.result[0]).to.have.property('_id');
expect(res.body.result[0]).to.have.property('name');
expect(res.body.result[0]).to.have.property('usersCount').and.to.be.an('number');
expect(res.body.result[0]).to.have.property('ts');
});
it('should return an error when send invalid query', async () => {
const res = await request
.get(api('directory'))
.set(credentials)
.query({
text: 'invalid channel',
type: 'invalid',
})
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
});
it('should return an error when have more than one sort parameter', async () => {
const res = await request
.get(api('directory'))
.set(credentials)
.query({
text: testChannel.name,
type: 'channels',
sort: JSON.stringify({
name: 1,
test: 1,
}),
})
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
});
it('should return an object containing rooms and totalCount from teams', async () => {
const res = await request
.get(api('directory'))
.set(normalUserCredentials)
.query({
text: '',
type: 'teams',
sort: JSON.stringify({
name: 1,
}),
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('result');
expect(res.body.result).to.be.an(`array`);
expect(res.body).to.have.property('total');
expect(res.body.total).to.be.an('number');
expect(res.body.result[0]).to.have.property('_id', teamCreated.roomId);
expect(res.body.result[0]).to.have.property('fname');
expect(res.body.result[0]).to.have.property('teamMain');
expect(res.body.result[0]).to.have.property('name');
expect(res.body.result[0]).to.have.property('t');
expect(res.body.result[0]).to.have.property('usersCount');
expect(res.body.result[0]).to.have.property('ts');
expect(res.body.result[0]).to.have.property('teamId');
expect(res.body.result[0]).to.have.property('default');
expect(res.body.result[0]).to.have.property('roomsCount');
});
});
describe('[/spotlight]', () => {
let user: TestUser<IUser>;
let userCredentials: Credentials;
let testChannel: IRoom;
let testTeam: ITeam;
let testChannelSpecialChars: IRoom;
const fnameSpecialCharsRoom = `test ГДΕληνικά-${Date.now()}`;
const teamName = `team-test-${Date.now()}`;
before(async () => {
user = await createUser();
userCredentials = await doLogin(user.username, password);
await updateSetting('UI_Allow_room_names_with_special_chars', true);
testChannelSpecialChars = (await createRoom({ type: 'c', name: fnameSpecialCharsRoom, credentials: userCredentials })).body.channel;
testChannel = (await createRoom({ type: 'c', name: `channel.test.${Date.now()}`, credentials: userCredentials })).body.channel;
testTeam = await createTeam(userCredentials, teamName, TeamType.PUBLIC);
});
after(async () => {
await Promise.all([
deleteUser(user),
updateSetting('UI_Allow_room_names_with_special_chars', false),
deleteTeam(userCredentials, teamName),
]);
});
it('should fail when does not have query param', async () => {
const res = await request.get(api('spotlight')).set(credentials).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 object inside users array when search by a valid user', async () => {
const res = await request
.get(api('spotlight'))
.query({
query: `@${adminUsername}`,
})
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('users').and.to.be.an('array');
expect(res.body.users[0]).to.have.property('_id');
expect(res.body.users[0]).to.have.property('name');
expect(res.body.users[0]).to.have.property('username');
expect(res.body.users[0]).to.have.property('status');
expect(res.body).to.have.property('rooms').and.to.be.an('array');
});
it('must return the object inside the room array when searching for a valid room and that user is not a member of it', async () => {
const res = await request
.get(api('spotlight'))
.query({
query: `#${testChannel.name}`,
})
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('users').and.to.be.an('array');
expect(res.body).to.have.property('rooms').and.to.be.an('array');
expect(res.body.rooms[0]).to.have.property('_id');
expect(res.body.rooms[0]).to.have.property('name');
expect(res.body.rooms[0]).to.have.property('t');
});
it('must return the teamMain property when searching for a valid team that the user is not a member of', async () => {
const res = await request
.get(api('spotlight'))
.query({
query: `${testTeam.name}`,
})
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('users').and.to.be.an('array');
expect(res.body).to.have.property('rooms').and.to.be.an('array');
expect(res.body.rooms[0]).to.have.property('_id');
expect(res.body.rooms[0]).to.have.property('name');
expect(res.body.rooms[0]).to.have.property('t');
expect(res.body.rooms[0]).to.have.property('teamMain');
});
it('must return rooms when searching for a valid fname', async () => {
const res = await request
.get(api('spotlight'))
.query({
query: `#${fnameSpecialCharsRoom}`,
})
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('users').and.to.be.an('array');
expect(res.body).to.have.property('rooms').and.to.be.an('array');
expect(res.body.rooms[0]).to.have.property('_id', testChannelSpecialChars._id);
expect(res.body.rooms[0]).to.have.property('name', testChannelSpecialChars.name);
expect(res.body.rooms[0]).to.have.property('t', testChannelSpecialChars.t);
});
it('should not return users when the type param disables user search', async () => {
const res = await request
.get(api('spotlight'))
.query({
query: `${adminUsername}`,
type: JSON.stringify({ users: false, rooms: true }),
})
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('users').and.to.be.an('array').that.is.empty;
expect(res.body).to.have.property('rooms').and.to.be.an('array');
});
it('should exclude usernames passed in the usernames param from the results', async () => {
const res = await request
.get(api('spotlight'))
.query({
// Use a non-exact (prefix) query so the regex search path runs; the exact-username
// match branch in Spotlight.searchUsers does not honor the usernames exclusion list.
query: adminUsername.slice(0, -2),
usernames: adminUsername,
})
.set(credentials)
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('users').and.to.be.an('array');
expect(res.body.users.map((u: { username: string }) => u.username)).to.not.include(adminUsername);
});
describe('anonymous (unauthenticated) requests', () => {
after(() => updateSetting('Accounts_AllowAnonymousRead', false));
it('should return no rooms when anonymous read is disabled', async () => {
await updateSetting('Accounts_AllowAnonymousRead', false);
const res = await request
.get(api('spotlight'))
.query({
query: `#${testChannel.name}`,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('rooms').and.to.be.an('array').that.is.empty;
expect(res.body).to.have.property('users').and.to.be.an('array').that.is.empty;
});
// An unprefixed query keeps `type.users` enabled, so it also runs the user search with no
// user id - the code path that used to throw before the anonymous callers were guarded.
it('should return no rooms or users for an unprefixed query when anonymous read is disabled', async () => {
await updateSetting('Accounts_AllowAnonymousRead', false);
const res = await request
.get(api('spotlight'))
.query({
query: testChannel.name,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('rooms').and.to.be.an('array').that.is.empty;
expect(res.body).to.have.property('users').and.to.be.an('array').that.is.empty;
});
it('should return public rooms but no users when anonymous read is enabled', async () => {
await updateSetting('Accounts_AllowAnonymousRead', true);
const res = await request
.get(api('spotlight'))
.query({
query: `#${testChannel.name}`,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body.rooms.map((r: { _id: string }) => r._id)).to.include(testChannel._id);
expect(res.body).to.have.property('users').and.to.be.an('array').that.is.empty;
});
it('should return public rooms but no users for an unprefixed query when anonymous read is enabled', async () => {
await updateSetting('Accounts_AllowAnonymousRead', true);
const res = await request
.get(api('spotlight'))
.query({
query: testChannel.name,
})
.expect('Content-Type', 'application/json')
.expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body.rooms.map((r: { _id: string }) => r._id)).to.include(testChannel._id);
expect(res.body).to.have.property('users').and.to.be.an('array').that.is.empty;
});
});
});
describe('[/instances.get]', () => {
let unauthorizedUserCredentials: Credentials;
before(async () => {
const createdUser = await createUser();
unauthorizedUserCredentials = await doLogin(createdUser.username, password);
});
it('should fail if user is logged in but is unauthorized', async () => {
const res = await request
.get(api('instances.get'))
.set(unauthorizedUserCredentials)
.expect('Content-Type', 'application/json')
.expect(403);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'User does not have the permissions required for this action [error-unauthorized]');
});
it('should fail if not logged in', async () => {
const res = await request.get(api('instances.get')).expect('Content-Type', 'application/json').expect(401);
expect(res.body).to.have.property('status', 'error');
expect(res.body).to.have.property('message');
});
it('should return instances if user is logged in and is authorized', async () => {
const res = await request.get(api('instances.get')).set(credentials).expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('instances').and.to.be.an('array').with.lengthOf(1);
const instances = res.body.instances as IInstance[];
const instanceName = IS_EE ? 'ddp-streamer' : 'rocket.chat';
const instance = instances.find((i): i is IInstance & { instanceRecord: IInstanceStatus } => i.instanceRecord?.name === instanceName);
if (!instance) throw new AssertionError(`no instance named "${instanceName}"`);
expect(instance).to.have.property('instanceRecord');
expect(instance).to.have.property('currentStatus');
expect(instance.currentStatus).to.have.property('connected');
expect(instance.instanceRecord).to.have.property('_id');
expect(instance.instanceRecord).to.have.property('extraInformation');
expect(instance.instanceRecord).to.have.property('name');
expect(instance.instanceRecord).to.have.property('pid');
if (!IS_EE) {
expect(instance).to.have.property('address');
expect(instance.currentStatus).to.have.property('lastHeartbeatTime');
expect(instance.currentStatus).to.have.property('local');
const { extraInformation } = instance.instanceRecord;
expect(extraInformation).to.have.property('host');
expect(extraInformation).to.have.property('port');
expect(extraInformation).to.have.property('os').and.to.have.property('cpus').to.be.a('number');
expect(extraInformation).to.have.property('nodeVersion');
}
});
});
describe('[/shield.svg]', () => {
before(() => updateSetting('API_Enable_Shields', false));
after(() => updateSetting('API_Enable_Shields', true));
it('should fail if API_Enable_Shields is disabled', async () => {
const res = await request
.get(api('shield.svg'))
.query({
type: 'online',
icon: true,
channel: 'general',
name: 'Rocket.Chat',
})
.expect('Content-Type', 'application/json')
.expect(400);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('errorType', 'error-endpoint-disabled');
});
it('should succeed if API_Enable_Shields is enabled', async () => {
await updateSetting('API_Enable_Shields', true);
await request
.get(api('shield.svg'))
.query({
type: 'online',
icon: true,
channel: 'general',
name: 'Rocket.Chat',
})
.expect('Content-Type', 'image/svg+xml;charset=utf-8')
.expect(200);
});
});
describe('/pw.getPolicy', () => {
it('should return policies', async () => {
const res = await request.get(api('pw.getPolicy')).set(credentials).expect('Content-Type', 'application/json').expect(200);
expect(res.body).to.have.property('success', true);
expect(res.body).to.have.property('enabled');
expect(res.body).to.have.property('policy').and.to.be.an('array');
});
});
describe('/fingerprint', () => {
let unauthorizedUser: TestUser<IUser>;
let unauthorizedUserCredentials: Credentials;
before(async () => {
unauthorizedUser = await createUser();
unauthorizedUserCredentials = await doLogin(unauthorizedUser.username, password);
});
after(async () => {
await deleteUser(unauthorizedUser);
});
it('should return 401 when called without authentication', async () => {
const res = await request.post(api('fingerprint')).send({ setDeploymentAs: 'updated-configuration' });
expect(res.status).to.equal(401);
expect(res.body).to.have.property('status', 'error');
});
it('should return 403 when a user without the manage-cloud permission tries to acknowledge a deployment configuration change', async () => {
const res = await request
.post(api('fingerprint'))
.set(unauthorizedUserCredentials)
.send({ setDeploymentAs: 'updated-configuration' });
expect(res.status).to.equal(403);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'User does not have the permissions required for this action [error-unauthorized]');
});
it('should return 403 when a user without the manage-cloud permission tries to deregister the workspace as a new workspace', async () => {
const res = await request.post(api('fingerprint')).set(unauthorizedUserCredentials).send({ setDeploymentAs: 'new-workspace' });
expect(res.status).to.equal(403);
expect(res.body).to.have.property('success', false);
expect(res.body).to.have.property('error', 'User does not have the permissions required for this action [error-unauthorized]');
});
it('should return 200 when a user with the manage-cloud permission acknowledges a deployment configuration change', async () => {
const res = await request.post(api('fingerprint')).set(credentials).send({ setDeploymentAs: 'updated-configuration' });
expect(res.status).to.equal(200);
expect(res.body).to.have.property('success', true);
});
});
});