THRIFT-3278 NodeJS: Fix exception stack traces and names
diff --git a/lib/nodejs/lib/thrift/input_buffer_underrun_error.js b/lib/nodejs/lib/thrift/input_buffer_underrun_error.js
index 4d4237b..72555e5 100644
--- a/lib/nodejs/lib/thrift/input_buffer_underrun_error.js
+++ b/lib/nodejs/lib/thrift/input_buffer_underrun_error.js
@@ -21,7 +21,10 @@
 module.exports = InputBufferUnderrunError;
 
 function InputBufferUnderrunError(message) {
-  Error.call(this, message);
+  Error.call(this);
+  Error.captureStackTrace(this, this.constructor);
+  this.name = this.constructor.name;
+  this.message = message;
 };
 
 util.inherits(InputBufferUnderrunError, Error);
diff --git a/lib/nodejs/lib/thrift/thrift.js b/lib/nodejs/lib/thrift/thrift.js
index e6edf9e..f2b2896 100644
--- a/lib/nodejs/lib/thrift/thrift.js
+++ b/lib/nodejs/lib/thrift/thrift.js
@@ -45,9 +45,13 @@
   ONEWAY: 4
 };
 
-var TException = exports.TException = function(message) {
-  Error.call(this, message);
-  this.name = 'TException';
+exports.TException = TException;
+
+function TException(message) {
+  Error.call(this);
+  Error.captureStackTrace(this, this.constructor);
+  this.name = this.constructor.name;
+  this.message = message;
 };
 util.inherits(TException, Error);
 
@@ -65,10 +69,14 @@
   UNSUPPORTED_CLIENT_TYPE: 10
 };
 
-var TApplicationException = exports.TApplicationException = function(type, message) {
-  TException.call(this, message);
+exports.TApplicationException = TApplicationException;
+
+function TApplicationException(type, message) {
+  TException.call(this);
+  Error.captureStackTrace(this, this.constructor);
   this.type = type || TApplicationExceptionType.UNKNOWN;
-  this.name = 'TApplicationException';
+  this.name = this.constructor.name;
+  this.message = message;
 };
 util.inherits(TApplicationException, TException);
 
@@ -137,10 +145,14 @@
 };
 
 
-var TProtocolException = exports.TProtocolException = function(type, message) {
-  Error.call(this, message);
-  this.name = 'TProtocolException';
+exports.TProtocolException = TProtocolException;
+
+function TProtocolException(type, message) {
+  Error.call(this);
+  Error.captureStackTrace(this, this.constructor);
+  this.name = this.constructor.name;
   this.type = type;
+  this.message = message;
 };
 util.inherits(TProtocolException, Error);
 
diff --git a/lib/nodejs/test/exceptions.js b/lib/nodejs/test/exceptions.js
new file mode 100644
index 0000000..c6f2e4d
--- /dev/null
+++ b/lib/nodejs/test/exceptions.js
@@ -0,0 +1,55 @@
+'use strict';
+var test = require('tape');
+var thrift = require('../lib/thrift/thrift.js');
+var InputBufferUnderrunError = require('../lib/thrift/input_buffer_underrun_error');
+
+test('TApplicationException', function t(assert) {
+  var e = new thrift.TApplicationException(1, 'foo');
+  assert.ok(e instanceof thrift.TApplicationException, 'is instanceof TApplicationException');
+  assert.ok(e instanceof thrift.TException, 'is instanceof TException');
+  assert.ok(e instanceof Error, 'is instanceof Error');
+  assert.equal(typeof e.stack, 'string', 'has stack trace');
+  assert.ok(/^TApplicationException: foo/.test(e.stack), 'Stack trace has correct error name and message');
+  assert.ok(e.stack.indexOf('test/exceptions.js:7:11') !== -1, 'stack trace starts on correct line and column');
+  assert.equal(e.name, 'TApplicationException', 'has function name TApplicationException');
+  assert.equal(e.message, 'foo', 'has error message "foo"');
+  assert.equal(e.type, 1, 'has type 1');
+  assert.end();
+});
+
+test('TException', function t(assert) {
+  var e = new thrift.TException('foo');
+  assert.ok(e instanceof thrift.TException, 'is instanceof TException');
+  assert.ok(e instanceof Error, 'is instanceof Error');
+  assert.equal(typeof e.stack, 'string', 'has stack trace');
+  assert.ok(/^TException: foo/.test(e.stack), 'Stack trace has correct error name and message');
+  assert.ok(e.stack.indexOf('test/exceptions.js:21:11') !== -1, 'stack trace starts on correct line and column');
+  assert.equal(e.name, 'TException', 'has function name TException');
+  assert.equal(e.message, 'foo', 'has error message "foo"');
+  assert.end();
+});
+
+test('TProtocolException', function t(assert) {
+  var e = new thrift.TProtocolException(1, 'foo');
+  assert.ok(e instanceof thrift.TProtocolException, 'is instanceof TProtocolException');
+  assert.ok(e instanceof Error, 'is instanceof Error');
+  assert.equal(typeof e.stack, 'string', 'has stack trace');
+  assert.ok(/^TProtocolException: foo/.test(e.stack), 'Stack trace has correct error name and message');
+  assert.ok(e.stack.indexOf('test/exceptions.js:33:11') !== -1, 'stack trace starts on correct line and column');
+  assert.equal(e.name, 'TProtocolException', 'has function name TProtocolException');
+  assert.equal(e.message, 'foo', 'has error message "foo"');
+  assert.equal(e.type, 1, 'has type 1');
+  assert.end();
+});
+
+test('InputBufferUnderrunError', function t(assert) {
+  var e = new InputBufferUnderrunError('foo');
+  assert.ok(e instanceof InputBufferUnderrunError, 'is instanceof InputBufferUnderrunError');
+  assert.ok(e instanceof Error, 'is instanceof Error');
+  assert.equal(typeof e.stack, 'string', 'has stack trace');
+  assert.ok(/^InputBufferUnderrunError: foo/.test(e.stack), 'Stack trace has correct error name and message');
+  assert.ok(e.stack.indexOf('test/exceptions.js:46:11') !== -1, 'stack trace starts on correct line and column');
+  assert.equal(e.name, 'InputBufferUnderrunError', 'has function name InputBufferUnderrunError');
+  assert.equal(e.message, 'foo', 'has error message "foo"');
+  assert.end();
+});