feat: tests

This commit is contained in:
2023-04-21 12:08:35 +02:00
parent 8cb9285462
commit d8c79256c9
140 changed files with 2100 additions and 2693 deletions

View File

@@ -24,29 +24,27 @@ import {expect} from 'chai';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import chaiSpies from 'chai-spies';
import {suite, test} from '@testdeck/mocha';
import moment from 'moment';
import {Bulk} from '../src/bulk.js';
import {Client} from '../src/client.js';
import {BulkWithMultipleTypesError} from '../src/errors.js';
import {HttpClient} from '../src/http-client.js';
import {HttpClient, Bulk, Client, BulkWithMultipleTypesError} from '../src/index.js';
chai.should();
chai.use(chaiSpies);
chai.use(chaiAsPromised);
const sandbox = chai.spy.sandbox();
describe('Bulk', function () {
const sandbox = chai.spy.sandbox();
const bulkAddRoute = new SCBulkAddRoute();
const bulkDoneRoute = new SCBulkDoneRoute();
const bulkAddRoute = new SCBulkAddRoute();
const bulkDoneRoute = new SCBulkDoneRoute();
const httpClient = new HttpClient();
const client = new Client(httpClient, 'http://localhost');
const httpClient = new HttpClient();
const client = new Client(httpClient, 'http://localhost');
@suite()
export class BulkSpec {
@test
async add() {
afterEach(function () {
sandbox.restore();
})
it('should add', async function () {
sandbox.on(client, 'invokeRoute', () => {
return {};
});
@@ -82,10 +80,9 @@ export class BulkSpec {
},
dish,
);
}
});
@test
async addFails() {
it('should fail add', async function () {
const bulk = new Bulk(SCThingType.Dish, client, {
expiration: moment().add(3600, 'seconds').format(),
source: 'foo',
@@ -108,15 +105,10 @@ export class BulkSpec {
uid: 'foo',
};
return bulk.add(message).should.be.rejectedWith(BulkWithMultipleTypesError);
}
await bulk.add(message).should.be.rejectedWith(BulkWithMultipleTypesError);
});
async after() {
sandbox.restore();
}
@test
async construct() {
it('should construct', function () {
expect(() => {
return new Bulk(SCThingType.Dish, client, {
expiration: moment().add(3600, 'seconds').format(),
@@ -126,10 +118,9 @@ export class BulkSpec {
uid: 'bar',
});
}).not.to.throw();
}
});
@test
async done() {
it('should done', async function () {
sandbox.on(client, 'invokeRoute', () => {
return {};
});
@@ -149,5 +140,5 @@ export class BulkSpec {
expect(client.invokeRoute).to.have.been.first.called.with(bulkDoneRoute, {
UID: 'bar',
});
}
}
});
});

View File

@@ -28,11 +28,7 @@ import {expect} from 'chai';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import chaiSpies from 'chai-spies';
import {suite, test} from '@testdeck/mocha';
import {Client} from '../src/client.js';
import {ApiError, OutOfRangeError} from '../src/errors.js';
import {HttpClient} from '../src/http-client.js';
import {HttpClientResponse} from '../src/http-client-interface.js';
import {ApiError, OutOfRangeError, Client, HttpClient, HttpClientResponse} from '../src/index.js';
chai.should();
chai.use(chaiSpies);
@@ -84,21 +80,18 @@ async function invokeIndexRouteFails(): Promise<RecursivePartial<HttpClientRespo
};
}
@suite()
export class ClientSpec {
async after() {
describe('Client', function () {
afterEach(function () {
sandbox.restore();
}
});
@test
async construct() {
it('should construct', function () {
expect(() => {
return new Client(httpClient, 'http://localhost');
}).not.to.throw();
}
});
@test
async constructWithHeaders() {
it('should construct with headers', async function () {
sandbox.on(httpClient, 'request', invokeIndexRoute);
expect(httpClient.request).not.to.have.been.first.called();
@@ -115,10 +108,9 @@ export class ClientSpec {
method: indexRoute.method,
url: new URL('http://localhost' + indexRoute.getUrlPath()),
});
}
});
@test
async getThing() {
it('should get thing', async function () {
const message: SCMessage = {
audiences: ['employees'],
categories: ['news'],
@@ -174,10 +166,9 @@ export class ClientSpec {
method: searchRoute.method,
url: new URL('http://localhost' + searchRoute.getUrlPath()),
});
}
});
@test
async getThingFailsByEmptyResponse() {
it('should fail getThing by empty response', async function () {
sandbox.on(httpClient, 'request', async (): Promise<HttpClientResponse<SCSearchResponse>> => {
return {
body: {
@@ -202,10 +193,9 @@ export class ClientSpec {
const client = new Client(httpClient, 'http://localhost');
return client.getThing('bar').should.be.rejected;
}
});
@test
async getThingFailsByUid() {
it('should fail getThing by uid', async function () {
const message: SCMessage = {
audiences: ['employees'],
categories: ['news'],
@@ -244,10 +234,9 @@ export class ClientSpec {
const client = new Client(httpClient, 'http://localhost');
return client.getThing('bar').should.be.rejected;
}
});
@test
async handshake() {
it('should handshake', async function () {
sandbox.on(httpClient, 'request', invokeIndexRoute);
expect(httpClient.request).not.to.have.been.first.called();
@@ -263,10 +252,9 @@ export class ClientSpec {
method: indexRoute.method,
url: new URL('http://localhost' + indexRoute.getUrlPath()),
});
}
});
@test
async handshakeFails() {
it('should fail handshake', async function () {
sandbox.on(httpClient, 'request', invokeIndexRoute);
expect(httpClient.request).not.to.have.been.first.called();
@@ -274,10 +262,9 @@ export class ClientSpec {
const client = new Client(httpClient, 'http://localhost');
return client.handshake('bar.bar.dummy').should.be.rejectedWith(ApiError);
}
});
@test
async invokePlugin() {
it('should invoke plugin', async function () {
sandbox.on(
httpClient,
'request',
@@ -303,13 +290,12 @@ export class ClientSpec {
await client.invokePlugin('unsupportedPlugin').should.be.rejectedWith(ApiError, /.*supportedPlugin.*/gim);
// again with cached feature definitions
return client
await client
.invokePlugin('supportedPlugin')
.should.not.be.rejectedWith(ApiError, /.*supportedPlugin.*/gim);
}
});
@test
async invokePluginUnavailable() {
it('should invoke unavailable plugin', async function () {
sandbox.on(
httpClient,
'request',
@@ -350,10 +336,9 @@ export class ClientSpec {
);
// again with cached feature definitions
return client.invokePlugin('supportedPlugin').should.be.rejectedWith(ApiError, /.*supportedPlugin.*/gim);
}
});
@test
async invokeRoute() {
it('should invoke route', async function () {
sandbox.on(httpClient, 'request', invokeIndexRoute);
expect(httpClient.request).not.to.have.been.first.called();
@@ -369,10 +354,9 @@ export class ClientSpec {
method: indexRoute.method,
url: new URL('http://localhost' + indexRoute.getUrlPath()),
});
}
});
@test
async invokeRouteFails() {
it('should fail to invoke route', async function () {
sandbox.on(httpClient, 'request', invokeIndexRouteFails);
expect(httpClient.request).not.to.have.been.first.called();
@@ -380,10 +364,9 @@ export class ClientSpec {
const client = new Client(httpClient, 'http://localhost');
return client.invokeRoute(indexRoute).should.be.rejectedWith(ApiError);
}
});
@test
async multiSearch() {
it('should multi search', async function () {
sandbox.on(httpClient, 'request', async (): Promise<HttpClientResponse<SCMultiSearchResponse>> => {
return {
body: {
@@ -430,10 +413,9 @@ export class ClientSpec {
method: multiSearchRoute.method,
url: new URL('http://localhost' + multiSearchRoute.getUrlPath()),
});
}
});
@test
async multiSearchWithPreflight() {
it('should multi search with preflight', async function () {
sandbox.on(httpClient, 'request', async (): Promise<HttpClientResponse<SCMultiSearchResponse>> => {
return {
body: {
@@ -488,10 +470,9 @@ export class ClientSpec {
method: multiSearchRoute.method,
url: new URL('http://localhost' + multiSearchRoute.getUrlPath()),
});
}
});
@test
nextWindow() {
it('should next window', async function () {
let searchRequest: SCSearchRequest = {size: 30};
const searchResponse: SCSearchResponse = {
data: [],
@@ -515,10 +496,9 @@ export class ClientSpec {
expect(() => {
Client.nextWindow(searchRequest, searchResponse);
}).to.throw(OutOfRangeError);
}
});
@test
async search() {
it('should search', async function () {
sandbox.on(httpClient, 'request', async (): Promise<HttpClientResponse<SCSearchResponse>> => {
return {
body: {
@@ -551,10 +531,9 @@ export class ClientSpec {
method: searchRoute.method,
url: new URL('http://localhost' + searchRoute.getUrlPath()),
});
}
});
@test
async searchNext() {
it('should search next', async function () {
const searchResponse: SCSearchResponse = {
data: [],
facets: [],
@@ -589,10 +568,9 @@ export class ClientSpec {
method: searchRoute.method,
url: new URL('http://localhost' + searchRoute.getUrlPath()),
});
}
});
@test
async searchWithPreflight() {
it('should search with preflight', async function () {
sandbox.on(httpClient, 'request', async (): Promise<HttpClientResponse<SCSearchResponse>> => {
return {
body: {
@@ -633,5 +611,5 @@ export class ClientSpec {
method: searchRoute.method,
url: new URL('http://localhost' + searchRoute.getUrlPath()),
});
}
}
});
});

View File

@@ -1,3 +1,4 @@
/* eslint-disable unicorn/no-null */
/*
* Copyright (C) 2018 StApps
* This program is free software: you can redistribute it and/or modify it
@@ -12,7 +13,6 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {asyncPool} from '@krlwlfrt/async-pool/lib/async-pool';
import {
isThing,
SCBulkAddResponse,
@@ -33,16 +33,12 @@ import {expect} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import chaiSpies from 'chai-spies';
import clone = require('rfdc');
import {readdir, readFile} from 'fs';
import {suite, test} from '@testdeck/mocha';
import moment from 'moment';
import {join, resolve} from 'path';
import traverse from 'traverse';
import {promisify} from 'util';
import {ConnectorClient} from '../src/connector-client.js';
import {EmptyBulkError, NamespaceNotDefinedError} from '../src/errors.js';
import {HttpClient} from '../src/http-client.js';
import {HttpClientRequest, HttpClientResponse} from '../src/http-client-interface.js';
import {ConnectorClient, EmptyBulkError, NamespaceNotDefinedError, HttpClient, HttpClientRequest, HttpClientResponse} from '../src/index.js';
import path from "path";
import {fileURLToPath} from "url";
import {readdir, readFile} from "fs/promises";
chai.should();
chai.use(chaiSpies);
@@ -55,9 +51,6 @@ const bulkDoneRoute = new SCBulkDoneRoute();
const bulkRoute = new SCBulkRoute();
const thingUpdateRoute = new SCThingUpdateRoute();
const readdirPromisified = promisify(readdir);
const readFilePromisified = promisify(readFile);
const httpClient = new HttpClient();
/**
@@ -76,14 +69,12 @@ function doesContainThings<T extends SCThingWithoutReferences>(thing: T): boolea
}, false);
}
@suite()
export class ConnectorClientSpec {
async after() {
describe('ConnectorClient', function () {
afterEach(function () {
sandbox.restore();
}
});
@test
async bulk() {
it('should bulk', async function () {
sandbox.on(httpClient, 'request', async (): Promise<HttpClientResponse<SCBulkResponse>> => {
return {
body: {
@@ -115,10 +106,9 @@ export class ConnectorClientSpec {
method: bulkRoute.method,
url: new URL('http://localhost' + bulkRoute.getUrlPath()),
});
}
});
@test
async bulkWithoutTimeout() {
it('should bulk without timeout', async function () {
sandbox.on(httpClient, 'request', async (): Promise<HttpClientResponse<SCBulkResponse>> => {
return {
body: {
@@ -150,10 +140,9 @@ export class ConnectorClientSpec {
method: bulkRoute.method,
url: new URL('http://localhost' + bulkRoute.getUrlPath()),
});
}
});
@test
async index() {
it('should index', async function () {
const messages: SCMessage[] = [
{
audiences: ['employees'],
@@ -240,16 +229,14 @@ export class ConnectorClientSpec {
method: bulkRoute.method,
url: new URL('http://localhost' + bulkRoute.getUrlPath()),
});
}
});
@test
async indexFails() {
it('should fail to index', async function () {
const connectorClient = new ConnectorClient(httpClient, 'http://localhost');
return connectorClient.index([]).should.be.rejectedWith(EmptyBulkError);
}
await connectorClient.index([]).should.be.rejectedWith(EmptyBulkError);
});
@test
async indexWithoutSource() {
it('should index without source', async function () {
const messages: SCMessage[] = [
{
audiences: ['employees'],
@@ -336,28 +323,25 @@ export class ConnectorClientSpec {
method: bulkRoute.method,
url: new URL('http://localhost' + bulkRoute.getUrlPath()),
});
}
});
@test
makeUuid() {
it('should make uuid', async function () {
const uuid = ConnectorClient.makeUUID('foo', 'b-tu');
expect(uuid).to.be.equal('abad271e-d9e9-5802-b7bc-96d8a647b451');
expect(ConnectorClient.makeUUID('bar', 'b-tu')).not.to.be.equal(uuid);
expect(ConnectorClient.makeUUID('foo', 'f-u')).not.to.be.equal(uuid);
}
});
@test
makeUuidFails() {
it('should fail making a uuid', async function (){
expect(() => {
ConnectorClient.makeUUID('foo', 'b-u');
}).to.throw(NamespaceNotDefinedError);
}
});
@test
async removeReferences() {
const pathToTestFiles = resolve(
__dirname,
it('should remove references', async function () {
const pathToTestFiles = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'..',
'node_modules',
'@openstapps',
@@ -367,14 +351,14 @@ export class ConnectorClientSpec {
'indexable',
);
const testFiles = await readdirPromisified(pathToTestFiles);
const testFiles = await readdir(pathToTestFiles);
const testInstances = await asyncPool(5, testFiles, async testFile => {
const buffer = await readFilePromisified(join(pathToTestFiles, testFile));
const testInstances = await Promise.all(testFiles.map(async testFile => {
const buffer = await readFile(path.join(pathToTestFiles, testFile));
const content = JSON.parse(buffer.toString());
return content.instance;
});
}));
for (const testInstance of testInstances) {
const checkInstance = clone()(testInstance);
@@ -384,6 +368,7 @@ export class ConnectorClientSpec {
false,
JSON.stringify([testInstance, testInstanceWithoutReferences], null, 2),
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
expect((testInstanceWithoutReferences as any).origin).to.be.equal(
undefined,
JSON.stringify([testInstance, testInstanceWithoutReferences], null, 2),
@@ -393,10 +378,9 @@ export class ConnectorClientSpec {
'Removing the references of a thing could have side effects because no deep copy is used',
);
}
}
});
@test
async removeUndefinedProperties() {
it('should remove undefined properties', async function () {
const objectWithUndefinedProperties = {
value: 'foo',
novalue: undefined,
@@ -417,10 +401,9 @@ export class ConnectorClientSpec {
objectWithoutUndefinedProperties,
JSON.stringify([objectWithUndefinedProperties, objectWithoutUndefinedProperties], null, 2),
);
}
});
@test
async update() {
it('should update', async function () {
const message: SCMessage = {
audiences: ['employees'],
categories: ['news'],
@@ -462,5 +445,5 @@ export class ConnectorClientSpec {
}),
),
});
}
}
});
});

View File

@@ -27,12 +27,9 @@ import {
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
import chaiSpies from 'chai-spies';
import {suite, test} from '@testdeck/mocha';
import moment from 'moment';
import {copy} from '../src/copy.js';
import {ApiError} from '../src/errors.js';
import {HttpClient, RequestOptions, Response} from '../src/http-client.js';
import {RecursivePartial} from './client.spec';
import {copy, ApiError, HttpClient, RequestOptions, Response} from '../src/index.js';
import {RecursivePartial} from './client.spec.js';
chai.should();
chai.use(chaiSpies);
@@ -47,14 +44,12 @@ const searchRoute = new SCSearchRoute();
const httpClient = new HttpClient();
@suite()
export class CopySpec {
async after() {
describe('Copy', function () {
afterEach(function () {
sandbox.restore();
}
});
@test
async copy() {
it('should copy', async function () {
type responses = Response<SCBulkAddResponse | SCBulkDoneResponse | SCBulkResponse | SCSearchResponse>;
sandbox.on(
@@ -133,10 +128,9 @@ export class CopySpec {
type: SCThingType.Dish,
version: 'foo.bar.foobar',
});
}
});
@test
async copyShouldFail() {
it('should fail to copy', async function () {
type responses = Response<SCBulkAddResponse | SCBulkDoneResponse | SCBulkResponse | SCSearchResponse>;
sandbox.on(
@@ -206,7 +200,7 @@ export class CopySpec {
},
);
return copy(httpClient, {
await copy(httpClient, {
batchSize: 5,
from: 'http://foo.bar',
source: 'stapps-copy',
@@ -214,5 +208,5 @@ export class CopySpec {
type: SCThingType.Dish,
version: 'foo.bar.foobar',
}).should.be.rejectedWith(ApiError);
}
}
});
});

View File

@@ -12,9 +12,7 @@
* You should have received a copy of the GNU General Public License along with
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
// tslint:disable-next-line: max-line-length
// tslint:disable: completed-docs no-implicit-dependencies prefer-function-over-method newline-per-chained-call member-ordering
// eslint-disable-next-line unicorn/prevent-abbreviations
import {
SCBulkAddResponse,
SCBulkAddRoute,
@@ -32,12 +30,12 @@ import chaiSpies from 'chai-spies';
import clone = require('rfdc');
import {existsSync, mkdirSync, rmdirSync, unlinkSync} from 'fs';
import {createFileSync} from 'fs-extra';
import {suite, test} from '@testdeck/mocha';
import {join} from 'path';
import {e2eRun, getItemsFromSamples} from '../src/e2e.js';
import {ApiError} from '../src/errors.js';
import {HttpClient, RequestOptions, Response} from '../src/http-client.js';
import {RecursivePartial} from './client.spec';
// eslint-disable-next-line unicorn/prevent-abbreviations
import {e2eRun, getItemsFromSamples, ApiError, HttpClient, RequestOptions, Response} from '../src/index.js';
import {RecursivePartial} from './client.spec.js';
import {expect} from "chai";
import path from "path";
import {fileURLToPath} from "url";
chai.should();
chai.use(chaiSpies);
@@ -55,26 +53,21 @@ const httpClient = new HttpClient();
const storedThings: Map<string, SCThings> = new Map();
@suite
export class E2EConnectorSpec {
async after() {
describe('e2e Connector', function () {
afterEach(function () {
sandbox.restore();
}
});
@test
async getCoreTestSamples() {
it('should get core test samples', async function () {
const items = await getItemsFromSamples('./node_modules/@openstapps/core/test/resources');
// tslint:disable-next-line: no-unused-expression
chai.expect(items).to.not.be.empty;
}
expect(items).to.not.be.empty;
});
@test
async getCoreTestSamplesShouldFail() {
await chai.expect(getItemsFromSamples('./nonexistantdirectory')).to.be.rejectedWith(Error);
}
it('should fail to get core test samples', async function () {
await chai.expect(getItemsFromSamples('./non-existent-directory')).to.be.rejectedWith(Error);
});
@test
async e2eRunSimulation() {
it('should run e2e simulation', async function () {
type responses = Response<SCBulkAddResponse | SCBulkDoneResponse | SCBulkResponse | SCSearchResponse>;
let failOnCompare = false;
@@ -167,10 +160,9 @@ export class E2EConnectorSpec {
to: 'http://localhost',
samplesLocation: './node_modules/@openstapps/core/test/resources',
}).should.be.rejectedWith('Unexpected difference');
}
});
@test
async indexShouldFail() {
it('should fail to index', async function () {
type responses = Response<SCBulkAddResponse | SCBulkDoneResponse | SCBulkResponse>;
sandbox.on(httpClient, 'request', async (): Promise<RecursivePartial<responses>> => {
@@ -185,33 +177,31 @@ export class E2EConnectorSpec {
to: 'http://localhost',
samplesLocation: './node_modules/@openstapps/core/test/resources',
}).should.be.rejectedWith(ApiError);
}
});
@test
async indexShouldFailDirectoryWithoutData() {
const emptyDirPath = join(__dirname, 'emptyDir');
if (!existsSync(emptyDirPath)) {
mkdirSync(emptyDirPath);
it('should fail to index directory without data', async function () {
const emptyDirectoryPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'emptyDir');
if (!existsSync(emptyDirectoryPath)) {
mkdirSync(emptyDirectoryPath);
}
await e2eRun(httpClient, {to: 'http://localhost', samplesLocation: emptyDirPath}).should.be.rejectedWith(
await e2eRun(httpClient, {to: 'http://localhost', samplesLocation: emptyDirectoryPath}).should.be.rejectedWith(
'Could not index samples. None were retrieved from the file system.',
);
rmdirSync(emptyDirPath);
}
rmdirSync(emptyDirectoryPath);
});
@test
async indexShouldFailDirectoryWithoutJsonData() {
const somewhatFilledDirPath = join(__dirname, 'somewhatFilledDir');
if (!existsSync(somewhatFilledDirPath)) {
mkdirSync(somewhatFilledDirPath);
it('should fail to index directory without json data', async function () {
const somewhatFilledDirectoryPath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'somewhatFilledDir');
if (!existsSync(somewhatFilledDirectoryPath)) {
mkdirSync(somewhatFilledDirectoryPath);
}
const nonJsonFile = join(somewhatFilledDirPath, 'nonjson.txt');
const nonJsonFile = path.join(somewhatFilledDirectoryPath, 'nonjson.txt');
createFileSync(nonJsonFile);
await e2eRun(httpClient, {
to: 'http://localhost',
samplesLocation: somewhatFilledDirPath,
samplesLocation: somewhatFilledDirectoryPath,
}).should.be.rejectedWith('Could not index samples. None were retrieved from the file system.');
unlinkSync(nonJsonFile);
rmdirSync(somewhatFilledDirPath);
}
}
rmdirSync(somewhatFilledDirectoryPath);
});
});

View File

@@ -16,8 +16,7 @@ import chai from 'chai';
import {expect} from 'chai';
import chaiAsPromised from 'chai-as-promised';
import chaiSpies from 'chai-spies';
import {suite, test} from '@testdeck/mocha';
import {ApiError} from '../src/errors.js';
import {ApiError} from '../src/index.js';
chai.should();
chai.use(chaiSpies);
@@ -25,36 +24,32 @@ chai.use(chaiAsPromised);
const sandbox = chai.spy.sandbox();
@suite()
export class ErrorsSpec {
async after() {
describe('Errors', function () {
afterEach(function () {
sandbox.restore();
}
});
@test
async shouldAddAdditionalData() {
it('should add additional data', function () {
const error = new ApiError({
additionalData: 'Lorem ipsum',
});
expect(error.toString()).to.contain('Lorem ipsum');
}
});
@test
async shouldAddRemoteStackTrace() {
it('should add remote stack-trace', async function () {
const error = new ApiError({
stack: 'Lorem ipsum',
});
expect(error.toString()).to.contain('Lorem ipsum');
}
});
@test
async shouldSetName() {
it('should set name', async function () {
const error = new ApiError({
name: 'Foo',
});
expect(error.name).to.be.equal('Foo');
}
}
});
});

View File

@@ -13,27 +13,23 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {expect} from 'chai';
import {suite, test} from '@testdeck/mocha';
import nock from 'nock';
import {HttpClient} from '../src/http-client.js';
import {HttpClient} from '../src/index.js';
// TODO: use after each to clean up the nock (then there is no need for numerated resource links)
@suite()
export class HttpClientSpec {
@test
async construct() {
describe('HttpClient', function () {
afterEach(function () {
nock.cleanAll();
})
it('should construct', function () {
expect(() => {
return new HttpClient();
}).not.to.throw();
}
});
async after() {
nock.cleanAll();
}
@test
async request() {
it('should request', async function () {
const client = new HttpClient();
nock('http://www.example.com').get('/resource').reply(200, 'foo');
@@ -43,10 +39,9 @@ export class HttpClientSpec {
});
expect(response.body).to.be.equal('foo');
}
});
@test
async requestWithBody() {
it('should request with body', async function () {
const client = new HttpClient();
nock('http://www.example.com').get('/resource').reply(200, 'foo');
@@ -56,12 +51,11 @@ export class HttpClientSpec {
});
expect(response.body).to.be.equal('foo');
}
});
@test
async requestWithError() {
it('should request with error', async function () {
const client = new HttpClient();
let caughtErr;
let caughtError;
nock('http://www.example.com').get('/resource').replyWithError('foo');
@@ -72,15 +66,14 @@ export class HttpClientSpec {
},
url: new URL('http://www.example.com/resource'),
});
} catch (err) {
caughtErr = err;
} catch (error) {
caughtError = error;
}
expect(caughtErr).not.to.be.undefined;
}
expect(caughtError).not.to.be.undefined;
});
@test
async requestWithHeaders() {
it('should request with headers', async function () {
const client = new HttpClient();
nock('http://www.example.com').get('/resource').reply(200, 'foo');
@@ -93,10 +86,9 @@ export class HttpClientSpec {
});
expect(response.body).to.be.equal('foo');
}
});
@test
async requestWithMethodGet() {
it('should request with method GET', async function () {
const client = new HttpClient();
nock('http://www.example.com').get('/resource').reply(200, 'foo');
@@ -107,10 +99,9 @@ export class HttpClientSpec {
});
expect(response.body).to.be.equal('foo');
}
});
@test
async requestWithMethodPost() {
it('should request with method POST', async function () {
const client = new HttpClient();
nock('http://www.example.com').post('/resource').reply(200, 'foo');
@@ -121,5 +112,5 @@ export class HttpClientSpec {
});
expect(response.body).to.be.equal('foo');
}
}
});
});

View File

@@ -16,10 +16,7 @@ import {SCPluginRegisterRequest, SCPluginRegisterResponse, SCPluginRegisterRoute
import chai from 'chai';
import {expect} from 'chai';
import chaiSpies from 'chai-spies';
import {suite, test, timeout} from '@testdeck/mocha';
import {HttpClient} from '../src/http-client.js';
import {HttpClientResponse} from '../src/http-client-interface.js';
import {PluginClient} from '../src/plugin-client.js';
import {HttpClient, HttpClientResponse, PluginClient} from '../src/index.js';
import {TestPlugin} from './plugin-resources/test-plugin.js';
chai.use(chaiSpies);
@@ -27,21 +24,16 @@ chai.use(chaiSpies);
const sandbox = chai.spy.sandbox();
const httpClient = new HttpClient();
const pluginRegisterRoute = new SCPluginRegisterRoute();
const pluginClient = new PluginClient(httpClient, 'http://localhost');
@suite(timeout(10000))
export class PluginClientSpec {
static plugin: TestPlugin;
describe('PluginClient', function () {
this.timeout(10_000);
static async after() {
await this.plugin.close();
}
let plugin: TestPlugin;
static async before() {
this.plugin = new TestPlugin(
beforeEach(async function () {
plugin = new TestPlugin(
4000,
'',
'',
@@ -51,19 +43,19 @@ export class PluginClientSpec {
getSchema: () => {
/***/
},
} as any,
} as never,
'',
'',
'',
);
}
});
async after() {
afterEach(async function () {
await plugin.close();
sandbox.restore();
}
});
@test
async registerPlugin() {
it('should register the plugin', async function () {
sandbox.on(httpClient, 'request', async (): Promise<HttpClientResponse<SCPluginRegisterResponse>> => {
return {
body: {
@@ -76,16 +68,16 @@ export class PluginClientSpec {
expect(httpClient.request).not.to.have.been.called();
await pluginClient.registerPlugin(PluginClientSpec.plugin);
await pluginClient.registerPlugin(plugin);
const request: SCPluginRegisterRequest = {
action: 'add',
plugin: {
address: PluginClientSpec.plugin.fullUrl,
name: PluginClientSpec.plugin.name,
requestSchema: PluginClientSpec.plugin.requestSchema,
responseSchema: PluginClientSpec.plugin.responseSchema,
route: PluginClientSpec.plugin.route,
address: plugin.fullUrl,
name: plugin.name,
requestSchema: plugin.requestSchema,
responseSchema: plugin.responseSchema,
route: plugin.route,
},
};
@@ -97,10 +89,9 @@ export class PluginClientSpec {
method: pluginRegisterRoute.method,
url: new URL(`http://localhost${pluginRegisterRoute.getUrlPath()}`),
});
}
});
@test
async unregisterPlugin() {
it('should unregister the plugin', async function () {
sandbox.on(httpClient, 'request', async (): Promise<HttpClientResponse<SCPluginRegisterResponse>> => {
return {
body: {
@@ -113,11 +104,11 @@ export class PluginClientSpec {
expect(httpClient.request).not.to.have.been.called();
await pluginClient.unregisterPlugin(PluginClientSpec.plugin);
await pluginClient.unregisterPlugin(plugin);
const request: SCPluginRegisterRequest = {
action: 'remove',
route: PluginClientSpec.plugin.route,
route: plugin.route,
};
expect(httpClient.request).to.have.been.first.called.with({
@@ -128,5 +119,5 @@ export class PluginClientSpec {
method: pluginRegisterRoute.method,
url: new URL(`http://localhost${pluginRegisterRoute.getUrlPath()}`),
});
}
}
});
});

View File

@@ -13,36 +13,35 @@
* this program. If not, see <https://www.gnu.org/licenses/>.
*/
import {Converter} from '@openstapps/core-tools/lib/schema';
import {Converter} from '@openstapps/core-tools';
import chai from 'chai';
import {expect} from 'chai';
import chaiSpies from 'chai-spies';
import {readFileSync} from 'fs';
import {suite, test, timeout} from '@testdeck/mocha';
import {resolve} from 'path';
import {HttpClient} from '../src/http-client.js';
import {HttpClient} from '../src/index.js';
import {TestPlugin} from './plugin-resources/test-plugin.js';
import path from "path";
import {readFile} from "fs/promises";
import {fileURLToPath} from "url";
chai.use(chaiSpies);
process.on('unhandledRejection', err => {
throw err;
process.on('unhandledRejection', error => {
throw error;
});
const sandbox = chai.spy.sandbox();
const httpClient = new HttpClient();
@suite(timeout(20000))
export class PluginSpec {
static testPlugin: TestPlugin;
const dirname = path.dirname(fileURLToPath(import.meta.url));
static async after() {
PluginSpec.testPlugin.close();
}
describe('Plugin', function () {
this.timeout(20_000);
static async before() {
PluginSpec.testPlugin = new TestPlugin(
let testPlugin: TestPlugin;
beforeEach(function () {
testPlugin = new TestPlugin(
4000,
'',
'',
@@ -52,22 +51,22 @@ export class PluginSpec {
getSchema: () => {
/***/
},
} as any,
} as never,
'',
'',
'',
);
}
})
async after() {
afterEach(async function () {
await testPlugin.close();
sandbox.restore();
}
})
@test
async construct() {
it('should construct', async function () {
const converter = new Converter(
__dirname,
resolve(__dirname, 'plugin-resources', 'test-plugin-response.ts'),
dirname,
path.resolve(dirname, 'plugin-resources', 'test-plugin-response.ts'),
);
sandbox.on(converter, 'getSchema', schemaName => {
@@ -80,20 +79,19 @@ export class PluginSpec {
'http://B',
'/C', // this doesn't matter for our tests, it's only something that affects the backend
'http://D',
// @ts-ignore fake converter is not a converter
converter,
'PluginTestRequest',
'PluginTestResponse',
JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json')).toString()).version,
JSON.parse(await readFile(path.resolve(dirname, '..', 'package.json'), 'utf8')).version,
);
expect(constructTestPlugin.port).to.be.equal(4001);
expect(constructTestPlugin.name).to.be.equal('A');
expect(constructTestPlugin.url).to.be.equal('http://B');
expect(constructTestPlugin.route).to.be.equal('/C');
// @ts-ignore backendUrl is protected
// @ts-expect-error private property
expect(constructTestPlugin.backendUrl).to.be.equal('http://D');
// schemas are already covered, together with the directory and version
// @ts-ignore active is private
// @ts-expect-error private property
expect(constructTestPlugin.active).to.be.equal(false);
expect(constructTestPlugin.requestSchema.$id).to.be.equal('PluginTestRequest');
expect(constructTestPlugin.responseSchema.$id).to.be.equal('PluginTestResponse');
@@ -103,15 +101,14 @@ export class PluginSpec {
url: new URL('http://localhost:4001'),
});
// onRouteInvoke is a protected method, but we need to access it from the outside to test it
// @ts-ignore
// @ts-expect-error protected method
expect(constructTestPlugin.onRouteInvoke).not.to.have.been.called();
await constructTestPlugin.close();
sandbox.restore(constructTestPlugin, 'onRouteInvoke');
}
});
@test
async fullUrl() {
it('should have full url', async function () {
const constructTestPlugin = new TestPlugin(
4001,
'',
@@ -122,37 +119,35 @@ export class PluginSpec {
getSchema: () => {
/***/
},
} as any,
} as never,
'',
'',
'',
);
expect(constructTestPlugin.fullUrl).to.be.equal('http://B:4001');
await constructTestPlugin.close();
}
});
@test
async start() {
PluginSpec.testPlugin.start();
it('should start', async function () {
testPlugin.start();
sandbox.on(PluginSpec.testPlugin, 'onRouteInvoke');
sandbox.on(testPlugin, 'onRouteInvoke');
await httpClient.request({
url: new URL('http://localhost:4000'),
});
// onRouteInvoke is a protected method, but we need to access it from the outside to test it
// @ts-ignore
expect(PluginSpec.testPlugin.onRouteInvoke).to.have.been.called();
}
// @ts-expect-error protected method
expect(testPlugin.onRouteInvoke).to.have.been.called();
});
@test
async stop() {
it('should stop', async function () {
// simulate a normal use case by first starting the plugin and then stopping it
PluginSpec.testPlugin.start();
PluginSpec.testPlugin.stop();
testPlugin.start();
testPlugin.stop();
sandbox.on(PluginSpec.testPlugin, 'onRouteInvoke');
sandbox.on(testPlugin, 'onRouteInvoke');
const response = await httpClient.request({
url: new URL('http://localhost:4000'),
@@ -160,7 +155,7 @@ export class PluginSpec {
await expect(response.statusCode).to.be.equal(404);
// onRouteInvoke is a protected method, but we need to access it from the outside to test it
// @ts-ignore
expect(PluginSpec.testPlugin.onRouteInvoke).not.to.have.been.called();
}
}
// @ts-expect-error protected method
expect(testPlugin.onRouteInvoke).not.to.have.been.called();
});
});