snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
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
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
This commit is contained in:
24
packages/apps/tests/server/managers/AppApi.test.ts
Normal file
24
packages/apps/tests/server/managers/AppApi.test.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
import type { IApi } from '@rocket.chat/apps-engine/definition/api';
|
||||
import type { IApiEndpoint } from '@rocket.chat/apps-engine/definition/api/IApiEndpoint';
|
||||
|
||||
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
|
||||
import { AppApi } from '../../../src/server/managers/AppApi';
|
||||
|
||||
describe('AppApi', () => {
|
||||
it('ensureAppApi', () => {
|
||||
const mockApp = {
|
||||
getID() {
|
||||
return 'id';
|
||||
},
|
||||
} as ProxiedApp;
|
||||
|
||||
assert.doesNotThrow(() => new AppApi(mockApp, {} as IApi, {} as IApiEndpoint));
|
||||
|
||||
const ascr = new AppApi(mockApp, {} as IApi, {} as IApiEndpoint);
|
||||
assert.ok(ascr.app !== undefined);
|
||||
assert.ok(ascr.api !== undefined);
|
||||
});
|
||||
});
|
||||
219
packages/apps/tests/server/managers/AppApiManager.test.ts
Normal file
219
packages/apps/tests/server/managers/AppApiManager.test.ts
Normal file
@@ -0,0 +1,219 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
|
||||
|
||||
import { RequestMethod } from '@rocket.chat/apps-engine/definition/accessors';
|
||||
import type { IApi, IApiRequest } from '@rocket.chat/apps-engine/definition/api';
|
||||
|
||||
import type { AppManager } from '../../../src/server/AppManager';
|
||||
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
|
||||
import type { AppBridges } from '../../../src/server/bridges';
|
||||
import type {
|
||||
AppExternalComponentManager,
|
||||
AppSchedulerManager,
|
||||
AppSlashCommandManager,
|
||||
AppVideoConfProviderManager,
|
||||
} from '../../../src/server/managers';
|
||||
import { AppApiManager } from '../../../src/server/managers';
|
||||
import { AppApi } from '../../../src/server/managers/AppApi';
|
||||
import type { UIActionButtonManager } from '../../../src/server/managers/UIActionButtonManager';
|
||||
import type { AppLogStorage, IAppStorageItem } from '../../../src/server/storage';
|
||||
import { TestsAppBridges } from '../../test-data/bridges/appBridges';
|
||||
import { TestsAppLogStorage } from '../../test-data/storage/logStorage';
|
||||
import { TestData } from '../../test-data/utilities';
|
||||
|
||||
describe('AppApiManager', () => {
|
||||
let mockBridges: TestsAppBridges;
|
||||
let mockApp: ProxiedApp;
|
||||
let mockManager: AppManager;
|
||||
|
||||
beforeEach(() => {
|
||||
mockBridges = new TestsAppBridges();
|
||||
|
||||
mockApp = TestData.getMockApp({ info: { id: 'testing', name: 'TestApp' } } as IAppStorageItem, {} as AppManager);
|
||||
|
||||
const bri = mockBridges;
|
||||
const app = mockApp;
|
||||
mockManager = {
|
||||
getBridges(): AppBridges {
|
||||
return bri;
|
||||
},
|
||||
getCommandManager() {
|
||||
return {} as AppSlashCommandManager;
|
||||
},
|
||||
getExternalComponentManager() {
|
||||
return {} as AppExternalComponentManager;
|
||||
},
|
||||
getApiManager() {
|
||||
return {} as AppApiManager;
|
||||
},
|
||||
getOneById(appId: string): ProxiedApp {
|
||||
return appId === 'failMePlease' ? undefined : app;
|
||||
},
|
||||
getLogStorage(): AppLogStorage {
|
||||
return new TestsAppLogStorage();
|
||||
},
|
||||
getSchedulerManager() {
|
||||
return {} as AppSchedulerManager;
|
||||
},
|
||||
getUIActionButtonManager() {
|
||||
return {} as UIActionButtonManager;
|
||||
},
|
||||
getVideoConfProviderManager() {
|
||||
return {} as AppVideoConfProviderManager;
|
||||
},
|
||||
} as AppManager;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restoreAll();
|
||||
});
|
||||
|
||||
it('basicAppApiManager', () => {
|
||||
assert.throws(() => new AppApiManager({} as AppManager));
|
||||
assert.doesNotThrow(() => new AppApiManager(mockManager));
|
||||
|
||||
const ascm = new AppApiManager(mockManager);
|
||||
assert.strictEqual((ascm as any).manager, mockManager);
|
||||
assert.strictEqual((ascm as any).bridge, mockBridges.getApiBridge());
|
||||
assert.ok((ascm as any).providedApis !== undefined);
|
||||
assert.strictEqual((ascm as any).providedApis.size, 0);
|
||||
});
|
||||
|
||||
it('registerApi', async () => {
|
||||
const doRegisterApiSpy = mock.method(mockBridges.getApiBridge(), 'doRegisterApi');
|
||||
const ascm = new AppApiManager(mockManager);
|
||||
|
||||
const api: IApi = TestData.getApi('path');
|
||||
const regInfo = new AppApi(mockApp, api, api.endpoints[0]);
|
||||
|
||||
await assert.doesNotReject(() => (ascm as any).registerApi('testing', regInfo));
|
||||
assert.strictEqual(doRegisterApiSpy.mock.calls.length, 1);
|
||||
assert.deepStrictEqual(doRegisterApiSpy.mock.calls[0].arguments, [regInfo, 'testing']);
|
||||
});
|
||||
|
||||
it('addApi', () => {
|
||||
mockBridges = new TestsAppBridges();
|
||||
const bri = mockBridges;
|
||||
mockManager.getBridges = function _refreshedGetBridges(): AppBridges {
|
||||
return bri;
|
||||
};
|
||||
|
||||
const doRegisterApiSpy = mock.method(mockBridges.getApiBridge(), 'doRegisterApi');
|
||||
void doRegisterApiSpy;
|
||||
|
||||
const api = TestData.getApi('apipath');
|
||||
const ascm = new AppApiManager(mockManager);
|
||||
assert.doesNotThrow(() => ascm.addApi('testing', api));
|
||||
assert.strictEqual(mockBridges.getApiBridge().apis.size, 1);
|
||||
assert.strictEqual((ascm as any).providedApis.size, 1);
|
||||
assert.strictEqual((ascm as any).providedApis.get('testing').get('apipath').api, api);
|
||||
|
||||
assert.throws(() => ascm.addApi('testing', api), {
|
||||
name: 'PathAlreadyExists',
|
||||
message: 'The api path "apipath" already exists in the system.',
|
||||
});
|
||||
|
||||
assert.throws(() => ascm.addApi('failMePlease', TestData.getApi('yet-another')), {
|
||||
name: 'Error',
|
||||
message: 'App must exist in order for an api to be added.',
|
||||
});
|
||||
assert.doesNotThrow(() => ascm.addApi('testing', TestData.getApi('another-api')));
|
||||
assert.strictEqual((ascm as any).providedApis.size, 1);
|
||||
assert.strictEqual((ascm as any).providedApis.get('testing').size, 2);
|
||||
});
|
||||
|
||||
it('registerApis', async () => {
|
||||
mockBridges = new TestsAppBridges();
|
||||
const bri = mockBridges;
|
||||
mockManager.getBridges = function _refreshedGetBridges(): AppBridges {
|
||||
return bri;
|
||||
};
|
||||
const doRegisterApiSpy = mock.method(mockBridges.getApiBridge(), 'doRegisterApi');
|
||||
|
||||
const ascm = new AppApiManager(mockManager);
|
||||
|
||||
const registerApiSpy = mock.method(ascm as any, 'registerApi');
|
||||
|
||||
ascm.addApi('testing', TestData.getApi('apipath'));
|
||||
const regInfo = (ascm as any).providedApis.get('testing').get('apipath') as AppApi;
|
||||
|
||||
await assert.doesNotReject(() => ascm.registerApis('non-existant'));
|
||||
await assert.doesNotReject(() => ascm.registerApis('testing'));
|
||||
assert.strictEqual(registerApiSpy.mock.calls.filter((c: any) => c.arguments[0] === 'testing' && c.arguments[1] === regInfo).length, 1);
|
||||
assert.strictEqual(
|
||||
doRegisterApiSpy.mock.calls.filter((c: any) => c.arguments[0] === regInfo && c.arguments[1] === 'testing').length,
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
it('unregisterApis', async () => {
|
||||
mockBridges = new TestsAppBridges();
|
||||
const bri = mockBridges;
|
||||
mockManager.getBridges = function _refreshedGetBridges(): AppBridges {
|
||||
return bri;
|
||||
};
|
||||
const doUnregisterApisSpy = mock.method(mockBridges.getApiBridge(), 'doUnregisterApis');
|
||||
|
||||
const ascm = new AppApiManager(mockManager);
|
||||
|
||||
ascm.addApi('testing', TestData.getApi('apipath'));
|
||||
|
||||
await assert.doesNotReject(() => ascm.unregisterApis('non-existant'));
|
||||
await assert.doesNotReject(() => ascm.unregisterApis('testing'));
|
||||
assert.strictEqual(doUnregisterApisSpy.mock.calls.length, 1);
|
||||
});
|
||||
|
||||
it('executeApis', async () => {
|
||||
mockBridges = new TestsAppBridges();
|
||||
const bri = mockBridges;
|
||||
mockManager.getBridges = function _refreshedGetBridges(): AppBridges {
|
||||
return bri;
|
||||
};
|
||||
|
||||
const ascm = new AppApiManager(mockManager);
|
||||
ascm.addApi('testing', TestData.getApi('api1'));
|
||||
ascm.addApi('testing', TestData.getApi('api2'));
|
||||
ascm.addApi('testing', TestData.getApi('api3'));
|
||||
await ascm.registerApis('testing');
|
||||
|
||||
const request: IApiRequest = {
|
||||
method: RequestMethod.GET,
|
||||
headers: {},
|
||||
query: {},
|
||||
params: {},
|
||||
content: '',
|
||||
};
|
||||
|
||||
await assert.doesNotReject(() => ascm.executeApi('testing', 'nope', request));
|
||||
await assert.doesNotReject(() => ascm.executeApi('testing', 'not-exists', request));
|
||||
await assert.doesNotReject(() => ascm.executeApi('testing', 'api1', request));
|
||||
await assert.doesNotReject(() => ascm.executeApi('testing', 'api2', request));
|
||||
await assert.doesNotReject(() => ascm.executeApi('testing', 'api3', request));
|
||||
});
|
||||
|
||||
it('listApis', async () => {
|
||||
mockBridges = new TestsAppBridges();
|
||||
const bri = mockBridges;
|
||||
mockManager.getBridges = function _refreshedGetBridges(): AppBridges {
|
||||
return bri;
|
||||
};
|
||||
|
||||
const ascm = new AppApiManager(mockManager);
|
||||
|
||||
assert.deepStrictEqual(ascm.listApis('testing'), []);
|
||||
|
||||
ascm.addApi('testing', TestData.getApi('api1'));
|
||||
await ascm.registerApis('testing');
|
||||
|
||||
assert.doesNotThrow(() => ascm.listApis('testing'));
|
||||
assert.notDeepStrictEqual(ascm.listApis('testing'), []);
|
||||
assert.deepStrictEqual(ascm.listApis('testing'), [
|
||||
{
|
||||
path: 'api1',
|
||||
computedPath: '/api/apps/public/testing/api1',
|
||||
methods: ['get'],
|
||||
examples: {},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,137 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
import type { IExternalComponent } from '@rocket.chat/apps-engine/definition/externalComponent/IExternalComponent';
|
||||
import { ExternalComponentLocation } from '@rocket.chat/apps-engine/definition/externalComponent/IExternalComponent';
|
||||
|
||||
import { AppExternalComponentManager } from '../../../src/server/managers';
|
||||
|
||||
describe('AppExternalComponentManager', () => {
|
||||
const mockExternalComponent1: IExternalComponent = {
|
||||
appId: '1eb382c0-3679-44a6-8af0-18802e342fb1',
|
||||
name: 'TestExternalComponent1',
|
||||
description: 'TestExternalComponent1',
|
||||
url: '',
|
||||
icon: '',
|
||||
location: ExternalComponentLocation.CONTEXTUAL_BAR,
|
||||
};
|
||||
|
||||
const mockExternalComponent2: IExternalComponent = {
|
||||
appId: '125a944b-9747-4e6e-b029-6e9b26bb3481',
|
||||
name: 'TestExternalComponent2',
|
||||
description: 'TestExternalComponent2',
|
||||
url: '',
|
||||
icon: '',
|
||||
location: ExternalComponentLocation.CONTEXTUAL_BAR,
|
||||
};
|
||||
|
||||
const mockExternalComponent3: IExternalComponent = {
|
||||
...mockExternalComponent2,
|
||||
appId: mockExternalComponent1.appId,
|
||||
name: mockExternalComponent2.name,
|
||||
description: 'TestExternalComponent3',
|
||||
};
|
||||
|
||||
function register(aecm: AppExternalComponentManager, externalComponent: IExternalComponent): void {
|
||||
const { appId } = externalComponent;
|
||||
aecm.addExternalComponent(appId, externalComponent);
|
||||
aecm.registerExternalComponents(appId);
|
||||
}
|
||||
|
||||
it('basicAppExternalComponentManager', () => {
|
||||
const aecm = new AppExternalComponentManager();
|
||||
|
||||
assert.strictEqual((aecm as any).registeredExternalComponents.size, 0);
|
||||
assert.strictEqual((aecm as any).appTouchedExternalComponents.size, 0);
|
||||
});
|
||||
|
||||
it('verifyGetRegisteredExternalComponents', () => {
|
||||
const aecm = new AppExternalComponentManager();
|
||||
const component = mockExternalComponent1;
|
||||
|
||||
assert.strictEqual(aecm.getRegisteredExternalComponents().size, 0);
|
||||
|
||||
register(aecm, component);
|
||||
assert.strictEqual(aecm.getRegisteredExternalComponents().size, 1);
|
||||
});
|
||||
|
||||
it('verifyGetAppTouchedExternalComponents', () => {
|
||||
const aecm = new AppExternalComponentManager();
|
||||
const component = mockExternalComponent1;
|
||||
|
||||
assert.strictEqual(aecm.getAppTouchedExternalComponents().size, 0);
|
||||
|
||||
aecm.addExternalComponent(component.appId, component);
|
||||
assert.strictEqual(aecm.getAppTouchedExternalComponents().size, 1);
|
||||
});
|
||||
|
||||
it('verifyGetExternalComponents', () => {
|
||||
const aecm = new AppExternalComponentManager();
|
||||
const component = mockExternalComponent1;
|
||||
|
||||
assert.strictEqual(aecm.getExternalComponents(component.appId), null);
|
||||
aecm.addExternalComponent(component.appId, component);
|
||||
const components = aecm.getExternalComponents(component.appId);
|
||||
assert.notStrictEqual(components, null);
|
||||
assert.strictEqual(components!.size, 1);
|
||||
});
|
||||
|
||||
it('verifyGetProvidedComponents', () => {
|
||||
const aecm = new AppExternalComponentManager();
|
||||
const component1 = mockExternalComponent1;
|
||||
const component2 = mockExternalComponent2;
|
||||
|
||||
assert.strictEqual(Array.isArray(aecm.getProvidedComponents()), true);
|
||||
register(aecm, component1);
|
||||
register(aecm, component2);
|
||||
assert.strictEqual(aecm.getProvidedComponents().length, 2);
|
||||
});
|
||||
|
||||
it('verifyAddExternalComponent', () => {
|
||||
const aecm1 = new AppExternalComponentManager();
|
||||
const component1 = mockExternalComponent1;
|
||||
const component3 = mockExternalComponent3;
|
||||
|
||||
aecm1.addExternalComponent(component1.appId, component1);
|
||||
assert.strictEqual(aecm1.getAppTouchedExternalComponents().size, 1);
|
||||
assert.strictEqual(aecm1.getExternalComponents(component1.appId).size, 1);
|
||||
|
||||
aecm1.addExternalComponent(component1.appId, component3);
|
||||
assert.strictEqual(aecm1.getExternalComponents(component1.appId).size, 2);
|
||||
});
|
||||
|
||||
it('verifyRegisterExternalComponents', () => {
|
||||
const aecm = new AppExternalComponentManager();
|
||||
const component = mockExternalComponent1;
|
||||
|
||||
assert.strictEqual(aecm.getRegisteredExternalComponents().size, 0);
|
||||
register(aecm, component);
|
||||
assert.strictEqual(aecm.getRegisteredExternalComponents().size, 1);
|
||||
});
|
||||
|
||||
it('verifyUnregisterExternalComponents', () => {
|
||||
const aecm = new AppExternalComponentManager();
|
||||
const component = mockExternalComponent1;
|
||||
|
||||
register(aecm, component);
|
||||
assert.strictEqual(aecm.getAppTouchedExternalComponents().size, 1);
|
||||
assert.strictEqual(aecm.getRegisteredExternalComponents().size, 1);
|
||||
|
||||
aecm.unregisterExternalComponents(component.appId);
|
||||
assert.strictEqual(aecm.getAppTouchedExternalComponents().size, 1);
|
||||
assert.strictEqual(aecm.getRegisteredExternalComponents().size, 0);
|
||||
});
|
||||
|
||||
it('verifyPurgeExternalComponents', () => {
|
||||
const aecm = new AppExternalComponentManager();
|
||||
const component = mockExternalComponent1;
|
||||
|
||||
register(aecm, component);
|
||||
assert.strictEqual(aecm.getAppTouchedExternalComponents().size, 1);
|
||||
assert.strictEqual(aecm.getRegisteredExternalComponents().size, 1);
|
||||
|
||||
aecm.purgeExternalComponents(component.appId);
|
||||
assert.strictEqual(aecm.getAppTouchedExternalComponents().size, 0);
|
||||
assert.strictEqual(aecm.getRegisteredExternalComponents().size, 0);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
import { AppInterface } from '@rocket.chat/apps-engine/definition/metadata';
|
||||
|
||||
import type { AppManager } from '../../../src/server/AppManager';
|
||||
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
|
||||
import { AppListenerManager } from '../../../src/server/managers';
|
||||
|
||||
describe('AppListenerManager', () => {
|
||||
const mockApp = {
|
||||
getID() {
|
||||
return 'testing';
|
||||
},
|
||||
getImplementationList() {
|
||||
return {
|
||||
[AppInterface.IPostMessageSent]: true,
|
||||
} as { [inte: string]: boolean };
|
||||
},
|
||||
} as ProxiedApp;
|
||||
|
||||
const mockManager = {
|
||||
getOneById(appId: string) {
|
||||
return mockApp;
|
||||
},
|
||||
} as AppManager;
|
||||
|
||||
it('basicAppListenerManager', () => {
|
||||
assert.doesNotThrow(() => new AppListenerManager(mockManager));
|
||||
|
||||
const alm = new AppListenerManager(mockManager);
|
||||
|
||||
assert.strictEqual(alm.getListeners(AppInterface.IPostMessageSent).length, 0);
|
||||
assert.doesNotThrow(() => alm.registerListeners(mockApp));
|
||||
assert.strictEqual(alm.getListeners(AppInterface.IPostMessageSent).length, 1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
import type { IOutboundMessageProviders } from '@rocket.chat/apps-engine/definition/outboundCommunication';
|
||||
|
||||
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
|
||||
import { OutboundMessageProvider } from '../../../src/server/managers/AppOutboundCommunicationProvider';
|
||||
|
||||
describe('OutboundMessageProvider', () => {
|
||||
it('ensureAppOutboundCommunicationProviderManager', () => {
|
||||
const mockApp = {} as ProxiedApp;
|
||||
|
||||
assert.doesNotThrow(() => new OutboundMessageProvider(mockApp, {} as IOutboundMessageProviders));
|
||||
|
||||
const aocp = new OutboundMessageProvider(mockApp, {} as IOutboundMessageProviders);
|
||||
|
||||
assert.strictEqual(aocp.isRegistered, false);
|
||||
|
||||
aocp.setRegistered(true);
|
||||
|
||||
assert.strictEqual(aocp.isRegistered, true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,276 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
|
||||
|
||||
import type { AppManager } from '../../../src/server/AppManager';
|
||||
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
|
||||
import type { AppBridges } from '../../../src/server/bridges';
|
||||
import type { AppApiManager, AppExternalComponentManager, AppSchedulerManager, AppSlashCommandManager } from '../../../src/server/managers';
|
||||
import { AppOutboundCommunicationProviderManager } from '../../../src/server/managers';
|
||||
import { OutboundMessageProvider } from '../../../src/server/managers/AppOutboundCommunicationProvider';
|
||||
import { AppPermissionManager } from '../../../src/server/managers/AppPermissionManager';
|
||||
import type { UIActionButtonManager } from '../../../src/server/managers/UIActionButtonManager';
|
||||
import type { AppLogStorage, IAppStorageItem } from '../../../src/server/storage';
|
||||
import { TestsAppBridges } from '../../test-data/bridges/appBridges';
|
||||
import { TestsAppLogStorage } from '../../test-data/storage/logStorage';
|
||||
import { TestData } from '../../test-data/utilities';
|
||||
|
||||
describe('AppOutboundCommunicationProviderManager', () => {
|
||||
let mockBridges: TestsAppBridges;
|
||||
let mockApp: ProxiedApp;
|
||||
let mockManager: AppManager;
|
||||
let hasPermissionSpy: ReturnType<typeof mock.method>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockBridges = new TestsAppBridges();
|
||||
|
||||
mockApp = TestData.getMockApp({ info: { id: 'testing', name: 'testing' } } as IAppStorageItem, {} as AppManager);
|
||||
|
||||
const bri = mockBridges;
|
||||
const app = mockApp;
|
||||
|
||||
mockManager = {
|
||||
getBridges(): AppBridges {
|
||||
return bri;
|
||||
},
|
||||
getCommandManager() {
|
||||
return {} as AppSlashCommandManager;
|
||||
},
|
||||
getExternalComponentManager(): AppExternalComponentManager {
|
||||
return {} as AppExternalComponentManager;
|
||||
},
|
||||
getApiManager() {
|
||||
return {} as AppApiManager;
|
||||
},
|
||||
getOneById(appId: string): ProxiedApp {
|
||||
return appId === 'failMePlease' ? undefined : app;
|
||||
},
|
||||
getLogStorage(): AppLogStorage {
|
||||
return new TestsAppLogStorage();
|
||||
},
|
||||
getSchedulerManager() {
|
||||
return {} as AppSchedulerManager;
|
||||
},
|
||||
getUIActionButtonManager() {
|
||||
return {} as UIActionButtonManager;
|
||||
},
|
||||
getOutboundCommunicationProviderManager() {
|
||||
return {} as AppOutboundCommunicationProviderManager;
|
||||
},
|
||||
} as AppManager;
|
||||
|
||||
hasPermissionSpy = mock.method(AppPermissionManager, 'hasPermission', () => true);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restoreAll();
|
||||
});
|
||||
|
||||
it('basicAppOutboundCommunicationProviderManager', () => {
|
||||
assert.throws(() => new AppOutboundCommunicationProviderManager({} as AppManager));
|
||||
assert.doesNotThrow(() => new AppOutboundCommunicationProviderManager(mockManager));
|
||||
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
assert.strictEqual((manager as any).manager, mockManager);
|
||||
assert.ok((manager as any).outboundMessageProviders !== undefined);
|
||||
assert.strictEqual((manager as any).outboundMessageProviders.size, 0);
|
||||
});
|
||||
|
||||
it('addProvider', () => {
|
||||
const provider = TestData.getOutboundPhoneMessageProvider();
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
|
||||
assert.doesNotThrow(() => manager.addProvider('testing', provider));
|
||||
assert.strictEqual((manager as any).outboundMessageProviders.size, 1);
|
||||
assert.throws(() => manager.addProvider('failMePlease', provider), {
|
||||
name: 'Error',
|
||||
message: 'App must exist in order for an outbound provider to be added.',
|
||||
});
|
||||
assert.strictEqual((manager as any).outboundMessageProviders.size, 1);
|
||||
});
|
||||
|
||||
it('isAlreadyDefined', () => {
|
||||
const provider = TestData.getOutboundPhoneMessageProvider();
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
|
||||
assert.strictEqual(manager.isAlreadyDefined('testing', 'phone'), false);
|
||||
|
||||
manager.addProvider('testing', provider);
|
||||
|
||||
assert.strictEqual(manager.isAlreadyDefined('testing', 'phone'), true);
|
||||
assert.strictEqual(manager.isAlreadyDefined('testing', 'email'), false);
|
||||
assert.strictEqual(manager.isAlreadyDefined('another-app', 'phone'), false);
|
||||
});
|
||||
|
||||
it('addProviderTwiceShouldOverwrite', () => {
|
||||
const provider1 = TestData.getOutboundPhoneMessageProvider('provider1');
|
||||
const provider2 = TestData.getOutboundPhoneMessageProvider('provider2');
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('testing', provider1);
|
||||
const firstProviderInfo = (manager as any).outboundMessageProviders.get('testing').get('phone');
|
||||
assert.strictEqual(firstProviderInfo.provider.name, 'provider1');
|
||||
|
||||
// Adding a provider of the same type should overwrite the previous one
|
||||
manager.addProvider('testing', provider2);
|
||||
const secondProviderInfo = (manager as any).outboundMessageProviders.get('testing').get('phone');
|
||||
assert.strictEqual(secondProviderInfo.provider.name, 'provider2');
|
||||
assert.strictEqual((manager as any).outboundMessageProviders.get('testing').size, 1);
|
||||
});
|
||||
|
||||
it('addProviderWithoutPermission', () => {
|
||||
const provider = TestData.getOutboundPhoneMessageProvider();
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
|
||||
hasPermissionSpy.mock.mockImplementation(() => false);
|
||||
|
||||
assert.throws(() => manager.addProvider('testing', provider));
|
||||
});
|
||||
|
||||
it('ignoreAppsWithoutProviders', async () => {
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
|
||||
await assert.doesNotReject(() => manager.registerProviders('non-existant'));
|
||||
});
|
||||
|
||||
it('registerProviders', async () => {
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('firstApp', TestData.getOutboundPhoneMessageProvider());
|
||||
const appInfo = (manager as any).outboundMessageProviders.get('firstApp');
|
||||
assert.ok(appInfo !== undefined);
|
||||
const regInfo = appInfo.get('phone');
|
||||
assert.ok(regInfo !== undefined);
|
||||
|
||||
assert.strictEqual(regInfo.isRegistered, false);
|
||||
await assert.doesNotReject(async () => manager.registerProviders('firstApp'));
|
||||
assert.strictEqual(regInfo.isRegistered, true);
|
||||
});
|
||||
|
||||
it('registerTwoProviders', async () => {
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('firstApp', TestData.getOutboundPhoneMessageProvider());
|
||||
manager.addProvider('firstApp', TestData.getOutboundEmailMessageProvider());
|
||||
const firstApp = (manager as any).outboundMessageProviders.get('firstApp');
|
||||
assert.ok(firstApp !== undefined);
|
||||
const firstRegInfo = firstApp.get('phone');
|
||||
assert.ok(firstRegInfo !== undefined);
|
||||
const secondRegInfo = firstApp.get('email');
|
||||
assert.ok(secondRegInfo !== undefined);
|
||||
|
||||
assert.strictEqual(firstRegInfo.isRegistered, false);
|
||||
assert.strictEqual(secondRegInfo.isRegistered, false);
|
||||
await assert.doesNotReject(async () => manager.registerProviders('firstApp'));
|
||||
assert.strictEqual(firstRegInfo.isRegistered, true);
|
||||
assert.strictEqual(secondRegInfo.isRegistered, true);
|
||||
});
|
||||
|
||||
it('registerProvidersFromMultipleApps', async () => {
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('firstApp', TestData.getOutboundPhoneMessageProvider());
|
||||
manager.addProvider('firstApp', TestData.getOutboundEmailMessageProvider());
|
||||
manager.addProvider('secondApp', TestData.getOutboundPhoneMessageProvider('another-phone-provider'));
|
||||
|
||||
const firstApp = (manager as any).outboundMessageProviders.get('firstApp');
|
||||
assert.ok(firstApp !== undefined);
|
||||
const firstRegInfo = firstApp.get('phone');
|
||||
const secondRegInfo = firstApp.get('email');
|
||||
assert.ok(firstRegInfo !== undefined);
|
||||
assert.ok(secondRegInfo !== undefined);
|
||||
const secondApp = (manager as any).outboundMessageProviders.get('secondApp');
|
||||
assert.ok(secondApp !== undefined);
|
||||
const thirdRegInfo = secondApp.get('phone');
|
||||
assert.ok(thirdRegInfo !== undefined);
|
||||
|
||||
assert.strictEqual(firstRegInfo.isRegistered, false);
|
||||
assert.strictEqual(secondRegInfo.isRegistered, false);
|
||||
await assert.doesNotReject(async () => manager.registerProviders('firstApp'));
|
||||
assert.strictEqual(firstRegInfo.isRegistered, true);
|
||||
assert.strictEqual(secondRegInfo.isRegistered, true);
|
||||
assert.strictEqual(thirdRegInfo.isRegistered, false);
|
||||
await assert.doesNotReject(async () => manager.registerProviders('secondApp'));
|
||||
assert.strictEqual(thirdRegInfo.isRegistered, true);
|
||||
});
|
||||
|
||||
it('unregisterProviders', async () => {
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
|
||||
const regInfo = (manager as any).outboundMessageProviders.get('testing').get('phone');
|
||||
await assert.doesNotReject(async () => manager.registerProviders('testing'));
|
||||
|
||||
await assert.doesNotReject(async () => manager.unregisterProviders('non-existant'));
|
||||
assert.strictEqual(regInfo.isRegistered, true);
|
||||
await assert.doesNotReject(async () => manager.unregisterProviders('testing'));
|
||||
assert.strictEqual(regInfo.isRegistered, false);
|
||||
// It should be removed from the map
|
||||
assert.strictEqual((manager as any).outboundMessageProviders.has('testing'), false);
|
||||
});
|
||||
|
||||
it('unregisterProvidersWithKeepReferences', async () => {
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
|
||||
const appInfo = (manager as any).outboundMessageProviders.get('testing');
|
||||
const regInfo = appInfo.get('phone');
|
||||
|
||||
await assert.doesNotReject(async () => manager.registerProviders('testing'));
|
||||
assert.strictEqual(regInfo.isRegistered, true);
|
||||
await assert.doesNotReject(async () => manager.unregisterProviders('testing', { keepReferences: true }));
|
||||
assert.strictEqual(regInfo.isRegistered, false);
|
||||
// It should not be removed from the map
|
||||
assert.strictEqual((manager as any).outboundMessageProviders.has('testing'), true);
|
||||
});
|
||||
|
||||
it('failToGetMetadataWithoutProvider', () => {
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
|
||||
assert.throws(() => manager.getProviderMetadata('testing', 'phone'), { name: 'Error', message: 'provider-not-registered' });
|
||||
|
||||
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
|
||||
|
||||
assert.throws(() => manager.getProviderMetadata('testing', 'email'), { name: 'Error', message: 'provider-not-registered' });
|
||||
});
|
||||
|
||||
it('getProviderMetadata', async () => {
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
|
||||
|
||||
mock.method(OutboundMessageProvider.prototype, 'runGetProviderMetadata', () =>
|
||||
Promise.resolve({
|
||||
name: 'test-provider',
|
||||
capabilities: ['sms'],
|
||||
}),
|
||||
);
|
||||
|
||||
const metadata = await manager.getProviderMetadata('testing', 'phone');
|
||||
assert.deepStrictEqual(metadata, {
|
||||
name: 'test-provider',
|
||||
capabilities: ['sms'],
|
||||
});
|
||||
});
|
||||
|
||||
it('failToSendOutboundMessageWithoutProvider', () => {
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
const message = TestData.getOutboundMessage();
|
||||
|
||||
assert.throws(() => manager.sendOutboundMessage('testing', 'phone', message), { name: 'Error', message: 'provider-not-registered' });
|
||||
|
||||
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
|
||||
|
||||
assert.throws(() => manager.sendOutboundMessage('testing', 'email', message), { name: 'Error', message: 'provider-not-registered' });
|
||||
});
|
||||
|
||||
it('sendOutboundMessage', async () => {
|
||||
const manager = new AppOutboundCommunicationProviderManager(mockManager);
|
||||
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
|
||||
|
||||
const message = TestData.getOutboundMessage();
|
||||
|
||||
mock.method(OutboundMessageProvider.prototype, 'runSendOutboundMessage', () => Promise.resolve('message-id'));
|
||||
|
||||
const result = await manager.sendOutboundMessage('testing', 'phone', message);
|
||||
assert.strictEqual(result, 'message-id');
|
||||
});
|
||||
});
|
||||
140
packages/apps/tests/server/managers/AppRuntimeManager.test.ts
Normal file
140
packages/apps/tests/server/managers/AppRuntimeManager.test.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
|
||||
|
||||
import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus';
|
||||
|
||||
import type { AppManager } from '../../../src/server/AppManager';
|
||||
import type { IParseAppPackageResult } from '../../../src/server/compiler';
|
||||
import { AppRuntimeManager } from '../../../src/server/managers/AppRuntimeManager';
|
||||
import type { IRuntimeController } from '../../../src/server/runtime/IRuntimeController';
|
||||
import type { DenoRuntimeSubprocessController } from '../../../src/server/runtime/deno/AppsEngineDenoRuntime';
|
||||
import type { IAppStorageItem } from '../../../src/server/storage';
|
||||
import { TestInfastructureSetup } from '../../test-data/utilities';
|
||||
|
||||
describe('AppRuntimeManager', () => {
|
||||
let mockManager: AppManager;
|
||||
let runtimeManager: AppRuntimeManager;
|
||||
let mockAppPackage: IParseAppPackageResult;
|
||||
let mockStorageItem: IAppStorageItem;
|
||||
let mockSubprocessController: IRuntimeController;
|
||||
|
||||
beforeEach(() => {
|
||||
const testInfrastructure = new TestInfastructureSetup();
|
||||
|
||||
mockManager = testInfrastructure.getMockManager();
|
||||
|
||||
mockAppPackage = {
|
||||
info: {
|
||||
id: 'test-app',
|
||||
name: 'Test App',
|
||||
nameSlug: 'test-app',
|
||||
version: '1.0.0',
|
||||
description: 'Test app for unit testing',
|
||||
author: {
|
||||
name: 'Test Author',
|
||||
homepage: 'https://test.com',
|
||||
support: 'https://test.com/support',
|
||||
},
|
||||
permissions: [],
|
||||
requiredApiVersion: '1.0.0',
|
||||
classFile: 'main.js',
|
||||
iconFile: 'icon.png',
|
||||
implements: [],
|
||||
},
|
||||
files: {
|
||||
'main.js': 'console.log("Hello World");',
|
||||
},
|
||||
languageContent: {} as unknown as IParseAppPackageResult['languageContent'],
|
||||
implemented: {} as unknown as IParseAppPackageResult['implemented'],
|
||||
} as IParseAppPackageResult;
|
||||
|
||||
mockStorageItem = {
|
||||
id: 'test-app',
|
||||
status: AppStatus.MANUALLY_ENABLED,
|
||||
info: mockAppPackage.info,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
} as IAppStorageItem;
|
||||
|
||||
mockSubprocessController = {
|
||||
async setupApp() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
async stopApp() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
getAppId() {
|
||||
return 'test-app';
|
||||
},
|
||||
|
||||
async getStatus() {
|
||||
return Promise.resolve(AppStatus.MANUALLY_ENABLED);
|
||||
},
|
||||
|
||||
async sendRequest() {
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
|
||||
on: () => mockSubprocessController,
|
||||
once: () => mockSubprocessController,
|
||||
off: () => mockSubprocessController,
|
||||
emit: () => true,
|
||||
addListener: () => mockSubprocessController,
|
||||
removeListener: () => mockSubprocessController,
|
||||
removeAllListeners: () => mockSubprocessController,
|
||||
setMaxListeners: () => mockSubprocessController,
|
||||
getMaxListeners: () => 10,
|
||||
listeners: () => [],
|
||||
rawListeners: () => [],
|
||||
listenerCount: () => 0,
|
||||
prependListener: () => mockSubprocessController,
|
||||
prependOnceListener: () => mockSubprocessController,
|
||||
eventNames: () => [],
|
||||
} as IRuntimeController;
|
||||
|
||||
runtimeManager = new AppRuntimeManager(mockManager, () => mockSubprocessController as unknown as DenoRuntimeSubprocessController);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restoreAll();
|
||||
});
|
||||
|
||||
it('Starts runtime for app successfully', async () => {
|
||||
await assert.doesNotReject(() => runtimeManager.startRuntimeForApp(mockAppPackage, mockStorageItem));
|
||||
|
||||
/* eslint-disable-next-line dot-notation -- We need to access the property like this for the compile not to complain */
|
||||
assert.strictEqual(runtimeManager['subprocesses'][mockAppPackage.info.id], mockSubprocessController);
|
||||
});
|
||||
|
||||
it('Fails to start runtime for app that already has a runtime', async () => {
|
||||
await assert.doesNotReject(() => runtimeManager.startRuntimeForApp(mockAppPackage, mockStorageItem));
|
||||
|
||||
await assert.rejects(() => runtimeManager.startRuntimeForApp(mockAppPackage, mockStorageItem), {
|
||||
name: 'Error',
|
||||
message: 'App already has an associated runtime',
|
||||
});
|
||||
});
|
||||
|
||||
it('Starts multiple runtimes for app successfully with force option', async () => {
|
||||
await assert.doesNotReject(() => runtimeManager.startRuntimeForApp(mockAppPackage, mockStorageItem));
|
||||
|
||||
await assert.doesNotReject(() => runtimeManager.startRuntimeForApp(mockAppPackage, mockStorageItem, { force: true }));
|
||||
|
||||
/* eslint-disable-next-line dot-notation -- We need to access the property like this for the compile not to complain */
|
||||
assert.strictEqual(runtimeManager['subprocesses'][mockAppPackage.info.id], mockSubprocessController);
|
||||
});
|
||||
|
||||
it('startRuntimeThatFailsToSetup', async () => {
|
||||
mock.method(mockSubprocessController, 'setupApp', () => Promise.reject(new Error('Nope')));
|
||||
|
||||
await assert.rejects(() => runtimeManager.startRuntimeForApp(mockAppPackage, mockStorageItem), {
|
||||
name: 'Error',
|
||||
message: 'Nope',
|
||||
});
|
||||
|
||||
/* eslint-disable-next-line dot-notation -- We need to access the property like this for the compile not to complain */
|
||||
assert.strictEqual(runtimeManager['subprocesses'][mockAppPackage.info.id], undefined);
|
||||
});
|
||||
});
|
||||
152
packages/apps/tests/server/managers/AppSettingsManager.test.ts
Normal file
152
packages/apps/tests/server/managers/AppSettingsManager.test.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
|
||||
|
||||
import { AppMethod } from '@rocket.chat/apps-engine/definition/metadata';
|
||||
import type { ISetting } from '@rocket.chat/apps-engine/definition/settings';
|
||||
|
||||
import type { AppManager } from '../../../src/server/AppManager';
|
||||
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
|
||||
import type { AppBridges } from '../../../src/server/bridges';
|
||||
import type {
|
||||
AppApiManager,
|
||||
AppExternalComponentManager,
|
||||
AppSchedulerManager,
|
||||
AppSlashCommandManager,
|
||||
AppVideoConfProviderManager,
|
||||
} from '../../../src/server/managers';
|
||||
import { AppSettingsManager } from '../../../src/server/managers';
|
||||
import type { UIActionButtonManager } from '../../../src/server/managers/UIActionButtonManager';
|
||||
import type { AppMetadataStorage, IAppStorageItem } from '../../../src/server/storage';
|
||||
import { TestsAppBridges } from '../../test-data/bridges/appBridges';
|
||||
import { TestData } from '../../test-data/utilities';
|
||||
|
||||
describe('AppSettingsManager', () => {
|
||||
let mockStorageItem: IAppStorageItem;
|
||||
let mockApp: ProxiedApp;
|
||||
let mockBridges: AppBridges;
|
||||
let mockStorage: AppMetadataStorage;
|
||||
let mockManager: AppManager;
|
||||
|
||||
beforeEach(() => {
|
||||
mockStorageItem = {
|
||||
_id: 'test_underscore_id',
|
||||
settings: {},
|
||||
} as IAppStorageItem;
|
||||
|
||||
mockStorageItem.settings.testing = TestData.getSetting('testing');
|
||||
|
||||
const si = mockStorageItem;
|
||||
mockApp = {
|
||||
getID() {
|
||||
return 'testing';
|
||||
},
|
||||
getStorageItem() {
|
||||
return si;
|
||||
},
|
||||
setStorageItem(item: IAppStorageItem) {},
|
||||
call(method: AppMethod, ...args: Array<any>): Promise<any> {
|
||||
return Promise.resolve();
|
||||
},
|
||||
} as ProxiedApp;
|
||||
|
||||
mockBridges = new TestsAppBridges();
|
||||
|
||||
mockStorage = {
|
||||
updateSetting(appId: string, setting: ISetting): Promise<boolean> {
|
||||
return Promise.resolve(true);
|
||||
},
|
||||
} as AppMetadataStorage;
|
||||
|
||||
const st = mockStorage;
|
||||
const bri = mockBridges;
|
||||
const app = mockApp;
|
||||
mockManager = {
|
||||
getOneById(appId: string) {
|
||||
return appId === 'testing' ? app : undefined;
|
||||
},
|
||||
getBridges(): AppBridges {
|
||||
return bri;
|
||||
},
|
||||
getStorage(): AppMetadataStorage {
|
||||
return st;
|
||||
},
|
||||
getCommandManager(): AppSlashCommandManager {
|
||||
return {} as AppSlashCommandManager;
|
||||
},
|
||||
getExternalComponentManager(): AppExternalComponentManager {
|
||||
return {} as AppExternalComponentManager;
|
||||
},
|
||||
getApiManager(): AppApiManager {
|
||||
return {} as AppApiManager;
|
||||
},
|
||||
getSchedulerManager() {
|
||||
return {} as AppSchedulerManager;
|
||||
},
|
||||
getUIActionButtonManager() {
|
||||
return {} as UIActionButtonManager;
|
||||
},
|
||||
getVideoConfProviderManager() {
|
||||
return {} as AppVideoConfProviderManager;
|
||||
},
|
||||
} as AppManager;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restoreAll();
|
||||
});
|
||||
|
||||
it('basicAppSettingsManager', () => {
|
||||
assert.doesNotThrow(() => new AppSettingsManager(mockManager));
|
||||
|
||||
const asm = new AppSettingsManager(mockManager);
|
||||
assert.notStrictEqual(asm.getAppSettings('testing'), mockStorageItem.settings);
|
||||
assert.deepStrictEqual(asm.getAppSettings('testing'), mockStorageItem.settings);
|
||||
assert.throws(() => asm.getAppSettings('fake'), { name: 'Error', message: 'No App found by the provided id.' });
|
||||
assert.throws(() => {
|
||||
asm.getAppSettings('testing').testing.value = 'testing';
|
||||
});
|
||||
|
||||
assert.notStrictEqual(asm.getAppSetting('testing', 'testing'), mockStorageItem.settings.testing);
|
||||
assert.deepStrictEqual(asm.getAppSetting('testing', 'testing'), mockStorageItem.settings.testing);
|
||||
assert.throws(() => asm.getAppSetting('fake', 'testing'), { name: 'Error', message: 'No App found by the provided id.' });
|
||||
assert.throws(() => asm.getAppSetting('testing', 'fake'), {
|
||||
name: 'Error',
|
||||
message: 'No setting found for the App by the provided id.',
|
||||
});
|
||||
assert.throws(() => {
|
||||
asm.getAppSetting('testing', 'testing').value = 'testing';
|
||||
});
|
||||
});
|
||||
|
||||
it('updatingSettingViaAppSettingsManager', async () => {
|
||||
const asm = new AppSettingsManager(mockManager);
|
||||
|
||||
const updateSettingSpy = mock.method(mockStorage, 'updateSetting');
|
||||
const callSpy = mock.method(mockApp, 'call');
|
||||
const doOnAppSettingsChangeSpy = mock.method((mockBridges as TestsAppBridges).getAppDetailChangesBridge(), 'doOnAppSettingsChange');
|
||||
|
||||
await assert.rejects(() => asm.updateAppSetting('fake', TestData.getSetting()), {
|
||||
name: 'Error',
|
||||
message: 'No App found by the provided id.',
|
||||
});
|
||||
await assert.rejects(() => asm.updateAppSetting('testing', TestData.getSetting('fake')), {
|
||||
name: 'Error',
|
||||
message: 'No setting found for the App by the provided id.',
|
||||
});
|
||||
|
||||
const set = TestData.getSetting('testing');
|
||||
await assert.doesNotReject(() => asm.updateAppSetting('testing', set));
|
||||
|
||||
assert.strictEqual(updateSettingSpy.mock.calls.length, 1);
|
||||
assert.strictEqual(updateSettingSpy.mock.calls[0].arguments[0], 'test_underscore_id');
|
||||
const settingArg = updateSettingSpy.mock.calls[0].arguments[1] as any;
|
||||
assert.ok(Object.keys(set).every((k) => (settingArg as any)[k] === (set as any)[k]));
|
||||
|
||||
assert.strictEqual(doOnAppSettingsChangeSpy.mock.calls.length, 1);
|
||||
assert.deepStrictEqual(doOnAppSettingsChangeSpy.mock.calls[0].arguments, ['testing', set]);
|
||||
|
||||
const onsettingupdatedCalls = callSpy.mock.calls.filter((c: any) => c.arguments[0] === AppMethod.ONSETTINGUPDATED);
|
||||
assert.strictEqual(onsettingupdatedCalls.length, 1);
|
||||
assert.deepStrictEqual(onsettingupdatedCalls[0].arguments, [AppMethod.ONSETTINGUPDATED, set]);
|
||||
});
|
||||
});
|
||||
27
packages/apps/tests/server/managers/AppSlashCommand.test.ts
Normal file
27
packages/apps/tests/server/managers/AppSlashCommand.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
import type { ISlashCommand } from '@rocket.chat/apps-engine/definition/slashcommands';
|
||||
|
||||
import type { AppManager } from '../../../src/server/AppManager';
|
||||
import { AppSlashCommand } from '../../../src/server/managers/AppSlashCommand';
|
||||
import type { IAppStorageItem } from '../../../src/server/storage';
|
||||
import { TestData } from '../../test-data/utilities';
|
||||
|
||||
describe('AppSlashCommand', () => {
|
||||
it('ensureAppSlashCommand', () => {
|
||||
const mockApp = TestData.getMockApp({ info: { id: 'test', name: 'TestApp' } } as IAppStorageItem, {} as AppManager);
|
||||
|
||||
assert.doesNotThrow(() => new AppSlashCommand(mockApp, {} as ISlashCommand));
|
||||
|
||||
const ascr = new AppSlashCommand(mockApp, {} as ISlashCommand);
|
||||
assert.strictEqual(ascr.isRegistered, false);
|
||||
assert.strictEqual(ascr.isEnabled, false);
|
||||
assert.strictEqual(ascr.isDisabled, false);
|
||||
|
||||
ascr.hasBeenRegistered();
|
||||
assert.strictEqual(ascr.isDisabled, false);
|
||||
assert.strictEqual(ascr.isEnabled, true);
|
||||
assert.strictEqual(ascr.isRegistered, true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,454 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
|
||||
|
||||
import type { ISlashCommandPreviewItem } from '@rocket.chat/apps-engine/definition/slashcommands';
|
||||
import { SlashCommandContext } from '@rocket.chat/apps-engine/definition/slashcommands';
|
||||
|
||||
import type { AppManager } from '../../../src/server/AppManager';
|
||||
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
|
||||
import type { AppBridges } from '../../../src/server/bridges';
|
||||
import type {
|
||||
AppApiManager,
|
||||
AppExternalComponentManager,
|
||||
AppSchedulerManager,
|
||||
AppVideoConfProviderManager,
|
||||
} from '../../../src/server/managers';
|
||||
import { AppSlashCommandManager } from '../../../src/server/managers';
|
||||
import { AppSlashCommand } from '../../../src/server/managers/AppSlashCommand';
|
||||
import type { UIActionButtonManager } from '../../../src/server/managers/UIActionButtonManager';
|
||||
import { Room } from '../../../src/server/rooms/Room';
|
||||
import type { AppLogStorage, IAppStorageItem } from '../../../src/server/storage';
|
||||
import { TestsAppBridges } from '../../test-data/bridges/appBridges';
|
||||
import { TestsAppLogStorage } from '../../test-data/storage/logStorage';
|
||||
import { TestData } from '../../test-data/utilities';
|
||||
|
||||
describe('AppSlashCommandManager', () => {
|
||||
let mockBridges: TestsAppBridges;
|
||||
let mockApp: ProxiedApp;
|
||||
let mockManager: AppManager;
|
||||
|
||||
function setupMocks() {
|
||||
mockBridges = new TestsAppBridges();
|
||||
const bri = mockBridges;
|
||||
mockManager.getBridges = function _refreshedGetBridges(): AppBridges {
|
||||
return bri;
|
||||
};
|
||||
|
||||
mock.method(mockBridges.getCommandBridge(), 'doDoesCommandExist');
|
||||
mock.method(mockBridges.getCommandBridge(), 'doRegisterCommand');
|
||||
mock.method(mockBridges.getCommandBridge(), 'doUnregisterCommand');
|
||||
mock.method(mockBridges.getCommandBridge(), 'doEnableCommand');
|
||||
mock.method(mockBridges.getCommandBridge(), 'doDisableCommand');
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
mockBridges = new TestsAppBridges();
|
||||
|
||||
mockApp = TestData.getMockApp({ info: { id: 'testing', name: 'TestApp' } } as IAppStorageItem, {} as AppManager);
|
||||
|
||||
const bri = mockBridges;
|
||||
const app = mockApp;
|
||||
mockManager = {
|
||||
getBridges(): AppBridges {
|
||||
return bri;
|
||||
},
|
||||
getCommandManager() {
|
||||
return {} as AppSlashCommandManager;
|
||||
},
|
||||
getExternalComponentManager(): AppExternalComponentManager {
|
||||
return {} as AppExternalComponentManager;
|
||||
},
|
||||
getApiManager() {
|
||||
return {} as AppApiManager;
|
||||
},
|
||||
getOneById(appId: string): ProxiedApp {
|
||||
return appId === 'failMePlease' ? undefined : app;
|
||||
},
|
||||
getLogStorage(): AppLogStorage {
|
||||
return new TestsAppLogStorage();
|
||||
},
|
||||
getSchedulerManager() {
|
||||
return {} as AppSchedulerManager;
|
||||
},
|
||||
getUIActionButtonManager() {
|
||||
return {} as UIActionButtonManager;
|
||||
},
|
||||
getVideoConfProviderManager() {
|
||||
return {} as AppVideoConfProviderManager;
|
||||
},
|
||||
} as AppManager;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restoreAll();
|
||||
});
|
||||
|
||||
it('basicAppSlashCommandManager', () => {
|
||||
setupMocks();
|
||||
assert.throws(() => new AppSlashCommandManager({} as AppManager));
|
||||
assert.doesNotThrow(() => new AppSlashCommandManager(mockManager));
|
||||
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
assert.strictEqual((ascm as any).manager, mockManager);
|
||||
assert.strictEqual((ascm as any).bridge, mockBridges.getCommandBridge());
|
||||
assert.ok((ascm as any).providedCommands !== undefined);
|
||||
assert.strictEqual((ascm as any).providedCommands.size, 0);
|
||||
assert.ok((ascm as any).modifiedCommands !== undefined);
|
||||
assert.strictEqual((ascm as any).modifiedCommands.size, 0);
|
||||
assert.ok((ascm as any).touchedCommandsToApps !== undefined);
|
||||
assert.strictEqual((ascm as any).touchedCommandsToApps.size, 0);
|
||||
assert.ok((ascm as any).appsTouchedCommands !== undefined);
|
||||
assert.strictEqual((ascm as any).appsTouchedCommands.size, 0);
|
||||
});
|
||||
|
||||
it('canCommandBeTouchedBy', () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
|
||||
assert.strictEqual(ascm.canCommandBeTouchedBy('testing', 'command'), true);
|
||||
(ascm as any).touchedCommandsToApps.set('just-a-test', 'anotherAppId');
|
||||
assert.strictEqual(ascm.canCommandBeTouchedBy('testing', 'just-a-test'), false);
|
||||
});
|
||||
|
||||
it('isAlreadyDefined', () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
|
||||
const reg = new Map<string, AppSlashCommand>();
|
||||
reg.set('command', new AppSlashCommand(mockApp, TestData.getSlashCommand('command')));
|
||||
|
||||
assert.strictEqual(ascm.isAlreadyDefined('command'), false);
|
||||
(ascm as any).providedCommands.set('testing', reg);
|
||||
assert.strictEqual(ascm.isAlreadyDefined('command'), true);
|
||||
assert.strictEqual(ascm.isAlreadyDefined('cOMMand'), true);
|
||||
assert.strictEqual(ascm.isAlreadyDefined(' command '), true);
|
||||
assert.strictEqual(ascm.isAlreadyDefined('c0mmand'), false);
|
||||
});
|
||||
|
||||
it('setAsTouched', () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
|
||||
assert.doesNotThrow(() => (ascm as any).setAsTouched('testing', 'command'));
|
||||
assert.strictEqual((ascm as any).appsTouchedCommands.has('testing'), true);
|
||||
assert.ok((ascm as any).appsTouchedCommands.get('testing').length > 0);
|
||||
assert.strictEqual((ascm as any).appsTouchedCommands.get('testing').length, 1);
|
||||
assert.strictEqual((ascm as any).touchedCommandsToApps.has('command'), true);
|
||||
assert.strictEqual((ascm as any).touchedCommandsToApps.get('command'), 'testing');
|
||||
assert.doesNotThrow(() => (ascm as any).setAsTouched('testing', 'command'));
|
||||
assert.strictEqual((ascm as any).appsTouchedCommands.get('testing').length, 1);
|
||||
});
|
||||
|
||||
it('registerCommand', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
const doRegisterCommandSpy = (mockBridges.getCommandBridge() as any).doRegisterCommand;
|
||||
|
||||
const regInfo = new AppSlashCommand(mockApp, TestData.getSlashCommand('command'));
|
||||
|
||||
await assert.doesNotReject(() => (ascm as any).registerCommand('testing', regInfo));
|
||||
assert.strictEqual(doRegisterCommandSpy.mock.calls.length, 1);
|
||||
assert.deepStrictEqual(doRegisterCommandSpy.mock.calls[0].arguments, [regInfo.slashCommand, 'testing']);
|
||||
assert.strictEqual(regInfo.isRegistered, true);
|
||||
assert.strictEqual(regInfo.isDisabled, false);
|
||||
assert.strictEqual(regInfo.isEnabled, true);
|
||||
});
|
||||
|
||||
it('addCommand', async () => {
|
||||
setupMocks();
|
||||
const cmd = TestData.getSlashCommand('my-cmd');
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
|
||||
await assert.doesNotReject(async () => ascm.addCommand('testing', cmd));
|
||||
assert.strictEqual(mockBridges.getCommandBridge().commands.size, 1);
|
||||
assert.strictEqual((ascm as any).providedCommands.size, 1);
|
||||
assert.strictEqual((ascm as any).touchedCommandsToApps.get('my-cmd'), 'testing');
|
||||
assert.strictEqual((ascm as any).appsTouchedCommands.get('testing').length, 1);
|
||||
await assert.rejects(() => ascm.addCommand('another-app', cmd), {
|
||||
name: 'CommandHasAlreadyBeenTouched',
|
||||
message: 'The command "my-cmd" has already been touched by another App.',
|
||||
});
|
||||
await assert.rejects(() => ascm.addCommand('testing', cmd), {
|
||||
name: 'CommandAlreadyExists',
|
||||
message: 'The command "my-cmd" already exists in the system.',
|
||||
});
|
||||
await assert.rejects(() => ascm.addCommand('failMePlease', TestData.getSlashCommand('yet-another')), {
|
||||
name: 'Error',
|
||||
message: 'App must exist in order for a command to be added.',
|
||||
});
|
||||
await assert.doesNotReject(async () => ascm.addCommand('testing', TestData.getSlashCommand('another-command')));
|
||||
assert.strictEqual((ascm as any).providedCommands.size, 1);
|
||||
assert.strictEqual((ascm as any).providedCommands.get('testing').size, 2);
|
||||
await assert.rejects(() => ascm.addCommand('even-another-app', TestData.getSlashCommand('it-exists')), {
|
||||
name: 'CommandAlreadyExists',
|
||||
message: 'The command "it-exists" already exists in the system.',
|
||||
});
|
||||
});
|
||||
|
||||
it('failToModifyAnotherAppsCommand', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
await ascm.addCommand('other-app', TestData.getSlashCommand('my-cmd'));
|
||||
|
||||
await assert.rejects(() => ascm.modifyCommand('testing', TestData.getSlashCommand('my-cmd')), {
|
||||
name: 'CommandHasAlreadyBeenTouched',
|
||||
message: 'The command "my-cmd" has already been touched by another App.',
|
||||
});
|
||||
});
|
||||
|
||||
it('failToModifyNonExistantAppCommand', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
|
||||
await assert.rejects(() => ascm.modifyCommand('failMePlease', TestData.getSlashCommand('yet-another')), {
|
||||
name: 'Error',
|
||||
message: 'App must exist in order to modify a command.',
|
||||
});
|
||||
});
|
||||
|
||||
it('modifyMyCommand', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
|
||||
await assert.rejects(() => ascm.modifyCommand('testing', TestData.getSlashCommand()), {
|
||||
name: 'Error',
|
||||
message: 'You must first register a command before you can modify it.',
|
||||
});
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('the-cmd'));
|
||||
await assert.doesNotReject(() => ascm.modifyCommand('testing', TestData.getSlashCommand('the-cmd')));
|
||||
});
|
||||
|
||||
it('modifySystemCommand', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
|
||||
await assert.doesNotReject(() => ascm.modifyCommand('brand-new-id', TestData.getSlashCommand('it-exists')));
|
||||
assert.strictEqual((ascm as any).modifiedCommands.size, 1);
|
||||
assert.ok((ascm as any).modifiedCommands.get('it-exists') !== undefined);
|
||||
assert.strictEqual((ascm as any).touchedCommandsToApps.get('it-exists'), 'brand-new-id');
|
||||
});
|
||||
|
||||
it('enableMyCommand', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
const doDoesCommandExistSpy = (mockBridges.getCommandBridge() as any).doDoesCommandExist;
|
||||
|
||||
await assert.rejects(() => ascm.enableCommand('testing', 'doesnt-exist'), {
|
||||
name: 'Error',
|
||||
message: 'The command "doesnt-exist" does not exist to enable.',
|
||||
});
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('command'));
|
||||
await assert.doesNotReject(() => ascm.enableCommand('testing', 'command'));
|
||||
assert.strictEqual((ascm as any).providedCommands.get('testing').get('command').isDisabled, false);
|
||||
assert.strictEqual((ascm as any).providedCommands.get('testing').get('command').isEnabled, true);
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('another-command'));
|
||||
(ascm as any).providedCommands.get('testing').get('another-command').isRegistered = true;
|
||||
await assert.doesNotReject(() => ascm.enableCommand('testing', 'another-command'));
|
||||
assert.strictEqual(doDoesCommandExistSpy.mock.calls.length, 3);
|
||||
});
|
||||
|
||||
it('enableSystemCommand', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
const doEnableCommandSpy = (mockBridges.getCommandBridge() as any).doEnableCommand;
|
||||
const doDoesCommandExistSpy = (mockBridges.getCommandBridge() as any).doDoesCommandExist;
|
||||
|
||||
await assert.doesNotReject(() => ascm.enableCommand('testing', 'it-exists'));
|
||||
assert.strictEqual(doEnableCommandSpy.mock.calls.length, 1);
|
||||
assert.deepStrictEqual(doEnableCommandSpy.mock.calls[0].arguments, ['it-exists', 'testing']);
|
||||
assert.strictEqual(doDoesCommandExistSpy.mock.calls.length, 1);
|
||||
});
|
||||
|
||||
it('failToEnableAnotherAppsCommand', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
await ascm.addCommand('another-app', TestData.getSlashCommand('command'));
|
||||
|
||||
await assert.rejects(() => ascm.enableCommand('my-app', 'command'), {
|
||||
name: 'CommandHasAlreadyBeenTouched',
|
||||
message: 'The command "command" has already been touched by another App.',
|
||||
});
|
||||
});
|
||||
|
||||
it('disableMyCommand', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
const doDoesCommandExistSpy = (mockBridges.getCommandBridge() as any).doDoesCommandExist;
|
||||
|
||||
await assert.rejects(() => ascm.disableCommand('testing', 'doesnt-exist'), {
|
||||
name: 'Error',
|
||||
message: 'The command "doesnt-exist" does not exist to disable.',
|
||||
});
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('command'));
|
||||
await assert.doesNotReject(() => ascm.disableCommand('testing', 'command'));
|
||||
assert.strictEqual((ascm as any).providedCommands.get('testing').get('command').isDisabled, true);
|
||||
assert.strictEqual((ascm as any).providedCommands.get('testing').get('command').isEnabled, false);
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('another-command'));
|
||||
(ascm as any).providedCommands.get('testing').get('another-command').isRegistered = true;
|
||||
await assert.doesNotReject(() => ascm.disableCommand('testing', 'another-command'));
|
||||
assert.strictEqual(doDoesCommandExistSpy.mock.calls.length, 3);
|
||||
});
|
||||
|
||||
it('disableSystemCommand', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
const doDisableCommandSpy = (mockBridges.getCommandBridge() as any).doDisableCommand;
|
||||
const doDoesCommandExistSpy = (mockBridges.getCommandBridge() as any).doDoesCommandExist;
|
||||
|
||||
await assert.doesNotReject(() => ascm.disableCommand('testing', 'it-exists'));
|
||||
assert.strictEqual(doDisableCommandSpy.mock.calls.length, 1);
|
||||
assert.deepStrictEqual(doDisableCommandSpy.mock.calls[0].arguments, ['it-exists', 'testing']);
|
||||
assert.strictEqual(doDoesCommandExistSpy.mock.calls.length, 1);
|
||||
});
|
||||
|
||||
it('failToDisableAnotherAppsCommand', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
await ascm.addCommand('another-app', TestData.getSlashCommand('command'));
|
||||
|
||||
await assert.rejects(() => ascm.disableCommand('my-app', 'command'), {
|
||||
name: 'CommandHasAlreadyBeenTouched',
|
||||
message: 'The command "command" has already been touched by another App.',
|
||||
});
|
||||
});
|
||||
|
||||
it('registerCommands', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
const doRegisterCommandSpy = (mockBridges.getCommandBridge() as any).doRegisterCommand;
|
||||
|
||||
const registerCommandSpy = mock.method(ascm as any, 'registerCommand');
|
||||
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('enabled-command'));
|
||||
const enabledRegInfo = (ascm as any).providedCommands.get('testing').get('enabled-command') as AppSlashCommand;
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('disabled-command'));
|
||||
await ascm.disableCommand('testing', 'disabled-command');
|
||||
const disabledRegInfo = (ascm as any).providedCommands.get('testing').get('disabled-command') as AppSlashCommand;
|
||||
|
||||
await assert.doesNotReject(() => ascm.registerCommands('non-existant'));
|
||||
await assert.doesNotReject(() => ascm.registerCommands('testing'));
|
||||
assert.strictEqual(enabledRegInfo.isRegistered, true);
|
||||
assert.strictEqual(disabledRegInfo.isRegistered, false);
|
||||
assert.strictEqual(
|
||||
registerCommandSpy.mock.calls.filter((c: any) => c.arguments[0] === 'testing' && c.arguments[1] === enabledRegInfo).length,
|
||||
1,
|
||||
);
|
||||
assert.strictEqual(
|
||||
doRegisterCommandSpy.mock.calls.filter((c: any) => c.arguments[0] === enabledRegInfo.slashCommand && c.arguments[1] === 'testing')
|
||||
.length,
|
||||
1,
|
||||
);
|
||||
});
|
||||
|
||||
it('unregisterCommands', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
const doUnregisterCommandSpy = (mockBridges.getCommandBridge() as any).doUnregisterCommand;
|
||||
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('command'));
|
||||
await ascm.modifyCommand('testing', TestData.getSlashCommand('it-exists'));
|
||||
|
||||
await assert.doesNotReject(() => ascm.unregisterCommands('non-existant'));
|
||||
await assert.doesNotReject(() => ascm.unregisterCommands('testing'));
|
||||
assert.strictEqual(doUnregisterCommandSpy.mock.calls.length, 1);
|
||||
});
|
||||
|
||||
it('executeCommands', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('command'));
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('not-registered'));
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('disabled-command'));
|
||||
await ascm.disableCommand('testing', 'not-registered');
|
||||
await ascm.registerCommands('testing');
|
||||
(ascm as any).providedCommands.get('testing').get('disabled-command').isDisabled = true;
|
||||
await ascm.modifyCommand('testing', TestData.getSlashCommand('it-exists'));
|
||||
|
||||
const context = new SlashCommandContext(TestData.getUser(), TestData.getRoom(), []);
|
||||
|
||||
await assert.doesNotReject(() => ascm.executeCommand('nope', context));
|
||||
await assert.doesNotReject(() => ascm.executeCommand('it-exists', context));
|
||||
await assert.doesNotReject(() => ascm.executeCommand('command', context));
|
||||
await assert.doesNotReject(() => ascm.executeCommand('not-registered', context));
|
||||
await assert.doesNotReject(() => ascm.executeCommand('disabled-command', context));
|
||||
|
||||
const classContext = new SlashCommandContext(TestData.getUser(), new Room(TestData.getRoom(), mockManager), []);
|
||||
await assert.doesNotReject(() => ascm.executeCommand('it-exists', classContext));
|
||||
|
||||
// set it up for no "no app failure"
|
||||
const failedItems = new Map<string, AppSlashCommand>();
|
||||
const asm = new AppSlashCommand(mockApp, TestData.getSlashCommand('failure'));
|
||||
asm.hasBeenRegistered();
|
||||
failedItems.set('failure', asm);
|
||||
(ascm as any).providedCommands.set('failMePlease', failedItems);
|
||||
(ascm as any).touchedCommandsToApps.set('failure', 'failMePlease');
|
||||
await assert.rejects(() => ascm.executeCommand('failure', context));
|
||||
});
|
||||
|
||||
it('getPreviews', async () => {
|
||||
setupMocks();
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('command'));
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('not-registered'));
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('disabled-command'));
|
||||
await ascm.disableCommand('testing', 'not-registered');
|
||||
await ascm.registerCommands('testing');
|
||||
(ascm as any).providedCommands.get('testing').get('disabled-command').isDisabled = true;
|
||||
await ascm.modifyCommand('testing', TestData.getSlashCommand('it-exists'));
|
||||
|
||||
const context = new SlashCommandContext(TestData.getUser(), TestData.getRoom(), ['testing']);
|
||||
|
||||
await assert.doesNotReject(() => ascm.getPreviews('nope', context));
|
||||
await assert.doesNotReject(() => ascm.getPreviews('it-exists', context));
|
||||
await assert.doesNotReject(() => ascm.getPreviews('command', context));
|
||||
await assert.doesNotReject(() => ascm.getPreviews('not-registered', context));
|
||||
await assert.doesNotReject(() => ascm.getPreviews('disabled-command', context));
|
||||
|
||||
const classContext = new SlashCommandContext(TestData.getUser(), new Room(TestData.getRoom(), mockManager), []);
|
||||
await assert.doesNotReject(() => ascm.getPreviews('it-exists', classContext));
|
||||
|
||||
// set it up for no "no app failure"
|
||||
const failedItems = new Map<string, AppSlashCommand>();
|
||||
const asm = new AppSlashCommand(mockApp, TestData.getSlashCommand('failure'));
|
||||
asm.hasBeenRegistered();
|
||||
failedItems.set('failure', asm);
|
||||
(ascm as any).providedCommands.set('failMePlease', failedItems);
|
||||
(ascm as any).touchedCommandsToApps.set('failure', 'failMePlease');
|
||||
await assert.doesNotReject(() => ascm.getPreviews('failure', context));
|
||||
});
|
||||
|
||||
it('executePreview', async () => {
|
||||
setupMocks();
|
||||
const previewItem = {} as ISlashCommandPreviewItem;
|
||||
const ascm = new AppSlashCommandManager(mockManager);
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('command'));
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('not-registered'));
|
||||
await ascm.addCommand('testing', TestData.getSlashCommand('disabled-command'));
|
||||
await ascm.disableCommand('testing', 'not-registered');
|
||||
await ascm.registerCommands('testing');
|
||||
(ascm as any).providedCommands.get('testing').get('disabled-command').isDisabled = true;
|
||||
await ascm.modifyCommand('testing', TestData.getSlashCommand('it-exists'));
|
||||
|
||||
const context = new SlashCommandContext(TestData.getUser(), TestData.getRoom(), ['testing']);
|
||||
|
||||
await assert.doesNotReject(() => ascm.executePreview('nope', previewItem, context));
|
||||
await assert.doesNotReject(() => ascm.executePreview('it-exists', previewItem, context));
|
||||
await assert.doesNotReject(() => ascm.executePreview('command', previewItem, context));
|
||||
await assert.doesNotReject(() => ascm.executePreview('not-registered', previewItem, context));
|
||||
await assert.doesNotReject(() => ascm.executePreview('disabled-command', previewItem, context));
|
||||
|
||||
const classContext = new SlashCommandContext(TestData.getUser(), new Room(TestData.getRoom(), mockManager), []);
|
||||
await assert.doesNotReject(() => ascm.executePreview('it-exists', previewItem, classContext));
|
||||
|
||||
// set it up for no "no app failure"
|
||||
const failedItems = new Map<string, AppSlashCommand>();
|
||||
const asm = new AppSlashCommand(mockApp, TestData.getSlashCommand('failure'));
|
||||
asm.hasBeenRegistered();
|
||||
failedItems.set('failure', asm);
|
||||
(ascm as any).providedCommands.set('failMePlease', failedItems);
|
||||
(ascm as any).touchedCommandsToApps.set('failure', 'failMePlease');
|
||||
await ascm.executePreview('nope', previewItem, context).catch(() => {});
|
||||
|
||||
await assert.doesNotReject(() => ascm.executePreview('failure', previewItem, context));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
import type { IVideoConfProvider } from '@rocket.chat/apps-engine/definition/videoConfProviders';
|
||||
|
||||
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
|
||||
import { AppVideoConfProvider } from '../../../src/server/managers/AppVideoConfProvider';
|
||||
|
||||
describe('AppVideoConfProvider', () => {
|
||||
it('ensureAppVideoConfManager', () => {
|
||||
const mockApp = {} as ProxiedApp;
|
||||
|
||||
assert.doesNotThrow(() => new AppVideoConfProvider(mockApp, {} as IVideoConfProvider));
|
||||
|
||||
const ascr = new AppVideoConfProvider(mockApp, {} as IVideoConfProvider);
|
||||
assert.strictEqual(ascr.isRegistered, false);
|
||||
|
||||
ascr.hasBeenRegistered();
|
||||
assert.strictEqual(ascr.isRegistered, true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,351 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
|
||||
|
||||
import type { AppManager } from '../../../src/server/AppManager';
|
||||
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
|
||||
import type { AppBridges } from '../../../src/server/bridges';
|
||||
import type { AppApiManager, AppExternalComponentManager, AppSchedulerManager, AppSlashCommandManager } from '../../../src/server/managers';
|
||||
import { AppVideoConfProviderManager } from '../../../src/server/managers';
|
||||
import { AppVideoConfProvider } from '../../../src/server/managers/AppVideoConfProvider';
|
||||
import type { UIActionButtonManager } from '../../../src/server/managers/UIActionButtonManager';
|
||||
import type { AppLogStorage, IAppStorageItem } from '../../../src/server/storage';
|
||||
import { TestsAppBridges } from '../../test-data/bridges/appBridges';
|
||||
import { TestsAppLogStorage } from '../../test-data/storage/logStorage';
|
||||
import { TestData } from '../../test-data/utilities';
|
||||
|
||||
describe('AppVideoConfProviderManager', () => {
|
||||
let mockBridges: TestsAppBridges;
|
||||
let mockApp: ProxiedApp;
|
||||
let mockManager: AppManager;
|
||||
|
||||
beforeEach(() => {
|
||||
mockBridges = new TestsAppBridges();
|
||||
|
||||
mockApp = TestData.getMockApp({ info: { id: 'testing', name: 'testing' } } as IAppStorageItem, {} as AppManager);
|
||||
|
||||
const bri = mockBridges;
|
||||
const app = mockApp;
|
||||
mockManager = {
|
||||
getBridges(): AppBridges {
|
||||
return bri;
|
||||
},
|
||||
getCommandManager() {
|
||||
return {} as AppSlashCommandManager;
|
||||
},
|
||||
getExternalComponentManager(): AppExternalComponentManager {
|
||||
return {} as AppExternalComponentManager;
|
||||
},
|
||||
getApiManager() {
|
||||
return {} as AppApiManager;
|
||||
},
|
||||
getOneById(appId: string): ProxiedApp {
|
||||
return appId === 'failMePlease' ? undefined : app;
|
||||
},
|
||||
getLogStorage(): AppLogStorage {
|
||||
return new TestsAppLogStorage();
|
||||
},
|
||||
getSchedulerManager() {
|
||||
return {} as AppSchedulerManager;
|
||||
},
|
||||
getUIActionButtonManager() {
|
||||
return {} as UIActionButtonManager;
|
||||
},
|
||||
getVideoConfProviderManager() {
|
||||
return {} as AppVideoConfProviderManager;
|
||||
},
|
||||
} as AppManager;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restoreAll();
|
||||
});
|
||||
|
||||
it('basicAppVideoConfProviderManager', () => {
|
||||
assert.throws(() => new AppVideoConfProviderManager({} as AppManager));
|
||||
assert.doesNotThrow(() => new AppVideoConfProviderManager(mockManager));
|
||||
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
assert.strictEqual((manager as any).manager, mockManager);
|
||||
assert.ok((manager as any).videoConfProviders !== undefined);
|
||||
assert.strictEqual((manager as any).videoConfProviders.size, 0);
|
||||
});
|
||||
|
||||
it('addProvider', () => {
|
||||
const provider = TestData.getVideoConfProvider();
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
|
||||
assert.doesNotThrow(() => manager.addProvider('testing', provider));
|
||||
assert.strictEqual((manager as any).videoConfProviders.size, 1);
|
||||
assert.throws(() => manager.addProvider('failMePlease', provider), {
|
||||
name: 'Error',
|
||||
message: 'App must exist in order for a video conference provider to be added.',
|
||||
});
|
||||
assert.strictEqual((manager as any).videoConfProviders.size, 1);
|
||||
});
|
||||
|
||||
it('ignoreAppsWithoutProviders', async () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
|
||||
await assert.doesNotReject(() => manager.registerProviders('non-existant'));
|
||||
});
|
||||
|
||||
it('registerProviders', async () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('firstApp', TestData.getVideoConfProvider());
|
||||
const appInfo = (manager as any).videoConfProviders.get('firstApp') as Map<string, AppVideoConfProvider>;
|
||||
assert.ok(appInfo !== undefined);
|
||||
const regInfo = appInfo.get('test');
|
||||
assert.ok(regInfo !== undefined);
|
||||
|
||||
assert.strictEqual(regInfo.isRegistered, false);
|
||||
await assert.doesNotReject(() => manager.registerProviders('firstApp'));
|
||||
assert.strictEqual(regInfo.isRegistered, true);
|
||||
});
|
||||
|
||||
it('registerTwoProviders', async () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('firstApp', TestData.getVideoConfProvider());
|
||||
manager.addProvider('firstApp', TestData.getVideoConfProvider('another-test'));
|
||||
const firstApp = (manager as any).videoConfProviders.get('firstApp') as Map<string, AppVideoConfProvider>;
|
||||
assert.ok(firstApp !== undefined);
|
||||
const firstRegInfo = firstApp.get('test');
|
||||
assert.ok(firstRegInfo !== undefined);
|
||||
const secondRegInfo = firstApp.get('another-test');
|
||||
assert.ok(secondRegInfo !== undefined);
|
||||
|
||||
assert.strictEqual(firstRegInfo.isRegistered, false);
|
||||
assert.strictEqual(secondRegInfo.isRegistered, false);
|
||||
await assert.doesNotReject(() => manager.registerProviders('firstApp'));
|
||||
assert.strictEqual(firstRegInfo.isRegistered, true);
|
||||
assert.strictEqual(secondRegInfo.isRegistered, true);
|
||||
});
|
||||
|
||||
it('registerProvidersFromMultipleApps', async () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('firstApp', TestData.getVideoConfProvider());
|
||||
manager.addProvider('firstApp', TestData.getVideoConfProvider('another-test'));
|
||||
manager.addProvider('secondApp', TestData.getVideoConfProvider('test3'));
|
||||
|
||||
const firstApp = (manager as any).videoConfProviders.get('firstApp') as Map<string, AppVideoConfProvider>;
|
||||
assert.ok(firstApp !== undefined);
|
||||
const firstRegInfo = firstApp.get('test');
|
||||
const secondRegInfo = firstApp.get('another-test');
|
||||
assert.ok(firstRegInfo !== undefined);
|
||||
assert.ok(secondRegInfo !== undefined);
|
||||
const secondApp = (manager as any).videoConfProviders.get('secondApp') as Map<string, AppVideoConfProvider>;
|
||||
assert.ok(secondApp !== undefined);
|
||||
const thirdRegInfo = secondApp.get('test3');
|
||||
assert.ok(thirdRegInfo !== undefined);
|
||||
|
||||
assert.strictEqual(firstRegInfo.isRegistered, false);
|
||||
assert.strictEqual(secondRegInfo.isRegistered, false);
|
||||
await assert.doesNotReject(() => manager.registerProviders('firstApp'));
|
||||
assert.strictEqual(firstRegInfo.isRegistered, true);
|
||||
assert.strictEqual(secondRegInfo.isRegistered, true);
|
||||
assert.strictEqual(thirdRegInfo.isRegistered, false);
|
||||
await assert.doesNotReject(() => manager.registerProviders('secondApp'));
|
||||
assert.strictEqual(thirdRegInfo.isRegistered, true);
|
||||
});
|
||||
|
||||
it('failToRegisterSameProvider', () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('firstApp', TestData.getVideoConfProvider());
|
||||
|
||||
assert.throws(() => manager.addProvider('secondApp', TestData.getVideoConfProvider('test')), {
|
||||
name: 'VideoConfProviderAlreadyExists',
|
||||
message: `The video conference provider "test" was already registered by another App.`,
|
||||
});
|
||||
});
|
||||
|
||||
it('unregisterProviders', async () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('testing', TestData.getVideoConfProvider());
|
||||
const regInfo = (manager as any).videoConfProviders.get('testing').get('test') as AppVideoConfProvider;
|
||||
await assert.doesNotReject(() => manager.registerProviders('testing'));
|
||||
|
||||
await assert.doesNotReject(() => manager.unregisterProviders('non-existant'));
|
||||
assert.strictEqual(regInfo.isRegistered, true);
|
||||
await assert.doesNotReject(() => manager.unregisterProviders('testing'));
|
||||
assert.strictEqual(regInfo.isRegistered, false);
|
||||
});
|
||||
|
||||
it('failToGenerateUrlWithoutProvider', async () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
|
||||
const call = TestData.getVideoConfData();
|
||||
|
||||
await assert.rejects(() => manager.generateUrl('test', call), {
|
||||
name: 'VideoConfProviderNotRegistered',
|
||||
message: `The video conference provider "test" is not registered in the system.`,
|
||||
});
|
||||
|
||||
manager.addProvider('testing', TestData.getVideoConfProvider());
|
||||
|
||||
await assert.rejects(() => manager.generateUrl('test', call), {
|
||||
name: 'VideoConfProviderNotRegistered',
|
||||
message: `The video conference provider "test" is not registered in the system.`,
|
||||
});
|
||||
});
|
||||
|
||||
it('generateUrl', async () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
manager.addProvider('testing', TestData.getVideoConfProvider());
|
||||
await manager.registerProviders('testing');
|
||||
|
||||
const call = TestData.getVideoConfData();
|
||||
|
||||
mock.method(AppVideoConfProvider.prototype, 'runGenerateUrl', () => 'test/first-call');
|
||||
const url = await manager.generateUrl('test', call);
|
||||
assert.strictEqual(url, 'test/first-call');
|
||||
});
|
||||
|
||||
it('generateUrlWithMultipleProvidersAvailable', async () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
manager.addProvider('testing', TestData.getVideoConfProvider());
|
||||
manager.addProvider('testing', TestData.getVideoConfProvider('test2'));
|
||||
await manager.registerProviders('testing');
|
||||
manager.addProvider('secondApp', TestData.getVideoConfProvider('differentProvider'));
|
||||
await manager.registerProviders('secondApp');
|
||||
|
||||
const call = TestData.getVideoConfData();
|
||||
|
||||
const testProvider = (manager as any).videoConfProviders.get('testing').get('test') as AppVideoConfProvider;
|
||||
const test2Provider = (manager as any).videoConfProviders.get('testing').get('test2') as AppVideoConfProvider;
|
||||
const differentProvider = (manager as any).videoConfProviders.get('secondApp').get('differentprovider') as AppVideoConfProvider;
|
||||
|
||||
mock.method(testProvider, 'runGenerateUrl', () => 'test/first-call');
|
||||
mock.method(test2Provider, 'runGenerateUrl', () => 'test2/first-call');
|
||||
mock.method(differentProvider, 'runGenerateUrl', () => 'differentProvider/first-call');
|
||||
|
||||
assert.strictEqual(await manager.generateUrl('test', call), 'test/first-call');
|
||||
assert.strictEqual(await manager.generateUrl('test2', call), 'test2/first-call');
|
||||
assert.strictEqual(await manager.generateUrl('differentProvider', call), 'differentProvider/first-call');
|
||||
});
|
||||
|
||||
it('failToGenerateUrlWithUnknownProvider', async () => {
|
||||
const call = TestData.getVideoConfData();
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
await assert.rejects(() => manager.generateUrl('unknownProvider', call), {
|
||||
name: 'VideoConfProviderNotRegistered',
|
||||
message: `The video conference provider "unknownProvider" is not registered in the system.`,
|
||||
});
|
||||
});
|
||||
|
||||
it('failToGenerateUrlWithUnregisteredProvider', async () => {
|
||||
const call = TestData.getVideoConfData();
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
manager.addProvider('unregisteredApp', TestData.getVideoConfProvider('unregisteredProvider'));
|
||||
await assert.rejects(() => manager.generateUrl('unregisteredProvider', call), {
|
||||
name: 'VideoConfProviderNotRegistered',
|
||||
message: `The video conference provider "unregisteredProvider" is not registered in the system.`,
|
||||
});
|
||||
});
|
||||
|
||||
it('failToCustomizeUrlWithoutProvider', async () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
const call = TestData.getVideoConfDataExtended();
|
||||
const user = TestData.getVideoConferenceUser();
|
||||
|
||||
await assert.rejects(() => manager.customizeUrl('test', call, user, {}), {
|
||||
name: 'VideoConfProviderNotRegistered',
|
||||
message: `The video conference provider "test" is not registered in the system.`,
|
||||
});
|
||||
|
||||
manager.addProvider('testing', TestData.getVideoConfProvider());
|
||||
|
||||
await assert.rejects(() => manager.customizeUrl('test', call, user, {}), {
|
||||
name: 'VideoConfProviderNotRegistered',
|
||||
message: `The video conference provider "test" is not registered in the system.`,
|
||||
});
|
||||
});
|
||||
|
||||
it('customizeUrl', async () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
manager.addProvider('testing', TestData.getVideoConfProvider());
|
||||
await manager.registerProviders('testing');
|
||||
|
||||
const call = TestData.getVideoConfDataExtended();
|
||||
const user = TestData.getVideoConferenceUser();
|
||||
|
||||
const cases: any = [
|
||||
{
|
||||
name: 'test',
|
||||
call,
|
||||
user,
|
||||
options: {},
|
||||
runCustomizeUrl: 'test/first-call#caller',
|
||||
result: 'test/first-call#caller',
|
||||
},
|
||||
{
|
||||
name: 'test',
|
||||
call,
|
||||
user: undefined,
|
||||
options: {},
|
||||
runCustomizeUrl: 'test/first-call#',
|
||||
result: 'test/first-call#',
|
||||
},
|
||||
];
|
||||
|
||||
for (const c of cases) {
|
||||
mock.method(AppVideoConfProvider.prototype, 'runCustomizeUrl', () => c.runCustomizeUrl);
|
||||
assert.strictEqual(await manager.customizeUrl(c.name, c.call, c.user, c.options), c.result);
|
||||
}
|
||||
});
|
||||
|
||||
it('customizeUrlWithMultipleProvidersAvailable', async () => {
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
manager.addProvider('testing', TestData.getVideoConfProvider());
|
||||
manager.addProvider('testing', TestData.getVideoConfProvider('test2'));
|
||||
await manager.registerProviders('testing');
|
||||
manager.addProvider('secondApp', TestData.getVideoConfProvider('differentProvider'));
|
||||
await manager.registerProviders('secondApp');
|
||||
|
||||
const call = TestData.getVideoConfDataExtended();
|
||||
const user = TestData.getVideoConferenceUser();
|
||||
|
||||
const testProvider = (manager as any).videoConfProviders.get('testing').get('test') as AppVideoConfProvider;
|
||||
const test2Provider = (manager as any).videoConfProviders.get('testing').get('test2') as AppVideoConfProvider;
|
||||
const differentProvider = (manager as any).videoConfProviders.get('secondApp').get('differentprovider') as AppVideoConfProvider;
|
||||
|
||||
mock.method(testProvider, 'runCustomizeUrl', (_call: any, user: any) => (user ? 'test/first-call#caller' : 'test/first-call#'));
|
||||
mock.method(test2Provider, 'runCustomizeUrl', (_call: any, user: any) => (user ? 'test2/first-call#caller' : 'test2/first-call#'));
|
||||
mock.method(differentProvider, 'runCustomizeUrl', (_call: any, user: any) =>
|
||||
user ? 'differentProvider/first-call#caller' : 'differentProvider/first-call#',
|
||||
);
|
||||
|
||||
assert.strictEqual(await manager.customizeUrl('test', call, user, {}), 'test/first-call#caller');
|
||||
assert.strictEqual(await manager.customizeUrl('test', call, undefined, {}), 'test/first-call#');
|
||||
assert.strictEqual(await manager.customizeUrl('test2', call, user, {}), 'test2/first-call#caller');
|
||||
assert.strictEqual(await manager.customizeUrl('test2', call, undefined, {}), 'test2/first-call#');
|
||||
assert.strictEqual(await manager.customizeUrl('differentProvider', call, user, {}), 'differentProvider/first-call#caller');
|
||||
assert.strictEqual(await manager.customizeUrl('differentProvider', call, undefined, {}), 'differentProvider/first-call#');
|
||||
});
|
||||
|
||||
it('failToCustomizeUrlWithUnknownProvider', async () => {
|
||||
const call = TestData.getVideoConfDataExtended();
|
||||
const user = TestData.getVideoConferenceUser();
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
|
||||
await assert.rejects(() => manager.customizeUrl('unknownProvider', call, user, {}), {
|
||||
name: 'VideoConfProviderNotRegistered',
|
||||
message: `The video conference provider "unknownProvider" is not registered in the system.`,
|
||||
});
|
||||
});
|
||||
|
||||
it('failToCustomizeUrlWithUnregisteredProvider', async () => {
|
||||
const call = TestData.getVideoConfDataExtended();
|
||||
const user = TestData.getVideoConferenceUser();
|
||||
const manager = new AppVideoConfProviderManager(mockManager);
|
||||
|
||||
manager.addProvider('unregisteredApp', TestData.getVideoConfProvider('unregisteredProvider'));
|
||||
await assert.rejects(() => manager.customizeUrl('unregisteredProvider', call, user, {}), {
|
||||
name: 'VideoConfProviderNotRegistered',
|
||||
message: `The video conference provider "unregisteredProvider" is not registered in the system.`,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,311 @@
|
||||
import * as assert from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
|
||||
|
||||
import { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus';
|
||||
import type { IUIActionButtonDescriptor } from '@rocket.chat/apps-engine/definition/ui';
|
||||
import { UIActionButtonContext } from '@rocket.chat/apps-engine/definition/ui';
|
||||
|
||||
import type { AppManager } from '../../../src/server/AppManager';
|
||||
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
|
||||
import type { AppActivationBridge, AppBridges } from '../../../src/server/bridges';
|
||||
import { AppPermissionManager } from '../../../src/server/managers/AppPermissionManager';
|
||||
import { UIActionButtonManager } from '../../../src/server/managers/UIActionButtonManager';
|
||||
import { AppPermissions } from '../../../src/server/permissions/AppPermissions';
|
||||
import { TestsAppBridges } from '../../test-data/bridges/appBridges';
|
||||
|
||||
describe('UIActionButtonManager', () => {
|
||||
let mockBridges: TestsAppBridges;
|
||||
let mockApp: ProxiedApp;
|
||||
let mockApp2: ProxiedApp;
|
||||
let mockManager: AppManager;
|
||||
let mockActivationBridge: AppActivationBridge;
|
||||
let hasPermissionSpy: ReturnType<typeof mock.method>;
|
||||
let notifyAboutErrorSpy: ReturnType<typeof mock.method>;
|
||||
let doActionsChangedSpy: ReturnType<typeof mock.method>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockBridges = new TestsAppBridges();
|
||||
mockActivationBridge = mockBridges.getAppActivationBridge();
|
||||
|
||||
mockApp = {
|
||||
getID() {
|
||||
return 'testing-app';
|
||||
},
|
||||
getName() {
|
||||
return 'Test App';
|
||||
},
|
||||
getStatus() {
|
||||
return Promise.resolve(AppStatus.AUTO_ENABLED);
|
||||
},
|
||||
} as ProxiedApp;
|
||||
|
||||
mockApp2 = {
|
||||
getID() {
|
||||
return 'testing-app-2';
|
||||
},
|
||||
getName() {
|
||||
return 'Test App 2';
|
||||
},
|
||||
getStatus() {
|
||||
return Promise.resolve(AppStatus.AUTO_ENABLED);
|
||||
},
|
||||
} as ProxiedApp;
|
||||
|
||||
const bri = mockBridges;
|
||||
const app = mockApp;
|
||||
const app2 = mockApp2;
|
||||
mockManager = {
|
||||
getBridges(): AppBridges {
|
||||
return bri;
|
||||
},
|
||||
getOneById: (appId: string): ProxiedApp | undefined => {
|
||||
if (appId === 'testing-app') {
|
||||
return app;
|
||||
}
|
||||
if (appId === 'testing-app-2') {
|
||||
return app2;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
} as AppManager;
|
||||
|
||||
notifyAboutErrorSpy = mock.method(AppPermissionManager, 'notifyAboutError');
|
||||
hasPermissionSpy = mock.method(AppPermissionManager, 'hasPermission');
|
||||
doActionsChangedSpy = mock.method(mockActivationBridge, 'doActionsChanged');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restoreAll();
|
||||
});
|
||||
|
||||
it('basicUIActionButtonManager', () => {
|
||||
assert.doesNotThrow(() => new UIActionButtonManager(mockManager));
|
||||
|
||||
const manager = new UIActionButtonManager(mockManager);
|
||||
assert.strictEqual((manager as any).manager, mockManager);
|
||||
assert.strictEqual((manager as any).activationBridge, mockActivationBridge);
|
||||
assert.ok((manager as any).registeredActionButtons !== undefined);
|
||||
assert.strictEqual((manager as any).registeredActionButtons.size, 0);
|
||||
});
|
||||
|
||||
it('registerActionButtonWithPermission', () => {
|
||||
hasPermissionSpy.mock.mockImplementation(() => true);
|
||||
|
||||
const manager = new UIActionButtonManager(mockManager);
|
||||
const button: IUIActionButtonDescriptor = {
|
||||
actionId: 'test-action',
|
||||
context: UIActionButtonContext.MESSAGE_ACTION,
|
||||
labelI18n: 'test.label',
|
||||
};
|
||||
|
||||
const result = manager.registerActionButton('testing-app', button);
|
||||
|
||||
assert.strictEqual(result, true);
|
||||
assert.strictEqual(hasPermissionSpy.mock.calls.length, 1);
|
||||
assert.deepStrictEqual(hasPermissionSpy.mock.calls[0].arguments, ['testing-app', AppPermissions.ui.registerButtons]);
|
||||
assert.ok(doActionsChangedSpy.mock.calls.length > 0);
|
||||
assert.strictEqual((manager as any).registeredActionButtons.size, 1);
|
||||
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').size, 1);
|
||||
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').get('test-action'), button);
|
||||
});
|
||||
|
||||
it('registerActionButtonWithoutPermission', () => {
|
||||
hasPermissionSpy.mock.mockImplementation(() => false);
|
||||
notifyAboutErrorSpy.mock.mockImplementation(() => {});
|
||||
|
||||
const manager = new UIActionButtonManager(mockManager);
|
||||
const button: IUIActionButtonDescriptor = {
|
||||
actionId: 'test-action',
|
||||
context: UIActionButtonContext.MESSAGE_ACTION,
|
||||
labelI18n: 'test.label',
|
||||
};
|
||||
|
||||
const result = manager.registerActionButton('testing-app', button);
|
||||
|
||||
assert.strictEqual(result, false);
|
||||
assert.strictEqual(hasPermissionSpy.mock.calls.length, 1);
|
||||
assert.deepStrictEqual(hasPermissionSpy.mock.calls[0].arguments, ['testing-app', AppPermissions.ui.registerButtons]);
|
||||
assert.ok(notifyAboutErrorSpy.mock.calls.length > 0);
|
||||
assert.strictEqual(doActionsChangedSpy.mock.calls.length, 0);
|
||||
assert.strictEqual((manager as any).registeredActionButtons.size, 0);
|
||||
});
|
||||
|
||||
it('registerMultipleButtonsForSameApp', () => {
|
||||
hasPermissionSpy.mock.mockImplementation(() => true);
|
||||
|
||||
const manager = new UIActionButtonManager(mockManager);
|
||||
const button1: IUIActionButtonDescriptor = {
|
||||
actionId: 'action-1',
|
||||
context: UIActionButtonContext.MESSAGE_ACTION,
|
||||
labelI18n: 'test.label1',
|
||||
};
|
||||
const button2: IUIActionButtonDescriptor = {
|
||||
actionId: 'action-2',
|
||||
context: UIActionButtonContext.ROOM_ACTION,
|
||||
labelI18n: 'test.label2',
|
||||
};
|
||||
|
||||
manager.registerActionButton('testing-app', button1);
|
||||
manager.registerActionButton('testing-app', button2);
|
||||
|
||||
assert.strictEqual((manager as any).registeredActionButtons.size, 1);
|
||||
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').size, 2);
|
||||
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').get('action-1'), button1);
|
||||
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').get('action-2'), button2);
|
||||
});
|
||||
|
||||
it('clearAppActionButtons', () => {
|
||||
hasPermissionSpy.mock.mockImplementation(() => true);
|
||||
|
||||
const manager = new UIActionButtonManager(mockManager);
|
||||
const button: IUIActionButtonDescriptor = {
|
||||
actionId: 'test-action',
|
||||
context: UIActionButtonContext.MESSAGE_ACTION,
|
||||
labelI18n: 'test.label',
|
||||
};
|
||||
|
||||
manager.registerActionButton('testing-app', button);
|
||||
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').size, 1);
|
||||
|
||||
manager.clearAppActionButtons('testing-app');
|
||||
|
||||
assert.strictEqual((manager as any).registeredActionButtons.get('testing-app').size, 0);
|
||||
assert.strictEqual(doActionsChangedSpy.mock.calls.length, 2);
|
||||
});
|
||||
|
||||
it('getAppActionButtons', () => {
|
||||
hasPermissionSpy.mock.mockImplementation(() => true);
|
||||
|
||||
const manager = new UIActionButtonManager(mockManager);
|
||||
const button: IUIActionButtonDescriptor = {
|
||||
actionId: 'test-action',
|
||||
context: UIActionButtonContext.MESSAGE_ACTION,
|
||||
labelI18n: 'test.label',
|
||||
};
|
||||
|
||||
manager.registerActionButton('testing-app', button);
|
||||
|
||||
const buttons = manager.getAppActionButtons('testing-app');
|
||||
assert.ok(buttons !== undefined);
|
||||
assert.strictEqual(buttons?.size, 1);
|
||||
assert.strictEqual(buttons?.get('test-action'), button);
|
||||
|
||||
const nonExistentButtons = manager.getAppActionButtons('non-existent');
|
||||
assert.strictEqual(nonExistentButtons, undefined);
|
||||
});
|
||||
|
||||
it('getAllActionButtonsFromEnabledApp', async () => {
|
||||
hasPermissionSpy.mock.mockImplementation(() => true);
|
||||
|
||||
mock.method(mockApp, 'getStatus', () => Promise.resolve(AppStatus.AUTO_ENABLED));
|
||||
|
||||
const manager = new UIActionButtonManager(mockManager);
|
||||
const button: IUIActionButtonDescriptor = {
|
||||
actionId: 'test-action',
|
||||
context: UIActionButtonContext.MESSAGE_ACTION,
|
||||
labelI18n: 'test.label',
|
||||
};
|
||||
|
||||
manager.registerActionButton('testing-app', button);
|
||||
|
||||
const allButtons = await manager.getAllActionButtons();
|
||||
|
||||
assert.ok(allButtons !== undefined);
|
||||
assert.strictEqual(allButtons.length, 1);
|
||||
assert.strictEqual(allButtons[0].actionId, 'test-action');
|
||||
assert.strictEqual(allButtons[0].appId, 'testing-app');
|
||||
assert.strictEqual(allButtons[0].context, UIActionButtonContext.MESSAGE_ACTION);
|
||||
assert.strictEqual(allButtons[0].labelI18n, 'test.label');
|
||||
});
|
||||
|
||||
it('getAllActionButtonsFromDisabledApp', async () => {
|
||||
hasPermissionSpy.mock.mockImplementation(() => true);
|
||||
|
||||
mock.method(mockApp, 'getStatus', () => Promise.resolve(AppStatus.DISABLED));
|
||||
|
||||
const manager = new UIActionButtonManager(mockManager);
|
||||
const button: IUIActionButtonDescriptor = {
|
||||
actionId: 'test-action',
|
||||
context: UIActionButtonContext.MESSAGE_ACTION,
|
||||
labelI18n: 'test.label',
|
||||
};
|
||||
|
||||
manager.registerActionButton('testing-app', button);
|
||||
|
||||
const allButtons = await manager.getAllActionButtons();
|
||||
|
||||
assert.ok(allButtons !== undefined);
|
||||
assert.strictEqual(allButtons.length, 0);
|
||||
});
|
||||
|
||||
it('getAllActionButtonsFromNonExistentApp', async () => {
|
||||
hasPermissionSpy.mock.mockImplementation(() => true);
|
||||
|
||||
const manager = new UIActionButtonManager(mockManager);
|
||||
const button: IUIActionButtonDescriptor = {
|
||||
actionId: 'test-action',
|
||||
context: UIActionButtonContext.MESSAGE_ACTION,
|
||||
labelI18n: 'test.label',
|
||||
};
|
||||
|
||||
manager.registerActionButton('non-existent-app', button);
|
||||
|
||||
const allButtons = await manager.getAllActionButtons();
|
||||
|
||||
assert.ok(allButtons !== undefined);
|
||||
assert.strictEqual(allButtons.length, 0);
|
||||
});
|
||||
|
||||
it('getAllActionButtonsWithStatusError', async () => {
|
||||
hasPermissionSpy.mock.mockImplementation(() => true);
|
||||
|
||||
mock.method(mockApp, 'getStatus', () => Promise.reject(new Error('Status error')));
|
||||
|
||||
const manager = new UIActionButtonManager(mockManager);
|
||||
const button: IUIActionButtonDescriptor = {
|
||||
actionId: 'test-action',
|
||||
context: UIActionButtonContext.MESSAGE_ACTION,
|
||||
labelI18n: 'test.label',
|
||||
};
|
||||
|
||||
manager.registerActionButton('testing-app', button);
|
||||
|
||||
const allButtons = await manager.getAllActionButtons();
|
||||
|
||||
assert.ok(allButtons !== undefined);
|
||||
assert.strictEqual(allButtons.length, 0);
|
||||
});
|
||||
|
||||
it('getAllActionButtonsFromMultipleApps', async () => {
|
||||
hasPermissionSpy.mock.mockImplementation(() => true);
|
||||
|
||||
const button1: IUIActionButtonDescriptor = {
|
||||
actionId: 'action-1',
|
||||
context: UIActionButtonContext.MESSAGE_ACTION,
|
||||
labelI18n: 'test.label1',
|
||||
};
|
||||
const button2: IUIActionButtonDescriptor = {
|
||||
actionId: 'action-2',
|
||||
context: UIActionButtonContext.ROOM_ACTION,
|
||||
labelI18n: 'test.label2',
|
||||
};
|
||||
|
||||
const manager = new UIActionButtonManager(mockManager);
|
||||
|
||||
manager.registerActionButton('testing-app', button1);
|
||||
manager.registerActionButton('testing-app-2', button2);
|
||||
|
||||
const allButtons = await manager.getAllActionButtons();
|
||||
|
||||
assert.ok(allButtons !== undefined);
|
||||
assert.strictEqual(allButtons.length, 2);
|
||||
|
||||
const app1Button = allButtons.find((b) => b.appId === 'testing-app');
|
||||
const app2Button = allButtons.find((b) => b.appId === 'testing-app-2');
|
||||
|
||||
assert.ok(app1Button !== undefined);
|
||||
assert.strictEqual(app1Button!.actionId, 'action-1');
|
||||
assert.ok(app2Button !== undefined);
|
||||
assert.strictEqual(app2Button!.actionId, 'action-2');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user