Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/sessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ export class ClientSession extends TypedEventEmitter<ClientSessionEvents> {
*/
startTransaction(options?: TransactionOptions): void {
if (this[kSnapshotEnabled]) {
throw new MongoCompatibilityError('Transactions are not allowed with snapshot sessions');
throw new MongoCompatibilityError('Transactions are not supported in snapshot sessions');
}

if (this.inTransaction()) {
Expand Down
40 changes: 21 additions & 19 deletions test/tools/unified-spec-runner/entities.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { expect } from 'chai';

import { ChangeStream } from '../../../src/change_stream';
Expand Down Expand Up @@ -64,8 +65,6 @@ function getClient(address) {
return new MongoClient(`mongodb://${address}`, getEnvironmentalOptions());
}

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

export class UnifiedMongoClient extends MongoClient {
commandEvents: CommandEvent[];
cmapEvents: CmapEvent[];
Expand Down Expand Up @@ -139,6 +138,17 @@ export class UnifiedMongoClient extends MongoClient {
return this.ignoredEvents.includes(e.commandName);
}

getCapturedEvents(eventType: string): CommandEvent[] | CmapEvent[] {
switch (eventType) {
case 'command':
return this.commandEvents;
case 'cmap':
return this.cmapEvents;
default:
throw new Error(`Unknown eventType: ${eventType}`);
}
}

// NOTE: pushCommandEvent must be an arrow function
pushCommandEvent: (e: CommandEvent) => void = e => {
if (!this.isIgnored(e)) {
Expand All @@ -151,22 +161,14 @@ export class UnifiedMongoClient extends MongoClient {
this.cmapEvents.push(e);
};

stopCapturingEvents(pushFn: PushFunction): void {
const observedEvents = [...this.observedCommandEvents, ...this.observedCmapEvents];
for (const eventName of observedEvents) {
this.off(eventName, pushFn);
}
}

/** 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;
stopCapturingEvents(): void {
for (const eventName of this.observedCommandEvents) {
this.off(eventName, this.pushCommandEvent);
}
for (const eventName of this.observedCmapEvents) {
this.off(eventName, this.pushCmapEvent);
}
}
}

Expand All @@ -179,7 +181,7 @@ export class FailPointMap extends Map<string, Document> {
let address: string;
if (addressOrClient instanceof MongoClient) {
client = addressOrClient;
address = client.topology.s.seedlist.join(',');
address = client.topology!.s.seedlist.join(',');
} else {
// create a new client
address = addressOrClient.toString();
Expand Down Expand Up @@ -300,7 +302,7 @@ export class EntitiesMap<E = Entity> extends Map<string, E> {
getEntity(type: 'cursor', key: string, assertExists?: boolean): AbstractCursor;
getEntity(type: 'stream', key: string, assertExists?: boolean): UnifiedChangeStream;
getEntity(type: 'clientEncryption', key: string, assertExists?: boolean): ClientEncryption;
getEntity(type: EntityTypeId, key: string, assertExists = true): Entity {
getEntity(type: EntityTypeId, key: string, assertExists = true): Entity | undefined {
const entity = this.get(key);
if (!entity) {
if (assertExists) throw new Error(`Entity '${key}' does not exist`);
Expand Down
90 changes: 54 additions & 36 deletions test/tools/unified-spec-runner/match.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { expect } from 'chai';
import { inspect } from 'util';

Expand All @@ -7,6 +8,7 @@ import {
Document,
Long,
MongoError,
MongoServerError,
ObjectId,
OneOrMore
} from '../../../src';
Expand Down Expand Up @@ -264,11 +266,11 @@ export function specialCheck(
entities: EntitiesMap,
path: string[] = [],
checkExtraKeys: boolean
): boolean {
): void {
if (isUnsetOrMatchesOperator(expected)) {
if (actual === null || actual === undefined) return;

resultCheck(actual, expected.$$unsetOrMatches, entities, path, checkExtraKeys);
resultCheck(actual, expected.$$unsetOrMatches as any, entities, path, checkExtraKeys);
} else if (isMatchesEntityOperator(expected)) {
// $$matchesEntity
const entity = entities.get(expected.$$matchesEntity);
Expand All @@ -290,15 +292,15 @@ export function specialCheck(
// $$sessionLsid
const session = entities.getEntity('session', expected.$$sessionLsid, false);
expect(session, `Session ${expected.$$sessionLsid} does not exist in entities`).to.exist;
const entitySessionHex = session.id.id.buffer.toString('hex').toUpperCase();
const entitySessionHex = session.id!.id.buffer.toString('hex').toUpperCase();
const actualSessionHex = actual.id.buffer.toString('hex').toUpperCase();
expect(
entitySessionHex,
`Session entity ${expected.$$sessionLsid} does not match lsid`
).to.equal(actualSessionHex);
} else if (isTypeOperator(expected)) {
// $$type
let ok: boolean;
let ok = false;
const types = Array.isArray(expected.$$type) ? expected.$$type : [expected.$$type];
for (const type of types) {
ok ||= TYPE_MAP.get(type)(actual);
Expand Down Expand Up @@ -364,19 +366,23 @@ function compareCommandStartedEvents(
entities: EntitiesMap,
prefix: string
) {
if (expected.command) {
resultCheck(actual.command, expected.command, entities, [`${prefix}.command`]);
if (expected!.command) {
resultCheck(actual.command, expected!.command, entities, [`${prefix}.command`]);
}
if (expected.commandName) {
if (expected!.commandName) {
expect(
expected.commandName,
`expected ${prefix}.commandName to equal ${expected.commandName} but received ${actual.commandName}`
expected!.commandName,
`expected ${prefix}.commandName to equal ${expected!.commandName} but received ${
actual.commandName
}`
).to.equal(actual.commandName);
}
if (expected.databaseName) {
if (expected!.databaseName) {
expect(
expected.databaseName,
`expected ${prefix}.databaseName to equal ${expected.databaseName} but received ${actual.databaseName}`
expected!.databaseName,
`expected ${prefix}.databaseName to equal ${expected!.databaseName} but received ${
actual.databaseName
}`
).to.equal(actual.databaseName);
}
}
Expand All @@ -387,13 +393,15 @@ function compareCommandSucceededEvents(
entities: EntitiesMap,
prefix: string
) {
if (expected.reply) {
resultCheck(actual.reply, expected.reply, entities, [prefix]);
if (expected!.reply) {
resultCheck(actual.reply as Document, expected!.reply, entities, [prefix]);
}
if (expected.commandName) {
if (expected!.commandName) {
expect(
expected.commandName,
`expected ${prefix}.commandName to equal ${expected.commandName} but received ${actual.commandName}`
expected!.commandName,
`expected ${prefix}.commandName to equal ${expected!.commandName} but received ${
actual.commandName
}`
).to.equal(actual.commandName);
}
}
Expand All @@ -404,10 +412,12 @@ function compareCommandFailedEvents(
entities: EntitiesMap,
prefix: string
) {
if (expected.commandName) {
if (expected!.commandName) {
expect(
expected.commandName,
`expected ${prefix}.commandName to equal ${expected.commandName} but received ${actual.commandName}`
expected!.commandName,
`expected ${prefix}.commandName to equal ${expected!.commandName} but received ${
actual.commandName
}`
).to.equal(actual.commandName);
}
}
Expand Down Expand Up @@ -489,28 +499,34 @@ export function matchesEvents(
}
}

function isMongoCryptError(err): boolean {
if (err.constructor.name === 'MongoCryptError') {
return true;
}
return err.stack.includes('at ClientEncryption');
}

export function expectErrorCheck(
error: Error | MongoError,
expected: ExpectedError,
entities: EntitiesMap
): boolean {
if (Object.keys(expected)[0] === 'isClientError' || Object.keys(expected)[0] === 'isError') {
// FIXME: We cannot tell if Error arose from driver and not from server
return;
): void {
const expectMessage = `\n\nOriginal Error Stack:\n${error.stack}\n\n`;

if (!isMongoCryptError(error)) {
expect(error, expectMessage).to.be.instanceOf(MongoError);
}

const expectMessage = `\n\nOriginal Error Stack:\n${error.stack}\n\n`;
if (expected.isClientError === false) {
expect(error).to.be.instanceOf(MongoServerError);
} else if (expected.isClientError === true) {
expect(error).not.to.be.instanceOf(MongoServerError);
}

if (expected.errorContains != null) {
expect(error.message, expectMessage).to.include(expected.errorContains);
}

if (!(error instanceof MongoError)) {
// if statement asserts type for TS, expect will always fail
expect(error, expectMessage).to.be.instanceOf(MongoError);
return;
}

if (expected.errorCode != null) {
expect(error, expectMessage).to.have.property('code', expected.errorCode);
}
Expand All @@ -520,24 +536,26 @@ export function expectErrorCheck(
}

if (expected.errorLabelsContain != null) {
const mongoError = error as MongoError;
for (const errorLabel of expected.errorLabelsContain) {
expect(
error.hasErrorLabel(errorLabel),
`Error was supposed to have label ${errorLabel}, has [${error.errorLabels}] -- ${expectMessage}`
mongoError.hasErrorLabel(errorLabel),
`Error was supposed to have label ${errorLabel}, has [${mongoError.errorLabels}] -- ${expectMessage}`
).to.be.true;
}
}

if (expected.errorLabelsOmit != null) {
const mongoError = error as MongoError;
for (const errorLabel of expected.errorLabelsOmit) {
expect(
error.hasErrorLabel(errorLabel),
`Error was supposed to have label ${errorLabel}, has [${error.errorLabels}] -- ${expectMessage}`
mongoError.hasErrorLabel(errorLabel),
`Error was not supposed to have label ${errorLabel}, has [${mongoError.errorLabels}] -- ${expectMessage}`
).to.be.false;
}
}

if (expected.expectResult != null) {
resultCheck(error, expected.expectResult, entities);
resultCheck(error, expected.expectResult as any, entities);
}
}
Loading