/* * Copyright (C) 2019 StApps * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ import { SCNotFoundErrorResponse, SCPluginAdd, SCPluginAlreadyRegisteredErrorResponse, SCPluginRegisterResponse, SCPluginRegisterRoute, SCPluginRemove, SCValidationErrorResponse, } from '@openstapps/core'; import nock from 'nock'; import {configFile, plugins} from '../../src/common'; import {pluginRegisterHandler} from '../../src/routes/plugin-register-route'; import {expect, use} from 'chai'; import chaiAsPromised from 'chai-as-promised'; import {instance as registerRequest} from '@openstapps/core/test/resources/PluginRegisterRequest.1.json'; import {DEFAULT_TEST_TIMEOUT} from '../common'; import {testApp} from '../tests-setup'; // for using promises in expectations (to.eventually.be...) use(chaiAsPromised); // cast it because of "TS2322: Type 'string' is not assignable to type '"add"'" export const registerAddRequest: SCPluginAdd = registerRequest as SCPluginAdd; export const registerRemoveRequest: SCPluginRemove = { action: 'remove', route: registerAddRequest.plugin.route, }; describe('Plugin registration', async function () { const bodySuccess: SCPluginRegisterResponse = {success: true}; describe('Middleware', async function () { after(function () { // remove plugins plugins.clear(); configFile.app.features = {}; }); it('should register a plugin', async function () { // register one plugin const response = await pluginRegisterHandler(registerAddRequest, {}); expect(response).to.deep.equal(bodySuccess) && expect(plugins.size).to.equal(1) && expect(configFile.app.features.plugins!['Foo Plugin']).to.not.be.empty; }); it('should allow re-registering the same plugin', async function () { // register one plugin await pluginRegisterHandler(registerAddRequest, {}); // register the same plugin again const response = await pluginRegisterHandler(registerAddRequest, {}); return ( expect(response).to.deep.equal(bodySuccess) && expect(plugins.size).to.equal(1) && expect(configFile.app.features.plugins!['Foo Plugin']).to.not.be.empty ); }); it('should show an error if a plugin has already been registered', async function () { // register one plugin await pluginRegisterHandler(registerAddRequest, {}); // create new request for adding a plugin with only name that changed const registerAddRequestAltered: SCPluginAdd = { ...registerAddRequest, plugin: {...registerAddRequest.plugin, name: registerAddRequest.plugin.name + 'foo'}, }; // register the same plugin again expect(pluginRegisterHandler(registerAddRequestAltered, {})) .to.eventually.be.rejectedWith('Plugin already registered') .and.be.an.instanceOf(SCPluginAlreadyRegisteredErrorResponse) // check that the right plugin information (of the previously registered plugin) is provided with the error .and.have.property('additionalData', registerAddRequest.plugin); }); it('should remove plugin if it exists', async function () { // register one plugin await pluginRegisterHandler(registerAddRequest, {}); expect(plugins.size).to.equal(1); const response = await pluginRegisterHandler(registerRemoveRequest, {}); expect(response).to.deep.equal(bodySuccess) && expect(plugins.size).to.equal(0) && expect(configFile.app.features.plugins).to.be.empty; }); it('should throw a "not found" error when removing a plugin whose registered route does not exist', async function () { // register one plugin await pluginRegisterHandler(registerAddRequest, {}); expect(pluginRegisterHandler({...registerRemoveRequest, route: '/not-foo'}, {})) .to.eventually.be.rejectedWith('Resource not found') .and.be.an.instanceOf(SCNotFoundErrorResponse); }); }); describe('Routes', async function () { // increase timeout for the suite this.timeout(DEFAULT_TEST_TIMEOUT); const pluginRegisterRoute = new SCPluginRegisterRoute(); const validationError = new SCValidationErrorResponse([]); const notFoundError = new SCNotFoundErrorResponse(); // @ts-expect-error not assignable const alreadyRegisteredError = new SCPluginAlreadyRegisteredErrorResponse('Foo Message', {}); afterEach(async function () { // remove routes plugins.clear(); // clean up request mocks (fixes issue with receiving response from mock from previous test case) nock.cleanAll(); }); it('should provide "not found" (404) if plugin/route is not registered', async function () { // lets simulate that the plugin is already registered plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin); // mock response of the plugin address nock('http://foo.com:1234') .post('/foo') .reply(200, {result: [{foo: 'bar'}, {bar: 'foo'}]}); const {status} = await testApp .post('/not-foo') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send({query: 'foo'}); expect(status).to.be.equal(notFoundError.statusCode); }); it('should respond with bad request if when providing invalid request', async function () { const {status} = await testApp .post(pluginRegisterRoute.urlPath) .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send({foo: 'bar'}); expect(status).to.be.equal(validationError.statusCode); }); it('should respond with an error if something went wrong with a valid request', async function () { // lets simulate that a plugin is already registered plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin); // registering a different plugin for the same route causes the expected error const registerAddRequestAltered = { ...registerAddRequest, plugin: {...registerAddRequest.plugin, name: 'FooBar Plugin'}, }; const {status, body} = await testApp .post(pluginRegisterRoute.urlPath) .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send(registerAddRequestAltered); expect(status).to.be.equal(alreadyRegisteredError.statusCode); expect(body).to.haveOwnProperty('name', 'SCPluginAlreadyRegisteredError'); }); it('should respond with success when de-registering already registered plugin', async function () { // lets simulate that a plugin is already registered plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin); const {status, body} = await testApp .post('/plugin/register') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send(registerRemoveRequest); expect(status).to.be.equal(new SCPluginRegisterRoute().statusCodeSuccess); expect(body).to.be.deep.equal(bodySuccess); }); it('should response with 404 when deleting a plugin which was not registered', async function () { // lets simulate that the plugin is already registered plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin); const {status} = await testApp .post('/plugin/register') .set('Content-Type', 'application/json') .set('Accept', 'application/json') // using a different route for the remove request .send({action: 'remove', route: '/not-foo'} as SCPluginRemove); expect(status).to.be.equal(notFoundError.statusCode); }); }); });