Skip to content

Commit 3171e02

Browse files
committed
ci(NODE-6860): add logging for known flaky tests
1 parent 9111f98 commit 3171e02

File tree

5 files changed

+122
-66
lines changed

5 files changed

+122
-66
lines changed

test/integration/client-side-encryption/client_side_encryption.spec.test.ts

-8
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,6 @@ describe('Client Side Encryption (Unified)', function () {
149149
if (typeof shouldSkip === 'string') return shouldSkip;
150150
}
151151

152-
const flakyTests = {
153-
'rewrap to azure:name1': 'TODO(NODE-6860): fix flaky tests'
154-
};
155-
156-
const skipReason = flakyTests[description];
157-
158-
if (skipReason) return skipReason;
159-
160152
return isServerless ? 'Unified CSFLE tests to not run on serverless' : false;
161153
}
162154
);

test/integration/server-discovery-and-monitoring/server_discovery_and_monitoring.prose.test.ts

+31-34
Original file line numberDiff line numberDiff line change
@@ -146,40 +146,37 @@ describe('Server Discovery and Monitoring Prose Tests', function () {
146146
await client.close();
147147
});
148148

149-
it.skip(
150-
'ensure monitors properly create and unpause connection pools when they discover servers',
151-
{
152-
metadata: { requires: { mongodb: '>=4.2.9', topology: '!load-balanced' } },
153-
test: async function () {
154-
await client.connect();
155-
expect(events.shift()).to.equal(SERVER_HEARTBEAT_SUCCEEDED);
156-
expect(events.shift()).to.equal(CONNECTION_POOL_READY);
157-
158-
expect(events).to.be.empty;
159-
160-
const heartBeatFailedEvent = once(client, SERVER_HEARTBEAT_FAILED);
161-
await client.db('admin').command({
162-
configureFailPoint: 'failCommand',
163-
mode: { times: 2 },
164-
data: {
165-
failCommands: ['hello'],
166-
errorCode: 1234,
167-
appName: 'SDAMPoolManagementTest'
168-
}
169-
});
170-
await heartBeatFailedEvent;
171-
expect(events.shift()).to.equal(SERVER_HEARTBEAT_FAILED);
172-
expect(events.shift()).to.equal(CONNECTION_POOL_CLEARED);
173-
174-
expect(events).to.be.empty;
175-
176-
await once(client, SERVER_HEARTBEAT_SUCCEEDED);
177-
expect(events.shift()).to.equal(SERVER_HEARTBEAT_SUCCEEDED);
178-
expect(events.shift()).to.equal(CONNECTION_POOL_READY);
179-
180-
expect(events).to.be.empty;
181-
}
149+
it('ensure monitors properly create and unpause connection pools when they discover servers', {
150+
metadata: { requires: { mongodb: '>=4.2.9', topology: '!load-balanced' } },
151+
test: async function () {
152+
await client.connect();
153+
expect(events.shift()).to.equal(SERVER_HEARTBEAT_SUCCEEDED);
154+
expect(events.shift()).to.equal(CONNECTION_POOL_READY);
155+
156+
expect(events).to.be.empty;
157+
158+
const heartBeatFailedEvent = once(client, SERVER_HEARTBEAT_FAILED);
159+
await client.db('admin').command({
160+
configureFailPoint: 'failCommand',
161+
mode: { times: 2 },
162+
data: {
163+
failCommands: ['hello'],
164+
errorCode: 1234,
165+
appName: 'SDAMPoolManagementTest'
166+
}
167+
});
168+
await heartBeatFailedEvent;
169+
expect(events.shift()).to.equal(SERVER_HEARTBEAT_FAILED);
170+
expect(events.shift()).to.equal(CONNECTION_POOL_CLEARED);
171+
172+
expect(events).to.be.empty;
173+
174+
await once(client, SERVER_HEARTBEAT_SUCCEEDED);
175+
expect(events.shift()).to.equal(SERVER_HEARTBEAT_SUCCEEDED);
176+
expect(events.shift()).to.equal(CONNECTION_POOL_READY);
177+
178+
expect(events).to.be.empty;
182179
}
183-
).skipReason = 'TODO(NODE-5206): fix flaky test';
180+
});
184181
});
185182
});

