Skip to content

Commit a874e5c

Browse files
committed
test: Complete all matching operations
Adds special operations support i.e. $$unsetOrMatches for testing nested schema equality of results and events. Adds Recursive equality test that tracks the path into the object it is testing for ease of error tracing. Enable Change Stream tests with the repurposed EventsCollector class to iterate change events. Adds find, insertMany, iterateUntilDocumentOrError, and failPoint operations. NODE-2287
1 parent 57d82f1 commit a874e5c

File tree

10 files changed

+497
-255
lines changed

10 files changed

+497
-255
lines changed

package-lock.json

Lines changed: 0 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/functional/change_stream.test.js

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const { MongoNetworkError } = require('../../src/error');
55
const { delay, setupDatabase, withClient, withCursor } = require('./shared');
66
const co = require('co');
77
const mock = require('../tools/mock');
8+
const { EventCollector } = require('../tools/utils');
89
const chai = require('chai');
910
const expect = chai.expect;
1011
const sinon = require('sinon');
@@ -233,56 +234,6 @@ describe('Change Streams', function () {
233234
}
234235
});
235236

236-
class EventCollector {
237-
constructor(obj, events, options) {
238-
this._events = [];
239-
this._timeout = options ? options.timeout : 5000;
240-
241-
events.forEach(eventName => {
242-
this._events[eventName] = [];
243-
obj.on(eventName, event => this._events[eventName].push(event));
244-
});
245-
}
246-
247-
waitForEvent(eventName, count, callback) {
248-
if (typeof count === 'function') {
249-
callback = count;
250-
count = 1;
251-
}
252-
253-
waitForEventImpl(this, Date.now(), eventName, count, callback);
254-
}
255-
256-
reset(eventName) {
257-
if (eventName == null) {
258-
Object.keys(this._events).forEach(eventName => {
259-
this._events[eventName] = [];
260-
});
261-
262-
return;
263-
}
264-
265-
if (this._events[eventName] == null) {
266-
throw new TypeError(`invalid event name "${eventName}" specified for reset`);
267-
}
268-
269-
this._events[eventName] = [];
270-
}
271-
}
272-
273-
function waitForEventImpl(collector, start, eventName, count, callback) {
274-
const events = collector._events[eventName];
275-
if (events.length >= count) {
276-
return callback(undefined, events);
277-
}
278-
279-
if (Date.now() - start >= collector._timeout) {
280-
return callback(new Error(`timed out waiting for event "${eventName}"`));
281-
}
282-
283-
setTimeout(() => waitForEventImpl(collector, start, eventName, count, callback), 10);
284-
}
285-
286237
it('should create a ChangeStream on a collection and emit `change` events', {
287238
metadata: { requires: { topology: 'replicaset', mongodb: '>=3.6' } },
288239

test/functional/unified-spec-runner/entities.ts

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,18 @@ import type {
99
} from '../../../src/cmap/events';
1010
import { patchCollectionOptions, patchDbOptions } from './unified-utils';
1111
import { TestConfiguration } from './unified.test';
12+
import { expect } from 'chai';
13+
14+
interface UnifiedChangeStream extends ChangeStream {
15+
eventCollector: InstanceType<typeof import('../../tools/utils')['EventCollector']>;
16+
}
1217

1318
export type CommandEvent = CommandStartedEvent | CommandSucceededEvent | CommandFailedEvent;
1419

1520
export class UnifiedMongoClient extends MongoClient {
1621
events: CommandEvent[];
22+
failPoints: Document[];
23+
ignoredEvents: string[];
1724
observedEvents: ('commandStarted' | 'commandSucceeded' | 'commandFailed')[];
1825

1926
static EVENT_NAME_LOOKUP = {
@@ -25,6 +32,11 @@ export class UnifiedMongoClient extends MongoClient {
2532
constructor(url: string, description: ClientEntity) {
2633
super(url, { monitorCommands: true, ...description.uriOptions });
2734
this.events = [];
35+
this.failPoints = [];
36+
this.ignoredEvents = [
37+
...(description.ignoreCommandMonitoringEvents ?? []),
38+
'configureFailPoint'
39+
];
2840
// apm
2941
this.observedEvents = (description.observeEvents ?? []).map(
3042
e => UnifiedMongoClient.EVENT_NAME_LOOKUP[e]
@@ -34,9 +46,11 @@ export class UnifiedMongoClient extends MongoClient {
3446
}
3547
}
3648

37-
// NOTE: this must be an arrow function for `this` to work.
49+
// NOTE: pushEvent must be an arrow function
3850
pushEvent: (e: CommandEvent) => void = e => {
39-
this.events.push(e);
51+
if (!this.ignoredEvents.includes(e.commandName)) {
52+
this.events.push(e);
53+
}
4054
};
4155

4256
/** Disables command monitoring for the client and returns a list of the captured events. */
@@ -46,14 +60,33 @@ export class UnifiedMongoClient extends MongoClient {
4660
}
4761
return this.events;
4862
}
63+
64+
async enableFailPoint(failPoint: Document): Promise<Document> {
65+
const admin = this.db().admin();
66+
const result = await admin.command(failPoint);
67+
expect(result).to.have.property('ok', 1);
68+
this.failPoints.push(failPoint.configureFailPoint);
69+
return result;
70+
}
71+
72+
async disableFailPoints(): Promise<Document[]> {
73+
return Promise.all(
74+
this.failPoints.map(configureFailPoint =>
75+
this.db().admin().command({
76+
configureFailPoint,
77+
mode: 'off'
78+
})
79+
)
80+
);
81+
}
4982
}
5083

5184
export type Entity =
5285
| UnifiedMongoClient
5386
| Db
5487
| Collection
5588
| ClientSession
56-
| ChangeStream
89+
| UnifiedChangeStream
5790
| GridFSBucket
5891
| Document; // Results from operations
5992

@@ -81,7 +114,7 @@ export class EntitiesMap<E = Entity> extends Map<string, E> {
81114
mapOf(type: 'collection'): EntitiesMap<Collection>;
82115
mapOf(type: 'session'): EntitiesMap<ClientSession>;
83116
mapOf(type: 'bucket'): EntitiesMap<GridFSBucket>;
84-
mapOf(type: 'stream'): EntitiesMap<ChangeStream>;
117+
mapOf(type: 'stream'): EntitiesMap<UnifiedChangeStream>;
85118
mapOf(type: EntityTypeId): EntitiesMap<Entity> {
86119
const ctor = ENTITY_CTORS.get(type);
87120
if (!ctor) {
@@ -95,7 +128,7 @@ export class EntitiesMap<E = Entity> extends Map<string, E> {
95128
getEntity(type: 'collection', key: string, assertExists?: boolean): Collection;
96129
getEntity(type: 'session', key: string, assertExists?: boolean): ClientSession;
97130
getEntity(type: 'bucket', key: string, assertExists?: boolean): GridFSBucket;
98-
getEntity(type: 'stream', key: string, assertExists?: boolean): ChangeStream;
131+
getEntity(type: 'stream', key: string, assertExists?: boolean): UnifiedChangeStream;
99132
getEntity(type: EntityTypeId, key: string, assertExists = true): Entity {
100133
const entity = this.get(key);
101134
if (!entity) {
@@ -114,6 +147,7 @@ export class EntitiesMap<E = Entity> extends Map<string, E> {
114147

115148
async cleanup(): Promise<void> {
116149
for (const [, client] of this.mapOf('client')) {
150+
await client.disableFailPoints();
117151
await client.close();
118152
}
119153
for (const [, session] of this.mapOf('session')) {

0 commit comments

Comments
 (0)