From fc62331e78e2822a87b49ef3b3f29823c3249016 Mon Sep 17 00:00:00 2001 From: Aditi Khare Date: Thu, 18 Apr 2024 14:33:18 -0400 Subject: [PATCH 1/6] fix(NODE-6102): Double.fromString prohibiting '+' charaacter --- src/double.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/double.ts b/src/double.ts index 8f7d31b8a..9da8f92c5 100644 --- a/src/double.ts +++ b/src/double.ts @@ -48,18 +48,24 @@ export class Double extends BSONValue { */ static fromString(value: string): Double { const coercedValue = Number(value); - const nonFiniteValidInputs = ['Infinity', '-Infinity', 'NaN']; + + if (value === 'NaN') { + return new Double(NaN); + } else if (value === 'Infinity') { + return new Double(Infinity); + } else if (value === '-Infinity') { + return new Double(-Infinity); + } if (value.trim() !== value) { throw new BSONError(`Input: '${value}' contains whitespace`); } else if (value === '') { throw new BSONError(`Input is an empty string`); - } else if (/[^-0-9.]/.test(value) && !nonFiniteValidInputs.includes(value)) { + } else if (/[^+-0-9.]/.test(value)) { throw new BSONError(`Input: '${value}' contains invalid characters`); - } else if ( - (!Number.isFinite(coercedValue) && !nonFiniteValidInputs.includes(value)) || - (Number.isNaN(coercedValue) && value !== 'NaN') - ) { + } else if (Number.isNaN(coercedValue)) { + throw new BSONError(`Input: ${value} is not representable as a Double`); // generic case + } else if (!Number.isFinite(coercedValue)) { throw new BSONError(`Input: ${value} is not representable as a Double`); // generic case } return new Double(coercedValue); From d8518112bc45a765e1451243357542a4bff24c90 Mon Sep 17 00:00:00 2001 From: Aditi Khare Date: Thu, 18 Apr 2024 14:42:05 -0400 Subject: [PATCH 2/6] fix(NODE-6102): Double.fromString prohibiting '+' charaacter --- src/double.ts | 6 +++--- test/node/double.test.ts | 14 +++++++++----- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/double.ts b/src/double.ts index 9da8f92c5..ffba633c9 100644 --- a/src/double.ts +++ b/src/double.ts @@ -59,14 +59,14 @@ export class Double extends BSONValue { if (value.trim() !== value) { throw new BSONError(`Input: '${value}' contains whitespace`); + } else if (!Number.isFinite(coercedValue) && !Number.isNaN(coercedValue)) { + throw new BSONError(`Input: ${value} is not representable as a Double`); // generic case } else if (value === '') { throw new BSONError(`Input is an empty string`); - } else if (/[^+-0-9.]/.test(value)) { + } else if (/[^-0-9.+]/.test(value)) { throw new BSONError(`Input: '${value}' contains invalid characters`); } else if (Number.isNaN(coercedValue)) { throw new BSONError(`Input: ${value} is not representable as a Double`); // generic case - } else if (!Number.isFinite(coercedValue)) { - throw new BSONError(`Input: ${value} is not representable as a Double`); // generic case } return new Double(coercedValue); } diff --git a/test/node/double.test.ts b/test/node/double.test.ts index 4fa6e03d9..d17189634 100644 --- a/test/node/double.test.ts +++ b/test/node/double.test.ts @@ -226,7 +226,7 @@ describe('BSON Double Precision', function () { }); }); - describe('fromString', () => { + describe.only('fromString', () => { const acceptedInputs = [ ['zero', '0', 0], ['non-leading zeros', '45000000', 45000000], @@ -239,7 +239,10 @@ describe('BSON Double Precision', function () { ['-Infinity', '-Infinity', -Infinity], ['NaN', 'NaN', NaN], ['basic floating point', '-4.556000', -4.556], - ['negative zero', '-0', -0] + ['negative zero', '-0', -0], + ['explicit plus zero', '+0', 0], + ['explicit plus decimal', '+78.23456', 78.23456], + ['explicit plus leading zeros', '+00000000000001.11', 1.11] ]; const errorInputs = [ @@ -250,10 +253,11 @@ describe('BSON Double Precision', function () { ['hex', '0x1', 'contains invalid characters'], ['empty string', '', 'is an empty string'], ['leading and trailing whitespace', ' 89 ', 'contains whitespace'], - ['fake positive infinity', '2e308', 'contains invalid characters'], - ['fake negative infinity', '-2e308', 'contains invalid characters'], + ['fake positive infinity', '2e308', 'is not representable as a Double'], + ['fake negative infinity', '-2e308', 'is not representable as a Double'], ['fraction', '3/4', 'contains invalid characters'], - ['foo', 'foo', 'contains invalid characters'] + ['foo', 'foo', 'contains invalid characters'], + ['malformed number without invalid characters', '9.0.+76', 'is not representable as a Double'] ]; for (const [testName, value, expectedDouble] of acceptedInputs) { From 130135b5b1a288fbdc7268b7dec99e3b690b1b75 Mon Sep 17 00:00:00 2001 From: Aditi Khare Date: Thu, 18 Apr 2024 14:46:44 -0400 Subject: [PATCH 3/6] lint fix --- test/node/double.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/test/node/double.test.ts b/test/node/double.test.ts index d17189634..ccc8ad79a 100644 --- a/test/node/double.test.ts +++ b/test/node/double.test.ts @@ -226,7 +226,7 @@ describe('BSON Double Precision', function () { }); }); - describe.only('fromString', () => { + describe('fromString', () => { const acceptedInputs = [ ['zero', '0', 0], ['non-leading zeros', '45000000', 45000000], @@ -257,7 +257,11 @@ describe('BSON Double Precision', function () { ['fake negative infinity', '-2e308', 'is not representable as a Double'], ['fraction', '3/4', 'contains invalid characters'], ['foo', 'foo', 'contains invalid characters'], - ['malformed number without invalid characters', '9.0.+76', 'is not representable as a Double'] + [ + 'malformed number without invalid characters', + '9.0.+76', + 'is not representable as a Double' + ] ]; for (const [testName, value, expectedDouble] of acceptedInputs) { From 81998d4ffa5972f501348d4092c7cfe74e0a9ab2 Mon Sep 17 00:00:00 2001 From: Aditi Khare Date: Thu, 18 Apr 2024 15:24:48 -0400 Subject: [PATCH 4/6] allow exponential notation and clear up code --- src/double.ts | 29 +++++++++++++---------------- test/node/double.test.ts | 19 +++++++++++-------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/double.ts b/src/double.ts index ffba633c9..c53206071 100644 --- a/src/double.ts +++ b/src/double.ts @@ -38,35 +38,32 @@ export class Double extends BSONValue { * * This method will throw a BSONError on any string input that is not representable as a IEEE-754 64-bit double. * Notably, this method will also throw on the following string formats: - * - Strings in non-decimal formats (exponent notation, binary, hex, or octal digits) + * - Strings in non-decimal and non-exponential formats (binary, hex, or octal digits) * - Strings with characters other than numeric, floating point, or leading sign characters (Note: 'Infinity', '-Infinity', and 'NaN' input strings are still allowed) * - Strings with leading and/or trailing whitespace * * Strings with leading zeros, however, are also allowed * - * @param value - the string we want to represent as an double. + * @param value - the string we want to represent as a double. */ static fromString(value: string): Double { const coercedValue = Number(value); - if (value === 'NaN') { - return new Double(NaN); - } else if (value === 'Infinity') { - return new Double(Infinity); - } else if (value === '-Infinity') { - return new Double(-Infinity); - } + if (value === 'NaN') return new Double(NaN); + if (value === 'Infinity') return new Double(Infinity); + if (value === '-Infinity') return new Double(-Infinity); + if (!Number.isFinite(coercedValue)) { + throw new BSONError(`Input: ${value} is not representable as a Double`); + } if (value.trim() !== value) { throw new BSONError(`Input: '${value}' contains whitespace`); - } else if (!Number.isFinite(coercedValue) && !Number.isNaN(coercedValue)) { - throw new BSONError(`Input: ${value} is not representable as a Double`); // generic case - } else if (value === '') { + } + if (value === '') { throw new BSONError(`Input is an empty string`); - } else if (/[^-0-9.+]/.test(value)) { - throw new BSONError(`Input: '${value}' contains invalid characters`); - } else if (Number.isNaN(coercedValue)) { - throw new BSONError(`Input: ${value} is not representable as a Double`); // generic case + } + if (/[^-0-9.+e]/.test(value)) { + throw new BSONError(`Input: '${value}' is not in decimal or exponential notation`); } return new Double(coercedValue); } diff --git a/test/node/double.test.ts b/test/node/double.test.ts index ccc8ad79a..852975a1c 100644 --- a/test/node/double.test.ts +++ b/test/node/double.test.ts @@ -242,21 +242,24 @@ describe('BSON Double Precision', function () { ['negative zero', '-0', -0], ['explicit plus zero', '+0', 0], ['explicit plus decimal', '+78.23456', 78.23456], - ['explicit plus leading zeros', '+00000000000001.11', 1.11] + ['explicit plus leading zeros', '+00000000000001.11', 1.11], + ['exponentiation notation', '1.34e16', 1.34e16], + ['exponentiation notation with negative exponent', '1.34e-16', 1.34e-16], + ['exponentiation notation with explicit positive exponent', '1.34e+16', 1.34e16], + ['exponentiation notation with negative base', '-1.34e16', -1.34e16] ]; const errorInputs = [ - ['commas', '34,450', 'contains invalid characters'], - ['exponentiation notation', '1.34e16', 'contains invalid characters'], - ['octal', '0o1', 'contains invalid characters'], - ['binary', '0b1', 'contains invalid characters'], - ['hex', '0x1', 'contains invalid characters'], + ['commas', '34,450', 'is not representable as a Double'], + ['octal', '0o1', 'is not in decimal or exponential notation'], + ['binary', '0b1', 'is not in decimal or exponential notation'], + ['hex', '0x1', 'is not in decimal or exponential notation'], ['empty string', '', 'is an empty string'], ['leading and trailing whitespace', ' 89 ', 'contains whitespace'], ['fake positive infinity', '2e308', 'is not representable as a Double'], ['fake negative infinity', '-2e308', 'is not representable as a Double'], - ['fraction', '3/4', 'contains invalid characters'], - ['foo', 'foo', 'contains invalid characters'], + ['fraction', '3/4', 'is not representable as a Double'], + ['foo', 'foo', 'is not representable as a Double'], [ 'malformed number without invalid characters', '9.0.+76', From 60730bc6f86b2506aa3b620327c5a81d78698878 Mon Sep 17 00:00:00 2001 From: Aditi Khare Date: Thu, 18 Apr 2024 15:51:14 -0400 Subject: [PATCH 5/6] change to deep equals (to assert -0), and make exponential case insensitive (/i flag was being finicky so just hard coded it --- src/double.ts | 2 +- test/node/double.test.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/double.ts b/src/double.ts index c53206071..4a3d12982 100644 --- a/src/double.ts +++ b/src/double.ts @@ -62,7 +62,7 @@ export class Double extends BSONValue { if (value === '') { throw new BSONError(`Input is an empty string`); } - if (/[^-0-9.+e]/.test(value)) { + if (/[^-0-9.+eE]/.test(value)) { throw new BSONError(`Input: '${value}' is not in decimal or exponential notation`); } return new Double(coercedValue); diff --git a/test/node/double.test.ts b/test/node/double.test.ts index 852975a1c..f99ca6304 100644 --- a/test/node/double.test.ts +++ b/test/node/double.test.ts @@ -226,7 +226,7 @@ describe('BSON Double Precision', function () { }); }); - describe('fromString', () => { + describe.only('fromString', () => { const acceptedInputs = [ ['zero', '0', 0], ['non-leading zeros', '45000000', 45000000], @@ -246,7 +246,8 @@ describe('BSON Double Precision', function () { ['exponentiation notation', '1.34e16', 1.34e16], ['exponentiation notation with negative exponent', '1.34e-16', 1.34e-16], ['exponentiation notation with explicit positive exponent', '1.34e+16', 1.34e16], - ['exponentiation notation with negative base', '-1.34e16', -1.34e16] + ['exponentiation notation with negative base', '-1.34e16', -1.34e16], + ['exponentiation notation with capital E', '-1.34E16', -1.34e16] ]; const errorInputs = [ @@ -273,7 +274,7 @@ describe('BSON Double Precision', function () { if (value === 'NaN') { expect(isNaN(Double.fromString(value))).to.be.true; } else { - expect(Double.fromString(value).value).to.equal(expectedDouble); + expect(Double.fromString(value).value).to.deep.equal(expectedDouble); } }); }); From aa7d41f1ff9c41489ac84736b14247d83fc96515 Mon Sep 17 00:00:00 2001 From: Aditi Khare Date: Thu, 18 Apr 2024 15:52:03 -0400 Subject: [PATCH 6/6] lint fix --- test/node/double.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/node/double.test.ts b/test/node/double.test.ts index f99ca6304..e5f3a2597 100644 --- a/test/node/double.test.ts +++ b/test/node/double.test.ts @@ -226,7 +226,7 @@ describe('BSON Double Precision', function () { }); }); - describe.only('fromString', () => { + describe('fromString', () => { const acceptedInputs = [ ['zero', '0', 0], ['non-leading zeros', '45000000', 45000000],