Skip to content

Commit

Permalink
test: unify event collection in unified runner
Browse files Browse the repository at this point in the history
  • Loading branch information
dariakp committed Aug 12, 2022
1 parent 648212c commit 8ec6dcb
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 70 deletions.
89 changes: 38 additions & 51 deletions test/tools/unified-spec-runner/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,7 @@ export interface UnifiedChangeStream extends ChangeStream {
eventCollector: InstanceType<typeof import('../../tools/utils')['EventCollector']>;
}

export type CommandEvent = CommandStartedEvent | CommandSucceededEvent | CommandFailedEvent;
export type CmapEvent =
export type CapturedEvent =
| ConnectionPoolCreatedEvent
| ConnectionPoolClosedEvent
| ConnectionCreatedEvent
Expand All @@ -59,21 +58,20 @@ export type CmapEvent =
| ConnectionCheckOutFailedEvent
| ConnectionCheckedOutEvent
| ConnectionCheckedInEvent
| ConnectionPoolClearedEvent;
| ConnectionPoolClearedEvent
| CommandStartedEvent
| CommandSucceededEvent
| CommandFailedEvent;

function getClient(address) {
return new MongoClient(`mongodb://${address}`, getEnvironmentalOptions());
}

type PushFunction = (e: any) => void;

export class UnifiedMongoClient extends MongoClient {
commandEvents: CommandEvent[];
cmapEvents: CmapEvent[];
capturedEvents: CapturedEvent[];
failPoints: Document[];
ignoredEvents: string[];
observedCommandEvents: ('commandStarted' | 'commandSucceeded' | 'commandFailed')[];
observedCmapEvents: (
observedEvents: (
| 'connectionPoolCreated'
| 'connectionPoolClosed'
| 'connectionPoolCleared'
Expand All @@ -84,16 +82,13 @@ export class UnifiedMongoClient extends MongoClient {
| 'connectionCheckOutFailed'
| 'connectionCheckedOut'
| 'connectionCheckedIn'
| 'commandStarted'
| 'commandSucceeded'
| 'commandFailed'
)[];
_credentials: MongoCredentials | null;

static COMMAND_EVENT_NAME_LOOKUP = {
commandStartedEvent: 'commandStarted',
commandSucceededEvent: 'commandSucceeded',
commandFailedEvent: 'commandFailed'
} as const;

static CMAP_EVENT_NAME_LOOKUP = {
static EVENT_NAME_LOOKUP = {
poolCreatedEvent: 'connectionPoolCreated',
poolClosedEvent: 'connectionPoolClosed',
poolClearedEvent: 'connectionPoolCleared',
Expand All @@ -103,7 +98,10 @@ export class UnifiedMongoClient extends MongoClient {
connectionCheckOutStartedEvent: 'connectionCheckOutStarted',
connectionCheckOutFailedEvent: 'connectionCheckOutFailed',
connectionCheckedOutEvent: 'connectionCheckedOut',
connectionCheckedInEvent: 'connectionCheckedIn'
connectionCheckedInEvent: 'connectionCheckedIn',
commandStartedEvent: 'commandStarted',
commandSucceededEvent: 'commandSucceeded',
commandFailedEvent: 'commandFailed'
} as const;

constructor(uri: string, description: ClientEntity) {
Expand All @@ -114,60 +112,49 @@ export class UnifiedMongoClient extends MongoClient {
...(description.serverApi ? { serverApi: description.serverApi } : {})
});

this.commandEvents = [];
this.cmapEvents = [];
this.capturedEvents = [];
this.failPoints = [];
this.ignoredEvents = [
...(description.ignoreCommandMonitoringEvents ?? []),
'configureFailPoint'
];

this.observedCommandEvents = (description.observeEvents ?? [])
.map(e => UnifiedMongoClient.COMMAND_EVENT_NAME_LOOKUP[e])
this.observedEvents = (description.observeEvents ?? [])
.map(e => UnifiedMongoClient.EVENT_NAME_LOOKUP[e])
.filter(e => !!e);
this.observedCmapEvents = (description.observeEvents ?? [])
.map(e => UnifiedMongoClient.CMAP_EVENT_NAME_LOOKUP[e])
this.observedEvents = (description.observeEvents ?? [])
.map(e => UnifiedMongoClient.EVENT_NAME_LOOKUP[e])
.filter(e => !!e);
for (const eventName of this.observedCommandEvents) {
this.on(eventName, this.pushCommandEvent);
for (const eventName of this.observedEvents) {
this.on(eventName, this.pushEvent);
}
for (const eventName of this.observedCmapEvents) {
this.on(eventName, this.pushCmapEvent);
for (const eventName of this.observedEvents) {
this.on(eventName, this.pushEvent);
}
}

isIgnored(e: CommandEvent): boolean {
return this.ignoredEvents.includes(e.commandName);
isIgnored(e: CapturedEvent): boolean {
const commandName = (e as any).commandName;
if (!commandName) {
return false;
}
return this.ignoredEvents.includes(commandName);
}

// NOTE: pushCommandEvent must be an arrow function
pushCommandEvent: (e: CommandEvent) => void = e => {
// NOTE: pushEvent must be an arrow function
pushEvent: (e: CapturedEvent) => void = e => {
if (!this.isIgnored(e)) {
this.commandEvents.push(e);
this.capturedEvents.push(e);
}
};

// NOTE: pushCmapEvent must be an arrow function
pushCmapEvent: (e: CmapEvent) => void = e => {
this.cmapEvents.push(e);
};

stopCapturingEvents(pushFn: PushFunction): void {
const observedEvents = [...this.observedCommandEvents, ...this.observedCmapEvents];
/** Disables command monitoring for the client and returns a list of the captured events. */
stopCapturingEvents(): CapturedEvent[] {
const observedEvents = [...this.observedEvents, ...this.observedEvents];
for (const eventName of observedEvents) {
this.off(eventName, pushFn);
this.off(eventName, this.pushEvent);
}
}

/** Disables command monitoring for the client and returns a list of the captured events. */
stopCapturingCommandEvents(): CommandEvent[] {
this.stopCapturingEvents(this.pushCommandEvent);
return this.commandEvents;
}

stopCapturingCmapEvents(): CmapEvent[] {
this.stopCapturingEvents(this.pushCmapEvent);
return this.cmapEvents;
return this.capturedEvents;
}
}

Expand Down
10 changes: 5 additions & 5 deletions test/tools/unified-spec-runner/match.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import {
ConnectionReadyEvent
} from '../../../src/cmap/connection_pool_events';
import { ejson } from '../utils';
import { CmapEvent, CommandEvent, EntitiesMap } from './entities';
import { CapturedEvent, EntitiesMap } from './entities';
import {
ExpectedCmapEvent,
ExpectedCommandEvent,
Expand Down Expand Up @@ -340,15 +340,15 @@ const EMPTY_CMAP_EVENTS = {

function validEmptyCmapEvent(
expected: ExpectedCommandEvent | ExpectedCmapEvent,
actual: CommandEvent | CmapEvent
actual: CapturedEvent | CapturedEvent
) {
return Object.values(EMPTY_CMAP_EVENTS).some(value => {
return actual instanceof value;
});
}

function failOnMismatchedCount(
actual: CommandEvent[] | CmapEvent[],
actual: CapturedEvent[] | CapturedEvent[],
expected: (ExpectedCommandEvent & ExpectedCmapEvent)[]
) {
const actualNames = actual.map(a => a.constructor.name);
Expand Down Expand Up @@ -423,7 +423,7 @@ function compareCommandFailedEvents(
}

function compareEvents(
actual: CommandEvent[] | CmapEvent[],
actual: CapturedEvent[] | CapturedEvent[],
expected: (ExpectedCommandEvent & ExpectedCmapEvent)[],
entities: EntitiesMap
) {
Expand Down Expand Up @@ -477,7 +477,7 @@ function compareEvents(

export function matchesEvents(
{ events: expected, ignoreExtraEvents }: ExpectedEventsForClient,
actual: CommandEvent[] | CmapEvent[],
actual: CapturedEvent[] | CapturedEvent[],
entities: EntitiesMap
): void {
ignoreExtraEvents = ignoreExtraEvents ?? false;
Expand Down
8 changes: 4 additions & 4 deletions test/tools/unified-spec-runner/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ operations.set('assertIndexNotExists', async ({ operation, client }) => {

operations.set('assertDifferentLsidOnLastTwoCommands', async ({ entities, operation }) => {
const client = entities.getEntity('client', operation.arguments!.client);
expect(client.observedCommandEvents.includes('commandStarted')).to.be.true;
expect(client.observedEvents.includes('commandStarted')).to.be.true;

const startedEvents = client.commandEvents.filter(
const startedEvents = client.capturedEvents.filter(
ev => ev instanceof CommandStartedEvent
) as CommandStartedEvent[];

Expand All @@ -115,9 +115,9 @@ operations.set('assertDifferentLsidOnLastTwoCommands', async ({ entities, operat

operations.set('assertSameLsidOnLastTwoCommands', async ({ entities, operation }) => {
const client = entities.getEntity('client', operation.arguments!.client);
expect(client.observedCommandEvents.includes('commandStarted')).to.be.true;
expect(client.observedEvents.includes('commandStarted')).to.be.true;

const startedEvents = client.commandEvents.filter(
const startedEvents = client.capturedEvents.filter(
ev => ev instanceof CommandStartedEvent
) as CommandStartedEvent[];

Expand Down
14 changes: 4 additions & 10 deletions test/tools/unified-spec-runner/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ReadPreference } from '../../../src/read_preference';
import { TopologyType } from '../../../src/sdam/common';
import { ns } from '../../../src/utils';
import { ejson } from '../utils';
import { CmapEvent, CommandEvent, EntitiesMap } from './entities';
import { CapturedEvent, EntitiesMap } from './entities';
import { matchesEvents } from './match';
import { executeOperationAndCheck } from './operations';
import * as uni from './schema';
Expand Down Expand Up @@ -198,23 +198,17 @@ async function runUnifiedTest(
}
}

const clientCommandEvents = new Map<string, CommandEvent[]>();
const clientCmapEvents = new Map<string, CmapEvent[]>();
const clientEvents = new Map<string, CapturedEvent[]>();
// If any event listeners were enabled on any client entities,
// the test runner MUST now disable those event listeners.
for (const [id, client] of entities.mapOf('client')) {
clientCommandEvents.set(id, client.stopCapturingCommandEvents());
clientCmapEvents.set(id, client.stopCapturingCmapEvents());
clientEvents.set(id, client.stopCapturingEvents());
}

if (test.expectEvents) {
for (const expectedEventsForClient of test.expectEvents) {
const clientId = expectedEventsForClient.client;
const eventType = expectedEventsForClient.eventType;
// If no event type is provided it defaults to 'command', so just
// check for 'cmap' here for now.
const actualEvents =
eventType === 'cmap' ? clientCmapEvents.get(clientId) : clientCommandEvents.get(clientId);
const actualEvents = clientEvents.get(clientId);

expect(actualEvents, `No client entity found with id ${clientId}`).to.exist;
matchesEvents(expectedEventsForClient, actualEvents!, entities);
Expand Down

0 comments on commit 8ec6dcb

Please sign in to comment.