From d072231bf22ee9ffd9fe3e929a0188bfde7e188e Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Thu, 17 Oct 2024 15:20:02 -0400 Subject: [PATCH 1/2] test(NODE-6438): check that BSON undefined is returned from deserialize --- test/node/bson_test.js | 74 ------------------- test/node/bson_undefined.test.ts | 106 ++++++++++++++++++++++++++++ test/node/parser/serializer.test.ts | 1 + 3 files changed, 107 insertions(+), 74 deletions(-) create mode 100644 test/node/bson_undefined.test.ts diff --git a/test/node/bson_test.js b/test/node/bson_test.js index d7edc17ff..99693649f 100644 --- a/test/node/bson_test.js +++ b/test/node/bson_test.js @@ -298,80 +298,6 @@ describe('BSON', function () { done(); }); - /** - * @ignore - */ - it('Should correctly ignore undefined values in arrays', function (done) { - var doc = { doc: { notdefined: undefined } }; - var serialized_data = BSON.serialize(doc, { - ignoreUndefined: true - }); - var serialized_data2 = Buffer.alloc( - BSON.calculateObjectSize(doc, { - ignoreUndefined: true - }) - ); - BSON.serializeWithBufferAndIndex(doc, serialized_data2, { - ignoreUndefined: true - }); - - assertBuffersEqual(done, serialized_data, serialized_data2, 0); - var doc1 = BSON.deserialize(serialized_data); - - expect(undefined).to.deep.equal(doc1.doc.notdefined); - done(); - }); - - it('Should correctly serialize undefined array entries as null values', function (done) { - var doc = { doc: { notdefined: undefined }, a: [1, 2, undefined, 3] }; - var serialized_data = BSON.serialize(doc, { - ignoreUndefined: true - }); - var serialized_data2 = Buffer.alloc( - BSON.calculateObjectSize(doc, { - ignoreUndefined: true - }) - ); - BSON.serializeWithBufferAndIndex(doc, serialized_data2, { - ignoreUndefined: true - }); - assertBuffersEqual(done, serialized_data, serialized_data2, 0); - var doc1 = BSON.deserialize(serialized_data); - expect(undefined).to.deep.equal(doc1.doc.notdefined); - expect(null).to.equal(doc1.a[2]); - done(); - }); - - it('Should correctly serialize undefined array entries as undefined values', function (done) { - var doc = { doc: { notdefined: undefined }, a: [1, 2, undefined, 3] }; - var serialized_data = BSON.serialize(doc, { - ignoreUndefined: false - }); - var serialized_data2 = Buffer.alloc( - BSON.calculateObjectSize(doc, { - ignoreUndefined: false - }) - ); - BSON.serializeWithBufferAndIndex(doc, serialized_data2, { - ignoreUndefined: false - }); - - // console.log("======================================== 0") - // console.log(serialized_data.toString('hex')) - // console.log(serialized_data2.toString('hex')) - - assertBuffersEqual(done, serialized_data, serialized_data2, 0); - var doc1 = BSON.deserialize(serialized_data); - var doc2 = BSON.deserialize(serialized_data2); - // console.log("======================================== 0") - // console.dir(doc1) - // console.dir(doc2) - - expect(null).to.deep.equal(doc1.doc.notdefined); - expect(null).to.deep.equal(doc2.doc.notdefined); - done(); - }); - /** * @ignore */ diff --git a/test/node/bson_undefined.test.ts b/test/node/bson_undefined.test.ts new file mode 100644 index 000000000..3c52edf24 --- /dev/null +++ b/test/node/bson_undefined.test.ts @@ -0,0 +1,106 @@ +import { expect } from 'chai'; +import { bufferFromHexArray } from './tools/utils'; +import { BSON } from '../register-bson'; +import { BSON_DATA_NULL } from '../../src/constants'; + +describe('BSON undefined', () => { + const KEY_A = '6100'; + const KEY_0 = '3000'; + const KEY_1 = '3100'; + const KEY_2 = '3200'; + const bsonDocWithUndefined = bufferFromHexArray([ + '06', // BSON undefined + KEY_A + ]); + + describe('when deserialize is given BSON bytes with undefined value', function () { + it('returns a javascript undefined value', () => { + const doc = BSON.deserialize(bsonDocWithUndefined); + expect(doc).to.have.own.property('a').that.is.undefined; + }); + }); + + describe('when serialize is given a javascript object that contains undefined', () => { + describe('when ignoreUndefined is set to false', function () { + it('serializes to document with a set to BSON null (type=10)', () => { + const jsObject = BSON.deserialize(bsonDocWithUndefined); + const bytes = BSON.serialize(jsObject, { ignoreUndefined: false }); + expect(bytes).to.have.lengthOf(8); + expect(bytes).to.have.own.property('4', BSON_DATA_NULL); + }); + }); + + describe('when ignoreUndefined is set to true', function () { + it('serializes to empty document', () => { + const jsObject = BSON.deserialize(bsonDocWithUndefined); + const bytes = BSON.serialize(jsObject, { ignoreUndefined: true }); + expect(bytes).to.deep.equal(Uint8Array.of(5, 0, 0, 0, 0)); + }); + }); + + describe('when ignoreUndefined is unset', function () { + it('serializes to empty document', () => { + const jsObject = BSON.deserialize(bsonDocWithUndefined); + const bytes = BSON.serialize(jsObject); + expect(bytes).to.deep.equal(Uint8Array.of(5, 0, 0, 0, 0)); + }); + }); + }); + + describe('when undefined appears inside an array', function () { + describe('when ignoreUndefined is set to true', function () { + it('does not ignore undefined values', function () { + // because this would change the size of the array + const doc = { a: [1, undefined, 3] }; + const bytes = BSON.serialize(doc, { ignoreUndefined: true }); + expect(bytes).to.deep.equal( + bufferFromHexArray([ + '04', // array + KEY_A, + bufferFromHexArray([ + ...['10', KEY_0, '01000000'], // int "0" = 1 + ...['0A', KEY_1], // null "1" + ...['10', KEY_2, '03000000'] // int "2" = 3 + ]).toString('hex') + ]) + ); + }); + }); + + describe('when ignoreUndefined is set to false', function () { + it('serializes undefined values as null', function () { + const doc = { a: [1, undefined, 3] }; + const bytes = BSON.serialize(doc, { ignoreUndefined: false }); + expect(bytes).to.deep.equal( + bufferFromHexArray([ + '04', // array + KEY_A, + bufferFromHexArray([ + ...['10', KEY_0, '01000000'], // int "0" = 1 + ...['0A', KEY_1], // null "1" + ...['10', KEY_2, '03000000'] // int "2" = 3 + ]).toString('hex') + ]) + ); + }); + }); + + describe('when ignoreUndefined is unset', function () { + it('serializes undefined values as null', function () { + const doc = { a: [1, undefined, 3] }; + const bytes = BSON.serialize(doc); + expect(bytes).to.deep.equal( + bufferFromHexArray([ + '04', // array + KEY_A, + bufferFromHexArray([ + ...['10', KEY_0, '01000000'], // int "0" = 1 + ...['0A', KEY_1], // null "1" + ...['10', KEY_2, '03000000'] // int "2" = 3 + ]).toString('hex') + ]) + ); + }); + }); + }); +}); diff --git a/test/node/parser/serializer.test.ts b/test/node/parser/serializer.test.ts index c06161ccf..6af3f27a2 100644 --- a/test/node/parser/serializer.test.ts +++ b/test/node/parser/serializer.test.ts @@ -26,6 +26,7 @@ describe('serialize()', () => { }); it('does not turn nested nulls into empty documents', () => { + // In JS typeof null is 'object' so it is possible it could be misinterpreted as an object with no keys const nestedNull = bufferFromHexArray([ '0A', // null type '6100', // 'a\x00' From ac7450354b6c5e853dfb2b885d1009ade426190a Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 21 Oct 2024 17:54:13 -0400 Subject: [PATCH 2/2] chore: comments --- test/node/bson_undefined.test.ts | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/node/bson_undefined.test.ts b/test/node/bson_undefined.test.ts index 3c52edf24..b902450cc 100644 --- a/test/node/bson_undefined.test.ts +++ b/test/node/bson_undefined.test.ts @@ -8,13 +8,13 @@ describe('BSON undefined', () => { const KEY_0 = '3000'; const KEY_1 = '3100'; const KEY_2 = '3200'; - const bsonDocWithUndefined = bufferFromHexArray([ - '06', // BSON undefined - KEY_A - ]); describe('when deserialize is given BSON bytes with undefined value', function () { it('returns a javascript undefined value', () => { + const bsonDocWithUndefined = bufferFromHexArray([ + '06', // BSON undefined + KEY_A + ]); const doc = BSON.deserialize(bsonDocWithUndefined); expect(doc).to.have.own.property('a').that.is.undefined; }); @@ -23,16 +23,18 @@ describe('BSON undefined', () => { describe('when serialize is given a javascript object that contains undefined', () => { describe('when ignoreUndefined is set to false', function () { it('serializes to document with a set to BSON null (type=10)', () => { - const jsObject = BSON.deserialize(bsonDocWithUndefined); + const jsObject = { a: undefined }; const bytes = BSON.serialize(jsObject, { ignoreUndefined: false }); expect(bytes).to.have.lengthOf(8); - expect(bytes).to.have.own.property('4', BSON_DATA_NULL); + const elements = BSON.onDemand.parseToElements(bytes); + expect(elements).to.have.lengthOf(1); + expect(elements[0][0]).to.deep.equal(BSON_DATA_NULL); }); }); describe('when ignoreUndefined is set to true', function () { it('serializes to empty document', () => { - const jsObject = BSON.deserialize(bsonDocWithUndefined); + const jsObject = { a: undefined }; const bytes = BSON.serialize(jsObject, { ignoreUndefined: true }); expect(bytes).to.deep.equal(Uint8Array.of(5, 0, 0, 0, 0)); }); @@ -40,7 +42,7 @@ describe('BSON undefined', () => { describe('when ignoreUndefined is unset', function () { it('serializes to empty document', () => { - const jsObject = BSON.deserialize(bsonDocWithUndefined); + const jsObject = { a: undefined }; const bytes = BSON.serialize(jsObject); expect(bytes).to.deep.equal(Uint8Array.of(5, 0, 0, 0, 0)); }); @@ -49,7 +51,7 @@ describe('BSON undefined', () => { describe('when undefined appears inside an array', function () { describe('when ignoreUndefined is set to true', function () { - it('does not ignore undefined values', function () { + it('serializes undefined values as null', function () { // because this would change the size of the array const doc = { a: [1, undefined, 3] }; const bytes = BSON.serialize(doc, { ignoreUndefined: true });