Files
openstapps/backend/backend/test/routes/plugin-register-route.spec.ts
Thea Schöbl e8d72683ef fix: backend tests break every year
refactor: update some backend unit tests
2024-03-27 09:55:28 +01:00

210 lines
8.2 KiB
TypeScript

/*
* 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 <https://www.gnu.org/licenses/>.
*/
import {
SCNotFoundErrorResponse,
SCPluginAdd,
SCPluginAlreadyRegisteredErrorResponse,
SCPluginRegisterResponse,
SCPluginRegisterRoute,
SCPluginRemove,
SCValidationErrorResponse,
} from '@openstapps/core';
import nock from 'nock';
import {plugins} from '../../src/common.js';
import {pluginRegisterHandler} from '../../src/routes/plugin-register-route.js';
import {expect, use} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import {DEFAULT_TEST_TIMEOUT} from '../common.js';
import {testApp} from '../tests-setup.js';
import {backendConfig} from '../../src/config.js';
import registerRequest from '@openstapps/core/test/resources/PluginRegisterRequest.1.json' assert {type: 'json'};
// 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.instance 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();
backendConfig.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(backendConfig.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(backendConfig.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(backendConfig.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);
});
});
});