THRIFT-2976: add browserify support and tests
Client: nodejs
Patch: Andrew de Andrade
diff --git a/lib/nodejs/test/binary.test.js b/lib/nodejs/test/binary.test.js
index dacadef..38ba634 100644
--- a/lib/nodejs/test/binary.test.js
+++ b/lib/nodejs/test/binary.test.js
@@ -17,117 +17,121 @@
  * under the License.
  */
 
-var testCase = require('nodeunit').testCase;
+var test = require('tape');
 var binary = require('thrift/binary');
 
-module.exports = testCase({
-  "Should read signed byte": function(test){
-    test.strictEqual(1, binary.readByte(0x01));
-    test.strictEqual(-1, binary.readByte(0xFF));
+var cases = {
+  "Should read signed byte": function(assert){
+    assert.equal(1, binary.readByte(0x01));
+    assert.equal(-1, binary.readByte(0xFF));
 
-    test.strictEqual(127, binary.readByte(0x7F));
-    test.strictEqual(-128, binary.readByte(0x80));
-    test.done();
+    assert.equal(127, binary.readByte(0x7F));
+    assert.equal(-128, binary.readByte(0x80));
+    assert.end();
   },
-  "Should write byte": function(test){
-  	//Protocol simply writes to the buffer. Nothing to test.. yet.
-  	test.ok(true);
-  	test.done();
+  "Should write byte": function(assert){
+    //Protocol simply writes to the buffer. Nothing to test.. yet.
+    assert.ok(true);
+    assert.end();
   },
-  "Should read I16": function(test) {
-    test.strictEqual(0, binary.readI16([0x00, 0x00]));
-    test.strictEqual(1, binary.readI16([0x00, 0x01]));
-    test.strictEqual(-1, binary.readI16([0xff, 0xff]));
+  "Should read I16": function(assert) {
+    assert.equal(0, binary.readI16([0x00, 0x00]));
+    assert.equal(1, binary.readI16([0x00, 0x01]));
+    assert.equal(-1, binary.readI16([0xff, 0xff]));
 
     // Min I16
-    test.strictEqual(-32768, binary.readI16([0x80, 0x00]));
+    assert.equal(-32768, binary.readI16([0x80, 0x00]));
     // Max I16
-    test.strictEqual(32767, binary.readI16([0x7f, 0xff]));
-    test.done();
+    assert.equal(32767, binary.readI16([0x7f, 0xff]));
+    assert.end();
   },
 
-  "Should write I16": function(test) {
-    test.deepEqual([0x00, 0x00], binary.writeI16([], 0));
-    test.deepEqual([0x00, 0x01], binary.writeI16([], 1));
-    test.deepEqual([0xff, 0xff], binary.writeI16([], -1));
+  "Should write I16": function(assert) {
+    assert.deepEqual([0x00, 0x00], binary.writeI16([], 0));
+    assert.deepEqual([0x00, 0x01], binary.writeI16([], 1));
+    assert.deepEqual([0xff, 0xff], binary.writeI16([], -1));
 
     // Min I16
-    test.deepEqual([0x80, 0x00], binary.writeI16([], -32768));
+    assert.deepEqual([0x80, 0x00], binary.writeI16([], -32768));
     // Max I16
-    test.deepEqual([0x7f, 0xff], binary.writeI16([], 32767));
-    test.done();
+    assert.deepEqual([0x7f, 0xff], binary.writeI16([], 32767));
+    assert.end();
   },
 
-  "Should read I32": function(test) {
-    test.strictEqual(0, binary.readI32([0x00, 0x00, 0x00, 0x00]));
-    test.strictEqual(1, binary.readI32([0x00, 0x00, 0x00, 0x01]));
-    test.strictEqual(-1, binary.readI32([0xff, 0xff, 0xff, 0xff]));
+  "Should read I32": function(assert) {
+    assert.equal(0, binary.readI32([0x00, 0x00, 0x00, 0x00]));
+    assert.equal(1, binary.readI32([0x00, 0x00, 0x00, 0x01]));
+    assert.equal(-1, binary.readI32([0xff, 0xff, 0xff, 0xff]));
 
     // Min I32
-    test.strictEqual(-2147483648, binary.readI32([0x80, 0x00, 0x00, 0x00]));
+    assert.equal(-2147483648, binary.readI32([0x80, 0x00, 0x00, 0x00]));
     // Max I32
-    test.strictEqual(2147483647, binary.readI32([0x7f, 0xff, 0xff, 0xff]));
-    test.done();
+    assert.equal(2147483647, binary.readI32([0x7f, 0xff, 0xff, 0xff]));
+    assert.end();
   },
 
-  "Should write I32": function(test) {
-    test.deepEqual([0x00, 0x00, 0x00, 0x00], binary.writeI32([], 0));
-    test.deepEqual([0x00, 0x00, 0x00, 0x01], binary.writeI32([], 1));
-    test.deepEqual([0xff, 0xff, 0xff, 0xff], binary.writeI32([], -1));
+  "Should write I32": function(assert) {
+    assert.deepEqual([0x00, 0x00, 0x00, 0x00], binary.writeI32([], 0));
+    assert.deepEqual([0x00, 0x00, 0x00, 0x01], binary.writeI32([], 1));
+    assert.deepEqual([0xff, 0xff, 0xff, 0xff], binary.writeI32([], -1));
 
     // Min I32
-    test.deepEqual([0x80, 0x00, 0x00, 0x00], binary.writeI32([], -2147483648));
+    assert.deepEqual([0x80, 0x00, 0x00, 0x00], binary.writeI32([], -2147483648));
     // Max I32
-    test.deepEqual([0x7f, 0xff, 0xff, 0xff], binary.writeI32([], 2147483647));
- 	test.done();
+    assert.deepEqual([0x7f, 0xff, 0xff, 0xff], binary.writeI32([], 2147483647));
+    assert.end();
   },
 
-  "Should read doubles": function(test) {
-    test.strictEqual(0, binary.readDouble([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
-    test.strictEqual(0, binary.readDouble([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
-    test.strictEqual(1, binary.readDouble([0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
-    test.strictEqual(2, binary.readDouble([0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
-    test.strictEqual(-2, binary.readDouble([0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
+  "Should read doubles": function(assert) {
+    assert.equal(0, binary.readDouble([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
+    assert.equal(0, binary.readDouble([0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
+    assert.equal(1, binary.readDouble([0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
+    assert.equal(2, binary.readDouble([0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
+    assert.equal(-2, binary.readDouble([0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
 
-    test.strictEqual(Math.PI, binary.readDouble([0x40, 0x9, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18]))
+    assert.equal(Math.PI, binary.readDouble([0x40, 0x9, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18]))
 
-    test.strictEqual(Infinity, binary.readDouble([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
-    test.strictEqual(-Infinity, binary.readDouble([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
+    assert.equal(Infinity, binary.readDouble([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
+    assert.equal(-Infinity, binary.readDouble([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
 
-    test.ok(isNaN(binary.readDouble([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])))
+    assert.ok(isNaN(binary.readDouble([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])))
 
-    test.strictEqual(1/3, binary.readDouble([0x3f, 0xd5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55]))
+    assert.equal(1/3, binary.readDouble([0x3f, 0xd5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55]))
 
     // Min subnormal positive double
-    test.strictEqual(4.9406564584124654e-324, binary.readDouble([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]))
+    assert.equal(4.9406564584124654e-324, binary.readDouble([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]))
     // Min normal positive double
-    test.strictEqual(2.2250738585072014e-308, binary.readDouble([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
+    assert.equal(2.2250738585072014e-308, binary.readDouble([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
     // Max positive double
-    test.strictEqual(1.7976931348623157e308, binary.readDouble([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]))
-  	test.done();
+    assert.equal(1.7976931348623157e308, binary.readDouble([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]))
+    assert.end();
   },
 
-  "Should write doubles": function(test) {
-    test.deepEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 0));
-    test.deepEqual([0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 1));
-    test.deepEqual([0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 2));
-    test.deepEqual([0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], -2));
+  "Should write doubles": function(assert) {
+    assert.deepEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 0));
+    assert.deepEqual([0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 1));
+    assert.deepEqual([0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 2));
+    assert.deepEqual([0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], -2));
 
-    test.deepEqual([0x40, 0x9, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18], binary.writeDouble([], Math.PI));
+    assert.deepEqual([0x40, 0x9, 0x21, 0xfb, 0x54, 0x44, 0x2d, 0x18], binary.writeDouble([], Math.PI));
 
-    test.deepEqual([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], Infinity));
-    test.deepEqual([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], -Infinity));
+    assert.deepEqual([0x7f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], Infinity));
+    assert.deepEqual([0xff, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], -Infinity));
 
-    test.deepEqual([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], NaN));
+    assert.deepEqual([0x7f, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], NaN));
 
-    test.deepEqual([0x3f, 0xd5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55], binary.writeDouble([], 1/3));
+    assert.deepEqual([0x3f, 0xd5, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55], binary.writeDouble([], 1/3));
 
     // Min subnormal positive double
-    test.deepEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], binary.writeDouble([], 4.9406564584124654e-324));
+    assert.deepEqual([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], binary.writeDouble([], 4.9406564584124654e-324));
     // Min normal positive double
-    test.deepEqual([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 2.2250738585072014e-308));
+    assert.deepEqual([0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], binary.writeDouble([], 2.2250738585072014e-308));
     // Max positive double
-    test.deepEqual([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], binary.writeDouble([], 1.7976931348623157e308));
-  	test.done();
+    assert.deepEqual([0x7f, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff], binary.writeDouble([], 1.7976931348623157e308));
+    assert.end();
   }
+};
+
+Object.keys(cases).forEach(function(caseName) {
+  test(caseName, cases[caseName]);
 });
diff --git a/lib/nodejs/test/browser_client.js b/lib/nodejs/test/browser_client.js
new file mode 100644
index 0000000..27db543
--- /dev/null
+++ b/lib/nodejs/test/browser_client.js
@@ -0,0 +1,27 @@
+
+var assert = require('assert');
+var thrift = require('thrift');
+var helpers = require('./helpers');
+var ThriftTest = require('./gen-nodejs/ThriftTest');
+var ThriftTestDriver = require('./test_driver').ThriftTestDriver;
+
+// createXHRConnection createWSConnection
+var connection = thrift.createXHRConnection("localhost", 9090, {
+    transport: helpers.transports['buffered'],
+    protocol: helpers.protocols['json'],
+    path: '/test'
+});
+
+connection.on('error', function(err) {
+    assert(false, err);
+});
+
+// Uncomment the following line to start a websockets connection
+// connection.open();
+
+// createWSClient createXHRClient
+var client = thrift.createXHRClient(ThriftTest, connection);
+
+ThriftTestDriver(client, function (status) {
+    console.log('Browser:', status);
+});
diff --git a/lib/nodejs/test/server.js b/lib/nodejs/test/server.js
index 2d53e11..b33e91b 100644
--- a/lib/nodejs/test/server.js
+++ b/lib/nodejs/test/server.js
@@ -57,7 +57,10 @@
   options.processor = ThriftTest;
 
   options = {
-    services: { "/test": options }
+    services: { "/test": options },
+    cors: {
+      '*': true
+    }
   }
 }
 
diff --git a/lib/nodejs/test/test-cases.js b/lib/nodejs/test/test-cases.js
index 3386657..0d13cdd 100644
--- a/lib/nodejs/test/test-cases.js
+++ b/lib/nodejs/test/test-cases.js
@@ -1,6 +1,5 @@
 'use strict';
 
-var assert = require('assert');
 var ttypes = require('./gen-nodejs/ThriftTest_types');
 
 //all Languages in UTF-8
@@ -55,17 +54,19 @@
   ['testByte', -1],
   ['testByte', -127],
   ['testI32', -1],
+  ['testDouble', -5.2098523],
+  ['testDouble', 7.012052175215044],
+  ['testEnum', ttypes.Numberz.ONE]
+];
+
+var simpleLoose = [
   ['testI64', 5],
   ['testI64', -5],
   ['testI64', 734359738368],
   ['testI64', -34359738368],
   ['testI64', -734359738368],
-  ['testDouble', -5.2098523],
-  ['testDouble', 7.012052175215044],
-  ['testEnum', ttypes.Numberz.ONE],
   ['testTypedef', 69]
-];
-
+]
 
 var mapout = {};
 for (var i = 0; i < 5; ++i) {
@@ -118,6 +119,7 @@
 };
 
 module.exports.simple = simple;
+module.exports.simpleLoose = simpleLoose;
 module.exports.deep = deep;
 
 module.exports.out = out;
diff --git a/lib/nodejs/test/testAll.sh b/lib/nodejs/test/testAll.sh
index 0d10758..fd11425 100755
--- a/lib/nodejs/test/testAll.sh
+++ b/lib/nodejs/test/testAll.sh
@@ -24,7 +24,7 @@
 DIR="$( cd "$( dirname "$0" )" && pwd )"
 
 ISTANBUL="$DIR/../../../node_modules/istanbul/lib/cli.js"
-NODEUNIT="${DIR}/../../../node_modules/nodeunit/bin/nodeunit"
+RUNBROWSER="$DIR/../../../node_modules/run-browser/bin/cli.js"
 
 REPORT_PREFIX="${DIR}/../coverage/report"
 
@@ -54,6 +54,18 @@
   return $RET
 }
 
+testBrowser()
+{
+  echo "   Testing browser client with http server with json protocol and buffered transport";
+  RET=0
+  node ${DIR}/server.js --type http -p json -t buffered &
+  SERVERPID=$!
+  sleep 1
+  ${RUNBROWSER} ${DIR}/browser_client.js --phantom || RET=1
+  kill -2 $SERVERPID || RET=1
+  return $RET
+}
+
 TESTOK=0
 
 #generating thrift code
@@ -62,11 +74,11 @@
 
 #unit tests
 
-${NODEUNIT} ${DIR}/binary.test.js || TESTOK=1
+node ${DIR}/binary.test.js || TESTOK=1
 
 #integration tests
 
-for type in tcp multiplex http websocket
+for type in tcp multiplex websocket http
 do
 
   for protocol in compact binary json
@@ -81,6 +93,9 @@
   done
 done
 
+# XHR only until phantomjs 2 is released.
+testBrowser
+
 if [ -n "${COVER}" ]; then
   ${ISTANBUL} report --dir "${DIR}/../coverage" --include "${DIR}/../coverage/report*/coverage.json" lcov cobertura html
   rm -r ${DIR}/../coverage/report*/*
diff --git a/lib/nodejs/test/test_driver.js b/lib/nodejs/test/test_driver.js
index 9f2b894..f79baa6 100644
--- a/lib/nodejs/test/test_driver.js
+++ b/lib/nodejs/test/test_driver.js
@@ -26,177 +26,195 @@
  // supports an optional callback function which is called with
  // a status message when the test is complete.
 
-var assert = require('assert');
+var test = require('tape');
+//var assert = require('assert');
 var ttypes = require('./gen-nodejs/ThriftTest_types');
 var Int64 = require('node-int64');
 var testCases = require('./test-cases');
 
 exports.ThriftTestDriver = function(client, callback) {
 
-  function makeAsserter(assertionFn) {
-    return function(c) {
-      var fnName = c[0];
-      var expected = c[1];
-      client[fnName](expected, function(err, actual) {
-        assert(!err);
-        assertionFn(actual, expected);
-      })
-    };
-  }
+  test('NodeJS Style Callback Client Tests', function(assert) {
 
-  testCases.simple.forEach(makeAsserter(assert.equal));
-  testCases.deep.forEach(makeAsserter(assert.deepEqual));
+    var checkRecursively = makeRecursiveCheck(assert);
 
-  client.testStruct(testCases.out, function(err, response) {
-    assert(!err);
-    checkRecursively(testCases.out, response);
-  });
+    function makeAsserter(assertionFn) {
+      return function(c) {
+        var fnName = c[0];
+        var expected = c[1];
+        client[fnName](expected, function(err, actual) {
+          assert.error(err, fnName + ': no callback error');
+          assertionFn(actual, expected, fnName);
+        })
+      };
+    }
 
-  client.testNest(testCases.out2, function(err, response) {
-    assert(!err);
-    checkRecursively(testCases.out2, response);
-  });
+    testCases.simple.forEach(makeAsserter(assert.equal));
+    testCases.simpleLoose.forEach(makeAsserter(function(a, e, m){
+      assert.ok(a == e, m);
+    }));
+    testCases.deep.forEach(makeAsserter(assert.deepEqual));
 
-  client.testInsanity(testCases.crazy, function(err, response) {
-    assert(!err);
-    checkRecursively(testCases.insanity, response);
-  });
-
-  client.testException('TException', function(err, response) {
-    assert(!err);
-    assert(!response);
-  });
-
-  client.testException('Xception', function(err, response) {
-    assert(!response);
-    assert.equal(err.errorCode, 1001);
-    assert.equal('Xception', err.message);
-  });
-
-  client.testException('no Exception', function(err, response) {
-    assert(!err);
-    assert.equal(undefined, response); //void
-  });
-
-  client.testOneway(0, function(err, response) {
-    assert(false); //should not answer
-  });
-
-  checkOffByOne(function(done) {
-    client.testI32(-1, function(err, response) {
-      assert(!err);
-      assert.equal(-1, response);
-      done();
+    client.testStruct(testCases.out, function(err, response) {
+      assert.error(err, 'testStruct: no callback error');
+      checkRecursively(testCases.out, response, 'testStruct');
     });
-  }, callback);
 
+    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.testException('TException', function(err, response) {
+      assert.error(err, 'testException: no callback error');
+      assert.ok(!response, 'testException: no response');
+    });
+
+    client.testException('Xception', function(err, response) {
+      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.fail('testOneway should not answer');
+    });
+
+    checkOffByOne(function(done) {
+      client.testI32(-1, function(err, response) {
+        assert.error(err, 'checkOffByOne: no callback error');
+        assert.equal(-1, response);
+        assert.end();
+        done();
+      });
+    }, callback);
+
+  });
 };
 
 exports.ThriftTestDriverPromise = function(client, callback) {
 
-  function makeAsserter(assertionFn) {
-    return function(c) {
-      var fnName = c[0];
-      var expected = c[1];
-      client[fnName](expected)
-        .then(function(actual) {
-          assert.equal(actual, expected);
-        })
-        .fail(failTest);
-    };
-  }
+  test('Q Promise Client Tests', function(assert) {
 
-  testCases.simple.forEach(makeAsserter(assert.equal));
-  testCases.deep.forEach(makeAsserter(assert.deepEqual));
+    var checkRecursively = makeRecursiveCheck(assert);
 
-  client.testStruct(testCases.out)
-    .then(function(response) {
-      checkRecursivelyP(testCases.out, response);
-    })
-    .fail(failTest);
+    function fail(msg) {
+      return function() {
+        assert.fail(msg);
+      }
+    }
 
-  client.testNest(testCases.out2)
-    .then(function(response) {
-      checkRecursivelyP(testCases.out2, response);
-    })
-    .fail(failTest);
+    function makeAsserter(assertionFn) {
+      return function(c) {
+        var fnName = c[0];
+        var expected = c[1];
+        client[fnName](expected)
+          .then(function(actual) {
+            assertionFn(actual, expected, fnName);
+          })
+          .fail(fail('fnName'));
+      };
+    }
 
-  client.testInsanity(testCases.crazy)
-    .then(function(response) {
-      checkRecursivelyP(testCases.insanity, response);
-    })
-    .fail(failTest);
+    testCases.simple.forEach(makeAsserter(assert.equal));
+    testCases.simpleLoose.forEach(makeAsserter(function(a, e, m){
+      assert.ok(a == e, m);
+    }));
+    testCases.deep.forEach(makeAsserter(assert.deepEqual));
 
-  client.testException('TException')
-    .then(failTest);
-
-  client.testException('Xception')
-    .then(function(response) {
-      assert.equal(err.errorCode, 1001);
-      assert.equal('Xception', err.message);
-    })
-    .fail(failTest);
-
-  client.testException('no Exception')
-    .then(function(response) {
-      assert.equal(undefined, response); //void
-    })
-    .fail(failTest);
-
-  client.testOneway(0, failTest); //should not answer
-
-  checkOffByOne(function(done) {
-    client.testI32(-1)
+    client.testStruct(testCases.out)
       .then(function(response) {
-          assert.equal(-1, response);
-          done();
+        checkRecursively(testCases.out, response, 'testStruct');
       })
-      .fail(function() {
-        assert(false);
-      });
-  }, callback);
+      .fail(fail('testStruct'));
 
+    client.testNest(testCases.out2)
+      .then(function(response) {
+        checkRecursively(testCases.out2, response, 'testNest');
+      })
+      .fail(fail('testNest'));
+
+    client.testInsanity(testCases.crazy)
+      .then(function(response) {
+        checkRecursively(testCases.insanity, response, 'testInsanity');
+      })
+      .fail(fail('testInsanity'));
+
+    client.testException('TException')
+      .then(function(response) {
+        assert.ok(!response, 'testException: TException');
+      })
+      .fail(fail('testException: TException'));
+
+    client.testException('Xception')
+      .then(function(response) {
+        assert.ok(!response);
+      })
+      .fail(function(err) {
+        assert.equal(err.errorCode, 1001);
+        assert.equal('Xception', err.message);
+      });
+
+    client.testException('no Exception')
+      .then(function(response) {
+        assert.equal(undefined, response); //void
+      })
+      .fail(fail('testException'));
+
+    client.testOneway(0, fail('testOneway: should not answer'));
+
+    checkOffByOne(function(done) {
+      client.testI32(-1)
+        .then(function(response) {
+            assert.equal(-1, response);
+            assert.end();
+            done();
+        })
+        .fail(fail('checkOffByOne'));
+    }, callback);
+  });
 };
 
 
 // Helper Functions
 // =========================================================
 
-function failTest() {
-  assert(false);
-}
+function makeRecursiveCheck(assert) {
 
-// This is the version of checkRecursively that was in the vanilla callback
-// version of test_driver.
-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)) {
-        var n = new Int64(map2.buffer);
-        assert.equal(map1, n.toNumber());
-      } else {
-        assert.equal(map1, map2);
-      }
-    } else {
-      for (var key in map1) {
-        checkRecursively(map1[key], map2[key]);
-      }
-    }
-  }
-}
+  return function (map1, map2, msg) {
+    var equal = true;
 
-// This is the version of checkRecursively that was in the promise version of
-// test_driver.
-// deepEqual doesn't work with fields using node-int64
-function checkRecursivelyP(map1, map2) {
-  if (typeof map1 !== 'function' && typeof map2 !== 'function') {
-    if (!map1 || typeof map1 !== 'object') {
-        assert.equal(map1, map2);
-    } else {
-      for (var key in map1) {
-        checkRecursivelyP(map1[key], map2[key]);
+    var 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)) {
+            var 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]);
+          });
+        }
       }
     }
   }
diff --git a/lib/nodejs/test/test_handler.js b/lib/nodejs/test/test_handler.js
index 83b7d41..de6f503 100644
--- a/lib/nodejs/test/test_handler.js
+++ b/lib/nodejs/test/test_handler.js
@@ -25,7 +25,7 @@
 
 function makeSyncHandler(label) {
   return function(thing) {
-    console.log(label + '(\'' + thing + '\')');
+    //console.log(label + '(\'' + thing + '\')');
     return thing;
   }
 }
@@ -72,7 +72,7 @@
 ];
 
 function testVoid() {
-  console.log('testVoid()');
+  //console.log('testVoid()');
 }
 
 function testVoidAsync(result) {
@@ -80,7 +80,7 @@
 }
 
 function testMapMap(hello) {
-  console.log('testMapMap(' + hello + ')');
+  //console.log('testMapMap(' + hello + ')');
 
   var mapmap = [];
   var pos = [];
@@ -96,9 +96,9 @@
 }
 
 function testInsanity(argument) {
-  console.log('testInsanity(');
-  console.log(argument);
-  console.log(')');
+  //console.log('testInsanity(');
+  //console.log(argument);
+  //console.log(')');
 
   var hello = new ttypes.Xtruct();
   hello.string_thing = 'Hello2';
@@ -131,13 +131,13 @@
   insane[1] = first_map;
   insane[2] = second_map;
 
-  console.log('insane result:');
-  console.log(insane);
+  //console.log('insane result:');
+  //console.log(insane);
   return insane;
 }
 
 function testMulti(arg0, arg1, arg2, arg3, arg4, arg5) {
-  console.log('testMulti()');
+  //console.log('testMulti()');
 
   var hello = new ttypes.Xtruct();
   hello.string_thing = 'Hello2';
@@ -153,7 +153,7 @@
 }
 
 function testException(arg) {
-  console.log('testException('+arg+')');
+  //console.log('testException('+arg+')');
   if (arg === 'Xception') {
     var x = new ttypes.Xception();
     x.errorCode = 1001;
@@ -167,7 +167,7 @@
 }
 
 function testExceptionAsync(arg, result) {
-  console.log('testException('+arg+')');
+  //console.log('testException('+arg+')');
   if (arg === 'Xception') {
     var x = new ttypes.Xception();
     x.errorCode = 1001;
@@ -181,7 +181,7 @@
 }
 
 function testMultiException(arg0, arg1) {
-  console.log('testMultiException(' + arg0 + ', ' + arg1 + ')');
+  //console.log('testMultiException(' + arg0 + ', ' + arg1 + ')');
   if (arg0 === ('Xception')) {
     var x = new ttypes.Xception();
     x.errorCode = 1001;
@@ -201,7 +201,7 @@
 }
 
 function testMultiExceptionAsync(arg0, arg1, result) {
-  console.log('testMultiException(' + arg0 + ', ' + arg1 + ')');
+  //console.log('testMultiException(' + arg0 + ', ' + arg1 + ')');
   if (arg0 === ('Xception')) {
     var x = new ttypes.Xception();
     x.errorCode = 1001;
@@ -221,7 +221,7 @@
 }
 
 function testOneway(sleepFor) {
-  console.log('testOneway(' + sleepFor + ') => JavaScript (like Rust) never sleeps!');
+  //console.log('testOneway(' + sleepFor + ') => JavaScript (like Rust) never sleeps!');
 }
 
 function testOnewayAsync(sleepFor, result) {