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/AppOutboundCommunicationPro...

309 lines
12 KiB

import type { RestorableFunctionSpy } from 'alsatian';
import { AsyncTest, Expect, Setup, SetupFixture, SpyOn, Teardown, Test } from 'alsatian';
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 { AppAccessorManager, 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 } 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 AppOutboundCommunicationProviderManagerTestFixture {
public static doThrow = false;
private mockBridges: TestsAppBridges;
private mockApp: ProxiedApp;
private mockAccessors: AppAccessorManager;
private mockManager: AppManager;
private hasPermissionSpy: RestorableFunctionSpy;
@SetupFixture
public setupFixture() {
this.mockBridges = new TestsAppBridges();
this.mockApp = TestData.getMockApp({ id: 'testing', name: 'testing' }, this.mockManager);
const bri = this.mockBridges;
const app = this.mockApp;
this.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;
this.mockAccessors = new AppAccessorManager(this.mockManager);
const ac = this.mockAccessors;
this.mockManager.getAccessorManager = function _getAccessorManager(): AppAccessorManager {
return ac;
};
}
@Setup
public setup() {
this.hasPermissionSpy = SpyOn(AppPermissionManager, 'hasPermission');
this.hasPermissionSpy.andReturn(true);
}
@Teardown
public teardown() {
this.hasPermissionSpy.restore();
}
@Test()
public basicAppOutboundCommunicationProviderManager() {
Expect(() => new AppOutboundCommunicationProviderManager({} as AppManager)).toThrow();
Expect(() => new AppOutboundCommunicationProviderManager(this.mockManager)).not.toThrow();
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
Expect((manager as any).manager).toBe(this.mockManager);
Expect((manager as any).accessors).toBe(this.mockManager.getAccessorManager());
Expect((manager as any).outboundMessageProviders).toBeDefined();
Expect((manager as any).outboundMessageProviders.size).toBe(0);
}
@Test()
public addProvider() {
const provider = TestData.getOutboundPhoneMessageProvider();
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
Expect(() => manager.addProvider('testing', provider)).not.toThrow();
Expect((manager as any).outboundMessageProviders.size).toBe(1);
Expect(() => manager.addProvider('failMePlease', provider)).toThrowError(
Error,
'App must exist in order for an outbound provider to be added.',
);
Expect((manager as any).outboundMessageProviders.size).toBe(1);
}
@Test()
public isAlreadyDefined() {
const provider = TestData.getOutboundPhoneMessageProvider();
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
Expect(manager.isAlreadyDefined('testing', 'phone')).toBe(false);
manager.addProvider('testing', provider);
Expect(manager.isAlreadyDefined('testing', 'phone')).toBe(true);
Expect(manager.isAlreadyDefined('testing', 'email')).toBe(false);
Expect(manager.isAlreadyDefined('another-app', 'phone')).toBe(false);
}
@Test()
public addProviderTwiceShouldOverwrite() {
const provider1 = TestData.getOutboundPhoneMessageProvider('provider1');
const provider2 = TestData.getOutboundPhoneMessageProvider('provider2');
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
manager.addProvider('testing', provider1);
const firstProviderInfo = (manager as any).outboundMessageProviders.get('testing').get('phone');
Expect(firstProviderInfo.provider.name).toBe('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');
Expect(secondProviderInfo.provider.name).toBe('provider2');
Expect((manager as any).outboundMessageProviders.get('testing').size).toBe(1);
}
@Test()
public addProviderWithoutPermission() {
const provider = TestData.getOutboundPhoneMessageProvider();
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
this.hasPermissionSpy.andReturn(false);
Expect(() => manager.addProvider('testing', provider)).toThrow();
}
@Test()
public ignoreAppsWithoutProviders() {
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
Expect(() => manager.registerProviders('non-existant')).not.toThrow();
}
@AsyncTest()
public async registerProviders() {
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
manager.addProvider('firstApp', TestData.getOutboundPhoneMessageProvider());
const appInfo = (manager as any).outboundMessageProviders.get('firstApp');
Expect(appInfo).toBeDefined();
const regInfo = appInfo.get('phone');
Expect(regInfo).toBeDefined();
Expect(regInfo.isRegistered).toBe(false);
await Expect(async () => manager.registerProviders('firstApp')).not.toThrowAsync();
Expect(regInfo.isRegistered).toBe(true);
}
@AsyncTest()
public async registerTwoProviders() {
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
manager.addProvider('firstApp', TestData.getOutboundPhoneMessageProvider());
manager.addProvider('firstApp', TestData.getOutboundEmailMessageProvider());
const firstApp = (manager as any).outboundMessageProviders.get('firstApp');
Expect(firstApp).toBeDefined();
const firstRegInfo = firstApp.get('phone');
Expect(firstRegInfo).toBeDefined();
const secondRegInfo = firstApp.get('email');
Expect(secondRegInfo).toBeDefined();
Expect(firstRegInfo.isRegistered).toBe(false);
Expect(secondRegInfo.isRegistered).toBe(false);
await Expect(async () => manager.registerProviders('firstApp')).not.toThrowAsync();
Expect(firstRegInfo.isRegistered).toBe(true);
Expect(secondRegInfo.isRegistered).toBe(true);
}
@AsyncTest()
public async registerProvidersFromMultipleApps() {
const manager = new AppOutboundCommunicationProviderManager(this.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');
Expect(firstApp).toBeDefined();
const firstRegInfo = firstApp.get('phone');
const secondRegInfo = firstApp.get('email');
Expect(firstRegInfo).toBeDefined();
Expect(secondRegInfo).toBeDefined();
const secondApp = (manager as any).outboundMessageProviders.get('secondApp');
Expect(secondApp).toBeDefined();
const thirdRegInfo = secondApp.get('phone');
Expect(thirdRegInfo).toBeDefined();
Expect(firstRegInfo.isRegistered).toBe(false);
Expect(secondRegInfo.isRegistered).toBe(false);
await Expect(async () => manager.registerProviders('firstApp')).not.toThrowAsync();
Expect(firstRegInfo.isRegistered).toBe(true);
Expect(secondRegInfo.isRegistered).toBe(true);
Expect(thirdRegInfo.isRegistered).toBe(false);
await Expect(async () => manager.registerProviders('secondApp')).not.toThrowAsync();
Expect(thirdRegInfo.isRegistered).toBe(true);
}
@AsyncTest()
public async unregisterProviders() {
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
const regInfo = (manager as any).outboundMessageProviders.get('testing').get('phone');
await Expect(async () => manager.registerProviders('testing')).not.toThrowAsync();
await Expect(async () => manager.unregisterProviders('non-existant')).not.toThrowAsync();
Expect(regInfo.isRegistered).toBe(true);
await Expect(async () => manager.unregisterProviders('testing')).not.toThrowAsync();
Expect(regInfo.isRegistered).toBe(false);
// It should be removed from the map
Expect((manager as any).outboundMessageProviders.has('testing')).toBe(false);
}
@AsyncTest()
public async unregisterProvidersWithKeepReferences() {
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
const appInfo = (manager as any).outboundMessageProviders.get('testing');
const regInfo = appInfo.get('phone');
await Expect(async () => manager.registerProviders('testing')).not.toThrowAsync();
Expect(regInfo.isRegistered).toBe(true);
await Expect(async () => manager.unregisterProviders('testing', { keepReferences: true })).not.toThrowAsync();
Expect(regInfo.isRegistered).toBe(false);
// It should not be removed from the map
Expect((manager as any).outboundMessageProviders.has('testing')).toBe(true);
}
@Test()
public failToGetMetadataWithoutProvider() {
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
Expect(() => manager.getProviderMetadata('testing', 'phone')).toThrowError(Error, 'provider-not-registered');
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
Expect(() => manager.getProviderMetadata('testing', 'email')).toThrowError(Error, 'provider-not-registered');
}
@Test()
public getProviderMetadata() {
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
SpyOn(OutboundMessageProvider.prototype, 'runGetProviderMetadata').andReturn({
name: 'test-provider',
capabilities: ['sms'],
});
const metadata = manager.getProviderMetadata('testing', 'phone');
Expect(metadata).toEqual({
name: 'test-provider',
capabilities: ['sms'],
});
}
@Test()
public failToSendOutboundMessageWithoutProvider() {
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
const message = TestData.getOutboundMessage();
Expect(() => manager.sendOutboundMessage('testing', 'phone', message)).toThrowError(Error, 'provider-not-registered');
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
Expect(() => manager.sendOutboundMessage('testing', 'email', message)).toThrowError(Error, 'provider-not-registered');
}
@Test()
public sendOutboundMessage() {
const manager = new AppOutboundCommunicationProviderManager(this.mockManager);
manager.addProvider('testing', TestData.getOutboundPhoneMessageProvider());
const message = TestData.getOutboundMessage();
SpyOn(OutboundMessageProvider.prototype, 'runSendOutboundMessage').andReturn(Promise.resolve('message-id'));
const result = manager.sendOutboundMessage('testing', 'phone', message);
Expect(result).toBeDefined();
}
}