OperationErrors now bubble up to the top of the API.

Added test functionality for node api
refactor TestRegister into class
This commit is contained in:
d98762625 2018-05-03 10:20:13 +01:00
parent e50758f0a6
commit 5fb50a1759
6 changed files with 194 additions and 30 deletions

1
.gitignore vendored
View File

@ -10,3 +10,4 @@ src/core/config/modules/*
src/core/config/OperationConfig.json
src/core/operations/index.mjs
**/*.DS_Store

View File

@ -7,7 +7,6 @@
*/
import Dish from "../core/Dish";
import log from "loglevel";
/**
* Extract default arg value from operation argument
@ -33,13 +32,21 @@ export function wrap(Operation) {
/**
* Wrapped operation run function
*/
return async (input, args=null) => {
return async (input, args=null, callback) => {
if (callback && typeof callback !== "function") {
throw TypeError("Expected callback to be a function");
}
if (!callback && typeof args === "function") {
callback = args;
args = null;
}
const operation = new Operation();
const dish = new Dish();
// Stolen from Recipe. Only works there as raw input is one
// of these types. consider a mapping for all use cases like below.
const type = input instanceof ArrayBuffer ? Dish.ARRAY_BUFFER : Dish.STRING;
const type = Dish.typeEnum(input.constructor.name);
dish.set(input, type);
if (!args) {
@ -51,10 +58,22 @@ export function wrap(Operation) {
}
}
const transformedInput = await dish.get(operation.inputType);
return operation.run(transformedInput, args);
// Allow callback or promsise / async-await
if (callback) {
try {
const out = operation.run(transformedInput, args);
callback(null, out);
} catch (e) {
callback(e);
}
} else {
return operation.run(transformedInput, args);
}
};
}
/**
* First draft
* @param input

View File

@ -5,37 +5,49 @@
* ensure that they will get run by the frontend.
*
* @author tlwr [toby@toby.codes]
* @copyright Crown Copyright 2017
* @author d98762625 [d98762625@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import Chef from "../src/core/Chef";
(function() {
/**
* Object to store and run the list of tests.
*
* @class
* @constructor
*/
function TestRegister() {
this.tests = [];
}
/**
* Object to store and run the list of tests.
*
* @class
* @constructor
*/
class TestRegister {
/**
* initialise with no tests
*/
constructor() {
this.tests = [];
this.apiTests = [];
}
/**
* Add a list of tests to the register.
*
* @param {Object[]} tests
*/
TestRegister.prototype.addTests = function(tests) {
addTests(tests) {
this.tests = this.tests.concat(tests);
};
}
/**
* Add a list of api tests to the register
* @param {Object[]} tests
*/
addApiTests(tests) {
this.apiTests = this.apiTests.concat(tests);
}
/**
* Runs all the tests in the register.
*/
TestRegister.prototype.runTests = function() {
runTests () {
return Promise.all(
this.tests.map(function(test, i) {
const chef = new Chef();
@ -81,12 +93,29 @@ import Chef from "../src/core/Chef";
});
})
);
};
}
/**
* Run all api related tests and wrap results in report format
*/
runApiTests() {
return Promise.all(this.apiTests.map(async function(test, i) {
const result = {
test: test,
status: null,
output: null,
};
try {
await test.run();
result.status = "passing";
} catch (e) {
result.status = "erroring";
result.output = e.message;
}
return result;
}));
}
}
// Singleton TestRegister, keeping things simple and obvious.
global.TestRegister = global.TestRegister || new TestRegister();
})();
export default global.TestRegister;
// Export an instance to make a singleton
export default new TestRegister();

View File

@ -55,6 +55,9 @@ import "./tests/operations/SymmetricDifference";
import "./tests/operations/CartesianProduct";
import "./tests/operations/PowerSet";
import "./tests/nodeApi/nodeApi";
let allTestsPassing = true;
const testStatusCounts = {
total: 0,
@ -112,9 +115,12 @@ setTimeout(function() {
process.exit(1);
}, 10 * 1000);
TestRegister.runTests()
.then(function(results) {
Promise.all([
TestRegister.runTests(),
TestRegister.runApiTests()
])
.then(function(resultsPair) {
const results = resultsPair[0].concat(resultsPair[1]);
results.forEach(handleTestResult);
console.log("\n");
@ -132,3 +138,4 @@ TestRegister.runTests()
process.exit(allTestsPassing ? 0 : 1);
});

View File

@ -0,0 +1,45 @@
/**
* assertionHandler.mjs
*
* Pair native node assertions with a description for
* the benefit of the TestRegister.
*
* @author d98762625 [d98762625@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import assert from "assert";
/**
* it - wrapper for assertions to provide a helpful description
* to the TestRegister
* @param {String} description - The description of the test
* @param {Function} assertion - The test
*
* @example
* // One assertion
* it("should run one assertion", () => assert.equal(1,1))
*
* @example
* // multiple assertions
* it("should handle multiple assertions", () => {
* assert.equal(1,1)
* assert.notEqual(3,4)
* })
*
* @example
* // async assertions
* it("should handle async", async () => {
* let r = await asyncFunc()
* assert(r)
* })
*/
export function it(name, run) {
return {
name,
run
};
}
export default it;

View File

@ -0,0 +1,63 @@
/* eslint no-console: 0 */
/**
* nodeApi.js
*
* Test node api utilities
*
* @author d98762625 [d98762625@gmail.com]
* @copyright Crown Copyright 2018
* @license Apache-2.0
*/
import assert from "assert";
import it from "../assertionHandler";
import chef from "../../../src/node/index";
import TestRegister from "../../TestRegister";
TestRegister.addApiTests([
it("should have some operations", () => {
assert(chef);
assert(chef.toBase32);
assert(chef.setUnion);
assert(!chef.randomFunction);
}),
it("should have an async/await api", async () => {
try {
const result = await chef.toBase32("input");
assert.notEqual("something", result);
} catch (e) {
// shouldnt reach here
assert(false);
}
try {
const fail = chef.setUnion("1");
// shouldnt get here
assert(false);
} catch (e) {
assert(true);
}
}),
it("should have a callback API", async () => {
await chef.toBase32("something", (err, result) => {
if (err) {
assert(false);
} else {
assert.equal("ONXW2ZLUNBUW4ZY=", result);
}
});
}),
it("should handle errors in callback API", async () => {
await chef.setUnion("1", (err, result) => {
if (err) {
assert(true);
return;
}
assert(false);
});
})
]);