From 578389f268ace0e22c5bf0332071ccff121d4593 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 6 Jan 2023 12:25:19 -0500 Subject: [PATCH 1/9] fix(NODE-4932): remove .0 suffix from double extended json values --- src/double.ts | 6 +--- test/node/bson_corpus.spec.test.js | 48 ++++++++++++++++++++++++------ test/node/double_tests.js | 41 +++++++++++++++++++++---- 3 files changed, 75 insertions(+), 20 deletions(-) diff --git a/src/double.ts b/src/double.ts index c302eb8c..17af229f 100644 --- a/src/double.ts +++ b/src/double.ts @@ -58,11 +58,7 @@ export class Double { return { $numberDouble: '-0.0' }; } - if (Number.isInteger(this.value)) { - return { $numberDouble: `${this.value}.0` }; - } else { - return { $numberDouble: `${this.value}` }; - } + return { $numberDouble: this.value.toString() }; } /** @internal */ diff --git a/test/node/bson_corpus.spec.test.js b/test/node/bson_corpus.spec.test.js index 82d37977..9e3707ef 100644 --- a/test/node/bson_corpus.spec.test.js +++ b/test/node/bson_corpus.spec.test.js @@ -165,6 +165,11 @@ describe('BSON Corpus', function () { describe('valid-extjson', function () { for (const v of valid) { it(v.description, function () { + if (v.description === 'All BSON types') { + // TODO(NODE-3987): fix multi-type-deprecated test + this.skip(); + } + // read in test case data. if this scenario is for a deprecated // type, we want to use the "converted" BSON and EJSON, which // use the upgraded version of the deprecated type. otherwise, @@ -181,22 +186,29 @@ describe('BSON Corpus', function () { // convert inputs to native Javascript objects const nativeFromCB = bsonToNative(cB); - if (cEJ.includes('1.2345678921232E+18')) { + if (description === 'Double type') { // The following is special test logic for a "Double type" bson corpus test that uses a different // string format for the resulting double value // The test does not have a loss in precision, just different exponential output // We want to ensure that the stringified value when interpreted as a double is equal // as opposed to the string being precisely the same - if (description !== 'Double type') { - throw new Error('Unexpected test using 1.2345678921232E+18'); - } const eJSONParsedAsJSON = JSON.parse(cEJ); const eJSONParsed = EJSON.parse(cEJ, { relaxed: false }); expect(eJSONParsedAsJSON).to.have.nested.property('d.$numberDouble'); expect(eJSONParsed).to.have.nested.property('d._bsontype', 'Double'); const testInputAsFloat = Number.parseFloat(eJSONParsedAsJSON.d.$numberDouble); + const testInputAsNumber = Number(eJSONParsedAsJSON.d.$numberDouble); const ejsonOutputAsFloat = eJSONParsed.d.valueOf(); - expect(ejsonOutputAsFloat).to.equal(testInputAsFloat); + if (eJSONParsedAsJSON.d.$numberDouble === 'NaN') { + expect(ejsonOutputAsFloat).to.be.NaN; + expect(ejsonOutputAsFloat).to.be.NaN; + } else { + if (eJSONParsedAsJSON.d.$numberDouble === '-0.0') { + expect(Object.is(ejsonOutputAsFloat, -0)).to.be.true; + } + expect(ejsonOutputAsFloat).to.equal(testInputAsFloat); + expect(ejsonOutputAsFloat).to.equal(testInputAsNumber); + } } else { // round tripped EJSON should match the original expect(nativeToCEJSON(jsonToNative(cEJ))).to.equal(cEJ); @@ -220,18 +232,36 @@ describe('BSON Corpus', function () { expect(nativeToBson(jsonToNative(cEJ))).to.deep.equal(cB); } - if (cEJ.includes('1.2345678921232E+18')) { + if (description === 'Double type') { // The round tripped value should be equal in interpreted value, not in exact character match const eJSONFromBSONAsJSON = JSON.parse( EJSON.stringify(BSON.deserialize(cB), { relaxed: false }) ); const eJSONParsed = EJSON.parse(cEJ, { relaxed: false }); + const stringValueKey = Object.keys(eJSONFromBSONAsJSON.d)[0]; + const testInputAsFloat = Number.parseFloat(eJSONFromBSONAsJSON.d[stringValueKey]); + const testInputAsNumber = Number(eJSONFromBSONAsJSON.d[stringValueKey]); + // TODO(NODE-4377): EJSON transforms large doubles into longs - expect(eJSONFromBSONAsJSON).to.have.nested.property('d.$numberLong'); + expect(eJSONFromBSONAsJSON).to.have.nested.property( + Number.isFinite(testInputAsFloat) && Number.isInteger(testInputAsFloat) + ? testInputAsFloat <= 0x7fffffff && testInputAsFloat >= -0x80000000 + ? 'd.$numberInt' + : 'd.$numberLong' + : 'd.$numberDouble' + ); expect(eJSONParsed).to.have.nested.property('d._bsontype', 'Double'); - const testInputAsFloat = Number.parseFloat(eJSONFromBSONAsJSON.d.$numberLong); const ejsonOutputAsFloat = eJSONParsed.d.valueOf(); - expect(ejsonOutputAsFloat).to.equal(testInputAsFloat); + if (eJSONFromBSONAsJSON.d.$numberDouble === 'NaN') { + expect(ejsonOutputAsFloat).to.be.NaN; + expect(ejsonOutputAsFloat).to.be.NaN; + } else { + if (eJSONFromBSONAsJSON.d.$numberDouble === '-0.0') { + expect(Object.is(ejsonOutputAsFloat, -0)).to.be.true; + } + expect(ejsonOutputAsFloat).to.equal(testInputAsFloat); + expect(ejsonOutputAsFloat).to.equal(testInputAsNumber); + } } else { // the reverse direction, BSON -> native -> EJSON, should match canonical EJSON. expect(nativeToCEJSON(nativeFromCB)).to.equal(cEJ); diff --git a/test/node/double_tests.js b/test/node/double_tests.js index 73dfa7d7..27b9a0aa 100644 --- a/test/node/double_tests.js +++ b/test/node/double_tests.js @@ -38,18 +38,44 @@ describe('BSON Double Precision', function () { describe('.toExtendedJSON()', () => { const tests = [ - { input: new Double(0), output: { $numberDouble: '0.0' } }, + { input: new Double(0), output: { $numberDouble: '0' } }, { input: new Double(-0), output: { $numberDouble: '-0.0' } }, - { input: new Double(3), output: { $numberDouble: '3.0' } }, - { input: new Double(-3), output: { $numberDouble: '-3.0' } }, + { input: new Double(3), output: { $numberDouble: '3' } }, + { input: new Double(-3), output: { $numberDouble: '-3' } }, { input: new Double(3.4), output: { $numberDouble: '3.4' } }, { input: new Double(Number.EPSILON), output: { $numberDouble: '2.220446049250313e-16' } }, - { input: new Double(12345e7), output: { $numberDouble: '123450000000.0' } }, + { input: new Double(12345e7), output: { $numberDouble: '123450000000' } }, { input: new Double(12345e-1), output: { $numberDouble: '1234.5' } }, { input: new Double(-12345e-1), output: { $numberDouble: '-1234.5' } }, { input: new Double(Infinity), output: { $numberDouble: 'Infinity' } }, { input: new Double(-Infinity), output: { $numberDouble: '-Infinity' } }, - { input: new Double(NaN), output: { $numberDouble: 'NaN' } } + { input: new Double(NaN), output: { $numberDouble: 'NaN' } }, + { + input: new Double(Number.MAX_VALUE), + output: { $numberDouble: '1.7976931348623157e+308' } + }, + { input: new Double(Number.MIN_VALUE), output: { $numberDouble: '5e-324' } }, + { + input: new Double(-Number.MAX_VALUE), + output: { $numberDouble: '-1.7976931348623157e+308' } + }, + { input: new Double(-Number.MIN_VALUE), output: { $numberDouble: '-5e-324' } }, + // Reference: https://docs.oracle.com/cd/E19957-01/806-3568/ncg_math.html + { + // min positive normal number + input: new Double('2.2250738585072014e-308'), + output: { $numberDouble: '2.2250738585072014e-308' } + }, + { + // max subnormal number (NOTE: JS does not output same input string, but numeric values are equal) + input: new Double('2.225073858507201e-308'), + output: { $numberDouble: '2.225073858507201e-308' } + }, + { + // min positive subnormal number (NOTE: JS does not output same input string, but numeric values are equal) + input: new Double('4.9406564584124654e-324'), + output: { $numberDouble: '5e-324' } + } ]; for (const test of tests) { @@ -57,7 +83,10 @@ describe('BSON Double Precision', function () { const output = test.output; const title = `returns ${inspect(output)} when Double is ${input}`; it(title, () => { - expect(output).to.deep.equal(input.toExtendedJSON({ relaxed: false })); + expect(input.toExtendedJSON({ relaxed: false })).to.deep.equal(output); + if(!Number.isNaN(input.value)) { + expect(+input.toExtendedJSON({ relaxed: false }).$numberDouble).to.equal(input.value) + } }); } }); From 318a53b916ddf94d1017212a6b25f7dedb1159ed Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 6 Jan 2023 12:50:10 -0500 Subject: [PATCH 2/9] test byte translation --- test/node/double_tests.js | 64 +++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/test/node/double_tests.js b/test/node/double_tests.js index 27b9a0aa..67b4dc96 100644 --- a/test/node/double_tests.js +++ b/test/node/double_tests.js @@ -38,42 +38,42 @@ describe('BSON Double Precision', function () { describe('.toExtendedJSON()', () => { const tests = [ - { input: new Double(0), output: { $numberDouble: '0' } }, - { input: new Double(-0), output: { $numberDouble: '-0.0' } }, - { input: new Double(3), output: { $numberDouble: '3' } }, - { input: new Double(-3), output: { $numberDouble: '-3' } }, - { input: new Double(3.4), output: { $numberDouble: '3.4' } }, - { input: new Double(Number.EPSILON), output: { $numberDouble: '2.220446049250313e-16' } }, - { input: new Double(12345e7), output: { $numberDouble: '123450000000' } }, - { input: new Double(12345e-1), output: { $numberDouble: '1234.5' } }, - { input: new Double(-12345e-1), output: { $numberDouble: '-1234.5' } }, - { input: new Double(Infinity), output: { $numberDouble: 'Infinity' } }, - { input: new Double(-Infinity), output: { $numberDouble: '-Infinity' } }, - { input: new Double(NaN), output: { $numberDouble: 'NaN' } }, + { input: 0, output: { $numberDouble: '0' } }, + { input: -0, output: { $numberDouble: '-0.0' } }, + { input: 3, output: { $numberDouble: '3' } }, + { input: -3, output: { $numberDouble: '-3' } }, + { input: 3.4, output: { $numberDouble: '3.4' } }, + { input: Number.EPSILON, output: { $numberDouble: '2.220446049250313e-16' } }, + { input: 12345e7, output: { $numberDouble: '123450000000' } }, + { input: 12345e-1, output: { $numberDouble: '1234.5' } }, + { input: -12345e-1, output: { $numberDouble: '-1234.5' } }, + { input: Infinity, output: { $numberDouble: 'Infinity' } }, + { input: -Infinity, output: { $numberDouble: '-Infinity' } }, + { input: NaN, output: { $numberDouble: 'NaN' } }, { - input: new Double(Number.MAX_VALUE), + input: Number.MAX_VALUE, output: { $numberDouble: '1.7976931348623157e+308' } }, - { input: new Double(Number.MIN_VALUE), output: { $numberDouble: '5e-324' } }, + { input: Number.MIN_VALUE, output: { $numberDouble: '5e-324' } }, { - input: new Double(-Number.MAX_VALUE), + input: -Number.MAX_VALUE, output: { $numberDouble: '-1.7976931348623157e+308' } }, - { input: new Double(-Number.MIN_VALUE), output: { $numberDouble: '-5e-324' } }, + { input: -Number.MIN_VALUE, output: { $numberDouble: '-5e-324' } }, // Reference: https://docs.oracle.com/cd/E19957-01/806-3568/ncg_math.html { // min positive normal number - input: new Double('2.2250738585072014e-308'), + input: '2.2250738585072014e-308', output: { $numberDouble: '2.2250738585072014e-308' } }, { // max subnormal number (NOTE: JS does not output same input string, but numeric values are equal) - input: new Double('2.225073858507201e-308'), + input: '2.225073858507201e-308', output: { $numberDouble: '2.225073858507201e-308' } }, { // min positive subnormal number (NOTE: JS does not output same input string, but numeric values are equal) - input: new Double('4.9406564584124654e-324'), + input: '4.9406564584124654e-324', output: { $numberDouble: '5e-324' } } ]; @@ -83,11 +83,31 @@ describe('BSON Double Precision', function () { const output = test.output; const title = `returns ${inspect(output)} when Double is ${input}`; it(title, () => { - expect(input.toExtendedJSON({ relaxed: false })).to.deep.equal(output); - if(!Number.isNaN(input.value)) { - expect(+input.toExtendedJSON({ relaxed: false }).$numberDouble).to.equal(input.value) + const inputAsDouble = new Double(input); + expect(inputAsDouble.toExtendedJSON({ relaxed: false })).to.deep.equal(output); + if (!Number.isNaN(inputAsDouble.value)) { + expect(Number(inputAsDouble.toExtendedJSON({ relaxed: false }).$numberDouble)).to.equal( + inputAsDouble.value + ); } }); + + it(`input ${typeof input}: ${input} creates the same bytes after stringification`, () => { + const ejsonDoubleString = new Double(input).toExtendedJSON().$numberDouble; + const bytesFromInput = (() => { + const b = Buffer.alloc(8); + b.writeDoubleBE(Number(input)); + return b.toString('hex'); + })(); + + const bytesFromOutput = (() => { + const b = Buffer.alloc(8); + b.writeDoubleBE(Number(ejsonDoubleString)); + return b.toString('hex'); + })(); + + expect(bytesFromOutput).to.equal(bytesFromInput); + }); } }); }); From 114ca90033aa507e9b75c9b22043e65ae27284a9 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 6 Jan 2023 13:08:58 -0500 Subject: [PATCH 3/9] Add minus zero as a string --- test/node/double_tests.js | 1 + 1 file changed, 1 insertion(+) diff --git a/test/node/double_tests.js b/test/node/double_tests.js index 67b4dc96..f9e91fed 100644 --- a/test/node/double_tests.js +++ b/test/node/double_tests.js @@ -40,6 +40,7 @@ describe('BSON Double Precision', function () { const tests = [ { input: 0, output: { $numberDouble: '0' } }, { input: -0, output: { $numberDouble: '-0.0' } }, + { input: '-0.0', output: { $numberDouble: '-0.0' } }, { input: 3, output: { $numberDouble: '3' } }, { input: -3, output: { $numberDouble: '-3' } }, { input: 3.4, output: { $numberDouble: '3.4' } }, From 40757a0ca002dba4ce08628cdb61d5074d9d522e Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 6 Jan 2023 13:39:50 -0500 Subject: [PATCH 4/9] fix: ejson type info for -0 --- test/node/bson_corpus.spec.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/node/bson_corpus.spec.test.js b/test/node/bson_corpus.spec.test.js index 9e3707ef..8237359c 100644 --- a/test/node/bson_corpus.spec.test.js +++ b/test/node/bson_corpus.spec.test.js @@ -245,7 +245,9 @@ describe('BSON Corpus', function () { // TODO(NODE-4377): EJSON transforms large doubles into longs expect(eJSONFromBSONAsJSON).to.have.nested.property( Number.isFinite(testInputAsFloat) && Number.isInteger(testInputAsFloat) - ? testInputAsFloat <= 0x7fffffff && testInputAsFloat >= -0x80000000 + ? testInputAsFloat <= 0x7fffffff && + testInputAsFloat >= -0x80000000 && + !Object.is(testInputAsFloat, -0) ? 'd.$numberInt' : 'd.$numberLong' : 'd.$numberDouble' From 8734edd9b80796ab306012ca4c8f59949da80597 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Fri, 6 Jan 2023 16:54:36 -0500 Subject: [PATCH 5/9] use toFixed again --- src/double.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/double.ts b/src/double.ts index 17af229f..96e5fcc6 100644 --- a/src/double.ts +++ b/src/double.ts @@ -55,10 +55,12 @@ export class Double { if (Object.is(Math.sign(this.value), -0)) { // NOTE: JavaScript has +0 and -0, apparently to model limit calculations. If a user // explicitly provided `-0` then we need to ensure the sign makes it into the output - return { $numberDouble: '-0.0' }; + return { $numberDouble: `-${this.value.toFixed(1)}` }; } - return { $numberDouble: this.value.toString() }; + return { + $numberDouble: Number.isInteger(this.value) ? this.value.toFixed(1) : this.value.toString() + }; } /** @internal */ From 4c93bf3126d13e32f03220c57ff93982727c050b Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 9 Jan 2023 10:31:40 -0500 Subject: [PATCH 6/9] test: for toFixed precision --- test/node/double_tests.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/node/double_tests.js b/test/node/double_tests.js index f9e91fed..b360e47d 100644 --- a/test/node/double_tests.js +++ b/test/node/double_tests.js @@ -38,14 +38,14 @@ describe('BSON Double Precision', function () { describe('.toExtendedJSON()', () => { const tests = [ - { input: 0, output: { $numberDouble: '0' } }, + { input: 0, output: { $numberDouble: '0.0' } }, { input: -0, output: { $numberDouble: '-0.0' } }, { input: '-0.0', output: { $numberDouble: '-0.0' } }, - { input: 3, output: { $numberDouble: '3' } }, - { input: -3, output: { $numberDouble: '-3' } }, + { input: 3, output: { $numberDouble: '3.0' } }, + { input: -3, output: { $numberDouble: '-3.0' } }, { input: 3.4, output: { $numberDouble: '3.4' } }, { input: Number.EPSILON, output: { $numberDouble: '2.220446049250313e-16' } }, - { input: 12345e7, output: { $numberDouble: '123450000000' } }, + { input: 12345e7, output: { $numberDouble: '123450000000.0' } }, { input: 12345e-1, output: { $numberDouble: '1234.5' } }, { input: -12345e-1, output: { $numberDouble: '-1234.5' } }, { input: Infinity, output: { $numberDouble: 'Infinity' } }, @@ -76,6 +76,12 @@ describe('BSON Double Precision', function () { // min positive subnormal number (NOTE: JS does not output same input string, but numeric values are equal) input: '4.9406564584124654e-324', output: { $numberDouble: '5e-324' } + }, + { + // https://262.ecma-international.org/13.0/#sec-number.prototype.tofixed + // Note: calling toString on this integer returns 1000000000000000100, so toFixed is more precise + input: '1000000000000000128', + output: { $numberDouble: '1000000000000000128.0' } } ]; From eaaa2142115d488ba9691cb3dd03b19030ff370c Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 9 Jan 2023 16:47:26 -0500 Subject: [PATCH 7/9] test: titles and comments --- test/node/bson_corpus.spec.test.js | 2 - test/node/double_tests.js | 114 ++++++++++++++++++++++++----- 2 files changed, 95 insertions(+), 21 deletions(-) diff --git a/test/node/bson_corpus.spec.test.js b/test/node/bson_corpus.spec.test.js index 8237359c..7622450a 100644 --- a/test/node/bson_corpus.spec.test.js +++ b/test/node/bson_corpus.spec.test.js @@ -201,7 +201,6 @@ describe('BSON Corpus', function () { const ejsonOutputAsFloat = eJSONParsed.d.valueOf(); if (eJSONParsedAsJSON.d.$numberDouble === 'NaN') { expect(ejsonOutputAsFloat).to.be.NaN; - expect(ejsonOutputAsFloat).to.be.NaN; } else { if (eJSONParsedAsJSON.d.$numberDouble === '-0.0') { expect(Object.is(ejsonOutputAsFloat, -0)).to.be.true; @@ -256,7 +255,6 @@ describe('BSON Corpus', function () { const ejsonOutputAsFloat = eJSONParsed.d.valueOf(); if (eJSONFromBSONAsJSON.d.$numberDouble === 'NaN') { expect(ejsonOutputAsFloat).to.be.NaN; - expect(ejsonOutputAsFloat).to.be.NaN; } else { if (eJSONFromBSONAsJSON.d.$numberDouble === '-0.0') { expect(Object.is(ejsonOutputAsFloat, -0)).to.be.true; diff --git a/test/node/double_tests.js b/test/node/double_tests.js index b360e47d..8df13171 100644 --- a/test/node/double_tests.js +++ b/test/node/double_tests.js @@ -38,48 +38,121 @@ describe('BSON Double Precision', function () { describe('.toExtendedJSON()', () => { const tests = [ - { input: 0, output: { $numberDouble: '0.0' } }, - { input: -0, output: { $numberDouble: '-0.0' } }, - { input: '-0.0', output: { $numberDouble: '-0.0' } }, - { input: 3, output: { $numberDouble: '3.0' } }, - { input: -3, output: { $numberDouble: '-3.0' } }, - { input: 3.4, output: { $numberDouble: '3.4' } }, - { input: Number.EPSILON, output: { $numberDouble: '2.220446049250313e-16' } }, - { input: 12345e7, output: { $numberDouble: '123450000000.0' } }, - { input: 12345e-1, output: { $numberDouble: '1234.5' } }, - { input: -12345e-1, output: { $numberDouble: '-1234.5' } }, - { input: Infinity, output: { $numberDouble: 'Infinity' } }, - { input: -Infinity, output: { $numberDouble: '-Infinity' } }, - { input: NaN, output: { $numberDouble: 'NaN' } }, { + title: 'returns "0.0" when input is a number 0', + input: 0, + output: { $numberDouble: '0.0' } + }, + { + title: 'returns "-0.0" when input is a number -0', + input: -0, + output: { $numberDouble: '-0.0' } + }, + { + title: 'returns "0.0" when input is a string "-0.0"', + input: '-0.0', + output: { $numberDouble: '-0.0' } + }, + { + title: 'returns "3.0" when input is a number 3', + input: 3, + output: { $numberDouble: '3.0' } + }, + { + title: 'returns "-3.0" when input is a number -3', + input: -3, + output: { $numberDouble: '-3.0' } + }, + { + title: 'returns "3.4" when input is a number 3.4', + input: 3.4, + output: { $numberDouble: '3.4' } + }, + { + title: 'returns "2.220446049250313e-16" when input is Number.EPSILON', + input: Number.EPSILON, + output: { $numberDouble: '2.220446049250313e-16' } + }, + { + title: 'returns "123450000000.0" when input is a number 12345e7', + input: 12345e7, + output: { $numberDouble: '123450000000.0' } + }, + { + title: 'returns "1234.5" when input is a number 12345e-1', + input: 12345e-1, + output: { $numberDouble: '1234.5' } + }, + { + title: 'returns "-1234.5" when input is a number -12345e-1', + input: -12345e-1, + output: { $numberDouble: '-1234.5' } + }, + { + title: 'returns "Infinity" when input is a number Infinity', + input: Infinity, + output: { $numberDouble: 'Infinity' } + }, + { + title: 'returns "-Infinity" when input is a number -Infinity', + input: -Infinity, + output: { $numberDouble: '-Infinity' } + }, + { + title: 'returns "NaN" when input is a number NaN', + input: NaN, + output: { $numberDouble: 'NaN' } + }, + { + title: 'returns "1.7976931348623157e+308" when input is a number Number.MAX_VALUE', input: Number.MAX_VALUE, output: { $numberDouble: '1.7976931348623157e+308' } }, - { input: Number.MIN_VALUE, output: { $numberDouble: '5e-324' } }, { + title: 'returns "5e-324" when input is a number Number.MIN_VALUE', + input: Number.MIN_VALUE, + output: { $numberDouble: '5e-324' } + }, + { + title: 'returns "-1.7976931348623157e+308" when input is a number -Number.MAX_VALUE', input: -Number.MAX_VALUE, output: { $numberDouble: '-1.7976931348623157e+308' } }, - { input: -Number.MIN_VALUE, output: { $numberDouble: '-5e-324' } }, - // Reference: https://docs.oracle.com/cd/E19957-01/806-3568/ncg_math.html { + title: 'returns "-5e-324" when input is a number -Number.MIN_VALUE', + input: -Number.MIN_VALUE, + output: { $numberDouble: '-5e-324' } + }, + { + // Reference: https://docs.oracle.com/cd/E19957-01/806-3568/ncg_math.html // min positive normal number + title: + 'returns "2.2250738585072014e-308" when input is a number the minimum positive normal value', input: '2.2250738585072014e-308', output: { $numberDouble: '2.2250738585072014e-308' } }, { - // max subnormal number (NOTE: JS does not output same input string, but numeric values are equal) + // max subnormal number + title: + 'returns "2.225073858507201e-308" when input is a number the maximum positive subnormal value', input: '2.225073858507201e-308', output: { $numberDouble: '2.225073858507201e-308' } }, { // min positive subnormal number (NOTE: JS does not output same input string, but numeric values are equal) + title: 'returns "5e-324" when input is a number the minimum positive subnormal value', input: '4.9406564584124654e-324', output: { $numberDouble: '5e-324' } }, { // https://262.ecma-international.org/13.0/#sec-number.prototype.tofixed // Note: calling toString on this integer returns 1000000000000000100, so toFixed is more precise + // This test asserts we do not change _current_ behavior, however preserving this value is not + // something that is possible in BSON, if a future version of this library were to emit + // "1000000000000000100.0" instead, it would not be incorrect from a BSON/MongoDB/Double precision perspective, + // it would just constrain the string output to what is possible with 8 bytes of floating point precision + title: + 'returns "1000000000000000128.0" when input is an int-like number beyond 8-byte double precision', input: '1000000000000000128', output: { $numberDouble: '1000000000000000128.0' } } @@ -88,7 +161,7 @@ describe('BSON Double Precision', function () { for (const test of tests) { const input = test.input; const output = test.output; - const title = `returns ${inspect(output)} when Double is ${input}`; + const title = test.title; it(title, () => { const inputAsDouble = new Double(input); expect(inputAsDouble.toExtendedJSON({ relaxed: false })).to.deep.equal(output); @@ -99,7 +172,10 @@ describe('BSON Double Precision', function () { } }); - it(`input ${typeof input}: ${input} creates the same bytes after stringification`, () => { + it(`preserves the byte wise value of ${input} (${typeof input}) after stringification`, () => { + // Asserts the same bytes can be reconstructed from the generated string, + // sometimes the string changes "4.9406564584124654e-324" -> "5e-324" + // but both represent the same ieee754 double bytes const ejsonDoubleString = new Double(input).toExtendedJSON().$numberDouble; const bytesFromInput = (() => { const b = Buffer.alloc(8); From c3c63463d908ab5a102ce94754007da784d807b1 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 9 Jan 2023 16:57:52 -0500 Subject: [PATCH 8/9] test: all BSON types test --- test/node/bson_corpus.spec.test.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/node/bson_corpus.spec.test.js b/test/node/bson_corpus.spec.test.js index 7622450a..9b6d4634 100644 --- a/test/node/bson_corpus.spec.test.js +++ b/test/node/bson_corpus.spec.test.js @@ -165,11 +165,6 @@ describe('BSON Corpus', function () { describe('valid-extjson', function () { for (const v of valid) { it(v.description, function () { - if (v.description === 'All BSON types') { - // TODO(NODE-3987): fix multi-type-deprecated test - this.skip(); - } - // read in test case data. if this scenario is for a deprecated // type, we want to use the "converted" BSON and EJSON, which // use the upgraded version of the deprecated type. otherwise, From 9fabfe52b6141bb4319e94e14d51244bbb012d71 Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 9 Jan 2023 17:44:51 -0500 Subject: [PATCH 9/9] fix: lint --- test/node/double_tests.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/node/double_tests.js b/test/node/double_tests.js index 8df13171..6adf3d16 100644 --- a/test/node/double_tests.js +++ b/test/node/double_tests.js @@ -2,7 +2,6 @@ const BSON = require('../register-bson'); const Double = BSON.Double; -const inspect = require('util').inspect; describe('BSON Double Precision', function () { context('class Double', function () {