Skip to content

Commit 55d1c5f

Browse files
authored
refactor(NODE-3717): test reorg part 4 (#3086)
1 parent df8ac73 commit 55d1c5f

17 files changed

+754
-740
lines changed

test/functional/causal_consistency.test.js renamed to test/integration/causal-consistency/causal_consistency.test.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
'use strict';
22

3-
const { LEGACY_HELLO_COMMAND } = require('../../src/constants');
3+
const { LEGACY_HELLO_COMMAND } = require('../../../src/constants');
44

5-
const setupDatabase = require('./shared').setupDatabase,
6-
expect = require('chai').expect;
5+
const { setupDatabase } = require('../shared');
6+
const { expect } = require('chai');
77

88
const ignoredCommands = [LEGACY_HELLO_COMMAND, 'endSessions'];
99
const test = { commands: { started: [], succeeded: [] } };

test/functional/change_stream.test.js renamed to test/integration/change-streams/change_stream.test.js

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
'use strict';
22
const assert = require('assert');
33
const { Transform, PassThrough } = require('stream');
4-
const { MongoNetworkError } = require('../../src/error');
5-
const { delay, setupDatabase, withClient, withCursor } = require('./shared');
6-
const mock = require('../tools/mongodb-mock/index');
7-
const { EventCollector, getSymbolFrom } = require('../tools/utils');
4+
const { delay, setupDatabase, withClient, withCursor } = require('../shared');
5+
const mock = require('../../tools/mongodb-mock/index');
6+
const { EventCollector, getSymbolFrom } = require('../../tools/utils');
87
const chai = require('chai');
98

109
const expect = chai.expect;
1110
const sinon = require('sinon');
12-
const { ObjectId, Timestamp, Long, ReadPreference } = require('../../src');
11+
const { ObjectId, Timestamp, Long, ReadPreference, MongoNetworkError } = require('../../../src');
1312
const fs = require('fs');
1413
const os = require('os');
1514
const path = require('path');
1615
const crypto = require('crypto');
17-
const { LEGACY_HELLO_COMMAND } = require('../../src/constants');
16+
const { LEGACY_HELLO_COMMAND } = require('../../../src/constants');
1817
chai.use(require('chai-subset'));
19-
const { isHello } = require('../../src/utils');
18+
const { isHello } = require('../../../src/utils');
2019

2120
function withChangeStream(dbName, collectionName, callback) {
2221
if (arguments.length === 1) {

test/functional/change_stream_spec.test.js renamed to test/integration/change-streams/change_streams.spec.test.js

+5-8
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
'use strict';
22

33
const path = require('path');
4-
const chai = require('chai');
5-
const { loadSpecTests } = require('../spec');
6-
const { runUnifiedSuite } = require('../tools/unified-spec-runner/runner');
4+
const { expect } = require('chai');
5+
const { loadSpecTests } = require('../../spec');
6+
const { runUnifiedSuite } = require('../../tools/unified-spec-runner/runner');
77
const camelCase = require('lodash.camelcase');
8-
const { setupDatabase } = require('./shared');
9-
const { delay } = require('./shared');
10-
11-
const expect = chai.expect;
12-
const { LEGACY_HELLO_COMMAND } = require('../../src/constants');
8+
const { LEGACY_HELLO_COMMAND } = require('../../../src/constants');
9+
const { delay, setupDatabase } = require('../shared');
1310

1411
describe('Change Streams Spec - Unified', function () {
1512
runUnifiedSuite(loadSpecTests(path.join('change-streams', 'unified')));

test/functional/collection_management_spec.test.js renamed to test/integration/collection-management/collection_management.spec.test.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use strict';
22

33
const { expect } = require('chai');
4-
const { loadSpecTests } = require('../spec/index');
5-
const { runUnifiedTest } = require('../tools/unified-spec-runner/runner');
4+
const { loadSpecTests } = require('../../spec/index');
5+
const { runUnifiedTest } = require('../../tools/unified-spec-runner/runner');
66

77
describe('Collection management unified spec tests', function () {
88
for (const collectionManagementTest of loadSpecTests('collection-management')) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
'use strict';
2+
3+
const { setupDatabase, filterOutCommands } = require('../shared');
4+
const { loadSpecTests } = require('../../spec');
5+
const { LEGACY_HELLO_COMMAND } = require('../../../src/constants');
6+
const { expect } = require('chai');
7+
const { runUnifiedTest } = require('../../tools/unified-spec-runner/runner');
8+
9+
describe('Command Monitoring spec tests', function () {
10+
describe('command monitoring legacy spec tests', function () {
11+
before(function () {
12+
return setupDatabase(this.configuration);
13+
});
14+
15+
// TODO: The worst part about this custom validation method is that it does not
16+
// provide the rich context of failure location that chai gives us out of
17+
// the box. I investigated extending chai, however their internal implementation
18+
// does not reuse other internal methods, so we'd have to bring lodash in.
19+
// It may be worth seeing if we can improve on this, as we might need the
20+
// behavior in other future YAML tests.
21+
const maybeLong = val => (typeof val.equals === 'function' ? val.toNumber() : val);
22+
function apmExpect(actual, expected, parentKey) {
23+
Object.keys(expected).forEach(key => {
24+
expect(actual).to.include.key(key);
25+
26+
// TODO: This is a workaround that works because all sorts in the specs
27+
// are objects with one key; ideally we'd want to adjust the spec definitions
28+
// to indicate whether order matters for any given key and set general
29+
// expectations accordingly (see NODE-3235)
30+
if (key === 'sort') {
31+
expect(actual[key]).to.be.instanceOf(Map);
32+
expect(Object.keys(expected[key])).to.have.lengthOf(1);
33+
expect(actual[key].size).to.equal(1);
34+
expect(actual[key].get(Object.keys(expected[key])[0])).to.equal(
35+
Object.values(expected[key])[0]
36+
);
37+
return;
38+
}
39+
40+
if (Array.isArray(expected[key])) {
41+
expect(actual[key]).to.be.instanceOf(Array);
42+
expect(actual[key]).to.have.lengthOf(expected[key].length);
43+
for (let i = 0; i < expected[key].length; ++i) {
44+
apmExpect(actual[key][i], expected[key][i], key);
45+
}
46+
47+
return;
48+
}
49+
50+
if (expected[key] === 42 || expected[key] === '42' || expected[key] === '') {
51+
if (key === 'code' && expected[key] === 42) {
52+
expect(actual[key]).to.be.greaterThan(0);
53+
}
54+
55+
if (key === 'errmsg' && expected[key] === '') {
56+
expect(actual[key]).to.have.lengthOf.at.least(1); // >= 1
57+
}
58+
59+
if (key === 'getmore' || (parentKey === 'cursor' && key === 'id')) {
60+
expect(maybeLong(actual[key])).to.be.greaterThan(0);
61+
}
62+
63+
return;
64+
}
65+
66+
// cheap isPlainObject clone
67+
if (Object.prototype.toString.call(expected[key]) === '[object Object]') {
68+
apmExpect(actual[key], expected[key], key);
69+
return;
70+
}
71+
72+
// otherwise compare the values
73+
expect(maybeLong(actual[key]), key).to.deep.equal(expected[key]);
74+
});
75+
}
76+
77+
function validateCommandStartedEvent(expected, event) {
78+
expect(event.commandName).to.equal(expected.command_name);
79+
expect(event.databaseName).to.equal(expected.database_name);
80+
apmExpect(event.command, expected.command);
81+
}
82+
83+
function validateCommandSucceededEvent(expected, event) {
84+
expect(event.commandName).to.equal(expected.command_name);
85+
apmExpect(event.reply, expected.reply);
86+
}
87+
88+
function validateCommandFailedEvent(expected, event) {
89+
expect(event.commandName).to.equal(expected.command_name);
90+
}
91+
92+
function validateExpectations(expectation, results) {
93+
if (expectation.command_started_event) {
94+
validateCommandStartedEvent(expectation.command_started_event, results.starts.shift());
95+
} else if (expectation.command_succeeded_event) {
96+
validateCommandSucceededEvent(
97+
expectation.command_succeeded_event,
98+
results.successes.shift()
99+
);
100+
} else if (expectation.command_failed_event) {
101+
validateCommandFailedEvent(expectation.command_failed_event, results.failures.shift());
102+
}
103+
}
104+
105+
function executeOperation(client, scenario, test) {
106+
// Get the operation
107+
const operation = test.operation;
108+
// Get the command name
109+
const commandName = operation.name;
110+
// Get the arguments
111+
const args = operation.arguments || {};
112+
// Get the database instance
113+
const db = client.db(scenario.database_name);
114+
// Get the collection
115+
const collection = db.collection(scenario.collection_name);
116+
// Parameters
117+
const params = [];
118+
// Options
119+
let options = null;
120+
// Get the data
121+
const data = scenario.data;
122+
// Command Monitoring context
123+
const monitoringResults = {
124+
successes: [],
125+
failures: [],
126+
starts: []
127+
};
128+
129+
// Drop the collection
130+
return collection
131+
.drop()
132+
.catch(err => {
133+
// potentially skip this error
134+
if (!err.message.match(/ns not found/)) throw err;
135+
})
136+
.then(() => collection.insertMany(data))
137+
.then(r => {
138+
expect(data).to.have.length(Object.keys(r.insertedIds).length);
139+
140+
// Set up the listeners
141+
client.on(
142+
'commandStarted',
143+
filterOutCommands([LEGACY_HELLO_COMMAND, 'endSessions'], monitoringResults.starts)
144+
);
145+
client.on(
146+
'commandFailed',
147+
filterOutCommands([LEGACY_HELLO_COMMAND, 'endSessions'], monitoringResults.failures)
148+
);
149+
client.on(
150+
'commandSucceeded',
151+
filterOutCommands([LEGACY_HELLO_COMMAND, 'endSessions'], monitoringResults.successes)
152+
);
153+
154+
// Unpack the operation
155+
if (args.options) options = args.options;
156+
if (args.filter) params.push(args.filter);
157+
if (args.deletes) params.push(args.deletes);
158+
if (args.document) params.push(args.document);
159+
if (args.documents) params.push(args.documents);
160+
if (args.update) params.push(args.update);
161+
if (args.requests) {
162+
if (operation.name !== 'bulkWrite') {
163+
params.push(args.requests);
164+
} else {
165+
params.push(
166+
args.requests.map(r => {
167+
return { [r.name]: r.arguments.document || r.arguments };
168+
})
169+
);
170+
}
171+
}
172+
173+
if (args.writeConcern) {
174+
options = options || {};
175+
options.writeConcern = args.writeConcern;
176+
}
177+
178+
if (typeof args.ordered === 'boolean') {
179+
if (options == null) {
180+
options = { ordered: args.ordered };
181+
} else {
182+
options.ordered = args.ordered;
183+
}
184+
}
185+
186+
if (typeof args.upsert === 'boolean') {
187+
if (options == null) {
188+
options = { upsert: args.upsert };
189+
} else {
190+
options.upsert = args.upsert;
191+
}
192+
}
193+
194+
// Find command is special needs to executed using toArray
195+
if (operation.name === 'find') {
196+
let cursor = collection[commandName]();
197+
198+
// Set the options
199+
if (args.filter) cursor = cursor.filter(args.filter);
200+
if (args.batchSize) cursor = cursor.batchSize(args.batchSize);
201+
if (args.limit) cursor = cursor.limit(args.limit);
202+
if (args.skip) cursor = cursor.skip(args.skip);
203+
if (args.sort) cursor = cursor.sort(args.sort);
204+
205+
// Set any modifiers
206+
if (args.modifiers) {
207+
for (let modifier in args.modifiers) {
208+
cursor.addQueryModifier(modifier, args.modifiers[modifier]);
209+
}
210+
}
211+
212+
// Execute find
213+
return cursor
214+
.toArray()
215+
.catch(() => {} /* ignore */)
216+
.then(() =>
217+
test.expectations.forEach(expectation =>
218+
validateExpectations(expectation, monitoringResults)
219+
)
220+
);
221+
}
222+
// Add options if they exists
223+
if (options) params.push(options);
224+
225+
// Execute the operation
226+
const coll = operation.collectionOptions
227+
? db.collection(scenario.collection_name, operation.collectionOptions)
228+
: db.collection(scenario.collection_name);
229+
230+
const promise = coll[commandName].apply(coll, params);
231+
return promise
232+
.catch(() => {} /* ignore */)
233+
.then(() =>
234+
test.expectations.forEach(expectation =>
235+
validateExpectations(expectation, monitoringResults)
236+
)
237+
);
238+
});
239+
}
240+
241+
loadSpecTests('command-monitoring/legacy').forEach(scenario => {
242+
if (scenario.name === 'command') return; // FIXME(NODE-3074): remove when `count` spec tests have been fixed
243+
describe(scenario.name, function () {
244+
scenario.tests.forEach(test => {
245+
const requirements = { topology: ['single', 'replicaset', 'sharded'] };
246+
if (test.ignore_if_server_version_greater_than) {
247+
requirements.mongodb = `<${test.ignore_if_server_version_greater_than}`;
248+
} else if (test.ignore_if_server_version_less_than) {
249+
requirements.mongodb = `>${test.ignore_if_server_version_less_than}`;
250+
}
251+
252+
if (test.ignore_if_topology_type) {
253+
requirements.topology = requirements.topology.filter(
254+
top => test.ignore_if_topology_type.indexOf(top) < 0
255+
);
256+
}
257+
258+
it(test.description, {
259+
metadata: { requires: requirements },
260+
test: function () {
261+
if (
262+
test.description ===
263+
'A successful find event with a getmore and the server kills the cursor'
264+
) {
265+
this.skip();
266+
}
267+
268+
const client = this.configuration.newClient({}, { monitorCommands: true });
269+
return client.connect().then(client => {
270+
expect(client).to.exist;
271+
return executeOperation(client, scenario, test).then(() => client.close());
272+
});
273+
}
274+
});
275+
});
276+
});
277+
});
278+
});
279+
280+
describe('command monitoring unified spec tests', () => {
281+
for (const loadedSpec of loadSpecTests('command-monitoring/unified')) {
282+
expect(loadedSpec).to.include.all.keys(['description', 'tests']);
283+
context(String(loadedSpec.description), function () {
284+
for (const test of loadedSpec.tests) {
285+
it(String(test.description), {
286+
metadata: { sessions: { skipLeakTests: true } },
287+
test: async function () {
288+
await runUnifiedTest(this, loadedSpec, test);
289+
}
290+
});
291+
}
292+
});
293+
}
294+
});
295+
});

0 commit comments

Comments
 (0)