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

216 lines
7.2 KiB

import type { FunctionSpy, RestorableFunctionSpy } from 'alsatian';
import { AsyncTest, Expect, Setup, SetupFixture, SpyOn, Teardown, Test } from 'alsatian';
import { RequestMethod } from '../../../src/definition/accessors';
import type { IApi, IApiRequest } from '../../../src/definition/api';
import type { AppManager } from '../../../src/server/AppManager';
import type { ProxiedApp } from '../../../src/server/ProxiedApp';
import type { AppBridges } from '../../../src/server/bridges';
import { PathAlreadyExistsError } from '../../../src/server/errors';
import type {
AppExternalComponentManager,
AppSchedulerManager,
AppSlashCommandManager,
AppVideoConfProviderManager,
} from '../../../src/server/managers';
import { AppAccessorManager, 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';
export class AppApiManagerTestFixture {
public static doThrow = false;
private mockBridges: TestsAppBridges;
private mockApp: ProxiedApp;
private mockAccessors: AppAccessorManager;
private mockManager: AppManager;
private spies: Array<RestorableFunctionSpy>;
@SetupFixture
public setupFixture() {
this.mockBridges = new TestsAppBridges();
this.mockApp = TestData.getMockApp({ info: { id: 'testing', name: 'TestApp' } } as IAppStorageItem, this.mockManager);
const bri = this.mockBridges;
const app = this.mockApp;
this.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;
this.mockAccessors = new AppAccessorManager(this.mockManager);
const ac = this.mockAccessors;
this.mockManager.getAccessorManager = function _getAccessorManager(): AppAccessorManager {
return ac;
};
}
@Setup
public setup() {
this.mockBridges = new TestsAppBridges();
const bri = this.mockBridges;
this.mockManager.getBridges = function _refreshedGetBridges(): AppBridges {
return bri;
};
this.spies = [];
this.spies.push(SpyOn(this.mockBridges.getApiBridge(), 'doRegisterApi'));
this.spies.push(SpyOn(this.mockBridges.getApiBridge(), 'doUnregisterApis'));
}
@Teardown
public teardown() {
this.spies.forEach((s) => s.restore());
}
@Test()
public basicAppApiManager() {
Expect(() => new AppApiManager({} as AppManager)).toThrow();
Expect(() => new AppApiManager(this.mockManager)).not.toThrow();
const ascm = new AppApiManager(this.mockManager);
Expect((ascm as any).manager).toBe(this.mockManager);
Expect((ascm as any).bridge).toBe(this.mockBridges.getApiBridge());
Expect((ascm as any).accessors).toBe(this.mockManager.getAccessorManager());
Expect((ascm as any).providedApis).toBeDefined();
Expect((ascm as any).providedApis.size).toBe(0);
}
@AsyncTest()
public async registerApi() {
const ascm = new AppApiManager(this.mockManager);
const api: IApi = TestData.getApi('path');
const regInfo = new AppApi(this.mockApp, api, api.endpoints[0]);
await Expect(() => (ascm as any).registerApi('testing', regInfo)).not.toThrowAsync();
Expect(this.mockBridges.getApiBridge().doRegisterApi).toHaveBeenCalledWith(regInfo, 'testing');
}
@Test()
public addApi() {
const api = TestData.getApi('apipath');
const ascm = new AppApiManager(this.mockManager);
Expect(() => ascm.addApi('testing', api)).not.toThrow();
Expect(this.mockBridges.getApiBridge().apis.size).toBe(1);
Expect((ascm as any).providedApis.size).toBe(1);
Expect((ascm as any).providedApis.get('testing').get('apipath').api).toBe(api);
Expect(() => ascm.addApi('testing', api)).toThrowError(PathAlreadyExistsError, 'The api path "apipath" already exists in the system.');
Expect(() => ascm.addApi('failMePlease', TestData.getApi('yet-another'))).toThrowError(
Error,
'App must exist in order for an api to be added.',
);
Expect(() => ascm.addApi('testing', TestData.getApi('another-api'))).not.toThrow();
Expect((ascm as any).providedApis.size).toBe(1);
Expect((ascm as any).providedApis.get('testing').size).toBe(2);
}
@AsyncTest()
public async registerApis() {
const ascm = new AppApiManager(this.mockManager);
SpyOn(ascm, 'registerApi');
ascm.addApi('testing', TestData.getApi('apipath'));
const regInfo = (ascm as any).providedApis.get('testing').get('apipath') as AppApi;
await Expect(() => ascm.registerApis('non-existant')).not.toThrowAsync();
await Expect(() => ascm.registerApis('testing')).not.toThrowAsync();
Expect((ascm as any).registerApi as FunctionSpy)
.toHaveBeenCalledWith('testing', regInfo)
.exactly(1);
Expect(this.mockBridges.getApiBridge().doRegisterApi).toHaveBeenCalledWith(regInfo, 'testing').exactly(1);
}
@AsyncTest()
public async unregisterApis() {
const ascm = new AppApiManager(this.mockManager);
ascm.addApi('testing', TestData.getApi('apipath'));
await Expect(() => ascm.unregisterApis('non-existant')).not.toThrowAsync();
await Expect(() => ascm.unregisterApis('testing')).not.toThrowAsync();
Expect(this.mockBridges.getApiBridge().doUnregisterApis).toHaveBeenCalled().exactly(1);
}
@AsyncTest()
public async executeApis() {
const ascm = new AppApiManager(this.mockManager);
ascm.addApi('testing', TestData.getApi('api1'));
ascm.addApi('testing', TestData.getApi('api2'));
ascm.addApi('testing', TestData.getApi('api3'));
ascm.registerApis('testing');
const request: IApiRequest = {
method: RequestMethod.GET,
headers: {},
query: {},
params: {},
content: '',
};
await Expect(() => ascm.executeApi('testing', 'nope', request)).not.toThrowAsync();
await Expect(() => ascm.executeApi('testing', 'not-exists', request)).not.toThrowAsync();
await Expect(() => ascm.executeApi('testing', 'api1', request)).not.toThrowAsync();
await Expect(() => ascm.executeApi('testing', 'api2', request)).not.toThrowAsync();
await Expect(() => ascm.executeApi('testing', 'api3', request)).not.toThrowAsync();
}
@Test()
public listApis() {
const ascm = new AppApiManager(this.mockManager);
Expect(ascm.listApis('testing')).toEqual([]);
ascm.addApi('testing', TestData.getApi('api1'));
ascm.registerApis('testing');
Expect(() => ascm.listApis('testing')).not.toThrow();
Expect(ascm.listApis('testing')).not.toEqual([]);
Expect(ascm.listApis('testing')).toEqual([
{
path: 'api1',
computedPath: '/api/apps/public/testing/api1',
methods: ['get'],
examples: {},
},
]);
}
}