/* * 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 * as supertest from 'supertest'; import * as chaiAsPromised from 'chai-as-promised'; import {timeout, slow} from 'mocha-typescript'; import {should, use} from 'chai'; import {configureApp, app} from '../src/app'; import {registerAddRequest, registerRemoveRequest} from './routes/plugin-register-route.spec'; import {plugins} from '../src/common'; import {SCPluginRemove} from '@openstapps/core'; import * as nock from 'nock'; import * as got from 'got'; import * as sinon from 'sinon'; should(); use(chaiAsPromised); let appTest: supertest.SuperTest; // configures the backend and creates supertest const prepareTestApp = async () => { await configureApp(); appTest = supertest(app); }; /** * Tests plugin registration routes */ @suite(timeout(10000), slow(5000)) export class AppPluginRegisterSpec { static async before() { if (typeof appTest === 'undefined') { await prepareTestApp(); } } async after() { // remove plugins plugins.clear(); } @test 'should respond with success when providing valid request'() { return appTest .post('/plugin/register') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send(registerAddRequest) .expect(200, {success: true}); } @test async 'should respond with bad request if when providing invalid request'() { return appTest .post('/plugin/register') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send({foo: 'bar'}) .expect(400); } @test async 'should respond with false if something went wrong with a valid request'() { // 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'}}; await appTest .post('/plugin/register') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send(registerAddRequestAltered) .expect(409) .expect((res: supertest.Response) => { res.body.should.have.property('name', 'SCPluginAlreadyRegisteredError'); }); } @test async 'should respond with success when deregistering already registered plugin'() { // lets simulate that a plugin is already registered plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin); await appTest .post('/plugin/register') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send(registerRemoveRequest) .expect(200, {success: true}); } @test async 'should response with 404 when deleting non previously registered plugin'() { // lets simulate that the plugin is already registered plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin); await appTest .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(404); } } /** * Tests functioning of already registered plugins */ @suite(timeout(10000), slow(5000)) export class AppPluginSpec { static async before() { if (typeof appTest === 'undefined') { await prepareTestApp(); } } async after() { // remove plugins plugins.clear(); // restore everything to default methods (remove stubs) sinon.restore(); // clean up request mocks (fixes issue with receiving response from mock from previous test case) nock.cleanAll(); } @test async 'should properly provide the response of a registered plugin'() { // lets simulate that the plugin is already registered plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin); // mock responses of the plugin, depending on the body sent nock('http://foo.com:1234') .post('/foo', {query: 'foo'}) .reply(200, {result: [{foo: 'foo'}, {bar: 'foo'}]}); nock('http://foo.com:1234') .post('/foo', {query: 'bar'}) .reply(200, {result: [{foo: 'bar'}, {bar: 'bar'}]}); await appTest .post('/foo') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send({query: 'foo'}) .expect(200, {result: [{foo: 'foo'}, {bar: 'foo'}]}); await appTest .post('/foo') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send({query: 'bar'}) .expect(200, {result: [{foo: 'bar'}, {bar: 'bar'}]}); } @test async 'should provide 404 if plugin/route is not registered'() { // 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'}]}); return appTest .post('/not-foo') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send({query: 'foo'}) .expect(404); } @test async 'should return error response if plugin address is not responding'() { // lets simulate that the plugin is already registered plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin); class FooError extends Error { }; // fake that got's post method throws an error sinon.stub(got, 'post') .callsFake(() => { throw new FooError(); }); return appTest .post('/foo') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send({query: 'foo'}) .expect(502); } @test async 'should return error response if request is not valid'() { // lets simulate that the plugin is already registered plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin); return appTest .post('/foo') .set('Content-Type', 'application/json') .set('Accept', 'application/json') // using number for query instead of (in request schema) required text .send({query: 123}) .expect(502) .expect((res: supertest.Response) => { res.body.additionalData.should.have.property('name', 'ValidationError'); }); } @test async 'should return error response if plugin response is not valid'() { // 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, {not_valid_field: ['foo bar']}); return appTest .post('/foo') .set('Content-Type', 'application/json') .set('Accept', 'application/json') .send({query: 'foo'}) .expect(502) .expect((res: supertest.Response) => { res.body.additionalData.should.have.property('name', 'ValidationError'); }); } }