@mongosh/service-provider-node-driver 2.3.3
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 +20 -0
- package/.eslintignore +2 -0
- package/.eslintrc.js +10 -0
- package/.prettierignore +6 -0
- package/.prettierrc.json +1 -0
- package/AUTHORS +3 -0
- package/LICENSE +201 -0
- package/lib/compass/compass-service-provider.d.ts +11 -0
- package/lib/compass/compass-service-provider.js +12 -0
- package/lib/compass/compass-service-provider.js.map +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +8 -0
- package/lib/index.js.map +1 -0
- package/lib/mongodb-patches.d.ts +4 -0
- package/lib/mongodb-patches.js +18 -0
- package/lib/mongodb-patches.js.map +1 -0
- package/lib/node-driver-service-provider.d.ts +107 -0
- package/lib/node-driver-service-provider.js +622 -0
- package/lib/node-driver-service-provider.js.map +1 -0
- package/package.json +76 -0
- package/src/compass/compass-service-provider.ts +30 -0
- package/src/index.ts +4 -0
- package/src/mongodb-patches.spec.ts +67 -0
- package/src/mongodb-patches.ts +25 -0
- package/src/node-driver-provider.integration.spec.ts +841 -0
- package/src/node-driver-service-provider.spec.ts +1302 -0
- package/src/node-driver-service-provider.ts +1536 -0
- package/tsconfig-lint.json +5 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,1302 @@
|
|
|
1
|
+
import { CommonErrors } from '@mongosh/errors';
|
|
2
|
+
import chai, { expect } from 'chai';
|
|
3
|
+
import type { ClientSession, SearchIndexDescription } from 'mongodb';
|
|
4
|
+
import { Collection, Db, MongoClient } from 'mongodb';
|
|
5
|
+
import sinonChai from 'sinon-chai';
|
|
6
|
+
import type { StubbedInstance } from 'ts-sinon';
|
|
7
|
+
import sinon, { stubInterface } from 'ts-sinon';
|
|
8
|
+
import type { DevtoolsConnectOptions } from './node-driver-service-provider';
|
|
9
|
+
import { NodeDriverServiceProvider } from './node-driver-service-provider';
|
|
10
|
+
import ConnectionString from 'mongodb-connection-string-url';
|
|
11
|
+
import { EventEmitter } from 'events';
|
|
12
|
+
import type {
|
|
13
|
+
ClientEncryption,
|
|
14
|
+
ClientEncryptionDataKeyProvider,
|
|
15
|
+
Document,
|
|
16
|
+
IndexDescription,
|
|
17
|
+
} from '@mongosh/service-provider-core';
|
|
18
|
+
|
|
19
|
+
chai.use(sinonChai);
|
|
20
|
+
|
|
21
|
+
export const dummyOptions: DevtoolsConnectOptions = Object.freeze({
|
|
22
|
+
productName: 'Test Product',
|
|
23
|
+
productDocsLink: 'https://example.com/',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const DEFAULT_BASE_OPTS = { serializeFunctions: true, promoteLongs: false };
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a client stub from the provided collection stub.
|
|
30
|
+
*
|
|
31
|
+
* @note: We basically only care about the method under test
|
|
32
|
+
* which is always mocked on a new collection stub each
|
|
33
|
+
* test run. We we can use the boilerplate creation of the
|
|
34
|
+
* db and client here.
|
|
35
|
+
*
|
|
36
|
+
* @param {Stub} collectionStub - The collection stub.
|
|
37
|
+
*
|
|
38
|
+
* @returns {Stub} The client stub to pass to the transport.
|
|
39
|
+
*/
|
|
40
|
+
const createClientStub = (
|
|
41
|
+
collectionStub: StubbedInstance<Collection>
|
|
42
|
+
): StubbedInstance<MongoClient> => {
|
|
43
|
+
const clientStub = stubInterface<MongoClient>();
|
|
44
|
+
const dbStub = stubInterface<Db>();
|
|
45
|
+
dbStub.collection.returns(collectionStub);
|
|
46
|
+
clientStub.db.returns(dbStub);
|
|
47
|
+
return clientStub;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
describe('NodeDriverServiceProvider', function () {
|
|
51
|
+
let serviceProvider: NodeDriverServiceProvider;
|
|
52
|
+
let collectionStub: StubbedInstance<Collection>;
|
|
53
|
+
let bus: EventEmitter;
|
|
54
|
+
|
|
55
|
+
beforeEach(function () {
|
|
56
|
+
bus = new EventEmitter();
|
|
57
|
+
collectionStub = stubInterface<Collection>();
|
|
58
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
59
|
+
{} as any,
|
|
60
|
+
bus,
|
|
61
|
+
dummyOptions
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('#constructor', function () {
|
|
66
|
+
const mongoClient: any = sinon.spy();
|
|
67
|
+
beforeEach(function () {
|
|
68
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
69
|
+
mongoClient,
|
|
70
|
+
bus,
|
|
71
|
+
dummyOptions
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('sets the mongo client on the instance', function () {
|
|
76
|
+
expect((serviceProvider as any).mongoClient).to.equal(mongoClient);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('#aggregate', function () {
|
|
81
|
+
const pipeline = [{ $match: { name: 'Aphex Twin' } }];
|
|
82
|
+
const aggResult = [{ name: 'Aphex Twin' }];
|
|
83
|
+
|
|
84
|
+
beforeEach(function () {
|
|
85
|
+
collectionStub = stubInterface<Collection>();
|
|
86
|
+
collectionStub.aggregate.returns({
|
|
87
|
+
toArray: () => Promise.resolve(aggResult),
|
|
88
|
+
} as any);
|
|
89
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
90
|
+
createClientStub(collectionStub),
|
|
91
|
+
bus,
|
|
92
|
+
dummyOptions
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('executes the command against the database', async function () {
|
|
97
|
+
const cursor = serviceProvider.aggregate('music', 'bands', pipeline);
|
|
98
|
+
const result = await cursor.toArray();
|
|
99
|
+
expect(result).to.deep.equal(aggResult);
|
|
100
|
+
expect(collectionStub.aggregate).to.have.been.calledWith(pipeline);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
describe('#bulkWrite', function () {
|
|
105
|
+
const requests = [{ insertOne: { name: 'Aphex Twin' } } as any];
|
|
106
|
+
const commandResult = { result: { nInserted: 1, ok: 1 } };
|
|
107
|
+
|
|
108
|
+
beforeEach(function () {
|
|
109
|
+
collectionStub = stubInterface<Collection>();
|
|
110
|
+
collectionStub.bulkWrite.resolves(commandResult as any);
|
|
111
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
112
|
+
createClientStub(collectionStub),
|
|
113
|
+
bus,
|
|
114
|
+
dummyOptions
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('executes the command against the database', async function () {
|
|
119
|
+
const result = await serviceProvider.bulkWrite(
|
|
120
|
+
'music',
|
|
121
|
+
'bands',
|
|
122
|
+
requests
|
|
123
|
+
);
|
|
124
|
+
expect(result).to.deep.equal(commandResult);
|
|
125
|
+
expect(collectionStub.bulkWrite).to.have.been.calledWith(requests);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('#countDocuments', function () {
|
|
130
|
+
const countResult = 10;
|
|
131
|
+
|
|
132
|
+
beforeEach(function () {
|
|
133
|
+
collectionStub = stubInterface<Collection>();
|
|
134
|
+
collectionStub.countDocuments.resolves(countResult);
|
|
135
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
136
|
+
createClientStub(collectionStub),
|
|
137
|
+
bus,
|
|
138
|
+
dummyOptions
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('executes the command against the database', async function () {
|
|
143
|
+
const result = await serviceProvider.countDocuments('music', 'bands');
|
|
144
|
+
expect(result).to.deep.equal(countResult);
|
|
145
|
+
expect(collectionStub.countDocuments).to.have.been.calledWith({});
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('#deleteMany', function () {
|
|
150
|
+
const commandResult = { result: { n: 1, ok: 1 } };
|
|
151
|
+
|
|
152
|
+
beforeEach(function () {
|
|
153
|
+
collectionStub = stubInterface<Collection>();
|
|
154
|
+
collectionStub.deleteMany.resolves(commandResult as any);
|
|
155
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
156
|
+
createClientStub(collectionStub),
|
|
157
|
+
bus,
|
|
158
|
+
dummyOptions
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('executes the command against the database', async function () {
|
|
163
|
+
const result = await serviceProvider.deleteMany('music', 'bands', {});
|
|
164
|
+
expect(result).to.deep.equal(commandResult);
|
|
165
|
+
expect(collectionStub.deleteMany).to.have.been.calledWith({});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('#deleteOne', function () {
|
|
170
|
+
const commandResult = { result: { n: 1, ok: 1 } };
|
|
171
|
+
|
|
172
|
+
beforeEach(function () {
|
|
173
|
+
collectionStub = stubInterface<Collection>();
|
|
174
|
+
collectionStub.deleteOne.resolves(commandResult as any);
|
|
175
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
176
|
+
createClientStub(collectionStub),
|
|
177
|
+
bus,
|
|
178
|
+
dummyOptions
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('executes the command against the database', async function () {
|
|
183
|
+
const result = await serviceProvider.deleteOne('music', 'bands', {});
|
|
184
|
+
expect(result).to.deep.equal(commandResult);
|
|
185
|
+
expect(collectionStub.deleteOne).to.have.been.calledWith({});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
describe('#distinct', function () {
|
|
190
|
+
const distinctResult = ['Aphex Twin'];
|
|
191
|
+
|
|
192
|
+
beforeEach(function () {
|
|
193
|
+
collectionStub = stubInterface<Collection>();
|
|
194
|
+
collectionStub.distinct.resolves(distinctResult);
|
|
195
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
196
|
+
createClientStub(collectionStub),
|
|
197
|
+
bus,
|
|
198
|
+
dummyOptions
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('executes the command against the database', async function () {
|
|
203
|
+
const result = await serviceProvider.distinct('music', 'bands', 'name');
|
|
204
|
+
expect(result).to.deep.equal(distinctResult);
|
|
205
|
+
expect(collectionStub.distinct).to.have.been.calledWith(
|
|
206
|
+
'name',
|
|
207
|
+
{},
|
|
208
|
+
DEFAULT_BASE_OPTS
|
|
209
|
+
);
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
describe('#estimatedDocumentCount', function () {
|
|
214
|
+
const countResult = 10;
|
|
215
|
+
|
|
216
|
+
beforeEach(function () {
|
|
217
|
+
collectionStub = stubInterface<Collection>();
|
|
218
|
+
collectionStub.estimatedDocumentCount.resolves(countResult);
|
|
219
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
220
|
+
createClientStub(collectionStub),
|
|
221
|
+
bus,
|
|
222
|
+
dummyOptions
|
|
223
|
+
);
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('executes the command against the database', async function () {
|
|
227
|
+
const result = await serviceProvider.estimatedDocumentCount(
|
|
228
|
+
'music',
|
|
229
|
+
'bands'
|
|
230
|
+
);
|
|
231
|
+
expect(result).to.deep.equal(countResult);
|
|
232
|
+
expect(collectionStub.estimatedDocumentCount).to.have.been.calledWith(
|
|
233
|
+
DEFAULT_BASE_OPTS
|
|
234
|
+
);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
describe('#find', function () {
|
|
239
|
+
const filter = { name: 'Aphex Twin' };
|
|
240
|
+
const findResult = [{ name: 'Aphex Twin' }];
|
|
241
|
+
|
|
242
|
+
beforeEach(function () {
|
|
243
|
+
collectionStub = stubInterface<Collection>();
|
|
244
|
+
collectionStub.find.returns({
|
|
245
|
+
toArray: () => Promise.resolve(findResult),
|
|
246
|
+
} as any);
|
|
247
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
248
|
+
createClientStub(collectionStub),
|
|
249
|
+
bus,
|
|
250
|
+
dummyOptions
|
|
251
|
+
);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
it('executes the command against the database', async function () {
|
|
255
|
+
const cursor = serviceProvider.find('music', 'bands', filter);
|
|
256
|
+
const result = await cursor.toArray();
|
|
257
|
+
expect(result).to.deep.equal(findResult);
|
|
258
|
+
expect(collectionStub.find).to.have.been.calledWith(filter);
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
describe('#find with options', function () {
|
|
263
|
+
const filter = { name: 'Aphex Twin' };
|
|
264
|
+
const findResult = [{ name: 'Aphex Twin' }];
|
|
265
|
+
const options = {
|
|
266
|
+
allowPartialResults: true,
|
|
267
|
+
noCursorTimeout: true,
|
|
268
|
+
tailable: true,
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
beforeEach(function () {
|
|
272
|
+
collectionStub = stubInterface<Collection>();
|
|
273
|
+
collectionStub.find.returns({
|
|
274
|
+
toArray: () => Promise.resolve(findResult),
|
|
275
|
+
} as any);
|
|
276
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
277
|
+
createClientStub(collectionStub),
|
|
278
|
+
bus,
|
|
279
|
+
dummyOptions
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
it('executes the command against the database', async function () {
|
|
284
|
+
const cursor = serviceProvider.find('music', 'bands', filter, options);
|
|
285
|
+
const result = await cursor.toArray();
|
|
286
|
+
expect(result).to.deep.equal(findResult);
|
|
287
|
+
expect(collectionStub.find).to.have.been.calledWith(filter, {
|
|
288
|
+
...DEFAULT_BASE_OPTS,
|
|
289
|
+
...options,
|
|
290
|
+
partial: true,
|
|
291
|
+
timeout: true,
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
describe('#findOneAndDelete', function () {
|
|
297
|
+
const commandResult = { result: { n: 1, ok: 1 } };
|
|
298
|
+
|
|
299
|
+
beforeEach(function () {
|
|
300
|
+
collectionStub = stubInterface<Collection>();
|
|
301
|
+
collectionStub.findOneAndDelete.resolves(commandResult as any);
|
|
302
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
303
|
+
createClientStub(collectionStub),
|
|
304
|
+
bus,
|
|
305
|
+
dummyOptions
|
|
306
|
+
);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('executes the command against the database', async function () {
|
|
310
|
+
const result = await serviceProvider.findOneAndDelete(
|
|
311
|
+
'music',
|
|
312
|
+
'bands',
|
|
313
|
+
{}
|
|
314
|
+
);
|
|
315
|
+
expect(result).to.deep.equal(commandResult);
|
|
316
|
+
expect(collectionStub.findOneAndDelete).to.have.been.calledWith({});
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
describe('#findOneAndReplace', function () {
|
|
321
|
+
const commandResult = { result: { n: 1, ok: 1 } };
|
|
322
|
+
const filter = { name: 'Aphex Twin' };
|
|
323
|
+
const replacement = { name: 'Richard James' };
|
|
324
|
+
|
|
325
|
+
beforeEach(function () {
|
|
326
|
+
collectionStub = stubInterface<Collection>();
|
|
327
|
+
collectionStub.findOneAndReplace.resolves(commandResult as any);
|
|
328
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
329
|
+
createClientStub(collectionStub),
|
|
330
|
+
bus,
|
|
331
|
+
dummyOptions
|
|
332
|
+
);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it('executes the command against the database', async function () {
|
|
336
|
+
const result = await serviceProvider.findOneAndReplace(
|
|
337
|
+
'music',
|
|
338
|
+
'bands',
|
|
339
|
+
filter,
|
|
340
|
+
replacement
|
|
341
|
+
);
|
|
342
|
+
expect(result).to.deep.equal(commandResult);
|
|
343
|
+
expect(collectionStub.findOneAndReplace).to.have.been.calledWith(
|
|
344
|
+
filter,
|
|
345
|
+
replacement
|
|
346
|
+
);
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
describe('#findOneAndUpdate', function () {
|
|
351
|
+
const commandResult = { result: { n: 1, ok: 1 } };
|
|
352
|
+
const filter = { name: 'Aphex Twin' };
|
|
353
|
+
const update = { $set: { name: 'Richard James' } };
|
|
354
|
+
|
|
355
|
+
beforeEach(function () {
|
|
356
|
+
collectionStub = stubInterface<Collection>();
|
|
357
|
+
collectionStub.findOneAndUpdate.resolves(commandResult as any);
|
|
358
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
359
|
+
createClientStub(collectionStub),
|
|
360
|
+
bus,
|
|
361
|
+
dummyOptions
|
|
362
|
+
);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
it('executes the command against the database', async function () {
|
|
366
|
+
const result = await serviceProvider.findOneAndUpdate(
|
|
367
|
+
'music',
|
|
368
|
+
'bands',
|
|
369
|
+
filter,
|
|
370
|
+
update
|
|
371
|
+
);
|
|
372
|
+
expect(result).to.deep.equal(commandResult);
|
|
373
|
+
expect(collectionStub.findOneAndUpdate).to.have.been.calledWith(
|
|
374
|
+
filter,
|
|
375
|
+
update
|
|
376
|
+
);
|
|
377
|
+
});
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
describe('#insertMany', function () {
|
|
381
|
+
const doc = { name: 'Aphex Twin' };
|
|
382
|
+
const commandResult = { result: { n: 1, ok: 1 } };
|
|
383
|
+
|
|
384
|
+
beforeEach(function () {
|
|
385
|
+
collectionStub = stubInterface<Collection>();
|
|
386
|
+
collectionStub.insertMany.resolves(commandResult as any);
|
|
387
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
388
|
+
createClientStub(collectionStub),
|
|
389
|
+
bus,
|
|
390
|
+
dummyOptions
|
|
391
|
+
);
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('executes the command against the database', async function () {
|
|
395
|
+
const result = await serviceProvider.insertMany('music', 'bands', [doc]);
|
|
396
|
+
expect(result).to.deep.equal(commandResult);
|
|
397
|
+
expect(collectionStub.insertMany).to.have.been.calledWith([doc]);
|
|
398
|
+
});
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
describe('#insertOne', function () {
|
|
402
|
+
const doc = { name: 'Aphex Twin' };
|
|
403
|
+
const commandResult = { result: { n: 1, ok: 1 } };
|
|
404
|
+
|
|
405
|
+
beforeEach(function () {
|
|
406
|
+
collectionStub = stubInterface<Collection>();
|
|
407
|
+
collectionStub.insertOne.resolves(commandResult as any);
|
|
408
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
409
|
+
createClientStub(collectionStub),
|
|
410
|
+
bus,
|
|
411
|
+
dummyOptions
|
|
412
|
+
);
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
it('executes the command against the database', async function () {
|
|
416
|
+
const result = await serviceProvider.insertOne('music', 'bands', doc);
|
|
417
|
+
expect(result).to.deep.equal(commandResult);
|
|
418
|
+
expect(collectionStub.insertOne).to.have.been.calledWith(doc);
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
describe('#replaceOne', function () {
|
|
423
|
+
const filter = { name: 'Aphex Twin' };
|
|
424
|
+
const replacement = { name: 'Richard James' };
|
|
425
|
+
const commandResult = { result: { n: 1, ok: 1 } };
|
|
426
|
+
|
|
427
|
+
beforeEach(function () {
|
|
428
|
+
collectionStub = stubInterface<Collection>();
|
|
429
|
+
collectionStub.replaceOne.resolves(commandResult);
|
|
430
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
431
|
+
createClientStub(collectionStub),
|
|
432
|
+
bus,
|
|
433
|
+
dummyOptions
|
|
434
|
+
);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('executes the command against the database', async function () {
|
|
438
|
+
const result = await serviceProvider.replaceOne(
|
|
439
|
+
'music',
|
|
440
|
+
'bands',
|
|
441
|
+
filter,
|
|
442
|
+
replacement
|
|
443
|
+
);
|
|
444
|
+
expect(result).to.deep.equal(commandResult);
|
|
445
|
+
expect(collectionStub.replaceOne).to.have.been.calledWith(
|
|
446
|
+
filter,
|
|
447
|
+
replacement
|
|
448
|
+
);
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
describe('#runCommand', function () {
|
|
453
|
+
let clientStub: any;
|
|
454
|
+
let dbStub: any;
|
|
455
|
+
const commandResult = { ismaster: true };
|
|
456
|
+
|
|
457
|
+
beforeEach(function () {
|
|
458
|
+
dbStub = stubInterface<Db>();
|
|
459
|
+
clientStub = stubInterface<MongoClient>();
|
|
460
|
+
dbStub.command.resolves(commandResult);
|
|
461
|
+
clientStub.db.returns(dbStub);
|
|
462
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
463
|
+
clientStub,
|
|
464
|
+
bus,
|
|
465
|
+
dummyOptions
|
|
466
|
+
);
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
it('executes the command against the database', async function () {
|
|
470
|
+
const result = await serviceProvider.runCommand('admin', { ismaster: 1 });
|
|
471
|
+
expect(result).to.deep.equal(commandResult);
|
|
472
|
+
expect(dbStub.command).to.have.been.calledWith({ ismaster: 1 });
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
describe('#runCommandWithCheck', function () {
|
|
477
|
+
let clientStub: any;
|
|
478
|
+
let dbStub: any;
|
|
479
|
+
const commandResult = { ok: 0 };
|
|
480
|
+
|
|
481
|
+
beforeEach(function () {
|
|
482
|
+
dbStub = stubInterface<Db>();
|
|
483
|
+
clientStub = stubInterface<MongoClient>();
|
|
484
|
+
dbStub.command.resolves(commandResult);
|
|
485
|
+
clientStub.db.returns(dbStub);
|
|
486
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
487
|
+
clientStub,
|
|
488
|
+
bus,
|
|
489
|
+
dummyOptions
|
|
490
|
+
);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
it('executes the command against the database and throws if ok: 0', async function () {
|
|
494
|
+
try {
|
|
495
|
+
await serviceProvider.runCommandWithCheck('admin', { ismaster: 1 });
|
|
496
|
+
} catch (e: any) {
|
|
497
|
+
expect(e.message).to.include(JSON.stringify({ ismaster: 1 }));
|
|
498
|
+
expect(e.name).to.equal('MongoshCommandFailed');
|
|
499
|
+
expect(e.code).to.equal(CommonErrors.CommandFailed);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
expect.fail('Error not thrown');
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
describe('#runCursorCommand', function () {
|
|
507
|
+
let clientStub: any;
|
|
508
|
+
let dbStub: any;
|
|
509
|
+
const commandResult = 'a-cursor';
|
|
510
|
+
|
|
511
|
+
beforeEach(function () {
|
|
512
|
+
dbStub = stubInterface<Db>();
|
|
513
|
+
clientStub = stubInterface<MongoClient>();
|
|
514
|
+
dbStub.runCursorCommand.returns(commandResult);
|
|
515
|
+
clientStub.db.returns(dbStub);
|
|
516
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
517
|
+
clientStub,
|
|
518
|
+
bus,
|
|
519
|
+
dummyOptions
|
|
520
|
+
);
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('executes the command against the database', function () {
|
|
524
|
+
const result = serviceProvider.runCursorCommand('admin', {
|
|
525
|
+
checkMetadataConsistency: 1,
|
|
526
|
+
});
|
|
527
|
+
expect(result).to.deep.equal(commandResult);
|
|
528
|
+
expect(dbStub.runCursorCommand).to.have.been.calledWith({
|
|
529
|
+
checkMetadataConsistency: 1,
|
|
530
|
+
});
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
describe('#updateOne', function () {
|
|
535
|
+
const filter = { name: 'Aphex Twin' };
|
|
536
|
+
const update = { $set: { name: 'Richard James' } };
|
|
537
|
+
const commandResult = { result: { n: 1, ok: 1 } };
|
|
538
|
+
|
|
539
|
+
beforeEach(function () {
|
|
540
|
+
collectionStub = stubInterface<Collection>();
|
|
541
|
+
collectionStub.updateOne.resolves(commandResult as any);
|
|
542
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
543
|
+
createClientStub(collectionStub),
|
|
544
|
+
bus,
|
|
545
|
+
dummyOptions
|
|
546
|
+
);
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
it('executes the command against the database', async function () {
|
|
550
|
+
const result = await serviceProvider.updateOne(
|
|
551
|
+
'music',
|
|
552
|
+
'bands',
|
|
553
|
+
filter,
|
|
554
|
+
update
|
|
555
|
+
);
|
|
556
|
+
expect(result).to.deep.equal(commandResult);
|
|
557
|
+
expect(collectionStub.updateOne).to.have.been.calledWith(filter, update);
|
|
558
|
+
});
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
describe('#updateMany', function () {
|
|
562
|
+
const filter = { name: 'Aphex Twin' };
|
|
563
|
+
const update = { $set: { name: 'Richard James' } };
|
|
564
|
+
const commandResult = { result: { n: 1, ok: 1 } };
|
|
565
|
+
|
|
566
|
+
beforeEach(function () {
|
|
567
|
+
collectionStub = stubInterface<Collection>();
|
|
568
|
+
collectionStub.updateMany.resolves(commandResult as any);
|
|
569
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
570
|
+
createClientStub(collectionStub),
|
|
571
|
+
bus,
|
|
572
|
+
dummyOptions
|
|
573
|
+
);
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
it('executes the command against the database', async function () {
|
|
577
|
+
const result = await serviceProvider.updateMany(
|
|
578
|
+
'music',
|
|
579
|
+
'bands',
|
|
580
|
+
filter,
|
|
581
|
+
update
|
|
582
|
+
);
|
|
583
|
+
expect(result).to.deep.equal(commandResult);
|
|
584
|
+
expect(collectionStub.updateMany).to.have.been.calledWith(filter, update);
|
|
585
|
+
});
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
describe('#dropDatabase', function () {
|
|
589
|
+
let clientStub: StubbedInstance<MongoClient>;
|
|
590
|
+
let dbStub: StubbedInstance<Db>;
|
|
591
|
+
|
|
592
|
+
beforeEach(function () {
|
|
593
|
+
dbStub = stubInterface<Db>();
|
|
594
|
+
clientStub = stubInterface<MongoClient>();
|
|
595
|
+
clientStub.db.returns(dbStub);
|
|
596
|
+
|
|
597
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
598
|
+
clientStub,
|
|
599
|
+
bus,
|
|
600
|
+
dummyOptions
|
|
601
|
+
);
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
it('returns ok: 1 if dropped', async function () {
|
|
605
|
+
dbStub.dropDatabase.resolves(true);
|
|
606
|
+
const result = await serviceProvider.dropDatabase('db1');
|
|
607
|
+
expect(result).to.contain({ ok: 1 });
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
it('returns ok: 0 if not dropped', async function () {
|
|
611
|
+
dbStub.dropDatabase.resolves(false);
|
|
612
|
+
const result = await serviceProvider.dropDatabase('db1');
|
|
613
|
+
expect(result).to.contain({ ok: 0 });
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
it('returns dropped: "db name" if dropped', async function () {
|
|
617
|
+
dbStub.dropDatabase.resolves(true);
|
|
618
|
+
const result = await serviceProvider.dropDatabase('db1');
|
|
619
|
+
expect(result).to.contain({ dropped: 'db1' });
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
context('when write concern is omitted', function () {
|
|
623
|
+
it('runs against the database with default write concern', async function () {
|
|
624
|
+
dbStub.dropDatabase.resolves(true);
|
|
625
|
+
await serviceProvider.dropDatabase('db1');
|
|
626
|
+
expect(clientStub.db).to.have.been.calledOnceWith('db1');
|
|
627
|
+
});
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
context('with write concern', function () {
|
|
631
|
+
it('runs against the database passing write concern', async function () {
|
|
632
|
+
const opts = { serializeFunctions: true, w: 1 };
|
|
633
|
+
dbStub.dropDatabase.resolves(true);
|
|
634
|
+
await serviceProvider.dropDatabase('db1', opts);
|
|
635
|
+
expect(clientStub.db).to.have.been.calledOnceWith('db1');
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
describe('#createIndexes', function () {
|
|
641
|
+
let indexSpecs: IndexDescription[];
|
|
642
|
+
let nativeMethodResult: string[];
|
|
643
|
+
|
|
644
|
+
beforeEach(function () {
|
|
645
|
+
indexSpecs = [{ key: { key: 1 } }];
|
|
646
|
+
|
|
647
|
+
nativeMethodResult = ['key_1'];
|
|
648
|
+
|
|
649
|
+
collectionStub = stubInterface<Collection>();
|
|
650
|
+
collectionStub.createIndexes.resolves(nativeMethodResult);
|
|
651
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
652
|
+
createClientStub(collectionStub),
|
|
653
|
+
bus,
|
|
654
|
+
dummyOptions
|
|
655
|
+
);
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
it('executes the command against the database', async function () {
|
|
659
|
+
const result = await serviceProvider.createIndexes(
|
|
660
|
+
'db1',
|
|
661
|
+
'coll1',
|
|
662
|
+
indexSpecs
|
|
663
|
+
);
|
|
664
|
+
expect(result).to.deep.equal(nativeMethodResult);
|
|
665
|
+
expect(collectionStub.createIndexes).to.have.been.calledWith(indexSpecs);
|
|
666
|
+
});
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
describe('#getIndexes', function () {
|
|
670
|
+
let indexSpecs: Document[];
|
|
671
|
+
let nativeMethodResult: any;
|
|
672
|
+
|
|
673
|
+
beforeEach(function () {
|
|
674
|
+
indexSpecs = [{ key: 'x' }];
|
|
675
|
+
|
|
676
|
+
nativeMethodResult = {
|
|
677
|
+
toArray: (): Promise<any[]> => Promise.resolve(indexSpecs),
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
collectionStub = stubInterface<Collection>();
|
|
681
|
+
collectionStub.listIndexes.returns(nativeMethodResult);
|
|
682
|
+
|
|
683
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
684
|
+
createClientStub(collectionStub),
|
|
685
|
+
bus,
|
|
686
|
+
dummyOptions
|
|
687
|
+
);
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
it('executes the command against the database', async function () {
|
|
691
|
+
const result = await serviceProvider.getIndexes('db1', 'coll1');
|
|
692
|
+
|
|
693
|
+
expect(result).to.deep.equal(indexSpecs);
|
|
694
|
+
expect(collectionStub.listIndexes).to.have.been.calledWith(
|
|
695
|
+
DEFAULT_BASE_OPTS
|
|
696
|
+
);
|
|
697
|
+
});
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
describe('#listCollections', function () {
|
|
701
|
+
let dbStub: StubbedInstance<Db>;
|
|
702
|
+
let clientStub: StubbedInstance<MongoClient>;
|
|
703
|
+
|
|
704
|
+
beforeEach(function () {
|
|
705
|
+
dbStub = stubInterface<Db>();
|
|
706
|
+
clientStub = stubInterface<MongoClient>();
|
|
707
|
+
dbStub.listCollections.returns({
|
|
708
|
+
toArray: () => {
|
|
709
|
+
return Promise.resolve([
|
|
710
|
+
{
|
|
711
|
+
name: 'coll1',
|
|
712
|
+
},
|
|
713
|
+
]);
|
|
714
|
+
},
|
|
715
|
+
} as any);
|
|
716
|
+
clientStub.db.returns(dbStub);
|
|
717
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
718
|
+
clientStub,
|
|
719
|
+
bus,
|
|
720
|
+
dummyOptions
|
|
721
|
+
);
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
it('executes the command', async function () {
|
|
725
|
+
const result = await serviceProvider.listCollections('db1');
|
|
726
|
+
expect(result).to.deep.equal([
|
|
727
|
+
{
|
|
728
|
+
name: 'coll1',
|
|
729
|
+
},
|
|
730
|
+
]);
|
|
731
|
+
|
|
732
|
+
expect(dbStub.listCollections).to.have.been.calledWith(
|
|
733
|
+
{},
|
|
734
|
+
DEFAULT_BASE_OPTS
|
|
735
|
+
);
|
|
736
|
+
expect(clientStub.db).to.have.been.calledWith('db1');
|
|
737
|
+
});
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
describe('#renameCollection', function () {
|
|
741
|
+
let dbStub: StubbedInstance<Db>;
|
|
742
|
+
let clientStub: StubbedInstance<MongoClient>;
|
|
743
|
+
|
|
744
|
+
beforeEach(function () {
|
|
745
|
+
dbStub = stubInterface<Db>();
|
|
746
|
+
clientStub = stubInterface<MongoClient>();
|
|
747
|
+
dbStub.renameCollection.resolves({ ok: 1 } as any);
|
|
748
|
+
clientStub.db.returns(dbStub);
|
|
749
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
750
|
+
clientStub,
|
|
751
|
+
bus,
|
|
752
|
+
dummyOptions
|
|
753
|
+
);
|
|
754
|
+
});
|
|
755
|
+
|
|
756
|
+
it('executes the command against the database', async function () {
|
|
757
|
+
const result = await serviceProvider.renameCollection(
|
|
758
|
+
'db1',
|
|
759
|
+
'coll1',
|
|
760
|
+
'newName',
|
|
761
|
+
{ dropTarget: true, session: {} as any }
|
|
762
|
+
);
|
|
763
|
+
expect(result).to.deep.equal({ ok: 1 });
|
|
764
|
+
expect(dbStub.renameCollection).to.have.been.calledOnceWith(
|
|
765
|
+
'coll1',
|
|
766
|
+
'newName',
|
|
767
|
+
{
|
|
768
|
+
...DEFAULT_BASE_OPTS,
|
|
769
|
+
dropTarget: true,
|
|
770
|
+
session: {},
|
|
771
|
+
}
|
|
772
|
+
);
|
|
773
|
+
expect(clientStub.db).to.have.been.calledOnceWith('db1');
|
|
774
|
+
});
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
describe('#createCollection', function () {
|
|
778
|
+
let dbStub: StubbedInstance<Db>;
|
|
779
|
+
let clientStub: StubbedInstance<MongoClient>;
|
|
780
|
+
|
|
781
|
+
beforeEach(function () {
|
|
782
|
+
dbStub = stubInterface<Db>();
|
|
783
|
+
clientStub = stubInterface<MongoClient>();
|
|
784
|
+
dbStub.createCollection.resolves({} as any);
|
|
785
|
+
clientStub.db.returns(dbStub);
|
|
786
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
787
|
+
clientStub,
|
|
788
|
+
bus,
|
|
789
|
+
dummyOptions
|
|
790
|
+
);
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
it('executes the command', async function () {
|
|
794
|
+
const result = await serviceProvider.createCollection(
|
|
795
|
+
'db1',
|
|
796
|
+
'newcoll',
|
|
797
|
+
{}
|
|
798
|
+
);
|
|
799
|
+
expect(result).to.deep.equal({ ok: 1 });
|
|
800
|
+
expect(dbStub.createCollection).to.have.been.calledOnceWith(
|
|
801
|
+
'newcoll',
|
|
802
|
+
DEFAULT_BASE_OPTS
|
|
803
|
+
);
|
|
804
|
+
expect(clientStub.db).to.have.been.calledOnceWith('db1');
|
|
805
|
+
});
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
describe('#createEncryptedCollection', function () {
|
|
809
|
+
let dbStub: StubbedInstance<Db>;
|
|
810
|
+
let clientStub: StubbedInstance<MongoClient>;
|
|
811
|
+
let libmongoc: StubbedInstance<ClientEncryption>;
|
|
812
|
+
const createCollOptions = {
|
|
813
|
+
provider: 'local' as ClientEncryptionDataKeyProvider,
|
|
814
|
+
createCollectionOptions: {
|
|
815
|
+
encryptedFields: {
|
|
816
|
+
fields: [
|
|
817
|
+
{
|
|
818
|
+
path: 'ssn',
|
|
819
|
+
bsonType: 'string',
|
|
820
|
+
},
|
|
821
|
+
],
|
|
822
|
+
},
|
|
823
|
+
},
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
beforeEach(function () {
|
|
827
|
+
dbStub = stubInterface<Db>();
|
|
828
|
+
clientStub = stubInterface<MongoClient>();
|
|
829
|
+
clientStub.db.returns(dbStub);
|
|
830
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
831
|
+
clientStub,
|
|
832
|
+
bus,
|
|
833
|
+
dummyOptions
|
|
834
|
+
);
|
|
835
|
+
libmongoc = stubInterface<ClientEncryption>();
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
it('calls calls libmongocrypt.createEncryptedCollection', async function () {
|
|
839
|
+
await serviceProvider.createEncryptedCollection(
|
|
840
|
+
'db1',
|
|
841
|
+
'coll1',
|
|
842
|
+
createCollOptions,
|
|
843
|
+
libmongoc
|
|
844
|
+
);
|
|
845
|
+
expect(libmongoc.createEncryptedCollection).calledOnceWithExactly(
|
|
846
|
+
dbStub,
|
|
847
|
+
'coll1',
|
|
848
|
+
createCollOptions
|
|
849
|
+
);
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
it('returns whatever libmongocrypt.createEncryptedCollection returns', async function () {
|
|
853
|
+
const resolvedValue = {
|
|
854
|
+
collection: { name: 'secretCol' },
|
|
855
|
+
encryptedFields: [],
|
|
856
|
+
} as any;
|
|
857
|
+
libmongoc.createEncryptedCollection.resolves(resolvedValue);
|
|
858
|
+
const returnValue = await serviceProvider.createEncryptedCollection(
|
|
859
|
+
'db1',
|
|
860
|
+
'coll1',
|
|
861
|
+
createCollOptions,
|
|
862
|
+
libmongoc
|
|
863
|
+
);
|
|
864
|
+
expect(returnValue).to.deep.equal(resolvedValue);
|
|
865
|
+
});
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
describe('sessions', function () {
|
|
869
|
+
let clientStub: StubbedInstance<MongoClient>;
|
|
870
|
+
let serviceProvider: NodeDriverServiceProvider;
|
|
871
|
+
let db: StubbedInstance<Db>;
|
|
872
|
+
let driverSession: ClientSession;
|
|
873
|
+
beforeEach(function () {
|
|
874
|
+
clientStub = stubInterface<MongoClient>();
|
|
875
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
876
|
+
clientStub,
|
|
877
|
+
bus,
|
|
878
|
+
dummyOptions
|
|
879
|
+
);
|
|
880
|
+
driverSession = { dSession: 1 } as any;
|
|
881
|
+
clientStub.startSession.returns(driverSession);
|
|
882
|
+
db = stubInterface<Db>();
|
|
883
|
+
clientStub.db.returns(db);
|
|
884
|
+
});
|
|
885
|
+
describe('startSession', function () {
|
|
886
|
+
it('calls startSession without args', function () {
|
|
887
|
+
const opts = {};
|
|
888
|
+
const result = serviceProvider.startSession(opts);
|
|
889
|
+
expect(clientStub.startSession).to.have.been.calledOnceWith(opts);
|
|
890
|
+
expect(result).to.equal(driverSession);
|
|
891
|
+
});
|
|
892
|
+
});
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
describe('#watch', function () {
|
|
896
|
+
let options: Document;
|
|
897
|
+
let expectedResult: Document;
|
|
898
|
+
let watchMock: sinon.SinonStub;
|
|
899
|
+
let watchMock2: sinon.SinonStub;
|
|
900
|
+
let watchMock3: sinon.SinonStub;
|
|
901
|
+
let pipeline: Document[];
|
|
902
|
+
|
|
903
|
+
beforeEach(function () {
|
|
904
|
+
pipeline = [{ $match: { operationType: 'insertOne' } }];
|
|
905
|
+
options = { batchSize: 1 };
|
|
906
|
+
expectedResult = { ChangeStream: 1 };
|
|
907
|
+
|
|
908
|
+
watchMock = sinon
|
|
909
|
+
.mock()
|
|
910
|
+
.once()
|
|
911
|
+
.withArgs(pipeline, options)
|
|
912
|
+
.returns(expectedResult);
|
|
913
|
+
watchMock2 = sinon
|
|
914
|
+
.mock()
|
|
915
|
+
.once()
|
|
916
|
+
.withArgs(pipeline, options)
|
|
917
|
+
.returns(expectedResult);
|
|
918
|
+
watchMock3 = sinon
|
|
919
|
+
.mock()
|
|
920
|
+
.once()
|
|
921
|
+
.withArgs(pipeline, options)
|
|
922
|
+
.returns(expectedResult);
|
|
923
|
+
|
|
924
|
+
const collectionStub = sinon.createStubInstance(Collection, {
|
|
925
|
+
watch: watchMock3 as any,
|
|
926
|
+
});
|
|
927
|
+
const dbStub = sinon.createStubInstance(Db, {
|
|
928
|
+
watch: watchMock2 as any,
|
|
929
|
+
collection: sinon.stub().returns(collectionStub) as any,
|
|
930
|
+
});
|
|
931
|
+
const clientStub = sinon.createStubInstance(MongoClient, {
|
|
932
|
+
db: sinon.stub().returns(dbStub) as any,
|
|
933
|
+
watch: watchMock as any,
|
|
934
|
+
}) as any;
|
|
935
|
+
|
|
936
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
937
|
+
clientStub,
|
|
938
|
+
bus,
|
|
939
|
+
dummyOptions
|
|
940
|
+
);
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
it('executes watch on MongoClient', function () {
|
|
944
|
+
const result = serviceProvider.watch(pipeline, options);
|
|
945
|
+
expect(result).to.deep.equal(expectedResult);
|
|
946
|
+
expect(watchMock).to.have.been.calledOnce;
|
|
947
|
+
});
|
|
948
|
+
it('executes watch on Db', function () {
|
|
949
|
+
const result = serviceProvider.watch(pipeline, options, {}, 'dbname');
|
|
950
|
+
expect(result).to.deep.equal(expectedResult);
|
|
951
|
+
expect(watchMock2).to.have.been.calledOnce;
|
|
952
|
+
});
|
|
953
|
+
it('executes watch on collection', function () {
|
|
954
|
+
const result = serviceProvider.watch(
|
|
955
|
+
pipeline,
|
|
956
|
+
options,
|
|
957
|
+
{},
|
|
958
|
+
'dbname',
|
|
959
|
+
'collname'
|
|
960
|
+
);
|
|
961
|
+
expect(result).to.deep.equal(expectedResult);
|
|
962
|
+
expect(watchMock3).to.have.been.calledOnce;
|
|
963
|
+
});
|
|
964
|
+
});
|
|
965
|
+
|
|
966
|
+
describe('#getConnectionInfo', function () {
|
|
967
|
+
let clientStub: any;
|
|
968
|
+
let dbStub: StubbedInstance<Db>;
|
|
969
|
+
|
|
970
|
+
beforeEach(function () {
|
|
971
|
+
dbStub = stubInterface<Db>();
|
|
972
|
+
clientStub = stubInterface<MongoClient>();
|
|
973
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
974
|
+
dbStub.command.callsFake(async () => {
|
|
975
|
+
return { ok: 1 };
|
|
976
|
+
});
|
|
977
|
+
dbStub.collection.callsFake((): any => {
|
|
978
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
979
|
+
return { countDocuments: async () => 0 };
|
|
980
|
+
});
|
|
981
|
+
clientStub.db.returns(dbStub);
|
|
982
|
+
clientStub.topology = {
|
|
983
|
+
s: {
|
|
984
|
+
servers: new Map().set('localhost', {
|
|
985
|
+
description: { address: 'localhost' },
|
|
986
|
+
}),
|
|
987
|
+
},
|
|
988
|
+
};
|
|
989
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
990
|
+
clientStub,
|
|
991
|
+
bus,
|
|
992
|
+
dummyOptions,
|
|
993
|
+
new ConnectionString('mongodb://localhost/')
|
|
994
|
+
);
|
|
995
|
+
serviceProvider.getNewConnection = () => Promise.resolve(serviceProvider);
|
|
996
|
+
});
|
|
997
|
+
|
|
998
|
+
it('returns some connection info data', async function () {
|
|
999
|
+
const info = await serviceProvider.getConnectionInfo();
|
|
1000
|
+
expect(info.extraInfo?.is_atlas).to.equal(false);
|
|
1001
|
+
expect(info.extraInfo?.is_local_atlas).to.equal(false);
|
|
1002
|
+
expect(info.extraInfo?.is_localhost).to.equal(true);
|
|
1003
|
+
expect(info.extraInfo?.fcv).to.equal(undefined);
|
|
1004
|
+
expect(dbStub.command).to.have.callCount(3);
|
|
1005
|
+
expect(
|
|
1006
|
+
dbStub.collection,
|
|
1007
|
+
'calls countDocument on collection to check local atlas cli support'
|
|
1008
|
+
).to.have.callCount(1);
|
|
1009
|
+
});
|
|
1010
|
+
|
|
1011
|
+
context('when connected to an Atlas deployment', function () {
|
|
1012
|
+
it('correctly gathers info on the fake deployment', async function () {
|
|
1013
|
+
const serviceProvider = new NodeDriverServiceProvider(
|
|
1014
|
+
clientStub,
|
|
1015
|
+
bus,
|
|
1016
|
+
dummyOptions,
|
|
1017
|
+
new ConnectionString(
|
|
1018
|
+
'mongodb+srv://test-data-sets-a011bb.mongodb.net/admin'
|
|
1019
|
+
)
|
|
1020
|
+
);
|
|
1021
|
+
|
|
1022
|
+
const info = await serviceProvider.getConnectionInfo();
|
|
1023
|
+
expect(info.extraInfo?.is_genuine).to.be.true;
|
|
1024
|
+
expect(info.extraInfo?.is_atlas).to.be.true;
|
|
1025
|
+
});
|
|
1026
|
+
});
|
|
1027
|
+
|
|
1028
|
+
context('when connected to a DocumentDB deployment', function () {
|
|
1029
|
+
it('correctly gathers info on the fake deployment', async function () {
|
|
1030
|
+
const serviceProvider = new NodeDriverServiceProvider(
|
|
1031
|
+
clientStub,
|
|
1032
|
+
bus,
|
|
1033
|
+
dummyOptions,
|
|
1034
|
+
new ConnectionString(
|
|
1035
|
+
'mongodb://elastic-docdb-123456789.eu-central-1.docdb-elastic.amazonaws.com:27017'
|
|
1036
|
+
)
|
|
1037
|
+
);
|
|
1038
|
+
|
|
1039
|
+
const info = await serviceProvider.getConnectionInfo();
|
|
1040
|
+
expect(info.extraInfo?.is_genuine).to.be.false;
|
|
1041
|
+
expect(info.extraInfo?.non_genuine_server_name).to.equal('documentdb');
|
|
1042
|
+
});
|
|
1043
|
+
});
|
|
1044
|
+
|
|
1045
|
+
context('when connected to a CosmosDB deployment', function () {
|
|
1046
|
+
it('correctly gathers info on the fake deployment', async function () {
|
|
1047
|
+
const serviceProvider = new NodeDriverServiceProvider(
|
|
1048
|
+
clientStub,
|
|
1049
|
+
bus,
|
|
1050
|
+
dummyOptions,
|
|
1051
|
+
new ConnectionString(
|
|
1052
|
+
'mongodb+srv://compass-vcore.mongocluster.cosmos.azure.com'
|
|
1053
|
+
)
|
|
1054
|
+
);
|
|
1055
|
+
|
|
1056
|
+
const info = await serviceProvider.getConnectionInfo();
|
|
1057
|
+
expect(info.extraInfo?.is_genuine).to.be.false;
|
|
1058
|
+
expect(info.extraInfo?.non_genuine_server_name).to.equal('cosmosdb');
|
|
1059
|
+
});
|
|
1060
|
+
});
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
// Security-relevant tests -- description covered in e2e-oidc tests.
|
|
1064
|
+
describe('processDriverOptions', function () {
|
|
1065
|
+
it('shares user configuration options from an existing NodeDriverServiceProvider instance', function () {
|
|
1066
|
+
const cloneableOidcOptions = {
|
|
1067
|
+
redirectURI: 'http://localhost',
|
|
1068
|
+
openBrowser: { command: '/usr/bin/browser' },
|
|
1069
|
+
notifyDeviceFlow: () => {},
|
|
1070
|
+
allowedFlows: ['device-auth'],
|
|
1071
|
+
};
|
|
1072
|
+
const productInfo = {
|
|
1073
|
+
productDocsLink: 'https://example.com',
|
|
1074
|
+
productName: 'test',
|
|
1075
|
+
};
|
|
1076
|
+
expect(
|
|
1077
|
+
NodeDriverServiceProvider.prototype.processDriverOptions.call(
|
|
1078
|
+
{
|
|
1079
|
+
currentClientOptions: {
|
|
1080
|
+
oidc: {
|
|
1081
|
+
...cloneableOidcOptions,
|
|
1082
|
+
throwOnIncompatibleSerializedState: true,
|
|
1083
|
+
},
|
|
1084
|
+
...productInfo,
|
|
1085
|
+
readConcern: 'local',
|
|
1086
|
+
} as DevtoolsConnectOptions,
|
|
1087
|
+
uri: new ConnectionString('mongodb://localhost/'),
|
|
1088
|
+
} as any,
|
|
1089
|
+
new ConnectionString('mongodb://localhost/'),
|
|
1090
|
+
{}
|
|
1091
|
+
)
|
|
1092
|
+
).to.deep.equal({
|
|
1093
|
+
oidc: { ...cloneableOidcOptions },
|
|
1094
|
+
...productInfo,
|
|
1095
|
+
});
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
it('shares OIDC state if the auth options match', function () {
|
|
1099
|
+
const parentState: any = {};
|
|
1100
|
+
|
|
1101
|
+
expect(
|
|
1102
|
+
NodeDriverServiceProvider.prototype.processDriverOptions.call(
|
|
1103
|
+
{
|
|
1104
|
+
uri: new ConnectionString('mongodb://localhost/'),
|
|
1105
|
+
currentClientOptions: {
|
|
1106
|
+
auth: { username: 'meow' },
|
|
1107
|
+
parentState,
|
|
1108
|
+
},
|
|
1109
|
+
},
|
|
1110
|
+
new ConnectionString('mongodb://localhost'),
|
|
1111
|
+
{ auth: { username: 'meow' } }
|
|
1112
|
+
).parentState
|
|
1113
|
+
).to.equal(parentState);
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
it('does not share OIDC state if the auth options mismatch', function () {
|
|
1117
|
+
const parentState: any = {};
|
|
1118
|
+
|
|
1119
|
+
expect(
|
|
1120
|
+
NodeDriverServiceProvider.prototype.processDriverOptions.call(
|
|
1121
|
+
{
|
|
1122
|
+
uri: new ConnectionString('mongodb://localhost/'),
|
|
1123
|
+
currentClientOptions: {
|
|
1124
|
+
auth: { username: 'meow' },
|
|
1125
|
+
parentState,
|
|
1126
|
+
},
|
|
1127
|
+
},
|
|
1128
|
+
new ConnectionString('mongodb://localhost'),
|
|
1129
|
+
{ auth: { username: 'moo' } }
|
|
1130
|
+
).parentState
|
|
1131
|
+
).to.equal(undefined);
|
|
1132
|
+
});
|
|
1133
|
+
|
|
1134
|
+
it('does not share OIDC state if the endpoints mismatch', function () {
|
|
1135
|
+
const parentState: any = {};
|
|
1136
|
+
|
|
1137
|
+
expect(
|
|
1138
|
+
NodeDriverServiceProvider.prototype.processDriverOptions.call(
|
|
1139
|
+
{
|
|
1140
|
+
uri: new ConnectionString('mongodb://localhost/'),
|
|
1141
|
+
currentClientOptions: {
|
|
1142
|
+
auth: { username: 'meow' },
|
|
1143
|
+
parentState,
|
|
1144
|
+
},
|
|
1145
|
+
},
|
|
1146
|
+
new ConnectionString('mongodb://localghost'),
|
|
1147
|
+
{ auth: { username: 'meow' } }
|
|
1148
|
+
).parentState
|
|
1149
|
+
).to.equal(undefined);
|
|
1150
|
+
});
|
|
1151
|
+
});
|
|
1152
|
+
|
|
1153
|
+
describe('#getSearchIndexes', function () {
|
|
1154
|
+
let descriptions: Document[];
|
|
1155
|
+
let nativeMethodResult: any;
|
|
1156
|
+
let getSearchIndexesOptions: Document;
|
|
1157
|
+
|
|
1158
|
+
beforeEach(function () {
|
|
1159
|
+
descriptions = [{ name: 'foo' }, { name: 'bar' }];
|
|
1160
|
+
|
|
1161
|
+
nativeMethodResult = {
|
|
1162
|
+
toArray: () => {
|
|
1163
|
+
return Promise.resolve(descriptions);
|
|
1164
|
+
},
|
|
1165
|
+
};
|
|
1166
|
+
|
|
1167
|
+
getSearchIndexesOptions = { allowDiskUse: true };
|
|
1168
|
+
|
|
1169
|
+
collectionStub = stubInterface<Collection>();
|
|
1170
|
+
collectionStub.listSearchIndexes.returns(nativeMethodResult);
|
|
1171
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
1172
|
+
createClientStub(collectionStub),
|
|
1173
|
+
bus,
|
|
1174
|
+
dummyOptions
|
|
1175
|
+
);
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
context('without indexName', function () {
|
|
1179
|
+
it('calls listSearchIndexes and toArray on the resulting cursor', async function () {
|
|
1180
|
+
const result = await serviceProvider.getSearchIndexes(
|
|
1181
|
+
'db1',
|
|
1182
|
+
'coll1',
|
|
1183
|
+
undefined,
|
|
1184
|
+
getSearchIndexesOptions
|
|
1185
|
+
);
|
|
1186
|
+
expect(result).to.deep.equal(descriptions);
|
|
1187
|
+
expect(collectionStub.listSearchIndexes).to.have.been.calledWith(
|
|
1188
|
+
getSearchIndexesOptions
|
|
1189
|
+
);
|
|
1190
|
+
});
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
context('with indexName', function () {
|
|
1194
|
+
it('calls listSearchIndexes and toArray on the resulting cursor', async function () {
|
|
1195
|
+
const result = await serviceProvider.getSearchIndexes(
|
|
1196
|
+
'db1',
|
|
1197
|
+
'coll1',
|
|
1198
|
+
'my-index',
|
|
1199
|
+
getSearchIndexesOptions
|
|
1200
|
+
);
|
|
1201
|
+
expect(result).to.deep.equal(descriptions);
|
|
1202
|
+
expect(collectionStub.listSearchIndexes).to.have.been.calledWith(
|
|
1203
|
+
'my-index',
|
|
1204
|
+
getSearchIndexesOptions
|
|
1205
|
+
);
|
|
1206
|
+
});
|
|
1207
|
+
});
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
describe('#createSearchIndexes', function () {
|
|
1211
|
+
let descriptions: SearchIndexDescription[];
|
|
1212
|
+
let nativeMethodResult: string[];
|
|
1213
|
+
|
|
1214
|
+
beforeEach(function () {
|
|
1215
|
+
descriptions = [
|
|
1216
|
+
{ name: 'foo', definition: {} },
|
|
1217
|
+
{ name: 'bar', definition: {} },
|
|
1218
|
+
];
|
|
1219
|
+
|
|
1220
|
+
nativeMethodResult = ['index_1'];
|
|
1221
|
+
|
|
1222
|
+
collectionStub = stubInterface<Collection>();
|
|
1223
|
+
collectionStub.createSearchIndexes.resolves(nativeMethodResult);
|
|
1224
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
1225
|
+
createClientStub(collectionStub),
|
|
1226
|
+
bus,
|
|
1227
|
+
dummyOptions
|
|
1228
|
+
);
|
|
1229
|
+
});
|
|
1230
|
+
|
|
1231
|
+
it('executes the command against the database', async function () {
|
|
1232
|
+
const result = await serviceProvider.createSearchIndexes(
|
|
1233
|
+
'db1',
|
|
1234
|
+
'coll1',
|
|
1235
|
+
descriptions
|
|
1236
|
+
);
|
|
1237
|
+
expect(result).to.deep.equal(nativeMethodResult);
|
|
1238
|
+
expect(collectionStub.createSearchIndexes).to.have.been.calledWith(
|
|
1239
|
+
descriptions
|
|
1240
|
+
);
|
|
1241
|
+
});
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
describe('#dropSearchIndex', function () {
|
|
1245
|
+
let indexName: string;
|
|
1246
|
+
|
|
1247
|
+
beforeEach(function () {
|
|
1248
|
+
indexName = 'foo';
|
|
1249
|
+
|
|
1250
|
+
collectionStub = stubInterface<Collection>();
|
|
1251
|
+
collectionStub.dropSearchIndex.resolves();
|
|
1252
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
1253
|
+
createClientStub(collectionStub),
|
|
1254
|
+
bus,
|
|
1255
|
+
dummyOptions
|
|
1256
|
+
);
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1259
|
+
it('executes the command against the database', async function () {
|
|
1260
|
+
const result = await serviceProvider.dropSearchIndex(
|
|
1261
|
+
'db1',
|
|
1262
|
+
'coll1',
|
|
1263
|
+
indexName
|
|
1264
|
+
);
|
|
1265
|
+
expect(result).to.deep.equal(undefined);
|
|
1266
|
+
expect(collectionStub.dropSearchIndex).to.have.been.calledWith(indexName);
|
|
1267
|
+
});
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
describe('#updateSearchIndex', function () {
|
|
1271
|
+
let indexName: string;
|
|
1272
|
+
let description: SearchIndexDescription;
|
|
1273
|
+
|
|
1274
|
+
beforeEach(function () {
|
|
1275
|
+
indexName = 'foo';
|
|
1276
|
+
description = { definition: { x: 1, y: 2 } };
|
|
1277
|
+
|
|
1278
|
+
collectionStub = stubInterface<Collection>();
|
|
1279
|
+
|
|
1280
|
+
collectionStub.updateSearchIndex.resolves();
|
|
1281
|
+
serviceProvider = new NodeDriverServiceProvider(
|
|
1282
|
+
createClientStub(collectionStub),
|
|
1283
|
+
bus,
|
|
1284
|
+
dummyOptions
|
|
1285
|
+
);
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
it('executes the command against the database', async function () {
|
|
1289
|
+
const result = await serviceProvider.updateSearchIndex(
|
|
1290
|
+
'db1',
|
|
1291
|
+
'coll1',
|
|
1292
|
+
indexName,
|
|
1293
|
+
description
|
|
1294
|
+
);
|
|
1295
|
+
expect(result).to.deep.equal(undefined);
|
|
1296
|
+
expect(collectionStub.updateSearchIndex).to.have.been.calledWith(
|
|
1297
|
+
indexName,
|
|
1298
|
+
description
|
|
1299
|
+
);
|
|
1300
|
+
});
|
|
1301
|
+
});
|
|
1302
|
+
});
|