Skip to content

Commit d8f334b

Browse files
authored
fix(NODE-3724): Fix BSONTypeError and BSONError to correctly handle instanceof checks (#471)
1 parent 0aa8967 commit d8f334b

11 files changed

+93
-56
lines changed

src/ensure_buffer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { isAnyArrayBuffer } from './parser/utils';
88
* @param potentialBuffer - The potential buffer
99
* @returns Buffer the input if potentialBuffer is a buffer, or a buffer that
1010
* wraps a passed in Uint8Array
11-
* @throws TypeError If anything other than a Buffer or Uint8Array is passed in
11+
* @throws BSONTypeError If anything other than a Buffer or Uint8Array is passed in
1212
*/
1313
export function ensureBuffer(potentialBuffer: Buffer | ArrayBufferView | ArrayBuffer): Buffer {
1414
if (ArrayBuffer.isView(potentialBuffer)) {

src/error.ts

+10
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
/** @public */
22
export class BSONError extends Error {
3+
constructor(message: string) {
4+
super(message);
5+
Object.setPrototypeOf(this, BSONError.prototype);
6+
}
7+
38
get name(): string {
49
return 'BSONError';
510
}
611
}
712

813
/** @public */
914
export class BSONTypeError extends TypeError {
15+
constructor(message: string) {
16+
super(message);
17+
Object.setPrototypeOf(this, BSONTypeError.prototype);
18+
}
19+
1020
get name(): string {
1121
return 'BSONTypeError';
1222
}

test/binary_parser.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
'use strict';
2+
3+
const BSON = require('./register-bson');
4+
const BSONError = BSON.BSONError;
5+
26
/**
37
* Binary Parser.
48
* Jonas Raoni Soares Silva
@@ -20,7 +24,7 @@ function BinaryParser(bigEndian, allowExceptions) {
2024

2125
BinaryParser.warn = function warn(msg) {
2226
if (this.allowExceptions) {
23-
throw new Error(msg);
27+
throw new BSONError(msg);
2428
}
2529

2630
return 1;
@@ -419,7 +423,7 @@ BinaryParserBuffer.prototype.hasNeededBits = function hasNeededBits(neededBits)
419423

420424
BinaryParserBuffer.prototype.checkBuffer = function checkBuffer(neededBits) {
421425
if (!this.hasNeededBits(neededBits)) {
422-
throw new Error('checkBuffer::missing bytes');
426+
throw new BSONError('checkBuffer::missing bytes');
423427
}
424428
};
425429

test/node/bigint_tests.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
'use strict';
33

44
const BSON = require('../register-bson');
5+
const BSONTypeError = BSON.BSONTypeError;
56

67
describe('BSON BigInt Support', function () {
78
before(function () {
@@ -13,7 +14,7 @@ describe('BSON BigInt Support', function () {
1314
});
1415
it('Should serialize an int that fits in int32', function () {
1516
const testDoc = { b: BigInt(32) };
16-
expect(() => BSON.serialize(testDoc)).to.throw(TypeError);
17+
expect(() => BSON.serialize(testDoc)).to.throw(BSONTypeError);
1718

1819
// const serializedDoc = BSON.serialize(testDoc);
1920
// // prettier-ignore
@@ -25,7 +26,7 @@ describe('BSON BigInt Support', function () {
2526

2627
it('Should serialize an int that fits in int64', function () {
2728
const testDoc = { b: BigInt(0x1ffffffff) };
28-
expect(() => BSON.serialize(testDoc)).to.throw(TypeError);
29+
expect(() => BSON.serialize(testDoc)).to.throw(BSONTypeError);
2930

3031
// const serializedDoc = BSON.serialize(testDoc);
3132
// // prettier-ignore
@@ -37,7 +38,7 @@ describe('BSON BigInt Support', function () {
3738

3839
it('Should serialize an int that fits in decimal128', function () {
3940
const testDoc = { b: BigInt('9223372036854776001') }; // int64 max + 1
40-
expect(() => BSON.serialize(testDoc)).to.throw(TypeError);
41+
expect(() => BSON.serialize(testDoc)).to.throw(BSONTypeError);
4142

4243
// const serializedDoc = BSON.serialize(testDoc);
4344
// // prettier-ignore
@@ -52,7 +53,7 @@ describe('BSON BigInt Support', function () {
5253
const testDoc = {
5354
b: BigInt('9'.repeat(35))
5455
}; // decimal 128 can only encode 34 digits of precision
55-
expect(() => BSON.serialize(testDoc)).to.throw(TypeError);
56+
expect(() => BSON.serialize(testDoc)).to.throw(BSONTypeError);
5657
// expect(() => BSON.serialize(testDoc)).to.throw();
5758
});
5859

test/node/bson_corpus_tests.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
const Buffer = require('buffer').Buffer;
55
const BSON = require('../register-bson');
6+
const BSONError = BSON.BSONError;
67
const EJSON = BSON.EJSON;
78

89
const deserializeOptions = {
@@ -89,19 +90,19 @@ const parseErrorForRootDocument = scenario => {
8990
}
9091

9192
if (/Null/.test(parseError.description)) {
92-
expect(caughtError).to.be.instanceOf(Error);
93+
expect(caughtError).to.be.instanceOf(BSONError);
9394
expect(caughtError.message).to.match(/null bytes/);
9495
} else if (/Bad/.test(parseError.description)) {
9596
// There is a number of failing tests that start with 'Bad'
9697
// so this check is essentially making the test optional for now
97-
// This should assert that e is an Error and something about the message
98+
// This should assert that e is a BSONError and something about the message
9899
// TODO(NODE-3637): remove special logic and use expect().to.throw() and add errors to lib
99100
expect(caughtError).to.satisfy(e => {
100-
if (e instanceof Error) return true;
101+
if (e instanceof BSONError) return true;
101102
else this.skip();
102103
});
103104
} else {
104-
expect(caughtError).to.be.instanceOf(Error);
105+
expect(caughtError).to.be.instanceOf(BSONError);
105106
}
106107
});
107108
}

test/node/bson_test.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const Int32 = BSON.Int32;
1616
const Double = BSON.Double;
1717
const MinKey = BSON.MinKey;
1818
const MaxKey = BSON.MaxKey;
19+
const BSONError = BSON.BSONError;
1920
const { BinaryParser } = require('../binary_parser');
2021
const vm = require('vm');
2122
const { assertBuffersEqual } = require('./tools/utils');
@@ -33,7 +34,7 @@ var ISODate = function (string) {
3334

3435
const match = string.match(ISO_REGEX);
3536
if (!match) {
36-
throw new Error(`Invalid ISO 8601 date given: ${string}`);
37+
throw new BSONError(`Invalid ISO 8601 date given: ${string}`);
3738
}
3839

3940
var date = new Date();

test/node/ensure_buffer_test.js

+8-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
const { Buffer } = require('buffer');
55
const { ensureBuffer } = require('../register-bson');
6+
const BSON = require('../register-bson');
7+
const BSONTypeError = BSON.BSONTypeError;
68

79
describe('ensureBuffer tests', function () {
810
it('should be a function', function () {
@@ -15,7 +17,7 @@ describe('ensureBuffer tests', function () {
1517

1618
expect(function () {
1719
bufferOut = ensureBuffer(bufferIn);
18-
}).to.not.throw(Error);
20+
}).to.not.throw(BSONTypeError);
1921

2022
expect(bufferOut).to.be.an.instanceOf(Buffer);
2123
expect(bufferOut.buffer).to.equal(bufferIn.buffer);
@@ -29,7 +31,7 @@ describe('ensureBuffer tests', function () {
2931

3032
expect(function () {
3133
bufferOut = ensureBuffer(arrayIn);
32-
}).to.not.throw(Error);
34+
}).to.not.throw(BSONTypeError);
3335

3436
expect(bufferOut).to.be.an.instanceOf(Buffer);
3537
expect(bufferOut.buffer).to.equal(arrayIn.buffer);
@@ -41,7 +43,7 @@ describe('ensureBuffer tests', function () {
4143

4244
expect(function () {
4345
bufferOut = ensureBuffer(arrayBufferIn);
44-
}).to.not.throw(Error);
46+
}).to.not.throw(BSONTypeError);
4547

4648
expect(bufferOut).to.be.an.instanceOf(Buffer);
4749
expect(bufferOut.buffer).to.equal(arrayBufferIn);
@@ -57,7 +59,7 @@ describe('ensureBuffer tests', function () {
5759

5860
expect(function () {
5961
bufferOut = ensureBuffer(arrayBufferIn);
60-
}).to.not.throw(Error);
62+
}).to.not.throw(BSONTypeError);
6163

6264
expect(bufferOut).to.be.an.instanceOf(Buffer);
6365
expect(bufferOut.buffer).to.equal(arrayBufferIn);
@@ -69,7 +71,7 @@ describe('ensureBuffer tests', function () {
6971

7072
expect(function () {
7173
bufferOut = ensureBuffer(input);
72-
}).to.not.throw(Error);
74+
}).to.not.throw(BSONTypeError);
7375

7476
expect(bufferOut).to.be.an.instanceOf(Buffer);
7577
expect(bufferOut.byteLength).to.equal(3);
@@ -80,7 +82,7 @@ describe('ensureBuffer tests', function () {
8082
it(`should throw if input is ${typeof item}: ${item}`, function () {
8183
expect(function () {
8284
ensureBuffer(item);
83-
}).to.throw(TypeError);
85+
}).to.throw(BSONTypeError);
8486
});
8587
});
8688

test/node/error_test.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
const BSON = require('../register-bson');
4+
const BSONTypeError = BSON.BSONTypeError;
5+
const BSONError = BSON.BSONError;
6+
7+
describe('BSONTypeError', function () {
8+
it('should evaluate true on instanceof BSONTypeError and TypeError', function () {
9+
const bsonTypeErr = new BSONTypeError();
10+
expect(bsonTypeErr instanceof BSONTypeError).to.be.true;
11+
expect(bsonTypeErr instanceof TypeError).to.be.true;
12+
expect(bsonTypeErr).to.be.instanceOf(BSONTypeError);
13+
expect(bsonTypeErr).to.be.instanceOf(TypeError);
14+
});
15+
16+
it('should correctly set BSONTypeError name and message properties', function () {
17+
const bsonTypeErr = new BSONTypeError('This is a BSONTypeError message');
18+
expect(bsonTypeErr.name).equals('BSONTypeError');
19+
expect(bsonTypeErr.message).equals('This is a BSONTypeError message');
20+
});
21+
});
22+
23+
describe('BSONError', function () {
24+
it('should evaluate true on instanceof BSONError and Error', function () {
25+
const bsonErr = new BSONError();
26+
expect(bsonErr instanceof BSONError).to.be.true;
27+
expect(bsonErr instanceof Error).to.be.true;
28+
expect(bsonErr).to.be.instanceOf(BSONError);
29+
expect(bsonErr).to.be.instanceOf(Error);
30+
});
31+
32+
it('should correctly set BSONError name and message properties', function () {
33+
const bsonErr = new BSONError('This is a BSONError message');
34+
expect(bsonErr.name).equals('BSONError');
35+
expect(bsonErr.message).equals('This is a BSONError message');
36+
});
37+
});

test/node/object_id_tests.js

+15-15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const Buffer = require('buffer').Buffer;
44
const BSON = require('../register-bson');
5+
const BSONTypeError = BSON.BSONTypeError;
56
const util = require('util');
67
const ObjectId = BSON.ObjectId;
78

@@ -33,7 +34,7 @@ describe('ObjectId', function () {
3334

3435
for (const { input, description } of invalidInputs) {
3536
it(`should throw error if ${description} is passed in`, function () {
36-
expect(() => new ObjectId(input)).to.throw(TypeError);
37+
expect(() => new ObjectId(input)).to.throw(BSONTypeError);
3738
});
3839
}
3940

@@ -44,8 +45,7 @@ describe('ObjectId', function () {
4445
return noArgObjID.toHexString();
4546
}
4647
};
47-
48-
expect(() => new ObjectId(objectIdLike)).to.throw(TypeError);
48+
expect(() => new ObjectId(objectIdLike)).to.throw(BSONTypeError);
4949
});
5050

5151
it('should correctly create ObjectId from object with valid string id', function () {
@@ -117,15 +117,15 @@ describe('ObjectId', function () {
117117
const objectNullId = {
118118
id: null
119119
};
120-
expect(() => new ObjectId(objectNumId)).to.throw(TypeError);
121-
expect(() => new ObjectId(objectNullId)).to.throw(TypeError);
120+
expect(() => new ObjectId(objectNumId)).to.throw(BSONTypeError);
121+
expect(() => new ObjectId(objectNullId)).to.throw(BSONTypeError);
122122
});
123123

124124
it('should throw an error if object with invalid string id is passed in', function () {
125125
const objectInvalid24HexStr = {
126126
id: 'FFFFFFFFFFFFFFFFFFFFFFFG'
127127
};
128-
expect(() => new ObjectId(objectInvalid24HexStr)).to.throw(TypeError);
128+
expect(() => new ObjectId(objectInvalid24HexStr)).to.throw(BSONTypeError);
129129
});
130130

131131
it('should correctly create ObjectId from object with invalid string id and toHexString method', function () {
@@ -144,7 +144,7 @@ describe('ObjectId', function () {
144144
const objectInvalidBuffer = {
145145
id: Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13])
146146
};
147-
expect(() => new ObjectId(objectInvalidBuffer)).to.throw(TypeError);
147+
expect(() => new ObjectId(objectInvalidBuffer)).to.throw(BSONTypeError);
148148
});
149149

150150
it('should correctly create ObjectId from object with invalid Buffer id and toHexString method', function () {
@@ -185,11 +185,11 @@ describe('ObjectId', function () {
185185
});
186186

187187
it('should throw error if non-12 byte non-24 hex string passed in', function () {
188-
expect(() => new ObjectId('FFFFFFFFFFFFFFFFFFFFFFFG')).to.throw(TypeError);
189-
expect(() => new ObjectId('thisstringisdefinitelytoolong')).to.throw(TypeError);
190-
expect(() => new ObjectId('tooshort')).to.throw(TypeError);
191-
expect(() => new ObjectId('101010')).to.throw(TypeError);
192-
expect(() => new ObjectId('')).to.throw(TypeError);
188+
expect(() => new ObjectId('FFFFFFFFFFFFFFFFFFFFFFFG')).to.throw(BSONTypeError);
189+
expect(() => new ObjectId('thisstringisdefinitelytoolong')).to.throw(BSONTypeError);
190+
expect(() => new ObjectId('tooshort')).to.throw(BSONTypeError);
191+
expect(() => new ObjectId('101010')).to.throw(BSONTypeError);
192+
expect(() => new ObjectId('')).to.throw(BSONTypeError);
193193
});
194194

195195
it('should correctly create ObjectId from 24 hex string', function () {
@@ -234,7 +234,7 @@ describe('ObjectId', function () {
234234

235235
it('should throw an error if invalid Buffer passed in', function () {
236236
const a = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]);
237-
expect(() => new ObjectId(a)).to.throw(TypeError);
237+
expect(() => new ObjectId(a)).to.throw(BSONTypeError);
238238
});
239239

240240
it('should correctly allow for node.js inspect to work with ObjectId', function (done) {
@@ -260,11 +260,11 @@ describe('ObjectId', function () {
260260
const characterCodesLargerThan256 = 'abcdefŽhijkl';
261261
const length12Not12Bytes = '🐶🐶🐶🐶🐶🐶';
262262
expect(() => new ObjectId(characterCodesLargerThan256).toHexString()).to.throw(
263-
TypeError,
263+
BSONTypeError,
264264
'Argument passed in must be a string of 12 bytes'
265265
);
266266
expect(() => new ObjectId(length12Not12Bytes).id).to.throw(
267-
TypeError,
267+
BSONTypeError,
268268
'Argument passed in must be a string of 12 bytes'
269269
);
270270
});

test/node/uuid_tests.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const { Buffer } = require('buffer');
44
const { Binary, UUID } = require('../register-bson');
55
const { inspect } = require('util');
66
const { validate: uuidStringValidate, version: uuidStringVersion } = require('uuid');
7+
const BSON = require('../register-bson');
8+
const BSONTypeError = BSON.BSONTypeError;
79

810
// Test values
911
const UPPERCASE_DASH_SEPARATED_UUID_STRING = 'AAAAAAAA-AAAA-4AAA-AAAA-AAAAAAAAAAAA';
@@ -76,7 +78,7 @@ describe('UUID', () => {
7678
*/
7779
it('should throw if passed invalid 36-char uuid hex string', () => {
7880
expect(() => new UUID(LOWERCASE_DASH_SEPARATED_UUID_STRING)).to.not.throw();
79-
expect(() => new UUID('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa')).to.throw(TypeError);
81+
expect(() => new UUID('aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa')).to.throw(BSONTypeError);
8082
// Note: The version is missing here ^
8183
});
8284

@@ -85,7 +87,7 @@ describe('UUID', () => {
8587
*/
8688
it('should throw if passed unsupported argument', () => {
8789
expect(() => new UUID(LOWERCASE_DASH_SEPARATED_UUID_STRING)).to.not.throw();
88-
expect(() => new UUID({})).to.throw(TypeError);
90+
expect(() => new UUID({})).to.throw(BSONTypeError);
8991
});
9092

9193
/**

0 commit comments

Comments
 (0)