feat: migrate backend to cosmiconfig

This commit is contained in:
2023-04-25 15:54:06 +02:00
parent d8c79256c9
commit 0a76427ba8
70 changed files with 1786 additions and 1635 deletions

View File

@@ -20,11 +20,12 @@ import {
SCUnsupportedMediaTypeErrorResponse,
} from '@openstapps/core';
import {expect} from 'chai';
import {configFile, DEFAULT_TIMEOUT} from '../src/common.js';
import {DEFAULT_TIMEOUT} from '../src/common.js';
import {DEFAULT_TEST_TIMEOUT} from './common.js';
import {testApp} from './tests-setup.js';
import sinon from 'sinon';
import mockedEnv from 'mocked-env';
import {backendConfig} from '../src/config.js';
describe('App', async function () {
// increase timeout for the suite
@@ -39,6 +40,7 @@ describe('App', async function () {
const clock = sandbox.useFakeTimers();
const processExitStub = sandbox.stub(process, 'exit');
// fake NODE_ENV as integration test
// @ts-expect-error type definitions are not working for some reason
const restore = mockedEnv({
NODE_ENV: 'integration-test',
});
@@ -55,7 +57,7 @@ describe('App', async function () {
});
it('should provide request body too large error in case of a body larger than the max size', async function () {
sandbox.stub(configFile.backend, 'maxRequestBodySize').value('3');
sandbox.stub(backendConfig.backend, 'maxRequestBodySize').value('3');
const {status} = await testApp
.post('/')

View File

@@ -13,8 +13,8 @@
* 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 {yearSlice} from '../config/default.js';
import {expect} from 'chai';
import {yearSlice} from '../config/default/tools/semester-acronym.js';
describe('Common', function () {
describe('yearSlice', function () {

View File

@@ -20,12 +20,12 @@ import {getIndexUID} from '../src/storage/elasticsearch/util.js';
import {configureApp} from '../src/app.js';
import express from 'express';
import http from 'http';
import {configFile} from '../src/common.js';
import {MailQueue} from '../src/notification/mail-queue.js';
import {Bulk, BulkStorage} from '../src/storage/bulk-storage.js';
import getPort from 'get-port';
import {Database} from '../src/storage/database.js';
import {v4} from 'uuid';
import {backendConfig} from '../src/config.js';
/**
* Adds routers and configures an (express) app
@@ -108,7 +108,7 @@ export class ElasticsearchMock implements Database {
}
}
export const bulkStorageMock = new BulkStorage(new ElasticsearchMock(configFile));
export const bulkStorageMock = new BulkStorage(new ElasticsearchMock(backendConfig));
export const bulk: Bulk = {
expiration: moment().add(3600, 'seconds').format(),

View File

@@ -13,8 +13,7 @@
* 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 {SMTP} from '@openstapps/logger/lib/smtp';
import {Transport} from '@openstapps/logger/lib/transport';
import {SMTP, Transport} from '@openstapps/logger';
import {expect} from 'chai';
import mockedEnv from 'mocked-env';
import {BackendTransport, isTransportWithVerification} from '../../src/notification/backend-transport.js';
@@ -54,6 +53,7 @@ describe('Backend transport', function () {
it('should not throw in case of error getting SMTP instance when transport not allowed', function () {
sandbox.stub(SMTP, 'getInstance').throws('Foo Error');
// @ts-expect-error wrong type defs for some reason
const restore = mockedEnv({
ALLOW_NO_TRANSPORT: 'true',
});
@@ -66,6 +66,7 @@ describe('Backend transport', function () {
it('should throw in case of error getting SMTP instance when transport is allowed', function () {
sandbox.stub(SMTP, 'getInstance').throws('Foo Error');
// @ts-expect-error wrong type defs for some reason
const restore = mockedEnv({
ALLOW_NO_TRANSPORT: undefined,
});

View File

@@ -21,9 +21,13 @@ import {
SCNotFoundErrorResponse,
} from '@openstapps/core';
import {expect} from 'chai';
import {instance as book} from '@openstapps/core/test/resources/indexable/Book.1.json';
import {bulk, DEFAULT_TEST_TIMEOUT} from '../common.js';
import {testApp} from '../tests-setup.js';
import {readFile} from 'fs/promises';
const book = JSON.parse(
await readFile('node_modules/@openstapps/core/test/resources/indexable/Book.1.json', 'utf8'),
);
describe('Bulk routes', async function () {
// increase timeout for the suite

View File

@@ -23,19 +23,24 @@ import {
SCValidationErrorResponse,
} from '@openstapps/core';
import nock from 'nock';
import {configFile, plugins} from '../../src/common.js';
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 {instance as registerRequest} from '@openstapps/core/test/resources/PluginRegisterRequest.1.json';
import {DEFAULT_TEST_TIMEOUT} from '../common.js';
import {testApp} from '../tests-setup.js';
import {backendConfig} from '../../src/config.js';
import {readFile} from 'fs/promises';
// for using promises in expectations (to.eventually.be...)
use(chaiAsPromised);
const registerRequest = JSON.parse(
await readFile('node_modules/@openstapps/core/test/resources/PluginRegisterRequest.1.json', 'utf8'),
);
// cast it because of "TS2322: Type 'string' is not assignable to type '"add"'"
export const registerAddRequest: SCPluginAdd = registerRequest as SCPluginAdd;
export const registerAddRequest: SCPluginAdd = registerRequest.instance as SCPluginAdd;
export const registerRemoveRequest: SCPluginRemove = {
action: 'remove',
@@ -48,7 +53,7 @@ describe('Plugin registration', async function () {
after(function () {
// remove plugins
plugins.clear();
configFile.app.features = {};
backendConfig.app.features = {};
});
it('should register a plugin', async function () {
@@ -57,7 +62,7 @@ describe('Plugin registration', async function () {
expect(response).to.deep.equal(bodySuccess) &&
expect(plugins.size).to.equal(1) &&
expect(configFile.app.features.plugins!['Foo Plugin']).to.not.be.empty;
expect(backendConfig.app.features.plugins!['Foo Plugin']).to.not.be.empty;
});
it('should allow re-registering the same plugin', async function () {
@@ -70,7 +75,7 @@ describe('Plugin registration', async function () {
return (
expect(response).to.deep.equal(bodySuccess) &&
expect(plugins.size).to.equal(1) &&
expect(configFile.app.features.plugins!['Foo Plugin']).to.not.be.empty
expect(backendConfig.app.features.plugins!['Foo Plugin']).to.not.be.empty
);
});
@@ -102,7 +107,7 @@ describe('Plugin registration', async function () {
expect(response).to.deep.equal(bodySuccess) &&
expect(plugins.size).to.equal(0) &&
expect(configFile.app.features.plugins).to.be.empty;
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 () {

View File

@@ -21,7 +21,7 @@ import {
SCRouteHttpVerbs,
SCValidationErrorResponse,
} from '@openstapps/core';
import * as bodyParser from 'body-parser';
import bodyParser from 'body-parser';
import sinon from 'sinon';
import {expect} from 'chai';
import {Application} from 'express';

View File

@@ -21,10 +21,10 @@ import {
SCTooManyRequestsErrorResponse,
} from '@openstapps/core';
import {expect} from 'chai';
import {configFile} from '../../src/common.js';
import {DEFAULT_TEST_TIMEOUT} from '../common.js';
import {testApp} from '../tests-setup.js';
import sinon from 'sinon';
import {backendConfig} from '../../src/config.js';
describe('Search route', async function () {
// increase timeout for the suite
@@ -96,7 +96,7 @@ describe('Search route', async function () {
it('should respond with too many requests error if the number of sub-queries exceed their max number', async function () {
const sandbox = sinon.createSandbox();
sandbox.stub(configFile.backend, 'maxMultiSearchRouteQueries').value(2);
sandbox.stub(backendConfig.backend, 'maxMultiSearchRouteQueries').value(2);
const {status} = await testApp
.post(multiSearchRoute.urlPath)

View File

@@ -17,11 +17,15 @@ import {SCThingUpdateRoute} from '@openstapps/core';
import chaiAsPromised from 'chai-as-promised';
import {bulkStorageMock, DEFAULT_TEST_TIMEOUT} from '../common.js';
import {expect, use} from 'chai';
import {instance as book} from '@openstapps/core/test/resources/indexable/Book.1.json';
import {testApp} from '../tests-setup.js';
import {readFile} from 'fs/promises';
use(chaiAsPromised);
const book = JSON.parse(
await readFile('node_modules/@openstapps/core/test/resources/indexable/Book.1.json', 'utf8'),
);
describe('Thing update route', async function () {
// increase timeout for the suite
this.timeout(DEFAULT_TEST_TIMEOUT);

View File

@@ -25,7 +25,7 @@ import {mockReq} from 'sinon-express-mock';
import {plugins, validator} from '../../src/common.js';
import {virtualPluginRoute} from '../../src/routes/virtual-plugin-route.js';
import {DEFAULT_TEST_TIMEOUT, FooError} from '../common.js';
import {registerAddRequest} from './plugin-register-route.spec';
import {registerAddRequest} from './plugin-register-route.spec.js';
import {testApp} from '../tests-setup.js';
use(chaiAsPromised);
@@ -154,12 +154,12 @@ describe('Virtual plugin routes', async function () {
plugins.clear();
// // restore everything to default methods (remove stubs)
sandbox.restore();
// clean up request mocks (fixes issue with receiving response from mock from previous test case)
// cleanup request mocks (fixes issue with receiving response from mock from previous test case)
nock.cleanAll();
});
it('should properly provide the response of a plugin', async function () {
// lets simulate that the plugin is already registered
// let's 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')
@@ -174,20 +174,20 @@ describe('Virtual plugin routes', async function () {
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.send({query: 'foo'});
expect(fooResponse.status).to.be.equal(OK);
expect(fooResponse.body).to.be.deep.equal({result: [{foo: 'foo'}, {bar: 'foo'}]});
const barResponse = await testApp
.post('/foo')
.set('Content-Type', 'application/json')
.set('Accept', 'application/json')
.send({query: 'bar'});
expect(fooResponse.status).to.be.equal(OK);
expect(fooResponse.body).to.be.deep.equal({result: [{foo: 'foo'}, {bar: 'foo'}]});
expect(barResponse.status).to.be.equal(OK);
expect(barResponse.body).to.be.deep.equal({result: [{foo: 'bar'}, {bar: 'bar'}]});
});
it('should return error response if plugin address is not responding', async function () {
// lets simulate that the plugin is already registered
// let's simulate that the plugin is already registered
plugins.set(registerAddRequest.plugin.route, registerAddRequest.plugin);
class FooError extends Error {}

View File

@@ -17,12 +17,12 @@ import {SCBulkRequest, SCThingType} from '@openstapps/core';
import moment from 'moment';
// eslint-disable-next-line unicorn/import-style
import util from 'util';
import {configFile} from '../../src/common.js';
import {Bulk, BulkStorage} from '../../src/storage/bulk-storage.js';
import {expect} from 'chai';
import {ElasticsearchMock} from '../common.js';
import sinon from 'sinon';
import NodeCache from 'node-cache';
import {backendConfig} from '../../src/config.js';
describe('Bulk Storage', function () {
describe('Bulk', function () {
@@ -58,7 +58,7 @@ describe('Bulk Storage', function () {
let database: ElasticsearchMock;
beforeEach(function () {
database = new ElasticsearchMock(configFile);
database = new ElasticsearchMock(backendConfig);
esMock = sandbox.stub(database, 'bulkExpired');
});

View File

@@ -31,10 +31,7 @@ import {
SCThings,
SCThingType,
} from '@openstapps/core';
import {instance as book} from '@openstapps/core/test/resources/indexable/Book.1.json';
import {instance as message} from '@openstapps/core/test/resources/indexable/Message.1.json';
import {Logger} from '@openstapps/logger';
import {SMTP} from '@openstapps/logger/lib/smtp';
import {Logger, SMTP} from '@openstapps/logger';
import {expect, use} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import {beforeEach} from 'mocha';
@@ -58,6 +55,8 @@ import * as Monitoring from '../../../src/storage/elasticsearch/monitoring.js';
import * as templating from '../../../src/storage/elasticsearch/templating.js';
import {bulk, DEFAULT_TEST_TIMEOUT, getTransport, getIndex} from '../../common.js';
import fs from 'fs';
import {backendConfig} from '../../../src/config.js';
import {readFile} from 'fs/promises';
use(chaiAsPromised);
@@ -68,6 +67,13 @@ function searchResponse<T>(...hits: SearchHit<T>[]): SearchResponse<T> {
return {hits: {hits}, took: 0, timed_out: false, _shards: {total: 1, failed: 0, successful: 1}};
}
const message = JSON.parse(
await readFile('node_modules/@openstapps/core/test/resources/indexable/Message.1.json', 'utf8'),
);
const book = JSON.parse(
await readFile('node_modules/@openstapps/core/test/resources/indexable/Book.1.json', 'utf8'),
);
describe('Elasticsearch', function () {
// increase timeout for the suite
this.timeout(DEFAULT_TEST_TIMEOUT);
@@ -85,6 +91,7 @@ describe('Elasticsearch', function () {
describe('getElasticsearchUrl', function () {
it('should provide custom elasticsearch URL if defined', function () {
const customAddress = 'http://foo-address:9200';
// @ts-expect-error wrong type defs for some reason
const restore = mockedEnv({
ES_ADDR: customAddress,
});
@@ -95,6 +102,7 @@ describe('Elasticsearch', function () {
});
it('should provide local URL as fallback', function () {
// @ts-expect-error wrong type defs for some reason
const restore = mockedEnv({
ES_ADDR: undefined,
});
@@ -193,16 +201,19 @@ describe('Elasticsearch', function () {
});
it('should complain (throw an error) if database in config is undefined', function () {
const config: SCConfigFile = {...configFile, internal: {...configFile.internal, database: undefined}};
const config: SCConfigFile = {
...backendConfig,
internal: {...backendConfig.internal, database: undefined},
};
expect(() => new Elasticsearch(config)).to.throw(Error);
});
it('should complain (throw an error) if database version is not a string', function () {
const config: SCConfigFile = {
...configFile,
...backendConfig,
internal: {
...configFile.internal,
...backendConfig.internal,
database: {
name: 'foo',
version: 123,
@@ -218,7 +229,7 @@ describe('Elasticsearch', function () {
const loggerErrorStub = sandbox.stub(Logger, 'error').resolves('foo');
sandbox.stub(Diagnostic.prototype, 'on').yields(error);
new Elasticsearch(configFile);
new Elasticsearch(backendConfig);
expect(loggerErrorStub.calledWith(error)).to.be.true;
});
@@ -228,13 +239,14 @@ describe('Elasticsearch', function () {
const loggerLogStub = sandbox.stub(Logger, 'log');
sandbox.stub(Diagnostic.prototype, 'on').yields(null, fakeResponse);
new Elasticsearch(configFile);
new Elasticsearch(backendConfig);
expect(loggerLogStub.calledWith(fakeResponse)).to.be.false;
// @ts-expect-error wrong type defs for some reason
const restore = mockedEnv({
ES_DEBUG: 'true',
});
new Elasticsearch(configFile);
new Elasticsearch(backendConfig);
expect(loggerLogStub.calledWith(fakeResponse)).to.be.true;
// restore env variables
@@ -249,9 +261,9 @@ describe('Elasticsearch', function () {
it('should complain (throw an error) if monitoring is set but mail queue is undefined', async function () {
const config: SCConfigFile = {
...configFile,
...backendConfig,
internal: {
...configFile.internal,
...backendConfig.internal,
monitoring: {
actions: [],
watchers: [],
@@ -266,9 +278,9 @@ describe('Elasticsearch', function () {
it('should setup the monitoring if there is monitoring is set and mail queue is defined', function () {
const config: SCConfigFile = {
...configFile,
...backendConfig,
internal: {
...configFile.internal,
...backendConfig.internal,
monitoring: {
actions: [],
watchers: [],
@@ -420,7 +432,7 @@ describe('Elasticsearch', function () {
const sandbox = sinon.createSandbox();
before(function () {
es = new Elasticsearch(configFile);
es = new Elasticsearch(backendConfig);
});
afterEach(function () {
@@ -451,7 +463,7 @@ describe('Elasticsearch', function () {
const sandbox = sinon.createSandbox();
before(function () {
es = new Elasticsearch(configFile);
es = new Elasticsearch(backendConfig);
});
beforeEach(function () {
@@ -510,7 +522,7 @@ describe('Elasticsearch', function () {
const sandbox = sinon.createSandbox();
before(function () {
es = new Elasticsearch(configFile);
es = new Elasticsearch(backendConfig);
});
afterEach(function () {
sandbox.restore();

View File

@@ -146,7 +146,7 @@ describe('Query', function () {
tieBreaker: 0,
};
const config: SCConfigFile = {
...configFile,
...backendConfig,
};
beforeEach(function () {
esConfig = {