Skip to content

Commit a93b8fd

Browse files
committed
some unit tests
1 parent 72d8679 commit a93b8fd

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
import { type ServiceProvider } from '@mongosh/service-provider-core';
2+
import * as bson from 'bson';
3+
import * as chai from 'chai';
4+
import { expect } from 'chai';
5+
import sinonChai from 'sinon-chai';
6+
import sinon from 'sinon';
7+
import type { StubbedInstance } from 'ts-sinon';
8+
import { stubInterface } from 'ts-sinon';
9+
import { DeepInspectServiceProviderWrapper } from './deep-inspect-service-provider-wrapper';
10+
import * as util from 'util';
11+
chai.use(sinonChai);
12+
13+
const customInspectSymbol = Symbol.for('nodejs.util.inspect.custom');
14+
15+
function truncatedString(text: string): boolean {
16+
return /\d+ more character/.test(text);
17+
}
18+
19+
function truncatedArray(text: string): boolean {
20+
return /\d+ more item/.test(text);
21+
}
22+
function truncatedObject(text: string): boolean {
23+
return /\[Object\]/.test(text);
24+
}
25+
26+
function wasTruncated(text: string): boolean {
27+
return truncatedString(text) || truncatedArray(text) || truncatedObject(text);
28+
}
29+
30+
describe('DeepInspectServiceProviderWrapper', function () {
31+
let serviceProvider: StubbedInstance<ServiceProvider>;
32+
let sp: DeepInspectServiceProviderWrapper;
33+
34+
const doc = {
35+
array: Array.from(Array(1000), (_, i) => i),
36+
string: 'All work and no play makes Jack a dull boy. '.repeat(250),
37+
object: {
38+
foo: {
39+
bar: {
40+
baz: {
41+
qux: {
42+
quux: {
43+
corge: {
44+
grault: 'If you can read this, you are too close.',
45+
},
46+
},
47+
},
48+
},
49+
},
50+
},
51+
},
52+
};
53+
54+
function checkResultDoc(result: any) {
55+
expect(result).to.deep.equal(doc);
56+
expect((result as any)[customInspectSymbol]).to.be.a('function');
57+
expect((result as any).array[customInspectSymbol]).to.be.a('function');
58+
expect((result as any).object[customInspectSymbol]).to.be.a('function');
59+
expect((result as any).object.foo[customInspectSymbol]).to.be.a('function');
60+
expect(wasTruncated(util.inspect(result))).to.equal(false);
61+
expect(wasTruncated(util.inspect(result?.array))).to.equal(false);
62+
expect(wasTruncated(util.inspect(result?.object))).to.equal(false);
63+
expect(wasTruncated(util.inspect(result?.object.foo))).to.equal(false);
64+
}
65+
66+
const everyType = {
67+
double: new bson.Double(1.2),
68+
doubleThatIsAlsoAnInteger: new bson.Double(1),
69+
string: 'Hello, world!',
70+
binData: new bson.Binary(Buffer.from([1, 2, 3])),
71+
boolean: true,
72+
date: new Date('2023-04-05T13:25:08.445Z'),
73+
null: null,
74+
regex: new bson.BSONRegExp('pattern', 'i'),
75+
javascript: new bson.Code('function() {}'),
76+
symbol: new bson.BSONSymbol('symbol'),
77+
javascriptWithScope: new bson.Code('function() {}', { foo: 1, bar: 'a' }),
78+
int: new bson.Int32(12345),
79+
timestamp: new bson.Timestamp(bson.Long.fromString('7218556297505931265')),
80+
long: bson.Long.fromString('123456789123456789'),
81+
decimal: new bson.Decimal128(
82+
Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
83+
),
84+
minKey: new bson.MinKey(),
85+
maxKey: new bson.MaxKey(),
86+
87+
binaries: {
88+
generic: new bson.Binary(Buffer.from([1, 2, 3]), 0),
89+
functionData: new bson.Binary(Buffer.from('//8='), 1),
90+
binaryOld: new bson.Binary(Buffer.from('//8='), 2),
91+
uuidOld: new bson.Binary(Buffer.from('c//SZESzTGmQ6OfR38A11A=='), 3),
92+
uuid: new bson.UUID('AAAAAAAA-AAAA-4AAA-AAAA-AAAAAAAAAAAA'),
93+
md5: new bson.Binary(Buffer.from('c//SZESzTGmQ6OfR38A11A=='), 5),
94+
encrypted: new bson.Binary(Buffer.from('c//SZESzTGmQ6OfR38A11A=='), 6),
95+
compressedTimeSeries: new bson.Binary(
96+
Buffer.from(
97+
'CQCKW/8XjAEAAIfx//////////H/////////AQAAAAAAAABfAAAAAAAAAAEAAAAAAAAAAgAAAAAAAAAHAAAAAAAAAA4AAAAAAAAAAA==',
98+
'base64'
99+
)
100+
),
101+
custom: new bson.Binary(Buffer.from('//8='), 128),
102+
},
103+
104+
dbRef: new bson.DBRef(
105+
'namespace',
106+
new bson.ObjectId('642d76b4b7ebfab15d3c4a78')
107+
),
108+
};
109+
110+
beforeEach(function () {
111+
serviceProvider = stubInterface<ServiceProvider>();
112+
serviceProvider.initialDb = 'db1';
113+
serviceProvider.bsonLibrary = bson;
114+
sp = new DeepInspectServiceProviderWrapper(serviceProvider);
115+
});
116+
117+
it('would have truncated the test documents without deep inspection', function () {
118+
// make sure that our assertions would have caught truncation if it were to happen
119+
const text = util.inspect(doc);
120+
expect(truncatedString(text)).to.equal(true);
121+
expect(truncatedArray(text)).to.equal(true);
122+
expect(truncatedObject(text)).to.equal(true);
123+
expect(wasTruncated(text)).to.equal(true);
124+
});
125+
126+
it('wraps forwarded methods', async function () {
127+
serviceProvider.count.resolves(5);
128+
const result = await sp.count('testDb', 'testColl', {});
129+
expect(result).to.equal(5);
130+
});
131+
132+
it('wraps bson methods', async function () {
133+
serviceProvider.runCommand.resolves(doc);
134+
const result = await sp.runCommand('testDb', {}, {}, {});
135+
checkResultDoc(result);
136+
});
137+
138+
it('wraps find cursors', async function () {
139+
const stubs = {
140+
// forwarded method
141+
allowDiskUse: sinon.stub(),
142+
143+
// forwarded method that returns this for chaining
144+
withReadPreference: sinon.stub().returnsThis(),
145+
146+
// methods that return results, promises of results, etc.
147+
next: sinon.stub().resolves(doc),
148+
tryNext: sinon.stub().resolves(doc),
149+
toArray: sinon.stub().resolves([doc, everyType]),
150+
readBufferedDocuments: sinon.stub().returns([doc, everyType]),
151+
};
152+
serviceProvider.find.returns(stubs as any);
153+
154+
const cursor = sp.find('testDb', 'testColl', {}, {}, {});
155+
156+
cursor.withReadPreference('primary').allowDiskUse();
157+
expect(stubs.withReadPreference).to.have.been.calledOnce;
158+
expect(stubs.allowDiskUse).to.have.been.calledOnce;
159+
160+
const nextResult = await cursor.next();
161+
checkResultDoc(nextResult);
162+
163+
const tryNextResult = await cursor.tryNext();
164+
checkResultDoc(tryNextResult);
165+
166+
const toArrayResult = await cursor.toArray();
167+
expect(toArrayResult).to.deep.equal([doc, everyType]);
168+
checkResultDoc(toArrayResult[0]);
169+
170+
const readBufferedDocumentsResult = cursor.readBufferedDocuments();
171+
expect(readBufferedDocumentsResult).to.deep.equal([doc, everyType]);
172+
checkResultDoc(readBufferedDocumentsResult[0]);
173+
});
174+
175+
it('wraps aggregation cursors', async function () {
176+
const stubs = {
177+
// forwarded method
178+
project: sinon.stub(),
179+
180+
// forwarded method that returns this for chaining
181+
withReadPreference: sinon.stub().returnsThis(),
182+
183+
// methods that return results, promises of results, etc.
184+
next: sinon.stub().resolves(doc),
185+
tryNext: sinon.stub().resolves(doc),
186+
toArray: sinon.stub().resolves([doc, everyType]),
187+
readBufferedDocuments: sinon.stub().returns([doc, everyType]),
188+
};
189+
serviceProvider.aggregate.returns(stubs as any);
190+
191+
const cursor = sp.aggregate('testDb', 'testColl', [], {}, {});
192+
193+
cursor.withReadPreference('primary').project({});
194+
expect(stubs.withReadPreference).to.have.been.calledOnce;
195+
expect(stubs.project).to.have.been.calledOnce;
196+
197+
const nextResult = await cursor.next();
198+
checkResultDoc(nextResult);
199+
200+
const tryNextResult = await cursor.tryNext();
201+
checkResultDoc(tryNextResult);
202+
203+
const toArrayResult = await cursor.toArray();
204+
checkResultDoc(toArrayResult[0]);
205+
206+
const readBufferedDocumentsResult = cursor.readBufferedDocuments();
207+
checkResultDoc(readBufferedDocumentsResult[0]);
208+
});
209+
210+
it('wraps run command cursors', async function () {
211+
const stubs = {
212+
// forwarded method
213+
batchSize: sinon.stub(),
214+
215+
// methods that return results, promises of results, etc.
216+
next: sinon.stub().resolves(doc),
217+
tryNext: sinon.stub().resolves(doc),
218+
toArray: sinon.stub().resolves([doc, everyType]),
219+
readBufferedDocuments: sinon.stub().returns([doc, everyType]),
220+
};
221+
serviceProvider.runCursorCommand.returns(stubs as any);
222+
223+
const cursor = sp.runCursorCommand('testDb', {}, {}, {});
224+
225+
cursor.batchSize(10);
226+
expect(stubs.batchSize).to.have.been.calledOnce;
227+
228+
const nextResult = await cursor.next();
229+
checkResultDoc(nextResult);
230+
231+
const tryNextResult = await cursor.tryNext();
232+
checkResultDoc(tryNextResult);
233+
234+
const toArrayResult = await cursor.toArray();
235+
expect(toArrayResult).to.deep.equal([doc, everyType]);
236+
checkResultDoc(toArrayResult[0]);
237+
238+
const readBufferedDocumentsResult = cursor.readBufferedDocuments();
239+
expect(readBufferedDocumentsResult).to.deep.equal([doc, everyType]);
240+
checkResultDoc(readBufferedDocumentsResult[0]);
241+
});
242+
243+
it('wraps change streams', async function () {
244+
const stubs = {
245+
// forwarded method
246+
hasNext: sinon.stub().resolves(true),
247+
248+
// methods that return results, promises of results, etc.
249+
next: sinon.stub().resolves(doc),
250+
tryNext: sinon.stub().resolves(doc),
251+
toArray: sinon.stub().resolves([doc, everyType]),
252+
readBufferedDocuments: sinon.stub().returns([doc, everyType]),
253+
};
254+
serviceProvider.watch.returns(stubs as any);
255+
256+
const cursor = sp.watch([], {}, {});
257+
258+
await cursor.hasNext();
259+
expect(stubs.hasNext).to.have.been.calledOnce;
260+
261+
const nextResult = await cursor.next();
262+
checkResultDoc(nextResult);
263+
264+
const tryNextResult = await cursor.tryNext();
265+
checkResultDoc(tryNextResult);
266+
});
267+
});

0 commit comments

Comments
 (0)