| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * 'License'); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| // This is the Node.js test driver for the standard Apache Thrift |
| // test service. The driver invokes every function defined in the |
| // Thrift Test service with a representative range of parameters. |
| // |
| // The ThriftTestDriver function requires a client object |
| // connected to a server hosting the Thrift Test service and |
| // supports an optional callback function which is called with |
| // a status message when the test is complete. |
| |
| const test = require("tape"); |
| |
| const helpers = require("./helpers"); |
| const ttypes = require(`./${helpers.genPath}/ThriftTest_types`); |
| const TException = require("thrift").Thrift.TException; |
| const Int64 = require("node-int64"); |
| const testCases = require("./test-cases"); |
| |
| exports.ThriftTestDriver = function(client, callback) { |
| test( |
| "NodeJS Style Callback Client Tests", |
| { skip: helpers.ecmaMode === "es6" }, |
| function(assert) { |
| const checkRecursively = makeRecursiveCheck(assert); |
| |
| function makeAsserter(assertionFn) { |
| return function(c) { |
| const fnName = c[0]; |
| const expected = c[1]; |
| client[fnName](expected, function(err, actual) { |
| assert.error(err, fnName + ": no callback error"); |
| assertionFn(actual, expected, fnName); |
| }); |
| }; |
| } |
| |
| testCases.simple.forEach( |
| makeAsserter(function(a, e, m) { |
| if (a instanceof Int64) { |
| const e64 = e instanceof Int64 ? e : new Int64(e); |
| assert.deepEqual(a.buffer, e64.buffer, m); |
| } else { |
| assert.equal(a, e, m); |
| } |
| }) |
| ); |
| testCases.deep.forEach(makeAsserter(assert.deepEqual)); |
| testCases.deepUnordered.forEach( |
| makeAsserter(makeUnorderedDeepEqual(assert)) |
| ); |
| |
| const arr = []; |
| for (let i = 0; i < 256; ++i) { |
| arr[i] = 255 - i; |
| } |
| let buf = new Buffer(arr); |
| client.testBinary(buf, function(err, response) { |
| assert.error(err, "testBinary: no callback error"); |
| assert.equal(response.length, 256, "testBinary"); |
| assert.deepEqual(response, buf, "testBinary(Buffer)"); |
| }); |
| buf = new Buffer(arr); |
| client.testBinary(buf.toString("binary"), function(err, response) { |
| assert.error(err, "testBinary: no callback error"); |
| assert.equal(response.length, 256, "testBinary"); |
| assert.deepEqual(response, buf, "testBinary(string)"); |
| }); |
| |
| client.testMapMap(42, function(err, response) { |
| const expected = { |
| "4": { "1": 1, "2": 2, "3": 3, "4": 4 }, |
| "-4": { "-4": -4, "-3": -3, "-2": -2, "-1": -1 } |
| }; |
| assert.error(err, "testMapMap: no callback error"); |
| assert.deepEqual(expected, response, "testMapMap"); |
| }); |
| |
| client.testStruct(testCases.out, function(err, response) { |
| assert.error(err, "testStruct: no callback error"); |
| checkRecursively(testCases.out, response, "testStruct"); |
| }); |
| |
| client.testNest(testCases.out2, function(err, response) { |
| assert.error(err, "testNest: no callback error"); |
| checkRecursively(testCases.out2, response, "testNest"); |
| }); |
| |
| client.testInsanity(testCases.crazy, function(err, response) { |
| assert.error(err, "testInsanity: no callback error"); |
| checkRecursively(testCases.insanity, response, "testInsanity"); |
| }); |
| |
| client.testInsanity(testCases.crazy2, function(err, response) { |
| assert.error(err, "testInsanity2: no callback error"); |
| checkRecursively(testCases.insanity, response, "testInsanity2"); |
| }); |
| |
| client.testException("TException", function(err, response) { |
| assert.ok( |
| err instanceof TException, |
| "testException: correct error type" |
| ); |
| assert.ok(!response, "testException: no response"); |
| }); |
| |
| client.testException("Xception", function(err, response) { |
| assert.ok( |
| err instanceof ttypes.Xception, |
| "testException: correct error type" |
| ); |
| assert.ok(!response, "testException: no response"); |
| assert.equal(err.errorCode, 1001, "testException: correct error code"); |
| assert.equal( |
| "Xception", |
| err.message, |
| "testException: correct error message" |
| ); |
| }); |
| |
| client.testException("no Exception", function(err, response) { |
| assert.error(err, "testException: no callback error"); |
| assert.ok(!response, "testException: no response"); |
| }); |
| |
| client.testOneway(0, function(err, response) { |
| assert.error(err, "testOneway: no callback error"); |
| assert.strictEqual(response, undefined, "testOneway: void response"); |
| }); |
| |
| checkOffByOne(function(done) { |
| client.testI32(-1, function(err, response) { |
| assert.error(err, "checkOffByOne: no callback error"); |
| assert.equal(-1, response); |
| assert.end(); |
| done(); |
| }); |
| }, callback); |
| } |
| ); |
| |
| // ES6 does not support callback style |
| if (helpers.ecmaMode === "es6") { |
| checkOffByOne(done => done(), callback); |
| } |
| }; |
| |
| exports.ThriftTestDriverPromise = function(client, callback) { |
| test("Promise Client Tests", function(assert) { |
| const checkRecursively = makeRecursiveCheck(assert); |
| |
| function makeAsserter(assertionFn) { |
| return function(c) { |
| const fnName = c[0]; |
| const expected = c[1]; |
| client[fnName](expected) |
| .then(function(actual) { |
| assertionFn(actual, expected, fnName); |
| }) |
| .catch(() => assert.fail("fnName")); |
| }; |
| } |
| |
| testCases.simple.forEach( |
| makeAsserter(function(a, e, m) { |
| if (a instanceof Int64) { |
| const e64 = e instanceof Int64 ? e : new Int64(e); |
| assert.deepEqual(a.buffer, e64.buffer, m); |
| } else { |
| assert.equal(a, e, m); |
| } |
| }) |
| ); |
| testCases.deep.forEach(makeAsserter(assert.deepEqual)); |
| testCases.deepUnordered.forEach( |
| makeAsserter(makeUnorderedDeepEqual(assert)) |
| ); |
| |
| client |
| .testStruct(testCases.out) |
| .then(function(response) { |
| checkRecursively(testCases.out, response, "testStruct"); |
| }) |
| .catch(() => assert.fail("testStruct")); |
| |
| client |
| .testNest(testCases.out2) |
| .then(function(response) { |
| checkRecursively(testCases.out2, response, "testNest"); |
| }) |
| .catch(() => assert.fail("testNest")); |
| |
| client |
| .testInsanity(testCases.crazy) |
| .then(function(response) { |
| checkRecursively(testCases.insanity, response, "testInsanity"); |
| }) |
| .catch(() => assert.fail("testInsanity")); |
| |
| client |
| .testInsanity(testCases.crazy2) |
| .then(function(response) { |
| checkRecursively(testCases.insanity, response, "testInsanity2"); |
| }) |
| .catch(() => assert.fail("testInsanity2")); |
| |
| client |
| .testException("TException") |
| .then(function() { |
| assert.fail("testException: TException"); |
| }) |
| .catch(function(err) { |
| assert.ok(err instanceof TException); |
| }); |
| |
| client |
| .testException("Xception") |
| .then(function() { |
| assert.fail("testException: Xception"); |
| }) |
| .catch(function(err) { |
| assert.ok(err instanceof ttypes.Xception); |
| assert.equal(err.errorCode, 1001); |
| assert.equal("Xception", err.message); |
| }); |
| |
| client |
| .testException("no Exception") |
| .then(function(response) { |
| assert.equal(undefined, response); //void |
| }) |
| .catch(() => assert.fail("testException")); |
| |
| client |
| .testOneway(0) |
| .then(function(response) { |
| assert.strictEqual(response, undefined, "testOneway: void response"); |
| }) |
| .catch(() => assert.fail("testOneway: should not reject")); |
| |
| checkOffByOne(function(done) { |
| client |
| .testI32(-1) |
| .then(function(response) { |
| assert.equal(-1, response); |
| assert.end(); |
| done(); |
| }) |
| .catch(() => assert.fail("checkOffByOne")); |
| }, callback); |
| }); |
| }; |
| |
| // Helper Functions |
| // ========================================================= |
| |
| function makeRecursiveCheck(assert) { |
| return function(map1, map2, msg) { |
| const equal = checkRecursively(map1, map2); |
| |
| assert.ok(equal, msg); |
| |
| // deepEqual doesn't work with fields using node-int64 |
| function checkRecursively(map1, map2) { |
| if (typeof map1 !== "function" && typeof map2 !== "function") { |
| if (!map1 || typeof map1 !== "object") { |
| //Handle int64 types (which use node-int64 in Node.js JavaScript) |
| if ( |
| typeof map1 === "number" && |
| typeof map2 === "object" && |
| map2.buffer && |
| map2.buffer instanceof Buffer && |
| map2.buffer.length === 8 |
| ) { |
| const n = new Int64(map2.buffer); |
| return map1 === n.toNumber(); |
| } else { |
| return map1 == map2; |
| } |
| } else { |
| return Object.keys(map1).every(function(key) { |
| return checkRecursively(map1[key], map2[key]); |
| }); |
| } |
| } |
| } |
| }; |
| } |
| |
| function checkOffByOne(done, callback) { |
| const retry_limit = 30; |
| const retry_interval = 100; |
| let test_complete = false; |
| let retrys = 0; |
| |
| /** |
| * redo a simple test after the oneway to make sure we aren't "off by one" -- |
| * if the server treated oneway void like normal void, this next test will |
| * fail since it will get the void confirmation rather than the correct |
| * result. In this circumstance, the client will throw the exception: |
| * |
| * Because this is the last test against the server, when it completes |
| * the entire suite is complete by definition (the tests run serially). |
| */ |
| done(function() { |
| test_complete = true; |
| }); |
| |
| //We wait up to retry_limit * retry_interval for the test suite to complete |
| function TestForCompletion() { |
| if (test_complete && callback) { |
| callback("Server successfully tested!"); |
| } else { |
| if (++retrys < retry_limit) { |
| setTimeout(TestForCompletion, retry_interval); |
| } else if (callback) { |
| callback( |
| "Server test failed to complete after " + |
| (retry_limit * retry_interval) / 1000 + |
| " seconds" |
| ); |
| } |
| } |
| } |
| |
| setTimeout(TestForCompletion, retry_interval); |
| } |
| |
| function makeUnorderedDeepEqual(assert) { |
| return function(actual, expected, name) { |
| assert.equal(actual.length, expected.length, name); |
| for (const k in actual) { |
| let found = false; |
| for (const k2 in expected) { |
| if (actual[k] === expected[k2]) { |
| found = true; |
| } |
| } |
| if (!found) { |
| assert.fail("Unexpected value " + actual[k] + " with key " + k); |
| } |
| } |
| }; |
| } |