@mongosh/shell-bson 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.depcheckrc +16 -0
- package/.eslintignore +2 -0
- package/.eslintrc.js +10 -0
- package/.prettierignore +6 -0
- package/.prettierrc.json +1 -0
- package/AUTHORS +2 -0
- package/LICENSE +192 -0
- package/README.md +27 -0
- package/lib/bson-export.d.ts +19 -0
- package/lib/bson-export.js +3 -0
- package/lib/bson-export.js.map +1 -0
- package/lib/helpers.d.ts +11 -0
- package/lib/helpers.js +69 -0
- package/lib/helpers.js.map +1 -0
- package/lib/index.d.ts +3 -0
- package/lib/index.js +9 -0
- package/lib/index.js.map +1 -0
- package/lib/printable-bson.d.ts +6 -0
- package/lib/printable-bson.js +98 -0
- package/lib/printable-bson.js.map +1 -0
- package/lib/shell-bson.d.ts +58 -0
- package/lib/shell-bson.js +213 -0
- package/lib/shell-bson.js.map +1 -0
- package/package.json +55 -0
- package/src/bson-export.ts +51 -0
- package/src/helpers.ts +123 -0
- package/src/index.ts +3 -0
- package/src/printable-bson.spec.ts +137 -0
- package/src/printable-bson.ts +147 -0
- package/src/shell-bson.spec.ts +820 -0
- package/src/shell-bson.ts +447 -0
- package/tsconfig-lint.json +5 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,820 @@
|
|
|
1
|
+
import { CommonErrors, MongoshInvalidInputError } from '@mongosh/errors';
|
|
2
|
+
import * as bson from 'bson';
|
|
3
|
+
import {
|
|
4
|
+
serialize as bsonSerialize,
|
|
5
|
+
deserialize as bsonDeserialize,
|
|
6
|
+
} from 'bson';
|
|
7
|
+
import chai, { expect } from 'chai';
|
|
8
|
+
import sinonChai from 'sinon-chai';
|
|
9
|
+
import sinon from 'sinon';
|
|
10
|
+
import type { BSON, ShellBson } from './';
|
|
11
|
+
import { constructShellBson } from './';
|
|
12
|
+
chai.use(sinonChai);
|
|
13
|
+
|
|
14
|
+
const hex_1234 = '31323334';
|
|
15
|
+
const b64_1234 = 'MTIzNA==';
|
|
16
|
+
const utf_1234 = '1234';
|
|
17
|
+
|
|
18
|
+
interface TestHelp {
|
|
19
|
+
testHelpName: string;
|
|
20
|
+
type: 'Help';
|
|
21
|
+
}
|
|
22
|
+
const ALL_SERVER_VERSIONS = ['1.0'];
|
|
23
|
+
|
|
24
|
+
describe('Shell BSON', function () {
|
|
25
|
+
let shellBson: ShellBson<BSON, TestHelp>;
|
|
26
|
+
let printWarning: (msg: string) => void;
|
|
27
|
+
let constructHelp: (name: string) => TestHelp;
|
|
28
|
+
|
|
29
|
+
before(function () {
|
|
30
|
+
printWarning = sinon.stub();
|
|
31
|
+
constructHelp = sinon.stub().callsFake((name: string): TestHelp => {
|
|
32
|
+
return { testHelpName: name, type: 'Help' };
|
|
33
|
+
});
|
|
34
|
+
shellBson = constructShellBson({
|
|
35
|
+
bsonLibrary: bson,
|
|
36
|
+
printWarning,
|
|
37
|
+
constructHelp,
|
|
38
|
+
assignMetadata(target, { help }) {
|
|
39
|
+
target.serverVersions = ALL_SERVER_VERSIONS;
|
|
40
|
+
if (help) {
|
|
41
|
+
target.help = (): TestHelp => help;
|
|
42
|
+
Object.setPrototypeOf(target.help, help);
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('DBRef', function () {
|
|
49
|
+
it('without new', function () {
|
|
50
|
+
const s = shellBson.DBRef('namespace', 'oid');
|
|
51
|
+
expect(s._bsontype).to.equal('DBRef');
|
|
52
|
+
});
|
|
53
|
+
it('with new', function () {
|
|
54
|
+
const s = new (shellBson.DBRef as any)('namespace', 'oid');
|
|
55
|
+
expect(s._bsontype).to.equal('DBRef');
|
|
56
|
+
});
|
|
57
|
+
it('has help and other metadata', function () {
|
|
58
|
+
const s = shellBson.DBRef('namespace', 'oid');
|
|
59
|
+
expect(s.help?.type).to.equal('Help');
|
|
60
|
+
expect(s.help?.().type).to.equal('Help');
|
|
61
|
+
expect((s as any).serverVersions).to.deep.equal(ALL_SERVER_VERSIONS);
|
|
62
|
+
});
|
|
63
|
+
it('errors for missing arg 1', function () {
|
|
64
|
+
try {
|
|
65
|
+
(shellBson.DBRef as any)();
|
|
66
|
+
} catch (e: any) {
|
|
67
|
+
return expect(e.message).to.contain('Missing required argument');
|
|
68
|
+
}
|
|
69
|
+
expect.fail('Expecting error, nothing thrown');
|
|
70
|
+
});
|
|
71
|
+
it('errors for missing arg 2', function () {
|
|
72
|
+
try {
|
|
73
|
+
(shellBson.DBRef as any)('ns');
|
|
74
|
+
} catch (e: any) {
|
|
75
|
+
return expect(e.message).to.contain('Missing required argument');
|
|
76
|
+
}
|
|
77
|
+
expect.fail('Expecting error, nothing thrown');
|
|
78
|
+
});
|
|
79
|
+
it('errors for wrong type of arg 1', function () {
|
|
80
|
+
try {
|
|
81
|
+
(shellBson.DBRef as any)(1, 'oid');
|
|
82
|
+
} catch (e: any) {
|
|
83
|
+
return expect(e.message).to.contain('string, got number');
|
|
84
|
+
}
|
|
85
|
+
expect.fail('Expecting error, nothing thrown');
|
|
86
|
+
});
|
|
87
|
+
it('errors for wrong type of arg 3', function () {
|
|
88
|
+
try {
|
|
89
|
+
(shellBson.DBRef as any)('ns', 'oid', 1);
|
|
90
|
+
} catch (e: any) {
|
|
91
|
+
return expect(e.message).to.contain('string, got number');
|
|
92
|
+
}
|
|
93
|
+
expect.fail('Expecting error, nothing thrown');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('MaxKey', function () {
|
|
97
|
+
it('without new', function () {
|
|
98
|
+
const s = shellBson.MaxKey();
|
|
99
|
+
expect(s._bsontype).to.equal('MaxKey');
|
|
100
|
+
});
|
|
101
|
+
it('with new', function () {
|
|
102
|
+
const s = new (shellBson.MaxKey as any)();
|
|
103
|
+
expect(s._bsontype).to.equal('MaxKey');
|
|
104
|
+
});
|
|
105
|
+
it('using toBSON', function () {
|
|
106
|
+
const s = (shellBson.MaxKey as any).toBSON();
|
|
107
|
+
expect(s._bsontype).to.equal('MaxKey');
|
|
108
|
+
});
|
|
109
|
+
it('has help and other metadata', function () {
|
|
110
|
+
const s = shellBson.MaxKey();
|
|
111
|
+
expect(s.help?.type).to.equal('Help');
|
|
112
|
+
expect(s.help?.().type).to.equal('Help');
|
|
113
|
+
expect((s as any).serverVersions).to.deep.equal(ALL_SERVER_VERSIONS);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
describe('MinKey', function () {
|
|
117
|
+
it('without new', function () {
|
|
118
|
+
const s = shellBson.MinKey();
|
|
119
|
+
expect(s._bsontype).to.equal('MinKey');
|
|
120
|
+
});
|
|
121
|
+
it('with new', function () {
|
|
122
|
+
const s = new (shellBson.MinKey as any)();
|
|
123
|
+
expect(s._bsontype).to.equal('MinKey');
|
|
124
|
+
});
|
|
125
|
+
it('using toBSON', function () {
|
|
126
|
+
const s = (shellBson.MinKey as any).toBSON();
|
|
127
|
+
expect(s._bsontype).to.equal('MinKey');
|
|
128
|
+
});
|
|
129
|
+
it('has help and other metadata', function () {
|
|
130
|
+
const s = shellBson.MinKey();
|
|
131
|
+
expect(s.help?.type).to.equal('Help');
|
|
132
|
+
expect(s.help?.().type).to.equal('Help');
|
|
133
|
+
expect((s as any).serverVersions).to.deep.equal(ALL_SERVER_VERSIONS);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
describe('MinKey & MaxKey constructor special handling', function () {
|
|
137
|
+
it('round-trips through bson as expected', function () {
|
|
138
|
+
const { MinKey, MaxKey } = shellBson;
|
|
139
|
+
const expected = { a: { $minKey: 1 }, b: { $maxKey: 1 } };
|
|
140
|
+
function roundtrip(value: any): any {
|
|
141
|
+
return bson.EJSON.serialize(bsonDeserialize(bsonSerialize(value)));
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
expect(
|
|
145
|
+
roundtrip({ a: new (MinKey as any)(), b: new (MaxKey as any)() })
|
|
146
|
+
).to.deep.equal(expected);
|
|
147
|
+
expect(roundtrip({ a: MinKey(), b: MaxKey() })).to.deep.equal(expected);
|
|
148
|
+
expect(
|
|
149
|
+
roundtrip({ a: MinKey.toBSON(), b: MaxKey.toBSON() })
|
|
150
|
+
).to.deep.equal(expected);
|
|
151
|
+
expect(roundtrip({ a: MinKey, b: MaxKey })).to.deep.equal(expected);
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
describe('ObjectId', function () {
|
|
155
|
+
it('without new', function () {
|
|
156
|
+
const s = shellBson.ObjectId('5ebbe8e2905bb493d6981b6b');
|
|
157
|
+
expect(s._bsontype).to.equal('ObjectId');
|
|
158
|
+
expect(s.toHexString()).to.equal('5ebbe8e2905bb493d6981b6b');
|
|
159
|
+
});
|
|
160
|
+
it('with new', function () {
|
|
161
|
+
const s = new (shellBson.ObjectId as any)('5ebbe8e2905bb493d6981b6b');
|
|
162
|
+
expect(s._bsontype).to.equal('ObjectId');
|
|
163
|
+
expect(s.toHexString()).to.equal('5ebbe8e2905bb493d6981b6b');
|
|
164
|
+
});
|
|
165
|
+
it('works with an integer argument', function () {
|
|
166
|
+
const s = new (shellBson.ObjectId as any)(0x12345678);
|
|
167
|
+
expect(s._bsontype).to.equal('ObjectId');
|
|
168
|
+
expect(s.toHexString().slice(0, 8)).to.equal('12345678');
|
|
169
|
+
});
|
|
170
|
+
it('can be created through createFromTime', function () {
|
|
171
|
+
const s = (shellBson.ObjectId as any).createFromTime(0x12345678);
|
|
172
|
+
expect(s._bsontype).to.equal('ObjectId');
|
|
173
|
+
expect(s.toHexString().slice(0, 8)).to.equal('12345678');
|
|
174
|
+
});
|
|
175
|
+
it('can be created using createFromHexString', function () {
|
|
176
|
+
const s = shellBson.ObjectId.createFromHexString(
|
|
177
|
+
'64c122afaf44ca299136bbc3'
|
|
178
|
+
);
|
|
179
|
+
expect(s._bsontype).to.equal('ObjectId');
|
|
180
|
+
expect(s.toHexString()).to.equal('64c122afaf44ca299136bbc3');
|
|
181
|
+
});
|
|
182
|
+
it('has help and other metadata', function () {
|
|
183
|
+
const s = shellBson.ObjectId();
|
|
184
|
+
expect(s.help?.type).to.equal('Help');
|
|
185
|
+
expect(s.help?.().type).to.equal('Help');
|
|
186
|
+
expect((s as any).serverVersions).to.deep.equal(ALL_SERVER_VERSIONS);
|
|
187
|
+
});
|
|
188
|
+
it('errors for wrong type of arg 1', function () {
|
|
189
|
+
try {
|
|
190
|
+
(shellBson.ObjectId as any)(Symbol('foo'));
|
|
191
|
+
} catch (e: any) {
|
|
192
|
+
return expect(e.message).to.contain('object, got symbol');
|
|
193
|
+
}
|
|
194
|
+
expect.fail('Expecting error, nothing thrown');
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
describe('BSONSymbol', function () {
|
|
198
|
+
it('without new', function () {
|
|
199
|
+
const s = (shellBson.BSONSymbol as any)('5ebbe8e2905bb493d6981b6b');
|
|
200
|
+
expect(s._bsontype).to.equal('BSONSymbol');
|
|
201
|
+
expect(s.toString()).to.equal('5ebbe8e2905bb493d6981b6b');
|
|
202
|
+
});
|
|
203
|
+
it('with new', function () {
|
|
204
|
+
const s = new (shellBson.BSONSymbol as any)('5ebbe8e2905bb493d6981b6b');
|
|
205
|
+
expect(s._bsontype).to.equal('BSONSymbol');
|
|
206
|
+
expect(s.toString()).to.equal('5ebbe8e2905bb493d6981b6b');
|
|
207
|
+
});
|
|
208
|
+
it('has help and other metadata', function () {
|
|
209
|
+
const s = (shellBson.BSONSymbol as any)('5ebbe8e2905bb493d6981b6b');
|
|
210
|
+
expect(s.help?.type).to.equal('Help');
|
|
211
|
+
expect(s.help?.().type).to.equal('Help');
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
describe('Timestamp', function () {
|
|
215
|
+
it('without new', function () {
|
|
216
|
+
const s = shellBson.Timestamp(0, 100);
|
|
217
|
+
expect(s._bsontype).to.equal('Timestamp');
|
|
218
|
+
});
|
|
219
|
+
it('with new', function () {
|
|
220
|
+
const s = new (shellBson.Timestamp as any)(0, 100);
|
|
221
|
+
expect(s._bsontype).to.equal('Timestamp');
|
|
222
|
+
});
|
|
223
|
+
it('with a long argument', function () {
|
|
224
|
+
const s = shellBson.Timestamp((shellBson.Long as any)(1, 2));
|
|
225
|
+
expect(s._bsontype).to.equal('Timestamp');
|
|
226
|
+
expect((s as any).toExtendedJSON()).to.deep.equal({
|
|
227
|
+
$timestamp: { t: 2, i: 1 },
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
it('has help and other metadata', function () {
|
|
231
|
+
const s = shellBson.Timestamp(0, 100);
|
|
232
|
+
expect(s.help?.type).to.equal('Help');
|
|
233
|
+
expect(s.help?.().type).to.equal('Help');
|
|
234
|
+
expect((s as any).serverVersions).to.deep.equal(ALL_SERVER_VERSIONS);
|
|
235
|
+
});
|
|
236
|
+
it('errors for wrong type of arg 1', function () {
|
|
237
|
+
try {
|
|
238
|
+
(shellBson.Timestamp as any)('1');
|
|
239
|
+
} catch (e: any) {
|
|
240
|
+
return expect(e.message).to.contain('object, got string');
|
|
241
|
+
}
|
|
242
|
+
expect.fail('Expecting error, nothing thrown');
|
|
243
|
+
});
|
|
244
|
+
it('errors for wrong type of arg 2', function () {
|
|
245
|
+
try {
|
|
246
|
+
(shellBson.Timestamp as any)(1, '2');
|
|
247
|
+
} catch (e: any) {
|
|
248
|
+
return expect(e.message).to.contain('number, got string');
|
|
249
|
+
}
|
|
250
|
+
expect.fail('Expecting error, nothing thrown');
|
|
251
|
+
});
|
|
252
|
+
it('errors for out-of-range argument', function () {
|
|
253
|
+
try {
|
|
254
|
+
(shellBson.Timestamp as any)(2 ** 32);
|
|
255
|
+
} catch (e: any) {
|
|
256
|
+
return expect(e.message).to.contain('equal or less than uint32 max');
|
|
257
|
+
}
|
|
258
|
+
expect.fail('Expecting error, nothing thrown');
|
|
259
|
+
});
|
|
260
|
+
it('constructs with default args', function () {
|
|
261
|
+
const s = shellBson.Timestamp();
|
|
262
|
+
expect(s.low).to.equal(0);
|
|
263
|
+
expect(s.high).to.equal(0);
|
|
264
|
+
});
|
|
265
|
+
it('constructs with default args 1', function () {
|
|
266
|
+
const s = shellBson.Timestamp(1);
|
|
267
|
+
expect(s.low).to.equal(0);
|
|
268
|
+
expect(s.high).to.equal(1);
|
|
269
|
+
});
|
|
270
|
+
it('constructs with { t, i } signature', function () {
|
|
271
|
+
const s = shellBson.Timestamp({ t: 10, i: 20 });
|
|
272
|
+
expect(s.low).to.equal(20);
|
|
273
|
+
expect(s.high).to.equal(10);
|
|
274
|
+
expect((s as any).toExtendedJSON()).to.deep.equal({
|
|
275
|
+
$timestamp: { t: 10, i: 20 },
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
describe('Code', function () {
|
|
280
|
+
it('expects arguments in order', function () {
|
|
281
|
+
const code = shellBson.Code('code', { k: 'v' });
|
|
282
|
+
expect(code.code).to.equal('code');
|
|
283
|
+
expect(code.scope).to.deep.equal({ k: 'v' });
|
|
284
|
+
});
|
|
285
|
+
it('works with a function argument', function () {
|
|
286
|
+
const fn = function () {
|
|
287
|
+
expect.fail();
|
|
288
|
+
};
|
|
289
|
+
const code = shellBson.Code(fn, { k: 'v' });
|
|
290
|
+
expect(code.code).to.equal(fn.toString());
|
|
291
|
+
expect(code.scope).to.deep.equal({ k: 'v' });
|
|
292
|
+
});
|
|
293
|
+
it('has help and other metadata', function () {
|
|
294
|
+
const s = shellBson.Code('code', { k: 'v' });
|
|
295
|
+
expect(s.help?.type).to.equal('Help');
|
|
296
|
+
expect(s.help?.().type).to.equal('Help');
|
|
297
|
+
expect((s as any).serverVersions).to.deep.equal(ALL_SERVER_VERSIONS);
|
|
298
|
+
});
|
|
299
|
+
it('errors for wrong type of arg 1', function () {
|
|
300
|
+
try {
|
|
301
|
+
(shellBson.Code as any)(1);
|
|
302
|
+
} catch (e: any) {
|
|
303
|
+
return expect(e.message).to.contain('function, got number');
|
|
304
|
+
}
|
|
305
|
+
expect.fail('Expecting error, nothing thrown');
|
|
306
|
+
});
|
|
307
|
+
it('errors for wrong type of arg 2', function () {
|
|
308
|
+
try {
|
|
309
|
+
(shellBson.Code as any)('code', 1);
|
|
310
|
+
} catch (e: any) {
|
|
311
|
+
return expect(e.message).to.contain('object, got number');
|
|
312
|
+
}
|
|
313
|
+
expect.fail('Expecting error, nothing thrown');
|
|
314
|
+
});
|
|
315
|
+
it('constructs with default args 1', function () {
|
|
316
|
+
const s = shellBson.Code();
|
|
317
|
+
expect(s.code).to.equal('');
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
describe('ISODate', function () {
|
|
321
|
+
it('ISODate is always object', function () {
|
|
322
|
+
const date = new (shellBson.ISODate as any)();
|
|
323
|
+
expect(typeof date).to.equal('object');
|
|
324
|
+
const date2 = shellBson.ISODate();
|
|
325
|
+
expect(typeof date2).to.equal('object');
|
|
326
|
+
});
|
|
327
|
+
it('accepts ISO args', function () {
|
|
328
|
+
expect(shellBson.ISODate('2020-10-02').getTime()).to.equal(1601596800000);
|
|
329
|
+
expect(shellBson.ISODate('2020-10-02T10:29:50+00:00').getTime()).to.equal(
|
|
330
|
+
1601634590000
|
|
331
|
+
);
|
|
332
|
+
expect(shellBson.ISODate('2020-10-02T10:29:50+02:00').getTime()).to.equal(
|
|
333
|
+
1601627390000
|
|
334
|
+
);
|
|
335
|
+
expect(shellBson.ISODate('2020-10-02T10:29:50-02:00').getTime()).to.equal(
|
|
336
|
+
1601641790000
|
|
337
|
+
);
|
|
338
|
+
expect(shellBson.ISODate('2020-10-02T10:29:50Z').getTime()).to.equal(
|
|
339
|
+
1601634590000
|
|
340
|
+
);
|
|
341
|
+
expect(shellBson.ISODate('2020-10-02T10:29:50').getTime()).to.equal(
|
|
342
|
+
1601634590000
|
|
343
|
+
);
|
|
344
|
+
expect(shellBson.ISODate('2020-10-02T10:29:50.124Z').getTime()).to.equal(
|
|
345
|
+
1601634590124
|
|
346
|
+
);
|
|
347
|
+
expect(shellBson.ISODate('20201002T102950Z').getTime()).to.equal(
|
|
348
|
+
1601634590000
|
|
349
|
+
);
|
|
350
|
+
expect(shellBson.ISODate('20201002T102950').getTime()).to.equal(
|
|
351
|
+
1601634590000
|
|
352
|
+
);
|
|
353
|
+
expect(shellBson.ISODate('20201002T102950+0000').getTime()).to.equal(
|
|
354
|
+
1601634590000
|
|
355
|
+
);
|
|
356
|
+
expect(shellBson.ISODate('20201002T102950-0000').getTime()).to.equal(
|
|
357
|
+
1601634590000
|
|
358
|
+
);
|
|
359
|
+
expect(shellBson.ISODate('20201002T102950+0200').getTime()).to.equal(
|
|
360
|
+
1601627390000
|
|
361
|
+
);
|
|
362
|
+
expect(shellBson.ISODate('20201002T102950-0200').getTime()).to.equal(
|
|
363
|
+
1601641790000
|
|
364
|
+
);
|
|
365
|
+
expect(shellBson.ISODate('20201002 102950Z').getTime()).to.equal(
|
|
366
|
+
1601634590000
|
|
367
|
+
);
|
|
368
|
+
expect(shellBson.ISODate('20201002 102950+0000').getTime()).to.equal(
|
|
369
|
+
1601634590000
|
|
370
|
+
);
|
|
371
|
+
expect(shellBson.ISODate('20201002 102950+0200').getTime()).to.equal(
|
|
372
|
+
1601627390000
|
|
373
|
+
);
|
|
374
|
+
expect(shellBson.ISODate('20201002 102950-0200').getTime()).to.equal(
|
|
375
|
+
1601641790000
|
|
376
|
+
);
|
|
377
|
+
expect(shellBson.ISODate('20201002 102950').getTime()).to.equal(
|
|
378
|
+
1601634590000
|
|
379
|
+
);
|
|
380
|
+
expect(shellBson.ISODate('20201002 102950.842').getTime()).to.equal(
|
|
381
|
+
1601634590842
|
|
382
|
+
);
|
|
383
|
+
});
|
|
384
|
+
it('rejects non-ISO args', function () {
|
|
385
|
+
expect(() => shellBson.ISODate('1/4/1977')).to.throw(
|
|
386
|
+
'"1/4/1977" is not a valid ISODate'
|
|
387
|
+
);
|
|
388
|
+
expect(() => shellBson.ISODate('1-4-1977')).to.throw(
|
|
389
|
+
'"1-4-1977" is not a valid ISODate'
|
|
390
|
+
);
|
|
391
|
+
expect(() => shellBson.ISODate('9999-12-31T23:99:59.999Z')).to.throw(
|
|
392
|
+
'"9999-12-31T23:99:59.999Z" is not a valid ISODate'
|
|
393
|
+
);
|
|
394
|
+
expect(() => shellBson.ISODate('bah')).to.throw(
|
|
395
|
+
'"bah" is not a valid ISODate'
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
try {
|
|
399
|
+
shellBson.ISODate('"');
|
|
400
|
+
} catch (e: any) {
|
|
401
|
+
expect(e).to.be.instanceOf(MongoshInvalidInputError);
|
|
402
|
+
expect(e.message).to.contain('"\\"" is not a valid ISODate');
|
|
403
|
+
expect(e.code).to.equal(CommonErrors.InvalidArgument);
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
expect.fail('expected error');
|
|
407
|
+
});
|
|
408
|
+
it('passes through non-string args to `new Date()`', function () {
|
|
409
|
+
expect(shellBson.ISODate()).to.be.an.instanceOf(Date);
|
|
410
|
+
expect((shellBson.ISODate as any)(0).getTime()).to.equal(0);
|
|
411
|
+
expect((shellBson.ISODate as any)(null).getTime()).to.equal(0);
|
|
412
|
+
expect((shellBson.ISODate as any)(1234).getTime()).to.equal(1234);
|
|
413
|
+
expect(
|
|
414
|
+
(shellBson.ISODate as any)((shellBson.ISODate as any)(1234)).getTime()
|
|
415
|
+
).to.equal(1234);
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
describe('BinData', function () {
|
|
419
|
+
it('expects strings as base 64', function () {
|
|
420
|
+
const b = shellBson.BinData(128, b64_1234);
|
|
421
|
+
expect(b.toString()).to.equal(utf_1234);
|
|
422
|
+
});
|
|
423
|
+
it('has help and other metadata', function () {
|
|
424
|
+
const s = shellBson.BinData(128, b64_1234);
|
|
425
|
+
expect(s.help?.type).to.equal('Help');
|
|
426
|
+
expect(s.help?.().type).to.equal('Help');
|
|
427
|
+
expect((s as any).serverVersions).to.deep.equal(ALL_SERVER_VERSIONS);
|
|
428
|
+
});
|
|
429
|
+
it('errors for missing arg 1', function () {
|
|
430
|
+
try {
|
|
431
|
+
(shellBson.BinData as any)();
|
|
432
|
+
} catch (e: any) {
|
|
433
|
+
return expect(e.message).to.contain('Missing required argument');
|
|
434
|
+
}
|
|
435
|
+
expect.fail('Expecting error, nothing thrown');
|
|
436
|
+
});
|
|
437
|
+
it('errors for missing arg 2', function () {
|
|
438
|
+
try {
|
|
439
|
+
(shellBson.BinData as any)(0);
|
|
440
|
+
} catch (e: any) {
|
|
441
|
+
return expect(e.message).to.contain('Missing required argument');
|
|
442
|
+
}
|
|
443
|
+
expect.fail('Expecting error, nothing thrown');
|
|
444
|
+
});
|
|
445
|
+
it('errors for wrong type of arg 1', function () {
|
|
446
|
+
try {
|
|
447
|
+
(shellBson.BinData as any)('1', 'abc');
|
|
448
|
+
} catch (e: any) {
|
|
449
|
+
return expect(e.message).to.contain('number, got string');
|
|
450
|
+
}
|
|
451
|
+
expect.fail('Expecting error, nothing thrown');
|
|
452
|
+
});
|
|
453
|
+
it('errors for wrong type of arg 2', function () {
|
|
454
|
+
try {
|
|
455
|
+
(shellBson.BinData as any)(0, 1);
|
|
456
|
+
} catch (e: any) {
|
|
457
|
+
return expect(e.message).to.contain('string, got number');
|
|
458
|
+
}
|
|
459
|
+
expect.fail('Expecting error, nothing thrown');
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
describe('HexData', function () {
|
|
463
|
+
let b: any;
|
|
464
|
+
let h: any;
|
|
465
|
+
before(function () {
|
|
466
|
+
b = shellBson.BinData(128, b64_1234);
|
|
467
|
+
h = shellBson.HexData(128, hex_1234);
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
it('equals BinData', function () {
|
|
471
|
+
expect(b.value()).to.deep.equal(h.value());
|
|
472
|
+
expect(b.sub_type).to.equal(h.sub_type);
|
|
473
|
+
});
|
|
474
|
+
it('equals 1234', function () {
|
|
475
|
+
expect(h.toString()).to.equal(utf_1234);
|
|
476
|
+
});
|
|
477
|
+
it('has subtype', function () {
|
|
478
|
+
expect(h.sub_type).to.equal(128);
|
|
479
|
+
});
|
|
480
|
+
it('has help and other metadata', function () {
|
|
481
|
+
expect(h.help.type).to.equal('Help');
|
|
482
|
+
expect(h.help().type).to.equal('Help');
|
|
483
|
+
expect(h.serverVersions).to.deep.equal(ALL_SERVER_VERSIONS);
|
|
484
|
+
});
|
|
485
|
+
it('errors for missing arg 1', function () {
|
|
486
|
+
try {
|
|
487
|
+
(shellBson.HexData as any)();
|
|
488
|
+
} catch (e: any) {
|
|
489
|
+
return expect(e.message).to.contain('Missing required argument');
|
|
490
|
+
}
|
|
491
|
+
expect.fail('Expecting error, nothing thrown');
|
|
492
|
+
});
|
|
493
|
+
it('errors for missing arg 2', function () {
|
|
494
|
+
try {
|
|
495
|
+
(shellBson.HexData as any)(0);
|
|
496
|
+
} catch (e: any) {
|
|
497
|
+
return expect(e.message).to.contain('Missing required argument');
|
|
498
|
+
}
|
|
499
|
+
expect.fail('Expecting error, nothing thrown');
|
|
500
|
+
});
|
|
501
|
+
it('errors for wrong type of arg 1', function () {
|
|
502
|
+
try {
|
|
503
|
+
(shellBson.HexData as any)('1', 'abc');
|
|
504
|
+
} catch (e: any) {
|
|
505
|
+
return expect(e.message).to.contain('number, got string');
|
|
506
|
+
}
|
|
507
|
+
expect.fail('Expecting error, nothing thrown');
|
|
508
|
+
});
|
|
509
|
+
it('errors for wrong type of arg 2', function () {
|
|
510
|
+
try {
|
|
511
|
+
(shellBson.HexData as any)(0, 1);
|
|
512
|
+
} catch (e: any) {
|
|
513
|
+
return expect(e.message).to.contain('string, got number');
|
|
514
|
+
}
|
|
515
|
+
expect.fail('Expecting error, nothing thrown');
|
|
516
|
+
});
|
|
517
|
+
});
|
|
518
|
+
describe('UUID', function () {
|
|
519
|
+
let b: any;
|
|
520
|
+
let h: any;
|
|
521
|
+
before(function () {
|
|
522
|
+
b = shellBson.BinData(4, b64_1234);
|
|
523
|
+
h = shellBson.UUID(hex_1234);
|
|
524
|
+
});
|
|
525
|
+
it('equals BinData', function () {
|
|
526
|
+
expect(b.value()).to.deep.equal(h.value());
|
|
527
|
+
expect(b.sub_type).to.equal(h.sub_type);
|
|
528
|
+
});
|
|
529
|
+
it('equals 1234', function () {
|
|
530
|
+
expect(h.toString()).to.equal(utf_1234);
|
|
531
|
+
});
|
|
532
|
+
it('has subtype', function () {
|
|
533
|
+
expect(h.sub_type).to.equal(4);
|
|
534
|
+
});
|
|
535
|
+
it('has help and other metadata', function () {
|
|
536
|
+
expect(h.help.type).to.equal('Help');
|
|
537
|
+
expect(h.help().type).to.equal('Help');
|
|
538
|
+
expect(h.serverVersions).to.deep.equal(ALL_SERVER_VERSIONS);
|
|
539
|
+
});
|
|
540
|
+
it('strips dashes from input', function () {
|
|
541
|
+
expect(
|
|
542
|
+
shellBson.UUID('01234567-89ab-cdef-0123-456789abcdef').value()
|
|
543
|
+
).to.deep.equal(
|
|
544
|
+
shellBson.UUID('0123456789abcdef0123456789abcdef').value()
|
|
545
|
+
);
|
|
546
|
+
});
|
|
547
|
+
it('generates a random UUID when no arguments are passed', function () {
|
|
548
|
+
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Format
|
|
549
|
+
expect(shellBson.UUID().toString('hex')).to.match(
|
|
550
|
+
/^[a-z0-9]{12}4[a-z0-9]{3}[89ab][a-z0-9]{15}$/
|
|
551
|
+
);
|
|
552
|
+
});
|
|
553
|
+
it('errors for wrong type of arg 1', function () {
|
|
554
|
+
try {
|
|
555
|
+
(shellBson.UUID as any)(1);
|
|
556
|
+
} catch (e: any) {
|
|
557
|
+
return expect(e.message).to.contain('string, got number');
|
|
558
|
+
}
|
|
559
|
+
expect.fail('Expecting error, nothing thrown');
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
describe('MD5', function () {
|
|
563
|
+
let b: any;
|
|
564
|
+
let h: any;
|
|
565
|
+
before(function () {
|
|
566
|
+
b = shellBson.BinData(5, b64_1234);
|
|
567
|
+
h = shellBson.MD5(hex_1234);
|
|
568
|
+
});
|
|
569
|
+
it('equals BinData', function () {
|
|
570
|
+
expect(b.value()).to.deep.equal(h.value());
|
|
571
|
+
expect(b.sub_type).to.equal(h.sub_type);
|
|
572
|
+
});
|
|
573
|
+
it('equals 1234', function () {
|
|
574
|
+
expect(h.toString()).to.equal(utf_1234);
|
|
575
|
+
});
|
|
576
|
+
it('has subtype', function () {
|
|
577
|
+
expect(h.sub_type).to.equal(5);
|
|
578
|
+
});
|
|
579
|
+
it('has help and other metadata', function () {
|
|
580
|
+
expect(h.help.type).to.equal('Help');
|
|
581
|
+
expect(h.help().type).to.equal('Help');
|
|
582
|
+
expect(h.serverVersions).to.deep.equal(ALL_SERVER_VERSIONS);
|
|
583
|
+
});
|
|
584
|
+
it('errors for missing arg 1', function () {
|
|
585
|
+
try {
|
|
586
|
+
(shellBson.MD5 as any)();
|
|
587
|
+
} catch (e: any) {
|
|
588
|
+
return expect(e.message).to.contain('Missing required argument');
|
|
589
|
+
}
|
|
590
|
+
expect.fail('Expecting error, nothing thrown');
|
|
591
|
+
});
|
|
592
|
+
it('errors for wrong type of arg 1', function () {
|
|
593
|
+
try {
|
|
594
|
+
(shellBson.MD5 as any)(1);
|
|
595
|
+
} catch (e: any) {
|
|
596
|
+
return expect(e.message).to.contain('string, got number');
|
|
597
|
+
}
|
|
598
|
+
expect.fail('Expecting error, nothing thrown');
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
describe('bsonsize', function () {
|
|
602
|
+
it('calculates empty doc size', function () {
|
|
603
|
+
expect(shellBson.bsonsize({})).to.equal(5);
|
|
604
|
+
});
|
|
605
|
+
it('errors for missing arg', function () {
|
|
606
|
+
try {
|
|
607
|
+
(shellBson.bsonsize as any)();
|
|
608
|
+
} catch (e: any) {
|
|
609
|
+
return expect(e.message).to.contain('Missing required argument');
|
|
610
|
+
}
|
|
611
|
+
expect.fail('Expecting error, nothing thrown');
|
|
612
|
+
});
|
|
613
|
+
it('errors for wrong type of arg 1', function () {
|
|
614
|
+
try {
|
|
615
|
+
(shellBson.bsonsize as any)(1);
|
|
616
|
+
} catch (e: any) {
|
|
617
|
+
return expect(e.message).to.contain('object, got number');
|
|
618
|
+
}
|
|
619
|
+
expect.fail('Expecting error, nothing thrown');
|
|
620
|
+
});
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
describe('NumberLong', function () {
|
|
624
|
+
it('creates a bson.Long', function () {
|
|
625
|
+
const n = shellBson.NumberLong('123');
|
|
626
|
+
expect(n).to.be.instanceOf(bson.Long);
|
|
627
|
+
expect(bson.Long.fromString('123').eq(n)).to.be.true;
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
it('constructs 0 if the argument is not provided', function () {
|
|
631
|
+
expect(bson.Long.fromString('0').eq(shellBson.NumberLong())).to.be.true;
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it('correctly constructs numbers > MAX_SAFE_INTEGER', function () {
|
|
635
|
+
expect(shellBson.NumberLong('345678654321234552').toString()).to.equal(
|
|
636
|
+
'345678654321234552'
|
|
637
|
+
);
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
it('correctly constructs large numbers < MAX_SAFE_INTEGER from their JS number value', function () {
|
|
641
|
+
expect(shellBson.NumberLong(68719476736).toString()).to.equal(
|
|
642
|
+
'68719476736'
|
|
643
|
+
);
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
it('creates a bson.Long for unrecommended integer and prints warning', function () {
|
|
647
|
+
const n = shellBson.NumberLong(123.5);
|
|
648
|
+
expect(n).to.be.instanceOf(bson.Long);
|
|
649
|
+
expect(bson.Long.fromString('123').eq(n)).to.be.true;
|
|
650
|
+
expect(printWarning).to.have.been.calledWith(
|
|
651
|
+
'NumberLong: specifying a number as argument is deprecated and may lead to loss of precision, pass a string instead'
|
|
652
|
+
);
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
it('errors for wrong type of arg 1', function () {
|
|
656
|
+
try {
|
|
657
|
+
(shellBson.NumberLong as any)({});
|
|
658
|
+
} catch (e: any) {
|
|
659
|
+
return expect(e.message).to.match(
|
|
660
|
+
/string or number or Long or Int32, got object.+\(NumberLong\)/
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
expect.fail('Expecting error, nothing thrown');
|
|
664
|
+
});
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
describe('NumberDecimal', function () {
|
|
668
|
+
it('creates a bson.Decimal128', function () {
|
|
669
|
+
const n = shellBson.NumberDecimal('123.1');
|
|
670
|
+
expect(n).to.be.instanceOf(bson.Decimal128);
|
|
671
|
+
expect(n.toString()).to.equal('123.1');
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
it('constructs 0 if the argument is not provided', function () {
|
|
675
|
+
expect(shellBson.NumberDecimal().toString()).to.equal('0');
|
|
676
|
+
});
|
|
677
|
+
|
|
678
|
+
it('correctly constructs numbers > MAX_SAFE_INTEGER', function () {
|
|
679
|
+
expect(
|
|
680
|
+
shellBson.NumberDecimal('345678654321234552.0').toString()
|
|
681
|
+
).to.equal('345678654321234552.0');
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
it('creates a bson.Decimal128 for unrecommended integer and prints warning', function () {
|
|
685
|
+
const n = (shellBson.NumberDecimal as any)(123);
|
|
686
|
+
expect(n).to.be.instanceOf(bson.Decimal128);
|
|
687
|
+
expect(bson.Decimal128.fromString('123').toString()).to.equal(
|
|
688
|
+
n.toString()
|
|
689
|
+
);
|
|
690
|
+
expect(printWarning).to.have.been.calledWith(
|
|
691
|
+
'NumberDecimal: specifying a number as argument is deprecated and may lead to loss of precision, pass a string instead'
|
|
692
|
+
);
|
|
693
|
+
});
|
|
694
|
+
|
|
695
|
+
it('errors for wrong type of arg 1', function () {
|
|
696
|
+
try {
|
|
697
|
+
(shellBson.NumberDecimal as any)({});
|
|
698
|
+
} catch (e: any) {
|
|
699
|
+
return expect(e.message).to.match(
|
|
700
|
+
/string or number or Long or Int32 or Decimal128, got object.+\(NumberDecimal\)/
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
expect.fail('Expecting error, nothing thrown');
|
|
704
|
+
});
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
describe('NumberInt', function () {
|
|
708
|
+
it('creates a bson.Int32 from string', function () {
|
|
709
|
+
const n = shellBson.NumberInt('1');
|
|
710
|
+
expect(n).to.be.instanceOf(bson.Int32);
|
|
711
|
+
expect(n.value).to.equal(1);
|
|
712
|
+
});
|
|
713
|
+
|
|
714
|
+
it('creates a bson.Int32 from number', function () {
|
|
715
|
+
const n = (shellBson.NumberInt as any)(123);
|
|
716
|
+
expect(n).to.be.instanceOf(bson.Int32);
|
|
717
|
+
expect(new bson.Int32(123).value).to.equal(n.value);
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
it('creates a bson.Int32 from non-integer number', function () {
|
|
721
|
+
const n = (shellBson.NumberInt as any)(123.5);
|
|
722
|
+
expect(n).to.be.instanceOf(bson.Int32);
|
|
723
|
+
expect(new bson.Int32(123).value).to.equal(n.value);
|
|
724
|
+
});
|
|
725
|
+
|
|
726
|
+
it('constructs 0 if the argument is not provided', function () {
|
|
727
|
+
expect(shellBson.NumberInt().value).to.equal(0);
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
it('errors for wrong type of arg 1', function () {
|
|
731
|
+
try {
|
|
732
|
+
(shellBson.NumberInt as any)({});
|
|
733
|
+
} catch (e: any) {
|
|
734
|
+
return expect(e.message).to.match(
|
|
735
|
+
/string or number or Long or Int32, got object.+\(NumberInt\)/
|
|
736
|
+
);
|
|
737
|
+
}
|
|
738
|
+
expect.fail('Expecting error, nothing thrown');
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
describe('Binary', function () {
|
|
743
|
+
it('creates a Binary value using createFromBase64', function () {
|
|
744
|
+
const n = shellBson.Binary.createFromBase64('SGVsbG8sIFdvcmxkIQo=');
|
|
745
|
+
expect(n).to.be.instanceOf(bson.Binary);
|
|
746
|
+
expect(n.toString()).to.equal('Hello, World!\n');
|
|
747
|
+
});
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
describe('Number type cross-construction', function () {
|
|
751
|
+
it('matches the legacy shell', function () {
|
|
752
|
+
const { NumberInt, NumberLong, NumberDecimal } = shellBson as any;
|
|
753
|
+
expect(NumberInt(null).toString()).to.equal('0');
|
|
754
|
+
expect(NumberLong(null).toString()).to.equal('0');
|
|
755
|
+
|
|
756
|
+
expect(NumberInt(NumberInt(1234)).toString()).to.equal('1234');
|
|
757
|
+
expect(NumberInt(NumberLong(1234)).toString()).to.equal('1234');
|
|
758
|
+
expect(NumberInt(NumberLong(1234)).toString()).to.equal('1234');
|
|
759
|
+
expect(NumberLong(NumberInt(1234)).toString()).to.equal('1234');
|
|
760
|
+
expect(NumberLong(NumberLong(1234)).toString()).to.equal('1234');
|
|
761
|
+
expect(NumberDecimal(NumberInt(1234)).toString()).to.equal('1234');
|
|
762
|
+
expect(NumberDecimal(NumberLong(1234)).toString()).to.equal('1234');
|
|
763
|
+
expect(NumberDecimal(NumberDecimal(1234)).toString()).to.equal('1234');
|
|
764
|
+
});
|
|
765
|
+
});
|
|
766
|
+
|
|
767
|
+
describe('EJSON', function () {
|
|
768
|
+
it('serializes and de-serializes data', function () {
|
|
769
|
+
const input = { a: new Date() };
|
|
770
|
+
const output = shellBson.EJSON.parse(shellBson.EJSON.stringify(input));
|
|
771
|
+
expect(input).to.deep.equal(output);
|
|
772
|
+
});
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
describe('BSON constructor properties', function () {
|
|
776
|
+
for (const key of Object.keys(bson) as (keyof typeof bson &
|
|
777
|
+
keyof ShellBson)[]) {
|
|
778
|
+
it(`matches original BSON constructor properties (${key})`, function () {
|
|
779
|
+
if (!(key in shellBson) || bson[key] === shellBson[key]) {
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
if (key === 'UUID') {
|
|
784
|
+
// TODO(MONGOSH-2710): Special case, we still need to make these match,
|
|
785
|
+
// they currently do not because our UUID helper predates the addition
|
|
786
|
+
// the driver helper.
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
const bsonProperties = Object.getOwnPropertyDescriptors(bson[key]);
|
|
791
|
+
const shellProperties = Object.getOwnPropertyDescriptors(
|
|
792
|
+
shellBson[key]
|
|
793
|
+
);
|
|
794
|
+
delete shellProperties.help; // Not expected from the original BSON.
|
|
795
|
+
delete shellProperties.length; // Function length can vary depending on the specific arguments in TS.
|
|
796
|
+
delete bsonProperties.length;
|
|
797
|
+
delete bsonProperties.index; // ObjectId.index is a random number
|
|
798
|
+
delete (shellProperties as any).toBSON; // toBSON is something we add for MaxKey/MinKey as a shell-specific extension
|
|
799
|
+
delete shellProperties.prototype?.writable; // We don't want to care about writable vs non-writable prototypes
|
|
800
|
+
delete bsonProperties.prototype?.writable;
|
|
801
|
+
|
|
802
|
+
// Non-public methods:
|
|
803
|
+
delete (bsonProperties as any).fromExtendedJSON; // Long.fromExtendedJSON was not made public on purpose
|
|
804
|
+
delete bsonProperties.BSON_BINARY_SUBTYPE_DEFAULT; // private
|
|
805
|
+
delete bsonProperties.createPk; // @internal
|
|
806
|
+
delete bsonProperties.getInc; // private
|
|
807
|
+
delete bsonProperties.is; // private
|
|
808
|
+
delete bsonProperties._fromString; // private
|
|
809
|
+
delete bsonProperties.validateHexString; // private
|
|
810
|
+
|
|
811
|
+
try {
|
|
812
|
+
expect(shellProperties).to.deep.equal(bsonProperties);
|
|
813
|
+
} catch (err: any) {
|
|
814
|
+
err.message += ` (${key})`;
|
|
815
|
+
throw err;
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
});
|
|
820
|
+
});
|