Skip to content

Commit

Permalink
fix(NODE-5056): EJSON.parse date handling when useBigInt64=true (#562)
Browse files Browse the repository at this point in the history
  • Loading branch information
W-A-James committed Feb 16, 2023
1 parent 6ef1000 commit d5088af
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 6 deletions.
2 changes: 1 addition & 1 deletion src/bson.ts
Expand Up @@ -50,7 +50,7 @@ export {
Decimal128
};
export { BSONValue } from './bson_value';
export { BSONError, BSONVersionError } from './error';
export { BSONError, BSONVersionError, BSONRuntimeError } from './error';
export { BSONType } from './constants';
export { EJSON } from './extended_json';

Expand Down
29 changes: 27 additions & 2 deletions src/error.ts
Expand Up @@ -2,7 +2,11 @@ import { BSON_MAJOR_VERSION } from './constants';

/**
* @public
* `BSONError` objects are thrown when runtime errors occur.
* @category Error
*
* `BSONError` objects are thrown when BSON ecounters an error.
*
* This is the parent class for all the other errors thrown by this library.
*/
export class BSONError extends Error {
/**
Expand Down Expand Up @@ -46,7 +50,10 @@ export class BSONError extends Error {
}
}

/** @public */
/**
* @public
* @category Error
*/
export class BSONVersionError extends BSONError {
get name(): 'BSONVersionError' {
return 'BSONVersionError';
Expand All @@ -58,3 +65,21 @@ export class BSONVersionError extends BSONError {
);
}
}

/**
* @public
* @category Error
*
* An error generated when BSON functions encounter an unexpected input
* or reaches an unexpected/invalid internal state
*
*/
export class BSONRuntimeError extends BSONError {
get name(): 'BSONRuntimeError' {
return 'BSONRuntimeError';
}

constructor(message: string) {
super(message);
}
}
6 changes: 5 additions & 1 deletion src/extended_json.ts
Expand Up @@ -11,7 +11,7 @@ import {
import { DBRef, isDBRefLike } from './db_ref';
import { Decimal128 } from './decimal128';
import { Double } from './double';
import { BSONError, BSONVersionError } from './error';
import { BSONError, BSONRuntimeError, BSONVersionError } from './error';
import { Int32 } from './int_32';
import { Long } from './long';
import { MaxKey } from './max_key';
Expand Down Expand Up @@ -125,10 +125,14 @@ function deserializeValue(value: any, options: EJSONOptions = {}) {
if (options.legacy) {
if (typeof d === 'number') date.setTime(d);
else if (typeof d === 'string') date.setTime(Date.parse(d));
else if (typeof d === 'bigint') date.setTime(Number(d));
else throw new BSONRuntimeError(`Unrecognized type for EJSON date: ${typeof d}`);
} else {
if (typeof d === 'string') date.setTime(Date.parse(d));
else if (Long.isLong(d)) date.setTime(d.toNumber());
else if (typeof d === 'number' && options.relaxed) date.setTime(d);
else if (typeof d === 'bigint') date.setTime(Number(d));
else throw new BSONRuntimeError(`Unrecognized type for EJSON date: ${typeof d}`);
}
return date;
}
Expand Down
12 changes: 11 additions & 1 deletion test/node/error.test.ts
@@ -1,7 +1,7 @@
import { expect } from 'chai';
import { loadESModuleBSON } from '../load_bson';

import { __isWeb__, BSONError, BSONVersionError } from '../register-bson';
import { __isWeb__, BSONError, BSONVersionError, BSONRuntimeError } from '../register-bson';

const instanceOfChecksWork = !__isWeb__;

Expand Down Expand Up @@ -92,4 +92,14 @@ describe('BSONError', function () {
expect(new BSONVersionError()).to.have.property('name', 'BSONVersionError');
});
});

describe('class BSONRuntimeError', function () {
it('is a BSONError instance', function () {
expect(BSONError.isBSONError(new BSONRuntimeError('Oopsie'))).to.be.true;
});

it('has a name property equal to "BSONRuntimeError"', function () {
expect(new BSONRuntimeError('Woops!')).to.have.property('name', 'BSONRuntimeError');
});
});
});
1 change: 1 addition & 0 deletions test/node/exports.test.ts
Expand Up @@ -26,6 +26,7 @@ const EXPECTED_EXPORTS = [
'BSONRegExp',
'Decimal128',
'BSONError',
'BSONRuntimeError',
'setInternalBufferSize',
'serialize',
'serializeWithBufferAndIndex',
Expand Down
25 changes: 24 additions & 1 deletion test/node/extended_json.test.ts
Expand Up @@ -2,7 +2,7 @@ import * as BSON from '../register-bson';
const EJSON = BSON.EJSON;
import * as vm from 'node:vm';
import { expect } from 'chai';
import { BSONVersionError } from '../../src';
import { BSONVersionError, BSONRuntimeError } from '../../src';

// BSON types
const Binary = BSON.Binary;
Expand Down Expand Up @@ -440,6 +440,29 @@ describe('Extended JSON', function () {
expect(bson).to.deep.equal(doc);
});
});

context('when using useBigInt64=true', function () {
it('parses $date.$numberLong with millis since epoch', function () {
if (BSON.__noBigInt__) {
this.skip();
}
const date = new Date(1676315495987);
const doc = { field: date };
const stringified = EJSON.stringify(doc, { relaxed: false });
const parsedDoc = EJSON.parse(stringified, { useBigInt64: true, relaxed: false });
expect(parsedDoc).to.deep.equal(doc);
});
});

context('when deserializing object with invalid $date key', function () {
it('throws a BSONRuntimeError', function () {
const doc = { field: { $date: new ArrayBuffer(10) } };
const s = EJSON.stringify(doc, { relaxed: false });
expect(() => {
EJSON.parse(s, { relaxed: false });
}).to.throw(BSONRuntimeError, /Unrecognized type/i);
});
});
});

context('when deserializing regex', function () {
Expand Down

0 comments on commit d5088af

Please sign in to comment.