浏览代码

OperationErrors now bubble up to the top of the API.
Added test functionality for node api
refactor TestRegister into class

d98762625 7 年之前
父节点
当前提交
5fb50a1759
共有 6 个文件被更改,包括 191 次插入27 次删除
  1. 1 0
      .gitignore
  2. 25 6
      src/node/apiUtils.mjs
  3. 47 18
      test/TestRegister.mjs
  4. 10 3
      test/index.mjs
  5. 45 0
      test/tests/assertionHandler.mjs
  6. 63 0
      test/tests/nodeApi/nodeApi.mjs

+ 1 - 0
.gitignore

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

+ 25 - 6
src/node/apiUtils.mjs

@@ -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 

+ 47 - 18
test/TestRegister.mjs

@@ -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
+ */
+class TestRegister {
+
     /**
-     * Object to store and run the list of tests.
-     *
-     * @class
-     * @constructor
+     * initialise with no tests
      */
-    function TestRegister() {
+    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";
                 });
             })
         );
-    };
-
-
-    // Singleton TestRegister, keeping things simple and obvious.
-    global.TestRegister = global.TestRegister || new TestRegister();
-})();
+    }
 
-export default global.TestRegister;
+    /**
+     * 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;
+        }));
+    }
+}
 
+// Export an instance to make a singleton
+export default new TestRegister();

+ 10 - 3
test/index.mjs

@@ -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);
     });
+

+ 45 - 0
test/tests/assertionHandler.mjs

@@ -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;

+ 63 - 0
test/tests/nodeApi/nodeApi.mjs

@@ -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);
+        });
+    })
+]);