test/tools/runner/config.ts

+50
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import * as util from 'node:util';
2+
13
import { expect } from 'chai';
4+
import { type Context } from 'mocha';
25
import ConnectionString from 'mongodb-connection-string-url';
36
import * as qs from 'querystring';
47
import * as url from 'url';
@@ -205,6 +208,11 @@ export class TestConfiguration {
205208

206209
newClient(urlOrQueryOptions?: string | Record<string, any>, serverOptions?: MongoClientOptions) {
207210
serverOptions = Object.assign({}, getEnvironmentalOptions(), serverOptions);
211+
212+
if (this.loggingEnabled && !Object.hasOwn(serverOptions, 'mongodbLogPath')) {
213+
serverOptions = this.setupLogging(serverOptions);
214+
}
215+
208216
// Support MongoClient constructor form (url, options) for `newClient`.
209217
if (typeof urlOrQueryOptions === 'string') {
210218
if (Reflect.has(serverOptions, 'host') || Reflect.has(serverOptions, 'port')) {
@@ -427,6 +435,48 @@ export class TestConfiguration {
427435
makeAtlasTestConfiguration(): AtlasTestConfiguration {
428436
return new AtlasTestConfiguration(this.uri, this.context);
429437
}
438+
439+
loggingEnabled = false;
440+
logs = [];
441+
/**
442+
* Known flaky tests that we want to turn on logging for
443+
* so that we can get a better idea of what is failing when it fails
444+
*/
445+
testsToEnableLogging = [
446+
'Client Side Encryption (Unified) namedKMS-rewrapManyDataKey rewrap to azure:name1',
447+
'Server Discovery and Monitoring Prose Tests Connection Pool Management ensure monitors properly create and unpause connection pools when they discover servers',
448+
'CSOT spec tests legacy timeouts behave correctly for retryable operations operation succeeds after one socket timeout - aggregate on collection'
449+
];
450+
451+
setupLogging(options) {
452+
this.logs = [];
453+
const write = log => this.logs.push(log);
454+
options.mongodbLogPath = { write };
455+
options.mongodbLogComponentSeverities = { default: 'trace' };
456+
options.mongodbLogMaxDocumentLength = 300;
457+
return options;
458+
}
459+
460+
beforeEachLogging(ctx: Context) {
461+
this.loggingEnabled = this.testsToEnableLogging.includes(ctx.currentTest.fullTitle());
462+
}
463+
464+
afterEachLogging(ctx: Context) {
465+
(globalThis as any).shouldFlake = false;
466+
if (this.loggingEnabled && ctx.currentTest.state === 'failed') {
467+
for (const log of this.logs) {
468+
const logLine = util.inspect(log, {
469+
compact: true,
470+
breakLength: Infinity,
471+
colors: true,
472+
depth: 1000
473+
});
474+
console.error(logLine);
475+
}
476+
}
477+
this.loggingEnabled = false;
478+
this.logs = [];
479+
}
430480
}
431481

432482
/**

test/tools/runner/hooks/configuration.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { NodeVersionFilter } from '../filters/node_version_filter';
2222
import { OSFilter } from '../filters/os_filter';
2323
import { ServerlessFilter } from '../filters/serverless_filter';
2424
import { type Filter } from '../filters/filter';
25+
import { type Context } from 'mocha';
2526

2627
// Default our tests to have auth enabled
2728
// A better solution will be tackled in NODE-3714
@@ -211,8 +212,19 @@ const beforeAllPluginImports = () => {
211212
require('mocha-sinon');
212213
};
213214

215+
async function beforeEachLogging(this: Context) {
216+
if (this.currentTest == null) return;
217+
this.configuration.beforeEachLogging(this);
218+
}
219+
220+
async function afterEachLogging(this: Context) {
221+
if (this.currentTest == null) return;
222+
this.configuration.afterEachLogging(this);
223+
}
224+
214225
export const mochaHooks = {
215226
beforeAll: [beforeAllPluginImports, testConfigBeforeHook],
216-
beforeEach: [testSkipBeforeEachHook],
227+
beforeEach: [testSkipBeforeEachHook, beforeEachLogging],
228+
afterEach: [afterEachLogging],
217229
afterAll: [cleanUpMocksAfterHook]
218230
};

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

+28-23
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ import {
2828
type HostAddress,
2929
type Log,
3030
MongoClient,
31+
type MongoClientOptions,
3132
type MongoCredentials,
33+
type MongoDBLogWritable,
3234
ReadConcern,
3335
ReadPreference,
3436
SENSITIVE_COMMANDS,
@@ -126,7 +128,6 @@ export class UnifiedMongoClient extends MongoClient {
126128
cmapEvents: CmapEvent[] = [];
127129
sdamEvents: SdamEvent[] = [];
128130
failPoints: Document[] = [];
129-
logCollector: { buffer: LogMessage[]; write: (log: Log) => void };
130131

131132
ignoredEvents: string[];
132133
observeSensitiveCommands: boolean;
@@ -197,34 +198,37 @@ export class UnifiedMongoClient extends MongoClient {
197198
topology: 'MONGODB_LOG_TOPOLOGY'
198199
} as const;
199200

200-
constructor(uri: string, description: ClientEntity) {
201-
const logCollector: { buffer: LogMessage[]; write: (log: Log) => void } = {
202-
buffer: [],
203-
write(log: Log): void {
204-
const transformedLog = {
205-
level: log.s,
206-
component: log.c,
207-
data: { ...log }
208-
};
209-
210-
this.buffer.push(transformedLog);
211-
}
212-
};
213-
const mongodbLogComponentSeverities = description.observeLogMessages;
214-
215-
super(uri, {
201+
constructor(uri: string, description: ClientEntity, config: TestConfiguration) {
202+
const options: MongoClientOptions = {
216203
monitorCommands: true,
217204
__skipPingOnConnect: true,
218-
mongodbLogComponentSeverities,
219205
...getEnvironmentalOptions(),
220206
...(description.serverApi ? { serverApi: description.serverApi } : {}),
221-
mongodbLogPath: logCollector,
222207
// TODO(NODE-5785): We need to increase the truncation length because signature.hash is a Buffer making hellos too long
223208
mongodbLogMaxDocumentLength: 1250
224-
} as any);
209+
};
210+
211+
if (description.observeLogMessages != null) {
212+
options.mongodbLogComponentSeverities = description.observeLogMessages;
213+
options.mongodbLogPath = {
214+
buffer: [],
215+
write(log: Log): void {
216+
const transformedLog = {
217+
level: log.s,
218+
component: log.c,
219+
data: { ...log }
220+
};
221+
222+
this.buffer.push(transformedLog);
223+
}
224+
} as MongoDBLogWritable;
225+
} else if (config.loggingEnabled) {
226+
config.setupLogging(options);
227+
}
228+
229+
super(uri, options);
225230

226231
this.observedEventEmitter.on('error', () => null);
227-
this.logCollector = logCollector;
228232
this.observeSensitiveCommands = description.observeSensitiveCommands ?? false;
229233

230234
this.ignoredEvents = [
@@ -337,7 +341,7 @@ export class UnifiedMongoClient extends MongoClient {
337341
}
338342

339343
get collectedLogs(): LogMessage[] {
340-
return this.logCollector.buffer;
344+
return (this.options.mongodbLogPath as unknown as { buffer: any[] })?.buffer ?? [];
341345
}
342346
}
343347

@@ -578,7 +582,8 @@ export class EntitiesMap<E = Entity> extends Map<string, E> {
578582
} else {
579583
uri = makeConnectionString(config.url({ useMultipleMongoses }), entity.client.uriOptions);
580584
}
581-
const client = new UnifiedMongoClient(uri, entity.client);
585+
586+
const client = new UnifiedMongoClient(uri, entity.client, config);
582587
try {
583588
new EntityEventRegistry(client, entity.client, map).register();
584589
await client.connect();

0 commit comments

Comments
 (0)