Skip to content

Commit 2be5714

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

File tree

6 files changed

+130
-68
lines changed

6 files changed

+130
-68
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

+49
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,47 @@ 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+
if (this.loggingEnabled && ctx.currentTest.state === 'failed') {
466+
for (const log of this.logs) {
467+
const logLine = util.inspect(log, {
468+
compact: true,
469+
breakLength: Infinity,
470+
colors: true,
471+
depth: 1000
472+
});
473+
console.error(logLine);
474+
}
475+
}
476+
this.loggingEnabled = false;
477+
this.logs = [];
478+
}
430479
}
431480

432481
/**

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

+35-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,44 @@ 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(
202+
uri: string,
203+
description: ClientEntity,
204+
config: {
205+
loggingEnabled?: boolean;
206+
setupLogging?: (options: Record<string, any>) => Record<string, any>;
207+
}
208+
) {
209+
const options: MongoClientOptions = {
216210
monitorCommands: true,
217211
__skipPingOnConnect: true,
218-
mongodbLogComponentSeverities,
219212
...getEnvironmentalOptions(),
220213
...(description.serverApi ? { serverApi: description.serverApi } : {}),
221-
mongodbLogPath: logCollector,
222214
// TODO(NODE-5785): We need to increase the truncation length because signature.hash is a Buffer making hellos too long
223215
mongodbLogMaxDocumentLength: 1250
224-
} as any);
216+
};
217+
218+
if (description.observeLogMessages != null) {
219+
options.mongodbLogComponentSeverities = description.observeLogMessages;
220+
options.mongodbLogPath = {
221+
buffer: [],
222+
write(log: Log): void {
223+
const transformedLog = {
224+
level: log.s,
225+
component: log.c,
226+
data: { ...log }
227+
};
228+
229+
this.buffer.push(transformedLog);
230+
}
231+
} as MongoDBLogWritable;
232+
} else if (config.loggingEnabled) {
233+
config.setupLogging?.(options);
234+
}
235+
236+
super(uri, options);
225237

226238
this.observedEventEmitter.on('error', () => null);
227-
this.logCollector = logCollector;
228239
this.observeSensitiveCommands = description.observeSensitiveCommands ?? false;
229240

230241
this.ignoredEvents = [
@@ -337,7 +348,7 @@ export class UnifiedMongoClient extends MongoClient {
337348
}
338349

339350
get collectedLogs(): LogMessage[] {
340-
return this.logCollector.buffer;
351+
return (this.options.mongodbLogPath as unknown as { buffer: any[] })?.buffer ?? [];
341352
}
342353
}
343354

@@ -578,7 +589,8 @@ export class EntitiesMap<E = Entity> extends Map<string, E> {
578589
} else {
579590
uri = makeConnectionString(config.url({ useMultipleMongoses }), entity.client.uriOptions);
580591
}
581-
const client = new UnifiedMongoClient(uri, entity.client);
592+
593+
const client = new UnifiedMongoClient(uri, entity.client, config);
582594
try {
583595
new EntityEventRegistry(client, entity.client, map).register();
584596
await client.connect();

test/unit/tools/unified_spec_runner/entity_event_registry.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe('EntityEventRegistry', function () {
4848
};
4949
const entitesMap = new EntitiesMap();
5050
const uri = 'mongodb://127.0.0.1:27017';
51-
const client = new UnifiedMongoClient(uri, clientEntity);
51+
const client = new UnifiedMongoClient(uri, clientEntity, {});
5252
const registry = new EntityEventRegistry(client, clientEntity, entitesMap);
5353

5454
before(function () {
@@ -120,7 +120,7 @@ describe('EntityEventRegistry', function () {
120120
const clientEntity = { id: 'client0' };
121121
const entitesMap = new EntitiesMap();
122122
const uri = 'mongodb://127.0.0.1:27017';
123-
const client = new UnifiedMongoClient(uri, clientEntity);
123+
const client = new UnifiedMongoClient(uri, clientEntity, {});
124124
const registry = new EntityEventRegistry(client, clientEntity, entitesMap);
125125

126126
before(function () {

0 commit comments

Comments
 (0)