Skip to content

Commit 2097d09

Browse files
committed
test(bson-corpus): enable extended JSON tests in bson corpus runner
1 parent b7e89de commit 2097d09

File tree

1 file changed

+158
-13
lines changed

1 file changed

+158
-13
lines changed

test/node/bson_corpus_tests.js

Lines changed: 158 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const Buffer = require('buffer').Buffer;
44
const BSON = require('../../lib/bson');
55
const Decimal128 = BSON.Decimal128;
66
const expect = require('chai').expect;
7+
const EJSON = require('../../lib/extended_json');
78

89
var deserializeOptions = {
910
bsonRegExp: true,
@@ -15,37 +16,123 @@ var serializeOptions = {
1516
ignoreUndefined: false
1617
};
1718

18-
// tests from the corpus that we need to skip, and explanations why
19+
function nativeToBson(native) {
20+
const serializeOptions = {
21+
ignoreUndefined: false
22+
};
23+
24+
return BSON.serialize(native, serializeOptions);
25+
}
26+
27+
function bsonToNative(bson) {
28+
var deserializeOptions = {
29+
bsonRegExp: true,
30+
promoteLongs: true,
31+
promoteValues: false
32+
};
33+
34+
return BSON.deserialize(bson, deserializeOptions);
35+
}
36+
37+
function jsonToNative(json) {
38+
return EJSON.parse(json, { relaxed: false });
39+
}
1940

20-
var skip = {
41+
function nativeToCEJSON(native) {
42+
return EJSON.stringify(native, { relaxed: false });
43+
}
44+
45+
function nativeToREJSON(native) {
46+
return EJSON.stringify(native, { relaxed: true });
47+
}
48+
49+
function normalize(cEJ) {
50+
return JSON.stringify(JSON.parse(cEJ));
51+
}
52+
53+
// tests from the corpus that we need to skip, and explanations why
54+
const skipBSON = {
2155
'NaN with payload':
2256
'passing this would require building a custom type to store the NaN payload data.'
2357
};
2458

25-
const corpus = require('./tools/bson_corpus_test_loader');
59+
const skipExtendedJSON = {
60+
'Timestamp with high-order bit set on both seconds and increment':
61+
'Current BSON implementation of timestamp/long cannot hold these values - 1 too large.'
62+
};
63+
64+
// test modifications for JavaScript
65+
const modifiedDoubles = {
66+
'+1.0': { canonical_extjson: '{"d":{"$numberDouble":"1"}}' },
67+
'-1.0': { canonical_extjson: '{"d":{"$numberDouble":"-1"}}' },
68+
'1.23456789012345677E+18': { canonical_extjson: '{"d":{"$numberDouble":"1234567890123456800"}}' },
69+
'-1.23456789012345677E+18': {
70+
canonical_extjson: '{"d":{"$numberDouble":"-1234567890123456800"}}'
71+
},
72+
'0.0': { canonical_extjson: '{"d":{"$numberDouble":"0"}}' },
73+
'-0.0': {
74+
canonical_extjson: '{"d":{"$numberDouble":"0"}}',
75+
canonical_bson: '10000000016400000000000000000000'
76+
}
77+
};
78+
79+
const modifiedMultitype = {
80+
'All BSON types': {
81+
canonical_extjson:
82+
'{"_id":{"$oid":"57e193d7a9cc81b4027498b5"},"Symbol":"symbol","String":"string","Int32":{"$numberInt":"42"},"Int64":{"$numberLong":"42"},"Double":{"$numberDouble":"-1"},"Binary":{"$binary":{"base64":"o0w498Or7cijeBSpkquNtg==","subType":"03"}},"BinaryUserDefined":{"$binary":{"base64":"AQIDBAU=","subType":"80"}},"Code":{"$code":"function() {}"},"CodeWithScope":{"$code":"function() {}","$scope":{}},"Subdocument":{"foo":"bar"},"Array":[{"$numberInt":"1"},{"$numberInt":"2"},{"$numberInt":"3"},{"$numberInt":"4"},{"$numberInt":"5"}],"Timestamp":{"$timestamp":{"t":42,"i":1}},"Regex":{"$regularExpression":{"pattern":"pattern","options":""}},"DatetimeEpoch":{"$date":{"$numberLong":"0"}},"DatetimePositive":{"$date":{"$numberLong":"2147483647"}},"DatetimeNegative":{"$date":{"$numberLong":"-2147483648"}},"True":true,"False":false,"DBPointer":{"$ref":"collection","$id":{"$oid":"57e193d7a9cc81b4027498b1"}},"DBRef":{"$ref":"collection","$id":{"$oid":"57fd71e96e32ab4225b723fb"},"$db":"database"},"Minkey":{"$minKey":1},"Maxkey":{"$maxKey":1},"Null":null,"Undefined":null}',
83+
canonical_bson:
84+
'48020000075f69640057e193d7a9cc81b4027498b50253796d626f6c000700000073796d626f6c0002537472696e670007000000737472696e670010496e743332002a00000012496e743634002a0000000000000001446f75626c6500000000000000f0bf0542696e617279001000000003a34c38f7c3abedc8a37814a992ab8db60542696e61727955736572446566696e656400050000008001020304050d436f6465000e00000066756e6374696f6e2829207b7d000f436f64655769746853636f7065001b0000000e00000066756e6374696f6e2829207b7d00050000000003537562646f63756d656e74001200000002666f6f0004000000626172000004417272617900280000001030000100000010310002000000103200030000001033000400000010340005000000001154696d657374616d7000010000002a0000000b5265676578007061747465726e0000094461746574696d6545706f6368000000000000000000094461746574696d65506f73697469766500ffffff7f00000000094461746574696d654e656761746976650000000080ffffffff085472756500010846616c73650000034442506f696e746572002b0000000224726566000b000000636f6c6c656374696f6e00072469640057e193d7a9cc81b4027498b100034442526566003d0000000224726566000b000000636f6c6c656374696f6e00072469640057fd71e96e32ab4225b723fb02246462000900000064617461626173650000ff4d696e6b6579007f4d61786b6579000a4e756c6c000a556e646566696e65640000',
85+
converted_extjson:
86+
'{"_id":{"$oid":"57e193d7a9cc81b4027498b5"},"Symbol":"symbol","String":"string","Int32":{"$numberInt":"42"},"Int64":{"$numberLong":"42"},"Double":{"$numberDouble":"-1"},"Binary":{"$binary":{"base64":"o0w498Or7cijeBSpkquNtg==","subType":"03"}},"BinaryUserDefined":{"$binary":{"base64":"AQIDBAU=","subType":"80"}},"Code":{"$code":"function() {}"},"CodeWithScope":{"$code":"function() {}","$scope":{}},"Subdocument":{"foo":"bar"},"Array":[{"$numberInt":"1"},{"$numberInt":"2"},{"$numberInt":"3"},{"$numberInt":"4"},{"$numberInt":"5"}],"Timestamp":{"$timestamp":{"t":42,"i":1}},"Regex":{"$regularExpression":{"pattern":"pattern","options":""}},"DatetimeEpoch":{"$date":{"$numberLong":"0"}},"DatetimePositive":{"$date":{"$numberLong":"2147483647"}},"DatetimeNegative":{"$date":{"$numberLong":"-2147483648"}},"True":true,"False":false,"DBPointer":{"$ref":"collection","$id":{"$oid":"57e193d7a9cc81b4027498b1"}},"DBRef":{"$ref":"collection","$id":{"$oid":"57fd71e96e32ab4225b723fb"},"$db":"database"},"Minkey":{"$minKey":1},"Maxkey":{"$maxKey":1},"Null":null,"Undefined":null}',
87+
converted_bson:
88+
'48020000075f69640057e193d7a9cc81b4027498b50253796d626f6c000700000073796d626f6c0002537472696e670007000000737472696e670010496e743332002a00000012496e743634002a0000000000000001446f75626c6500000000000000f0bf0542696e617279001000000003a34c38f7c3abedc8a37814a992ab8db60542696e61727955736572446566696e656400050000008001020304050d436f6465000e00000066756e6374696f6e2829207b7d000f436f64655769746853636f7065001b0000000e00000066756e6374696f6e2829207b7d00050000000003537562646f63756d656e74001200000002666f6f0004000000626172000004417272617900280000001030000100000010310002000000103200030000001033000400000010340005000000001154696d657374616d7000010000002a0000000b5265676578007061747465726e0000094461746574696d6545706f6368000000000000000000094461746574696d65506f73697469766500ffffff7f00000000094461746574696d654e656761746976650000000080ffffffff085472756500010846616c73650000034442506f696e746572002b0000000224726566000b000000636f6c6c656374696f6e00072469640057e193d7a9cc81b4027498b100034442526566003d0000000224726566000b000000636f6c6c656374696f6e00072469640057fd71e96e32ab4225b723fb02246462000900000064617461626173650000ff4d696e6b6579007f4d61786b6579000a4e756c6c000a556e646566696e65640000'
89+
}
90+
};
2691

92+
const corpus = require('./tools/bson_corpus_test_loader');
2793
describe('BSON Corpus', function() {
2894
corpus.forEach(scenario => {
29-
describe(scenario.description, function() {
30-
if (scenario.valid) {
31-
describe('valid', function() {
32-
scenario.valid.forEach(v => {
33-
if (skip.hasOwnProperty(v.description)) {
95+
const deprecated = scenario.deprecated;
96+
const description = scenario.description;
97+
const valid = scenario.valid || [];
98+
99+
// since doubles are formatted differently in JS than in corpus, overwrite expected results
100+
if (description === 'Double type') {
101+
valid.forEach(v => {
102+
if (modifiedDoubles[v.description]) {
103+
Object.assign(v, modifiedDoubles[v.description]);
104+
}
105+
});
106+
// multitype test has a double nested in object, so change those expected values too
107+
} else if (description === 'Multiple types within the same document') {
108+
valid.forEach(v => {
109+
if (modifiedMultitype[v.description]) {
110+
Object.assign(v, modifiedMultitype[v.description]);
111+
}
112+
});
113+
}
114+
115+
describe(description, function() {
116+
if (valid) {
117+
describe('valid-bson', function() {
118+
valid.forEach(v => {
119+
if (skipBSON.hasOwnProperty(v.description)) {
34120
it.skip(v.description, () => {});
35121
return;
36122
}
37123

38124
it(v.description, function() {
39-
var cB = Buffer.from(v.canonical_bson, 'hex');
40-
if (v.degenerate_bson) var dB = Buffer.from(v.degenerate_bson, 'hex');
41-
if (v.converted_bson) var convB = Buffer.from(v.converted_bson, 'hex');
125+
const cB = Buffer.from(v.canonical_bson, 'hex');
126+
let dB, convB;
127+
if (v.degenerate_bson) dB = Buffer.from(v.degenerate_bson, 'hex');
128+
if (v.converted_bson) convB = Buffer.from(v.converted_bson, 'hex');
42129

43-
var roundTripped = BSON.serialize(
130+
const roundTripped = BSON.serialize(
44131
BSON.deserialize(cB, deserializeOptions),
45132
serializeOptions
46133
);
47134

48-
if (scenario.deprecated) expect(convB).to.deep.equal(roundTripped);
135+
if (deprecated) expect(convB).to.deep.equal(roundTripped);
49136
else expect(cB).to.deep.equal(roundTripped);
50137

51138
if (dB) {
@@ -56,6 +143,64 @@ describe('BSON Corpus', function() {
56143
});
57144
});
58145
});
146+
147+
describe('valid-extjson', function() {
148+
valid.forEach(v => {
149+
if (skipExtendedJSON.hasOwnProperty(v.description)) {
150+
it.skip(v.description, () => {});
151+
return;
152+
}
153+
154+
it(v.description, function() {
155+
// read in test case data. if this scenario is for a deprecated
156+
// type, we want to use the "converted" BSON and EJSON, which
157+
// use the upgraded version of the deprecated type. otherwise,
158+
// just use canonical.
159+
let cB, cEJ;
160+
if (deprecated) {
161+
cB = new Buffer(v.converted_bson, 'hex');
162+
cEJ = normalize(v.converted_extjson);
163+
} else {
164+
cB = new Buffer(v.canonical_bson, 'hex');
165+
cEJ = normalize(v.canonical_extjson);
166+
}
167+
168+
// convert inputs to native Javascript objects
169+
const nativeFromCB = bsonToNative(cB);
170+
171+
// round tripped EJSON should match the original
172+
expect(nativeToCEJSON(jsonToNative(cEJ))).to.equal(cEJ);
173+
174+
// invalid, but still parseable, EJSON. if provided, make sure that we
175+
// properly convert it to canonical EJSON and BSON.
176+
if (v.degenerate_extjson) {
177+
const dEJ = normalize(v.degenerate_extjson);
178+
const roundTrippedDEJ = nativeToCEJSON(jsonToNative(dEJ));
179+
expect(roundTrippedDEJ).to.equal(cEJ);
180+
if (!v.lossy) {
181+
expect(nativeToBson(jsonToNative(dEJ))).to.deep.equal(cB);
182+
}
183+
}
184+
185+
// as long as conversion isn't lossy (i.e. BSON can represent everything in
186+
// the EJSON), make sure EJSON -> native -> BSON matches canonical BSON.
187+
if (!v.lossy) {
188+
expect(nativeToBson(jsonToNative(cEJ))).to.deep.equal(cB);
189+
}
190+
191+
// the reverse direction, BSON -> native -> EJSON, should match canonical EJSON.
192+
expect(nativeToCEJSON(nativeFromCB)).to.equal(cEJ);
193+
194+
if (v.relaxed_extjson) {
195+
let rEJ = normalize(v.relaxed_extjson);
196+
// BSON -> native -> relaxed EJSON matches provided
197+
expect(nativeToREJSON(nativeFromCB)).to.equal(rEJ);
198+
// relaxed EJSON -> native -> relaxed EJSON unchanged
199+
expect(nativeToREJSON(jsonToNative(rEJ))).to.equal(rEJ);
200+
}
201+
});
202+
});
203+
});
59204
}
60205

61206
if (scenario.decodeErrors) {

0 commit comments

Comments
 (0)