diff --git a/src/change_stream.ts b/src/change_stream.ts index b73a2fa6fc..df0f0466fe 100644 --- a/src/change_stream.ts +++ b/src/change_stream.ts @@ -57,6 +57,7 @@ const CURSOR_OPTIONS = [ 'maxAwaitTimeMS', 'collation', 'readPreference', + 'comment', ...CHANGE_STREAM_OPTIONS ] as const; @@ -410,7 +411,7 @@ export class ChangeStream extends TypedEven export interface ChangeStreamCursorOptions extends AbstractCursorOptions { startAtOperationTime?: OperationTime; resumeAfter?: ResumeToken; - startAfter?: boolean; + startAfter?: ResumeToken; } /** @internal */ @@ -617,7 +618,7 @@ function applyKnownOptions(source: Document, options: ReadonlyArray) { const result: Document = {}; for (const option of options) { - if (source[option]) { + if (option in source) { result[option] = source[option]; } } diff --git a/src/cmap/connection.ts b/src/cmap/connection.ts index c15d0fadfb..bbc3981561 100644 --- a/src/cmap/connection.ts +++ b/src/cmap/connection.ts @@ -124,7 +124,15 @@ export interface GetMoreOptions extends CommandOptions { batchSize?: number; maxTimeMS?: number; maxAwaitTimeMS?: number; - comment?: Document | string; + /** + * Comment to apply to the operation. + * + * In server versions pre-4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions 4.4 and above, 'comment' can be any valid BSON type. + */ + comment?: unknown; } /** @public */ @@ -574,6 +582,11 @@ export class Connection extends TypedEventEmitter { if (typeof options.maxAwaitTimeMS === 'number') { getMoreCmd.maxTimeMS = options.maxAwaitTimeMS; } + // we check for undefined specifically here to allow falsy values + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { + getMoreCmd.comment = options.comment; + } const commandOptions = Object.assign( { diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index 1ef82f13f0..eb19198674 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -42,6 +42,8 @@ const kInitialized = Symbol('initialized'); const kClosed = Symbol('closed'); /** @internal */ const kKilled = Symbol('killed'); +/** @internal */ +const kInit = Symbol('kInit'); /** @public */ export const CURSOR_FLAGS = [ @@ -77,7 +79,15 @@ export interface AbstractCursorOptions extends BSONSerializeOptions { readConcern?: ReadConcernLike; batchSize?: number; maxTimeMS?: number; - comment?: Document | string; + /** + * Comment to apply to the operation. + * + * In server versions pre-4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions 4.4 and above, 'comment' can be any valid BSON type. + */ + comment?: unknown; tailable?: boolean; awaitData?: boolean; noCursorTimeout?: boolean; @@ -162,7 +172,9 @@ export abstract class AbstractCursor< this[kOptions].batchSize = options.batchSize; } - if (options.comment != null) { + // we check for undefined specifically here to allow falsy values + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { this[kOptions].comment = options.comment; } @@ -620,6 +632,65 @@ export abstract class AbstractCursor< executeOperation(this, getMoreOperation, callback); } + + /** + * @internal + * + * This function is exposed for the unified test runner's createChangeStream + * operation. We cannot refactor to use the abstract _initialize method without + * a significant refactor. + */ + [kInit](callback: Callback): void { + if (this[kSession] == null) { + if (this[kTopology].shouldCheckForSessionSupport()) { + return this[kTopology].selectServer(ReadPreference.primaryPreferred, {}, err => { + if (err) return callback(err); + return this[kInit](callback); + }); + } else if (this[kTopology].hasSessionSupport()) { + this[kSession] = this[kTopology].startSession({ owner: this, explicit: false }); + } + } + + this._initialize(this[kSession], (err, state) => { + if (state) { + const response = state.response; + this[kServer] = state.server; + this[kSession] = state.session; + + if (response.cursor) { + this[kId] = + typeof response.cursor.id === 'number' + ? Long.fromNumber(response.cursor.id) + : response.cursor.id; + + if (response.cursor.ns) { + this[kNamespace] = ns(response.cursor.ns); + } + + this[kDocuments] = response.cursor.firstBatch; + } + + // When server responses return without a cursor document, we close this cursor + // and return the raw server response. This is often the case for explain commands + // for example + if (this[kId] == null) { + this[kId] = Long.ZERO; + // TODO(NODE-3286): ExecutionResult needs to accept a generic parameter + this[kDocuments] = [state.response as TODO_NODE_3286]; + } + } + + // the cursor is now initialized, even if an error occurred or it is dead + this[kInitialized] = true; + + if (err || cursorIsDead(this)) { + return cleanupCursor(this, { error: err }, () => callback(err, nextDocument(this))); + } + + callback(); + }); + } } function nextDocument(cursor: AbstractCursor): T | null | undefined { @@ -653,61 +724,12 @@ function next(cursor: AbstractCursor, blocking: boolean, callback: Callback { - if (err) return callback(err); - return next(cursor, blocking, callback); - }); - } else if (cursor[kTopology].hasSessionSupport()) { - cursor[kSession] = cursor[kTopology].startSession({ owner: cursor, explicit: false }); - } - } - - cursor._initialize(cursor[kSession], (err, state) => { - if (state) { - const response = state.response; - cursor[kServer] = state.server; - cursor[kSession] = state.session; - - if (response.cursor) { - cursor[kId] = - typeof response.cursor.id === 'number' - ? Long.fromNumber(response.cursor.id) - : response.cursor.id; - - if (response.cursor.ns) { - cursor[kNamespace] = ns(response.cursor.ns); - } - - cursor[kDocuments] = response.cursor.firstBatch; - } else { - // NOTE: This is for support of older servers (<3.2) which do not use commands - cursor[kId] = - typeof response.cursorId === 'number' - ? Long.fromNumber(response.cursorId) - : response.cursorId; - cursor[kDocuments] = response.documents; - } - - // When server responses return without a cursor document, we close this cursor - // and return the raw server response. This is often the case for explain commands - // for example - if (cursor[kId] == null) { - cursor[kId] = Long.ZERO; - // TODO(NODE-3286): ExecutionResult needs to accept a generic parameter - cursor[kDocuments] = [state.response as TODO_NODE_3286]; - } - } - - // the cursor is now initialized, even if an error occurred or it is dead - cursor[kInitialized] = true; - - if (err || cursorIsDead(cursor)) { - return cleanupCursor(cursor, { error: err }, () => callback(err, nextDocument(cursor))); + cursor[kInit]((err, value) => { + if (err) return callback(err); + if (value) { + return callback(undefined, value); } - - next(cursor, blocking, callback); + return next(cursor, blocking, callback); }); return; diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index fb18d969f6..1acc6ac628 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -2,8 +2,7 @@ import type { Document } from '../bson'; import { MongoInvalidArgumentError } from '../error'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; -import type { Callback } from '../utils'; -import { maxWireVersion, MongoDBNamespace } from '../utils'; +import { Callback, maxWireVersion, MongoDBNamespace } from '../utils'; import { CollationOptions, CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects, Hint } from './operation'; @@ -31,6 +30,7 @@ export interface AggregateOptions extends CommandOperationOptions { hint?: Hint; /** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */ let?: Document; + out?: string; } @@ -121,6 +121,12 @@ export class AggregateOperation extends CommandOperation { command.let = options.let; } + // we check for undefined specifically here to allow falsy values + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { + command.comment = options.comment; + } + command.cursor = options.cursor || {}; if (options.batchSize && !this.hasWriteStage) { command.cursor.batchSize = options.batchSize; diff --git a/src/operations/command.ts b/src/operations/command.ts index b67979d422..a9110592d6 100644 --- a/src/operations/command.ts +++ b/src/operations/command.ts @@ -45,8 +45,15 @@ export interface CommandOperationOptions /** Collation */ collation?: CollationOptions; maxTimeMS?: number; - /** A user-provided comment to attach to this command */ - comment?: string | Document; + /** + * Comment to apply to the operation. + * + * In server versions pre-4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions 4.4 and above, 'comment' can be any valid BSON type. + */ + comment?: unknown; /** Should retry failed writes */ retryWrites?: boolean; diff --git a/src/operations/delete.ts b/src/operations/delete.ts index 5891c1028c..ccdfd1c80b 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -12,8 +12,6 @@ import { Aspect, defineAspects, Hint } from './operation'; export interface DeleteOptions extends CommandOperationOptions, WriteConcernOptions { /** If true, when an insert fails, don't execute the remaining writes. If false, continue with remaining inserts when one fails. */ ordered?: boolean; - /** A user-provided comment to attach to this command */ - comment?: string | Document; /** Specifies the collation to use for the operation */ collation?: CollationOptions; /** Specify that the update query should only consider plans using the hinted index */ @@ -43,8 +41,6 @@ export interface DeleteStatement { collation?: CollationOptions; /** A document or string that specifies the index to use to support the query predicate. */ hint?: Hint; - /** A user-provided comment to attach to this command */ - comment?: string | Document; } /** @internal */ @@ -80,6 +76,12 @@ export class DeleteOperation extends CommandOperation { command.let = options.let; } + // we check for undefined specifically here to allow falsy values + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { + command.comment = options.comment; + } + if (options.explain != null && maxWireVersion(server) < 3) { return callback ? callback( @@ -175,10 +177,6 @@ export function makeDeleteStatement( op.hint = options.hint; } - if (options.comment) { - op.comment = options.comment; - } - return op; } diff --git a/src/operations/find.ts b/src/operations/find.ts index f3e618596b..50bc5236bf 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -46,8 +46,6 @@ export interface FindOptions extends Comman min?: Document; /** The exclusive upper bound for a specific index */ max?: Document; - /** You can put a $comment field on a query to make looking in the profiler logs simpler. */ - comment?: string | Document; /** Number of milliseconds to wait before aborting the query. */ maxTimeMS?: number; /** The maximum amount of time for the server to wait on new documents to satisfy a tailable cursor query. Requires `tailable` and `awaitData` to be true */ @@ -241,7 +239,9 @@ function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOp findCommand.singleBatch = options.singleBatch; } - if (options.comment) { + // we check for undefined specifically here to allow falsy values + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { findCommand.comment = options.comment; } diff --git a/src/operations/find_and_modify.ts b/src/operations/find_and_modify.ts index 2e2a776d70..12d7ba5044 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -82,6 +82,15 @@ interface FindAndModifyCmdBase { maxTimeMS?: number; let?: Document; writeConcern?: WriteConcern | WriteConcernSettings; + /** + * Comment to apply to the operation. + * + * In server versions pre-4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions 4.4 and above, 'comment' can be any valid BSON type. + */ + comment?: unknown; } function configureFindAndModifyCmdBaseUpdateOpts( @@ -140,6 +149,12 @@ class FindAndModifyOperation extends CommandOperation { this.cmdBase.let = options.let; } + // we check for undefined specifically here to allow falsy values + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { + this.cmdBase.comment = options.comment; + } + // force primary read preference this.readPreference = ReadPreference.primary; diff --git a/src/operations/get_more.ts b/src/operations/get_more.ts index e0d1bd5430..51fe9cf4b7 100644 --- a/src/operations/get_more.ts +++ b/src/operations/get_more.ts @@ -2,7 +2,7 @@ import type { Document, Long } from '../bson'; import { MongoRuntimeError } from '../error'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; -import type { Callback, MongoDBNamespace } from '../utils'; +import { Callback, maxWireVersion, MongoDBNamespace } from '../utils'; import { AbstractOperation, Aspect, defineAspects, OperationOptions } from './operation'; /** @@ -12,8 +12,12 @@ import { AbstractOperation, Aspect, defineAspects, OperationOptions } from './op export interface GetMoreOptions extends OperationOptions { /** Set the batchSize for the getMoreCommand when iterating over the query results. */ batchSize?: number; - /** You can put a $comment field on a query to make looking in the profiler logs simpler. */ - comment?: string | Document; + /** + * Comment to apply to the operation. + * + * getMore only supports 'comment' in server versions 4.4 and above. + */ + comment?: unknown; /** Number of milliseconds to wait before aborting the query. */ maxTimeMS?: number; } @@ -25,7 +29,14 @@ export class GetMoreOperation extends AbstractOperation { constructor(ns: MongoDBNamespace, cursorId: Long, server: Server, options: GetMoreOptions = {}) { super(options); + this.options = options; + + // comment on getMore is only supported for server versions 4.4 and above + if (maxWireVersion(server) < 9) { + delete this.options.comment; + } + this.ns = ns; this.cursorId = cursorId; this.server = server; diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index f686d59aa2..d7a27adfe0 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -24,7 +24,6 @@ import { indexInformation, IndexInformationOptions } from './common_functions'; import { executeOperation, ExecutionResult } from './execute_operation'; import { AbstractOperation, Aspect, defineAspects } from './operation'; -const LIST_INDEXES_WIRE_VERSION = 3; const VALID_INDEX_OPTIONS = new Set([ 'background', 'unique', @@ -404,26 +403,18 @@ export class ListIndexesOperation extends CommandOperation { callback: Callback ): void { const serverWireVersion = maxWireVersion(server); - if (serverWireVersion < LIST_INDEXES_WIRE_VERSION) { - const systemIndexesNS = this.collectionNamespace.withCollection('system.indexes'); - const collectionNS = this.collectionNamespace.toString(); - - server.query( - systemIndexesNS, - { query: { ns: collectionNS } }, - { ...this.options, readPreference: this.readPreference }, - callback - ); - return; - } const cursor = this.options.batchSize ? { batchSize: this.options.batchSize } : {}; - super.executeCommand( - server, - session, - { listIndexes: this.collectionNamespace.collection, cursor }, - callback - ); + + const command: Document = { listIndexes: this.collectionNamespace.collection, cursor }; + + // we check for undefined specifically here to allow falsy values + // eslint-disable-next-line no-restricted-syntax + if (serverWireVersion >= 9 && this.options.comment !== undefined) { + command.comment = this.options.comment; + } + + super.executeCommand(server, session, command, callback); } } diff --git a/src/operations/insert.ts b/src/operations/insert.ts index 3762835914..4a31017bff 100644 --- a/src/operations/insert.ts +++ b/src/operations/insert.ts @@ -41,7 +41,9 @@ export class InsertOperation extends CommandOperation { command.bypassDocumentValidation = options.bypassDocumentValidation; } - if (options.comment != null) { + // we check for undefined specifically here to allow falsy values + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { command.comment = options.comment; } diff --git a/src/operations/list_collections.ts b/src/operations/list_collections.ts index 8e32c46405..ccc515baab 100644 --- a/src/operations/list_collections.ts +++ b/src/operations/list_collections.ts @@ -1,16 +1,13 @@ import type { Binary, Document } from '../bson'; -import * as CONSTANTS from '../constants'; import { AbstractCursor } from '../cursor/abstract_cursor'; import type { Db } from '../db'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; -import { Callback, getTopology, maxWireVersion, MongoDBNamespace } from '../utils'; +import { Callback, getTopology, maxWireVersion } from '../utils'; import { CommandOperation, CommandOperationOptions } from './command'; import { executeOperation, ExecutionResult } from './execute_operation'; import { Aspect, defineAspects } from './operation'; -const LIST_COLLECTIONS_WIRE_VERSION = 3; - /** @public */ export interface ListCollectionsOptions extends CommandOperationOptions { /** Since 4.0: If true, will only return the collection name in the response, and will omit additional info */ @@ -49,67 +46,31 @@ export class ListCollectionsOperation extends CommandOperation { session: ClientSession | undefined, callback: Callback ): void { - if (maxWireVersion(server) < LIST_COLLECTIONS_WIRE_VERSION) { - let filter = this.filter; - const databaseName = this.db.s.namespace.db; - - // If we have legacy mode and have not provided a full db name filter it - if (typeof filter.name === 'string' && !new RegExp(`^${databaseName}\\.`).test(filter.name)) { - filter = Object.assign({}, filter); - filter.name = this.db.s.namespace.withCollection(filter.name).toString(); - } - - // No filter, filter by current database - if (filter == null) { - filter = { name: `/${databaseName}/` }; - } - - // Rewrite the filter to use $and to filter out indexes - if (filter.name) { - filter = { $and: [{ name: filter.name }, { name: /^((?!\$).)*$/ }] }; - } else { - filter = { name: /^((?!\$).)*$/ }; - } - - const documentTransform = (doc: Document) => { - const matching = `${databaseName}.`; - const index = doc.name.indexOf(matching); - // Remove database name if available - if (doc.name && index === 0) { - doc.name = doc.name.substr(index + matching.length); - } - - return doc; - }; - - server.query( - new MongoDBNamespace(databaseName, CONSTANTS.SYSTEM_NAMESPACE_COLLECTION), - { query: filter }, - { batchSize: this.batchSize || 1000, readPreference: this.readPreference }, - (err, result) => { - if (result && result.documents && Array.isArray(result.documents)) { - result.documents = result.documents.map(documentTransform); - } - - callback(err, result); - } - ); - - return; - } - - return super.executeCommand(server, session, this.generateCommand(), callback); + return super.executeCommand( + server, + session, + this.generateCommand(maxWireVersion(server)), + callback + ); } /* This is here for the purpose of unit testing the final command that gets sent. */ - generateCommand(): Document { - return { + generateCommand(wireVersion: number): Document { + const command: Document = { listCollections: 1, filter: this.filter, cursor: this.batchSize ? { batchSize: this.batchSize } : {}, nameOnly: this.nameOnly, authorizedCollections: this.authorizedCollections }; + + // we check for undefined specifically here to allow falsy values + // eslint-disable-next-line no-restricted-syntax + if (wireVersion >= 9 && this.options.comment !== undefined) { + command.comment = this.options.comment; + } + + return command; } } diff --git a/src/operations/list_databases.ts b/src/operations/list_databases.ts index a49d63f47d..537c952903 100644 --- a/src/operations/list_databases.ts +++ b/src/operations/list_databases.ts @@ -2,7 +2,7 @@ import type { Document } from '../bson'; import type { Db } from '../db'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; -import { Callback, MongoDBNamespace } from '../utils'; +import { Callback, maxWireVersion, MongoDBNamespace } from '../utils'; import { CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; @@ -52,6 +52,12 @@ export class ListDatabasesOperation extends CommandOperation= 9 && this.options.comment !== undefined) { + cmd.comment = this.options.comment; + } + super.executeCommand(server, session, cmd, callback); } } diff --git a/src/operations/update.ts b/src/operations/update.ts index f6f66de1ab..a389878a14 100644 --- a/src/operations/update.ts +++ b/src/operations/update.ts @@ -107,6 +107,12 @@ export class UpdateOperation extends CommandOperation { command.let = options.let; } + // we check for undefined specifically here to allow falsy values + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { + command.comment = options.comment; + } + const statementWithCollation = this.statements.find(statement => !!statement.collation); if ( collationNotSupported(server, options) || diff --git a/test/integration/enumerate_collections.test.ts b/test/integration/enumerate_collections.test.ts new file mode 100644 index 0000000000..c376be5d50 --- /dev/null +++ b/test/integration/enumerate_collections.test.ts @@ -0,0 +1,97 @@ +import { runUnifiedSuite } from '../tools/unified-spec-runner/runner'; +import { TestBuilder, UnifiedTestSuiteBuilder } from '../tools/utils'; + +const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment option') + .initialData({ + collectionName: 'coll0', + databaseName: '', + documents: [{ _id: 1, x: 11 }] + }) + .databaseName('listCollections-with-falsy-values') + .test( + new TestBuilder('listCollections should not send comment for server versions < 4.4') + .runOnRequirement({ maxServerVersion: '4.3.99' }) + .operation({ + name: 'listCollections', + arguments: { + filter: {}, + comment: 'string value' + }, + object: 'database0' + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + listCollections: 1 + } + } + } + ] + }) + .toJSON() + ) + .test( + new TestBuilder('listCollections should send string comment for server versions >= 4.4') + .runOnRequirement({ minServerVersion: '4.4.0' }) + .operation({ + name: 'listCollections', + arguments: { + filter: {}, + comment: 'string value' + }, + object: 'database0' + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + listCollections: 1, + comment: 'string value' + } + } + } + ] + }) + .toJSON() + ) + .test( + new TestBuilder('listCollections should send non-string comment for server versions >= 4.4') + .runOnRequirement({ minServerVersion: '4.4.0' }) + .operation({ + name: 'listCollections', + arguments: { + filter: {}, + + comment: { + key: 'value' + } + }, + object: 'database0' + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + listCollections: 1, + comment: { + key: 'value' + } + } + } + } + ] + }) + .toJSON() + ) + .toJSON(); + +describe('listCollections w/ comment option', () => { + runUnifiedSuite([testSuite]); +}); diff --git a/test/integration/enumerate_databases/enumerate_databases.prose.test.ts b/test/integration/enumerate_databases.prose.test.ts similarity index 98% rename from test/integration/enumerate_databases/enumerate_databases.prose.test.ts rename to test/integration/enumerate_databases.prose.test.ts index a705885f83..81e9718a3c 100644 --- a/test/integration/enumerate_databases/enumerate_databases.prose.test.ts +++ b/test/integration/enumerate_databases.prose.test.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; -import type { MongoClient } from '../../../src'; +import type { MongoClient } from '../../src'; const REQUIRED_DBS = ['admin', 'local', 'config']; const DB_NAME = 'listDatabasesTest'; diff --git a/test/integration/enumerate_databases/enumerate_databases.test.ts b/test/integration/enumerate_databases.test.ts similarity index 67% rename from test/integration/enumerate_databases/enumerate_databases.test.ts rename to test/integration/enumerate_databases.test.ts index d25323d61d..f1f295c76c 100644 --- a/test/integration/enumerate_databases/enumerate_databases.test.ts +++ b/test/integration/enumerate_databases.test.ts @@ -1,6 +1,8 @@ import { expect } from 'chai'; -import { AddUserOptions, MongoClient, MongoServerError } from '../../../src'; +import { AddUserOptions, MongoClient, MongoServerError } from '../../src'; +import { runUnifiedSuite } from '../tools/unified-spec-runner/runner'; +import { TestBuilder, UnifiedTestSuiteBuilder } from '../tools/utils'; const metadata: MongoDBMetadataUI = { requires: { @@ -140,3 +142,97 @@ describe('listDatabases() authorizedDatabases flag', function () { } ); }); + +const testSuite = new UnifiedTestSuiteBuilder('listDatabases with comment option') + .initialData({ + collectionName: 'coll0', + databaseName: '', + documents: [{ _id: 1, x: 11 }] + }) + .databaseName('listDatabases-with-falsy-values') + .test( + new TestBuilder('listDatabases should not send comment for server versions < 4.4') + .runOnRequirement({ maxServerVersion: '4.3.99' }) + .operation({ + name: 'listDatabases', + arguments: { + filter: {} + }, + object: 'client0' + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + listDatabases: 1 + } + } + } + ] + }) + .toJSON() + ) + .test( + new TestBuilder('listDatabases should send string comment for server versions >= 4.4') + .runOnRequirement({ minServerVersion: '4.4.0' }) + .operation({ + name: 'listDatabases', + arguments: { + filter: {}, + comment: 'string value' + }, + object: 'client0' + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + listDatabases: 1, + comment: 'string value' + } + } + } + ] + }) + .toJSON() + ) + .test( + new TestBuilder('listDatabases should send non-string comment for server versions >= 4.4') + .runOnRequirement({ minServerVersion: '4.4.0' }) + .operation({ + name: 'listDatabases', + arguments: { + filter: {}, + + comment: { + key: 'value' + } + }, + object: 'client0' + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + listDatabases: 1, + comment: { + key: 'value' + } + } + } + } + ] + }) + .toJSON() + ) + .toJSON(); + +describe('listDatabases w/ comment option', () => { + runUnifiedSuite([testSuite]); +}); diff --git a/test/integration/enumerate_indexes.test.ts b/test/integration/enumerate_indexes.test.ts new file mode 100644 index 0000000000..d7fec608cc --- /dev/null +++ b/test/integration/enumerate_indexes.test.ts @@ -0,0 +1,92 @@ +import { runUnifiedSuite } from '../tools/unified-spec-runner/runner'; +import { TestBuilder, UnifiedTestSuiteBuilder } from '../tools/utils'; + +const testSuite = new UnifiedTestSuiteBuilder('listIndexes with comment option') + .initialData({ + collectionName: 'coll0', + databaseName: '', + documents: [{ _id: 1, x: 11 }] + }) + .databaseName('listIndexes-with-falsy-values') + .test( + new TestBuilder('listIndexes should not send comment for server versions < 4.4') + .runOnRequirement({ maxServerVersion: '4.3.99' }) + .operation({ + name: 'listIndexes', + arguments: { + filter: {}, + comment: 'string value' + }, + object: 'collection0' + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: {} + } + } + ] + }) + .toJSON() + ) + .test( + new TestBuilder('listIndexes should send string comment for server versions >= 4.4') + .runOnRequirement({ minServerVersion: '4.4.0' }) + .operation({ + name: 'listIndexes', + arguments: { + filter: {}, + comment: 'string value' + }, + object: 'collection0' + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + comment: 'string value' + } + } + } + ] + }) + .toJSON() + ) + .test( + new TestBuilder('listIndexes should send non-string comment for server versions >= 4.4') + .runOnRequirement({ minServerVersion: '4.4.0' }) + .operation({ + name: 'listIndexes', + arguments: { + filter: {}, + comment: { + key: 'value' + } + }, + object: 'collection0' + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + comment: { + key: 'value' + } + } + } + } + ] + }) + .toJSON() + ) + .toJSON(); + +describe('listIndexes w/ comment option', () => { + runUnifiedSuite([testSuite]); +}); diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts new file mode 100644 index 0000000000..016430457d --- /dev/null +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -0,0 +1,178 @@ +import { Long } from '../../../src'; +import { runUnifiedSuite } from '../../tools/unified-spec-runner/runner'; +import { TestBuilder, UnifiedTestSuiteBuilder } from '../../tools/utils'; + +const falsyValues = [0, false, '', Long.ZERO, null, NaN] as const; +const falsyToString = (value: typeof falsyValues[number]) => { + if (Number.isNaN(value)) { + return 'NaN'; + } + + if (value === '') { + return "''"; + } + + if (value?._bsontype === 'Long') { + return 'Long.ZERO'; + } + + return JSON.stringify(value); +}; + +function* generateTestCombinations() { + for (const [name, args] of [ + ['find', { filter: { _id: 1 } }] as const, + ['aggregate', { pipeline: [] }] as const, + ['insertMany', { documents: [{ name: 'john' }] }] as const, + ['deleteOne', { filter: { toBeDeleted: true } }] as const, + ['findOneAndReplace', { filter: { _id: 1 }, replacement: { x: 12 } }] as const + ]) { + for (const falsyValue of falsyValues) { + yield { name, args: { ...args, comment: falsyValue } }; + } + } +} + +const tests = Array.from(generateTestCombinations()).map(({ name, args }) => { + const description = `${name} should pass falsy value ${falsyToString( + args.comment + )} for comment option`; + return new TestBuilder(description) + .operation({ + name, + object: 'collection0', + arguments: args + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + comment: args.comment + } + } + } + ] + }) + .toJSON(); +}); + +const testSuite = new UnifiedTestSuiteBuilder('Comment with Falsy Values') + .runOnRequirement({ minServerVersion: '4.4.0' }) + .initialData({ + collectionName: 'coll0', + databaseName: '', + documents: [ + { _id: 1, x: 11 }, + { _id: 2, toBeDeleted: true } // This should only be used by the delete test + ] + }) + .databaseName('comment-with-falsy-values') + .test(tests) + .toJSON(); + +const testsForChangeStreamsAggregate = falsyValues.map(falsyValue => { + const description = `ChangeStreams should pass falsy value ${falsyToString( + falsyValue + )} for comment option on initial aggregate`; + + return new TestBuilder(description) + .operation({ + name: 'createChangeStream', + object: 'collection0', + arguments: { + pipeline: [], + comment: falsyValue + }, + saveResultAsEntity: 'changeStream0' + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + comment: falsyValue + } + } + } + ] + }) + .toJSON(); +}); + +const testsForGetMore = falsyValues.map(falsyValue => { + const description = `ChangeStreams should pass falsy value ${falsyToString( + falsyValue + )} for comment option on getMore`; + + return new TestBuilder(description) + .runOnRequirement({ topologies: ['replicaset'] }) + .operation({ + name: 'createChangeStream', + object: 'collection0', + arguments: { + pipeline: [], + comment: falsyValue + }, + saveResultAsEntity: 'changeStream0' + }) + .operation({ + name: 'insertOne', + object: 'collection0', + arguments: { + document: { + a: 1 + } + } + }) + .operation({ + name: 'iterateUntilDocumentOrError', + object: 'changeStream0', + arguments: {} + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + comment: falsyValue + } + } + }, + { + commandStartedEvent: {} + }, + { + commandStartedEvent: { + command: { + comment: falsyValue + } + } + } + ] + }) + .toJSON(); +}); + +const changeStreamTestSuite = new UnifiedTestSuiteBuilder( + 'Change Streams Comment with Falsy Values' +) + .schemaVersion('1.0') + .initialData({ + collectionName: 'coll0', + databaseName: '', + documents: [] + }) + .databaseName('change-streams-comment-with-falsy-values') + .runOnRequirement({ minServerVersion: '4.4.0', topologies: ['replicaset', 'sharded-replicaset'] }) + .test(testsForChangeStreamsAggregate) + .test(testsForGetMore) + .toJSON(); + +describe('comment w/ falsy values ', () => { + runUnifiedSuite([testSuite]); + runUnifiedSuite([changeStreamTestSuite]); +}); diff --git a/test/integration/unified-test-format/unified_test_format.spec.test.ts b/test/integration/unified-test-format/unified_test_format.spec.test.ts index 00e5ff1b64..c962a94c46 100644 --- a/test/integration/unified-test-format/unified_test_format.spec.test.ts +++ b/test/integration/unified-test-format/unified_test_format.spec.test.ts @@ -23,7 +23,10 @@ const SKIPPED_TESTS = [ 'Dirty explicit session is discarded', // TODO(NODE-3308): - 'A successful find event with a getmore and the server kills the cursor' + 'A successful find event with a getmore and the server kills the cursor', + + // TODO(NODE-4125): Fix change streams resume logic when used in iterator mode + 'Test consecutive resume' ].concat(process.env.AUTH === 'auth' ? FAILING_TESTS_AUTH_ENABLED : []); describe('Unified test format runner', function unifiedTestRunner() { diff --git a/test/spec/change-streams/unified/change-streams.json b/test/spec/change-streams/unified/change-streams.json index adaf00de2d..5fd2544ce0 100644 --- a/test/spec/change-streams/unified/change-streams.json +++ b/test/spec/change-streams/unified/change-streams.json @@ -1,10 +1,21 @@ { "description": "change-streams", "schemaVersion": "1.0", + "runOnRequirements": [ + { + "topologies": [ + "replicaset", + "sharded-replicaset" + ] + } + ], "createEntities": [ { "client": { - "id": "client0" + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] } }, { @@ -34,10 +45,7 @@ "description": "Test array truncation", "runOnRequirements": [ { - "minServerVersion": "4.7", - "topologies": [ - "replicaset" - ] + "minServerVersion": "4.7" } ], "operations": [ @@ -111,6 +119,313 @@ } } ] + }, + { + "description": "Test with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "comment": { + "name": "test1" + } + }, + "saveResultAsEntity": "changeStream0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$changeStream": {} + } + ], + "comment": { + "name": "test1" + } + } + } + } + ] + } + ] + }, + { + "description": "Test with document comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "comment": { + "name": "test1" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$changeStream": {} + } + ], + "comment": { + "name": "test1" + } + } + } + } + ] + } + ] + }, + { + "description": "Test with string comment", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0" + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "comment": "comment" + }, + "saveResultAsEntity": "changeStream0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$changeStream": {} + } + ], + "comment": "comment" + } + } + } + ] + } + ] + }, + { + "description": "Test that comment is set on getMore", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0", + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "comment": { + "key": "value" + } + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$changeStream": {} + } + ], + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "collection0", + "documents": [ + { + "_id": 1, + "a": 1 + } + ] + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection0", + "comment": { + "key": "value" + } + }, + "commandName": "getMore", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Test that comment is not set on getMore - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.3.99", + "topologies": [ + "replicaset" + ] + } + ], + "operations": [ + { + "name": "createChangeStream", + "object": "collection0", + "arguments": { + "pipeline": [], + "comment": "comment" + }, + "saveResultAsEntity": "changeStream0" + }, + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 1, + "a": 1 + } + } + }, + { + "name": "iterateUntilDocumentOrError", + "object": "changeStream0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "collection0", + "pipeline": [ + { + "$changeStream": {} + } + ], + "comment": "comment" + } + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "collection0", + "documents": [ + { + "_id": 1, + "a": 1 + } + ] + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection0", + "comment": { + "$$exists": false + } + }, + "commandName": "getMore", + "databaseName": "database0" + } + } + ] + } + ] } ] } diff --git a/test/spec/change-streams/unified/change-streams.yml b/test/spec/change-streams/unified/change-streams.yml index d8567db473..3bd8a4aa80 100644 --- a/test/spec/change-streams/unified/change-streams.yml +++ b/test/spec/change-streams/unified/change-streams.yml @@ -1,8 +1,12 @@ description: "change-streams" schemaVersion: "1.0" +runOnRequirements: + - topologies: [ replicaset, sharded-replicaset ] createEntities: - client: id: &client0 client0 + observeEvents: + - commandStartedEvent - database: id: &database0 database0 client: *client0 @@ -15,11 +19,11 @@ initialData: - collectionName: *collection0 databaseName: *database0 documents: [] + tests: - description: "Test array truncation" runOnRequirements: - minServerVersion: "4.7" - topologies: [replicaset] operations: - name: insertOne object: *collection0 @@ -70,3 +74,149 @@ tests: ] } } + + - description: "Test with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + comment: &comment0 { name: "test1" } + saveResultAsEntity: &changeStream0 changeStream0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $changeStream: {} + comment: *comment0 + + - description: "Test with document comment - pre 4.4" + runOnRequirements: + - minServerVersion: "3.6.0" + maxServerVersion: "4.2.99" + operations: + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + comment: &comment0 { name: "test1" } + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $changeStream: {} + comment: *comment0 + + - description: "Test with string comment" + runOnRequirements: + - minServerVersion: "3.6.0" + operations: + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + comment: "comment" + saveResultAsEntity: &changeStream0 changeStream0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $changeStream: {} + comment: "comment" + + - description: "Test that comment is set on getMore" + runOnRequirements: + - minServerVersion: "4.4.0" + topologies: [ replicaset ] + operations: + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + comment: &commentDoc + key: "value" + saveResultAsEntity: &changeStream0 changeStream0 + - name: insertOne + object: *collection0 + arguments: + document: &new_document + _id: 1 + a: 1 + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $changeStream: {} + comment: *commentDoc + - commandStartedEvent: + command: + insert: *collection0 + documents: + - *new_document + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0 + comment: *commentDoc + commandName: getMore + databaseName: *database0 + + - description: "Test that comment is not set on getMore - pre 4.4" + runOnRequirements: + - minServerVersion: "3.6.0" + maxServerVersion: "4.3.99" + topologies: [ replicaset ] + operations: + - name: createChangeStream + object: *collection0 + arguments: + pipeline: [] + comment: "comment" + saveResultAsEntity: &changeStream0 changeStream0 + - name: insertOne + object: *collection0 + arguments: + document: &new_document + _id: 1 + a: 1 + - name: iterateUntilDocumentOrError + object: *changeStream0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0 + pipeline: + - $changeStream: {} + comment: "comment" + - commandStartedEvent: + command: + insert: *collection0 + documents: + - *new_document + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0 + comment: { $$exists: false } + commandName: getMore + databaseName: *database0 diff --git a/test/spec/crud/unified/aggregate.json b/test/spec/crud/unified/aggregate.json index dcdad761e8..0cbfb4e6e9 100644 --- a/test/spec/crud/unified/aggregate.json +++ b/test/spec/crud/unified/aggregate.json @@ -161,6 +161,407 @@ ] } ] + }, + { + "description": "aggregate with a string comment", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0" + } + ], + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": "comment" + }, + "object": "collection0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": "comment" + } + } + } + ] + } + ] + }, + { + "description": "aggregate with a document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": { + "content": "test" + } + }, + "object": "collection0" + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": { + "content": "test" + } + } + } + } + ] + } + ] + }, + { + "description": "aggregate with a document comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "aggregate", + "object": "collection0", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": { + "content": "test" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "comment": { + "content": "test" + } + }, + "commandName": "aggregate", + "databaseName": "aggregate-tests" + } + } + ] + } + ] + }, + { + "description": "aggregate with comment sets comment on getMore", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "batchSize": 2, + "comment": { + "content": "test" + } + }, + "object": "collection0", + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "cursor": { + "batchSize": 2 + }, + "comment": { + "content": "test" + } + }, + "commandName": "aggregate", + "databaseName": "aggregate-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "content": "test" + } + }, + "commandName": "getMore", + "databaseName": "aggregate-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "content": "test" + } + }, + "commandName": "getMore", + "databaseName": "aggregate-tests" + } + } + ] + } + ] + }, + { + "description": "aggregate with comment does not set comment on getMore - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.3.99" + } + ], + "operations": [ + { + "name": "aggregate", + "arguments": { + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "batchSize": 2, + "comment": "comment" + }, + "object": "collection0", + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "aggregate": "coll0", + "pipeline": [ + { + "$match": { + "_id": { + "$gt": 1 + } + } + } + ], + "cursor": { + "batchSize": 2 + }, + "comment": "comment" + }, + "commandName": "aggregate", + "databaseName": "aggregate-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "$$exists": false + } + }, + "commandName": "getMore", + "databaseName": "aggregate-tests" + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "$$exists": false + } + }, + "commandName": "getMore", + "databaseName": "aggregate-tests" + } + } + ] + } + ] } ] } diff --git a/test/spec/crud/unified/aggregate.yml b/test/spec/crud/unified/aggregate.yml index 248b91cefb..032aece0fa 100644 --- a/test/spec/crud/unified/aggregate.yml +++ b/test/spec/crud/unified/aggregate.yml @@ -66,3 +66,150 @@ tests: commandName: getMore databaseName: *database0Name + - description: "aggregate with a string comment" + runOnRequirements: + - minServerVersion: "3.6.0" + operations: + - name: aggregate + arguments: + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + comment: "comment" + object: *collection0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: [ { $match: { _id: { $gt: 1 } } } ] + comment: "comment" + + - description: "aggregate with a document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: aggregate + arguments: + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + comment: &comment0 { content: "test" } + object: *collection0 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: [ { $match: { _id: { $gt: 1 } } } ] + comment: *comment0 + + - description: "aggregate with a document comment - pre 4.4" + runOnRequirements: + - minServerVersion: "3.6.0" + maxServerVersion: "4.2.99" + operations: + - name: aggregate + object: *collection0 + arguments: + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + comment: *comment0 + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + comment: *comment0 + commandName: aggregate + databaseName: *database0Name + + - description: "aggregate with comment sets comment on getMore" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - name: aggregate + arguments: + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + batchSize: 2 + comment: *comment0 + object: *collection0 + expectResult: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - { _id: 5, x: 55 } + - { _id: 6, x: 66 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + cursor: { batchSize: 2 } + comment: *comment0 + commandName: aggregate + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: *comment0 + commandName: getMore + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: *comment0 + commandName: getMore + databaseName: *database0Name + + - description: "aggregate with comment does not set comment on getMore - pre 4.4" + runOnRequirements: + - minServerVersion: "3.6.0" + maxServerVersion: "4.3.99" + operations: + - name: aggregate + arguments: + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + batchSize: 2 + comment: "comment" + object: *collection0 + expectResult: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - { _id: 5, x: 55 } + - { _id: 6, x: 66 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + aggregate: *collection0Name + pipeline: [ { $match: { _id: { $gt: 1 } }} ] + cursor: { batchSize: 2 } + comment: "comment" + commandName: aggregate + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: { $$exists: false } + commandName: getMore + databaseName: *database0Name + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: { $$exists: false } + commandName: getMore + databaseName: *database0Name diff --git a/test/spec/crud/unified/bulkWrite-comment.json b/test/spec/crud/unified/bulkWrite-comment.json new file mode 100644 index 0000000000..fac9644543 --- /dev/null +++ b/test/spec/crud/unified/bulkWrite-comment.json @@ -0,0 +1,494 @@ +{ + "description": "bulkWrite-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-v2" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "BulkWrite_comment" + } + } + ], + "initialData": [ + { + "collectionName": "BulkWrite_comment", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ], + "tests": [ + { + "description": "BulkWrite with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 5, + "x": "inserted" + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": "replaced" + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$set": { + "x": "updated" + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + } + ], + "comment": "comment" + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 5 + } + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "BulkWrite_comment", + "documents": [ + { + "_id": 5, + "x": "inserted" + } + ], + "ordered": true, + "comment": "comment" + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "BulkWrite_comment", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "x": "replaced" + } + }, + { + "q": { + "_id": 2 + }, + "u": { + "$set": { + "x": "updated" + } + } + } + ], + "ordered": true, + "comment": "comment" + } + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "BulkWrite_comment", + "deletes": [ + { + "q": { + "_id": 3 + }, + "limit": 1 + } + ], + "ordered": true, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_comment", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": "replaced" + }, + { + "_id": 2, + "x": "updated" + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": "inserted" + } + ] + } + ] + }, + { + "description": "BulkWrite with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 5, + "x": "inserted" + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": "replaced" + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$set": { + "x": "updated" + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + } + ], + "comment": { + "key": "value" + } + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "$$unsetOrMatches": { + "0": 5 + } + }, + "matchedCount": 2, + "modifiedCount": 2, + "upsertedCount": 0, + "upsertedIds": {} + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "BulkWrite_comment", + "documents": [ + { + "_id": 5, + "x": "inserted" + } + ], + "ordered": true, + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "BulkWrite_comment", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "_id": 1, + "x": "replaced" + } + }, + { + "q": { + "_id": 2 + }, + "u": { + "$set": { + "x": "updated" + } + } + } + ], + "ordered": true, + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "BulkWrite_comment", + "deletes": [ + { + "q": { + "_id": 3 + }, + "limit": 1 + } + ], + "ordered": true, + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_comment", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": "replaced" + }, + { + "_id": 2, + "x": "updated" + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": "inserted" + } + ] + } + ] + }, + { + "description": "BulkWrite with comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "object": "collection0", + "name": "bulkWrite", + "arguments": { + "requests": [ + { + "insertOne": { + "document": { + "_id": 5, + "x": "inserted" + } + } + }, + { + "replaceOne": { + "filter": { + "_id": 1 + }, + "replacement": { + "_id": 1, + "x": "replaced" + } + } + }, + { + "updateOne": { + "filter": { + "_id": 2 + }, + "update": { + "$set": { + "x": "updated" + } + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + } + ], + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "BulkWrite_comment", + "documents": [ + { + "_id": 5, + "x": "inserted" + } + ], + "ordered": true, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "BulkWrite_comment", + "databaseName": "crud-v2", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/bulkWrite-comment.yml b/test/spec/crud/unified/bulkWrite-comment.yml new file mode 100644 index 0000000000..1c262585f8 --- /dev/null +++ b/test/spec/crud/unified/bulkWrite-comment.yml @@ -0,0 +1,166 @@ +description: bulkWrite-comment +schemaVersion: '1.0' + +createEntities: + - client: + id: &client0 client0 + observeEvents: + - commandStartedEvent + - database: + id: &database0 database0 + client: client0 + databaseName: &database_name crud-v2 + - collection: + id: &collection0 collection0 + database: database0 + collectionName: &collection_name BulkWrite_comment + +initialData: &initial_data + - collectionName: *collection_name + databaseName: *database_name + documents: + - _id: 1 + x: 11 + - _id: 2 + x: 22 + - _id: 3 + x: 33 + - _id: 4 + x: 44 + +tests: + - description: 'BulkWrite with string comment' + runOnRequirements: + - minServerVersion: "4.4" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: &requests + - insertOne: + document: &inserted_document + _id: 5 + x: "inserted" + - replaceOne: + filter: &replaceOne_filter + _id: 1 + replacement: &replacement { _id: 1, x: "replaced" } + - updateOne: + filter: &updateOne_filter + _id: 2 + update: &update { $set: {x: "updated"} } + - deleteOne: + filter: &deleteOne_filter + _id: 3 + comment: &string_comment "comment" + expectResult: &expect_results + deletedCount: 1 + insertedCount: 1 + insertedIds: { $$unsetOrMatches: { 0: 5} } + matchedCount: 2 + modifiedCount: 2 + upsertedCount: 0 + upsertedIds: { } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *inserted_document + ordered: true + comment: *string_comment + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: *replaceOne_filter + u: *replacement + - q: *updateOne_filter + u: *update + ordered: true + comment: *string_comment + - commandStartedEvent: + command: + delete: *collection_name + deletes: + - q: *deleteOne_filter + limit: 1 + ordered: true + comment: *string_comment + outcome: &outcome + - collectionName: *collection_name + databaseName: *database_name + documents: + - _id: 1 + x: "replaced" + - _id: 2 + x: "updated" + - _id: 4 + x: 44 + - _id: 5 + x: "inserted" + + - description: 'BulkWrite with document comment' + runOnRequirements: + - minServerVersion: "4.4" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: *requests + comment: &document_comment { key: "value" } + expectResult: *expect_results + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *inserted_document + ordered: true + comment: *document_comment + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: *replaceOne_filter + u: *replacement + - q: *updateOne_filter + u: *update + ordered: true + comment: *document_comment + - commandStartedEvent: + command: + delete: *collection_name + deletes: + - q: *deleteOne_filter + limit: 1 + ordered: true + comment: *document_comment + outcome: *outcome + + - description: 'BulkWrite with comment - pre 4.4' + runOnRequirements: + - maxServerVersion: "4.2.99" + operations: + - object: *collection0 + name: bulkWrite + arguments: + requests: *requests + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection_name + documents: + - *inserted_document + ordered: true + comment: "comment" + outcome: *initial_data diff --git a/test/spec/crud/unified/deleteMany-comment.json b/test/spec/crud/unified/deleteMany-comment.json new file mode 100644 index 0000000000..ea6a8524d9 --- /dev/null +++ b/test/spec/crud/unified/deleteMany-comment.json @@ -0,0 +1,244 @@ +{ + "description": "deleteMany-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name2" + }, + { + "_id": 3, + "name": "name3" + } + ] + } + ], + "tests": [ + { + "description": "deleteMany with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "comment": "comment" + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "limit": 0 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "deleteMany with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "comment": { + "key": "value" + } + }, + "expectResult": { + "deletedCount": 2 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "limit": 0 + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + } + ] + } + ] + }, + { + "description": "deleteMany with comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "deleteMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": { + "$gt": 1 + } + }, + "limit": 0 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name2" + }, + { + "_id": 3, + "name": "name3" + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/deleteMany-comment.yml b/test/spec/crud/unified/deleteMany-comment.yml new file mode 100644 index 0000000000..b06016247b --- /dev/null +++ b/test/spec/crud/unified/deleteMany-comment.yml @@ -0,0 +1,96 @@ +description: "deleteMany-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2, name: "name2" } + - { _id: 3, name: "name3" } + +tests: + - description: "deleteMany with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: deleteMany + object: *collection0 + arguments: + filter: &filter { _id: { $gt: 1 } } + comment: "comment" + expectResult: &expect_result + deletedCount: 2 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 0 + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + + - description: "deleteMany with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: deleteMany + object: *collection0 + arguments: + filter: *filter + comment: &comment { key: "value" } + expectResult: *expect_result + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 0 + comment: *comment + outcome: *outcome + + - description: "deleteMany with comment - pre 4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + operations: + - name: deleteMany + object: *collection0 + arguments: + filter: *filter + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 0 + comment: "comment" + outcome: *initialData diff --git a/test/spec/crud/unified/deleteOne-comment.json b/test/spec/crud/unified/deleteOne-comment.json new file mode 100644 index 0000000000..37f356ec6f --- /dev/null +++ b/test/spec/crud/unified/deleteOne-comment.json @@ -0,0 +1,242 @@ +{ + "description": "deleteOne-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ], + "tests": [ + { + "description": "deleteOne with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": "comment" + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": 1 + }, + "limit": 1 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ] + }, + { + "description": "deleteOne with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + }, + "expectResult": { + "deletedCount": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": 1 + }, + "limit": 1 + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ] + }, + { + "description": "deleteOne with comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "deleteOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "delete": "coll0", + "deletes": [ + { + "q": { + "_id": 1 + }, + "limit": 1 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2, + "name": "name" + }, + { + "_id": 3, + "name": "name" + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/deleteOne-comment.yml b/test/spec/crud/unified/deleteOne-comment.yml new file mode 100644 index 0000000000..b68dcbacd0 --- /dev/null +++ b/test/spec/crud/unified/deleteOne-comment.yml @@ -0,0 +1,97 @@ +description: "deleteOne-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2, name: "name" } + - { _id: 3, name: "name" } + +tests: + - description: "deleteOne with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: deleteOne + object: *collection0 + arguments: + filter: &filter { _id: 1 } + comment: "comment" + expectResult: &expect_result + deletedCount: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 1 + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 2, name: "name" } + - { _id: 3, name: "name" } + + - description: "deleteOne with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: deleteOne + object: *collection0 + arguments: + filter: *filter + comment: &comment { key: "value" } + expectResult: *expect_result + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 1 + comment: *comment + outcome: *outcome + + - description: "deleteOne with comment - pre 4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + operations: + - name: deleteOne + object: *collection0 + arguments: + filter: *filter + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + delete: *collection0Name + deletes: + - q: *filter + limit: 1 + comment: "comment" + outcome: *initialData diff --git a/test/spec/crud/unified/find-comment.json b/test/spec/crud/unified/find-comment.json new file mode 100644 index 0000000000..600a3723f1 --- /dev/null +++ b/test/spec/crud/unified/find-comment.json @@ -0,0 +1,403 @@ +{ + "description": "find-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "tests": [ + { + "description": "find with string comment", + "runOnRequirements": [ + { + "minServerVersion": "3.6" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": "comment" + }, + "expectResult": [ + { + "_id": 1 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": 1 + }, + "comment": "comment" + } + } + } + ] + } + ] + }, + { + "description": "find with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + }, + "expectResult": [ + { + "_id": 1 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + } + } + } + ] + } + ] + }, + { + "description": "find with document comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99", + "minServerVersion": "3.6" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + } + } + } + ] + } + ] + }, + { + "description": "find with comment sets comment on getMore", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "batchSize": 2, + "comment": { + "key": "value" + } + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "batchSize": 2, + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "key": "value" + } + } + } + } + ] + } + ] + }, + { + "description": "find with comment does not set comment on getMore - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0", + "maxServerVersion": "4.3.99" + } + ], + "operations": [ + { + "name": "find", + "object": "collection0", + "arguments": { + "filter": { + "_id": { + "$gt": 1 + } + }, + "batchSize": 2, + "comment": "comment" + }, + "expectResult": [ + { + "_id": 2, + "x": 22 + }, + { + "_id": 3, + "x": 33 + }, + { + "_id": 4, + "x": 44 + }, + { + "_id": 5, + "x": 55 + }, + { + "_id": 6, + "x": 66 + } + ] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": { + "_id": { + "$gt": 1 + } + }, + "batchSize": 2, + "comment": "comment" + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "$$exists": false + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "coll0", + "batchSize": 2, + "comment": { + "$$exists": false + } + } + } + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/find-comment.yml b/test/spec/crud/unified/find-comment.yml new file mode 100644 index 0000000000..905241ad0e --- /dev/null +++ b/test/spec/crud/unified/find-comment.yml @@ -0,0 +1,166 @@ +description: "find-comment" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - { _id: 5, x: 55 } + - { _id: 6, x: 66 } + +tests: + - description: "find with string comment" + runOnRequirements: + - minServerVersion: "3.6" + operations: + - name: find + object: *collection0 + arguments: + filter: &filter + _id: 1 + comment: "comment" + expectResult: &expect_result + - { _id: 1 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: *filter + comment: "comment" + + - description: "find with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: find + object: *collection0 + arguments: + filter: *filter + comment: &comment { key: "value"} + expectResult: *expect_result + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: *filter + comment: *comment + + - description: "find with document comment - pre 4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + minServerVersion: "3.6" + operations: + - name: find + object: *collection0 + arguments: + filter: *filter + comment: *comment + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: *filter + comment: *comment + + - description: "find with comment sets comment on getMore" + runOnRequirements: + - minServerVersion: "4.4.0" + operations: + - name: find + object: *collection0 + arguments: + filter: &filter_get_more { _id: { $gt: 1 } } + batchSize: 2 + comment: *comment + expectResult: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - { _id: 5, x: 55 } + - { _id: 6, x: 66 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: { $gt: 1 } } + batchSize: 2 + comment: *comment + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: *comment + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: *comment + + - description: "find with comment does not set comment on getMore - pre 4.4" + runOnRequirements: + - minServerVersion: "3.6.0" + maxServerVersion: "4.3.99" + operations: + - name: find + object: *collection0 + arguments: + filter: &filter_get_more { _id: { $gt: 1 } } + batchSize: 2 + comment: "comment" + expectResult: + - { _id: 2, x: 22 } + - { _id: 3, x: 33 } + - { _id: 4, x: 44 } + - { _id: 5, x: 55 } + - { _id: 6, x: 66 } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: { _id: { $gt: 1 } } + batchSize: 2 + comment: "comment" + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: { $$exists: false } + - commandStartedEvent: + command: + getMore: { $$type: [ int, long ] } + collection: *collection0Name + batchSize: 2 + comment: { $$exists: false } diff --git a/test/spec/crud/unified/findOneAndDelete-comment.json b/test/spec/crud/unified/findOneAndDelete-comment.json new file mode 100644 index 0000000000..6853b9cc2d --- /dev/null +++ b/test/spec/crud/unified/findOneAndDelete-comment.json @@ -0,0 +1,211 @@ +{ + "description": "findOneAndDelete-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndDelete with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": "comment" + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "remove": true, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "findOneAndDelete with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": { + "key": "value" + } + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "remove": true, + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "findOneAndDelete with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "findOneAndDelete", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "remove": true, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/findOneAndDelete-comment.yml b/test/spec/crud/unified/findOneAndDelete-comment.yml new file mode 100644 index 0000000000..dfa7ff2ce0 --- /dev/null +++ b/test/spec/crud/unified/findOneAndDelete-comment.yml @@ -0,0 +1,96 @@ +description: "findOneAndDelete-comment" +schemaVersion: "1.0" + + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "findOneAndDelete with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndDelete + object: *collection0 + arguments: + filter: &filter + _id: 1 + comment: "comment" + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + remove: true + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 2 } + + - description: "findOneAndDelete with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndDelete + object: *collection0 + arguments: + filter: *filter + comment: &comment { key: "value"} + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + remove: true + comment: *comment + outcome: *outcome + + - description: "findOneAndDelete with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "4.2.0" # findAndModify option validation was introduced in 4.2 + maxServerVersion: "4.2.99" + operations: + - name: findOneAndDelete + object: *collection0 + arguments: + filter: *filter + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + remove: true + comment: "comment" + outcome: *initialData diff --git a/test/spec/crud/unified/findOneAndReplace-comment.json b/test/spec/crud/unified/findOneAndReplace-comment.json new file mode 100644 index 0000000000..f817bb6937 --- /dev/null +++ b/test/spec/crud/unified/findOneAndReplace-comment.json @@ -0,0 +1,234 @@ +{ + "description": "findOneAndReplace-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndReplace with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 5 + }, + "comment": "comment" + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "x": 5 + }, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 5 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "findOneAndReplace with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 5 + }, + "comment": { + "key": "value" + } + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "x": 5 + }, + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 5 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "findOneAndReplace with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "findOneAndReplace", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 5 + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": { + "x": 5 + }, + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/findOneAndReplace-comment.yml b/test/spec/crud/unified/findOneAndReplace-comment.yml new file mode 100644 index 0000000000..2c6aaef07b --- /dev/null +++ b/test/spec/crud/unified/findOneAndReplace-comment.yml @@ -0,0 +1,101 @@ +description: "findOneAndReplace-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "findOneAndReplace with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndReplace + object: *collection0 + arguments: + filter: &filter + _id: 1 + replacement: &replacement + x: 5 + comment: "comment" + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *replacement + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 5 } + - { _id: 2 } + + - description: "findOneAndReplace with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndReplace + object: *collection0 + arguments: + filter: *filter + replacement: *replacement + comment: &comment { key: "value"} + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *replacement + comment: *comment + outcome: *outcome + + + - description: "findOneAndReplace with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "4.2.0" # findAndModify option validation was introduced in 4.2 + maxServerVersion: "4.2.99" + operations: + - name: findOneAndReplace + object: *collection0 + arguments: + filter: *filter + replacement: *replacement + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *replacement + comment: "comment" + outcome: *initialData diff --git a/test/spec/crud/unified/findOneAndUpdate-comment.json b/test/spec/crud/unified/findOneAndUpdate-comment.json new file mode 100644 index 0000000000..6dec5b39ee --- /dev/null +++ b/test/spec/crud/unified/findOneAndUpdate-comment.json @@ -0,0 +1,228 @@ +{ + "description": "findOneAndUpdate-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "findOneAndUpdate with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + } + } + } + ] + } + ] + }, + { + "description": "findOneAndUpdate with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": { + "key": "value" + } + }, + "expectResult": { + "_id": 1 + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ] + }, + { + "description": "findOneAndUpdate with comment - pre 4.4", + "runOnRequirements": [ + { + "minServerVersion": "4.2.0", + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "findOneAndUpdate", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "findAndModify": "coll0", + "query": { + "_id": 1 + }, + "update": [ + { + "$set": { + "x": 5 + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/findOneAndUpdate-comment.yml b/test/spec/crud/unified/findOneAndUpdate-comment.yml new file mode 100644 index 0000000000..66d2d099c1 --- /dev/null +++ b/test/spec/crud/unified/findOneAndUpdate-comment.yml @@ -0,0 +1,95 @@ +description: "findOneAndUpdate-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1 } + - { _id: 2 } + +tests: + - description: "findOneAndUpdate with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: &filter + _id: 1 + update: &update + - $set: {x: 5 } + comment: "comment" + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *update + comment: "comment" + + - description: "findOneAndUpdate with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: &filter + _id: 1 + update: &update + - $set: {x: 5 } + comment: &comment { key: "value"} + expectResult: + _id: 1 + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *update + comment: *comment + + - description: "findOneAndUpdate with comment - pre 4.4" + runOnRequirements: + - minServerVersion: "4.2.0" # findAndModify option validation was introduced in 4.2 + maxServerVersion: "4.2.99" + operations: + - name: findOneAndUpdate + object: *collection0 + arguments: + filter: *filter + update: *update + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + findAndModify: *collection0Name + query: *filter + update: *update + comment: "comment" + outcome: *initialData diff --git a/test/spec/crud/unified/insertMany-comment.json b/test/spec/crud/unified/insertMany-comment.json new file mode 100644 index 0000000000..7e835e8011 --- /dev/null +++ b/test/spec/crud/unified/insertMany-comment.json @@ -0,0 +1,225 @@ +{ + "description": "insertMany-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "insertMany with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "insertMany with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": { + "key": "value" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "insertMany with comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "insertMany", + "object": "collection0", + "arguments": { + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/insertMany-comment.yml b/test/spec/crud/unified/insertMany-comment.yml new file mode 100644 index 0000000000..ff86029a4f --- /dev/null +++ b/test/spec/crud/unified/insertMany-comment.yml @@ -0,0 +1,92 @@ +description: "insertMany-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: + - description: "insertMany with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: insertMany + object: *collection0 + arguments: + documents: + - &document { _id: 2, x: 22 } + comment: "comment" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + + - description: "insertMany with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: insertMany + object: *collection0 + arguments: + documents: + - *document + comment: &comment { key: "value" } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: *comment + outcome: *outcome + + - description: "insertMany with comment - pre 4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + operations: + - name: insertMany + object: *collection0 + arguments: + documents: + - *document + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: "comment" + outcome: *initialData diff --git a/test/spec/crud/unified/insertOne-comment.json b/test/spec/crud/unified/insertOne-comment.json new file mode 100644 index 0000000000..a9f735ab6c --- /dev/null +++ b/test/spec/crud/unified/insertOne-comment.json @@ -0,0 +1,219 @@ +{ + "description": "insertOne-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "insertOne with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 2, + "x": 22 + }, + "comment": "comment" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "insertOne with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 2, + "x": 22 + }, + "comment": { + "key": "value" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": 2, + "x": 22 + } + ] + } + ] + }, + { + "description": "insertOne with comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "insertOne", + "object": "collection0", + "arguments": { + "document": { + "_id": 2, + "x": 22 + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "insert": "coll0", + "documents": [ + { + "_id": 2, + "x": 22 + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/insertOne-comment.yml b/test/spec/crud/unified/insertOne-comment.yml new file mode 100644 index 0000000000..729b038f2f --- /dev/null +++ b/test/spec/crud/unified/insertOne-comment.yml @@ -0,0 +1,90 @@ +description: "insertOne-comment" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: + - description: "insertOne with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: insertOne + object: *collection0 + arguments: + document: &document { _id: 2, x: 22 } + comment: "comment" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + - { _id: 2, x: 22 } + + - description: "insertOne with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: insertOne + object: *collection0 + arguments: + document: *document + comment: &comment { key: "value" } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: *comment + outcome: *outcome + + - description: "insertOne with comment - pre 4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + operations: + - name: insertOne + object: *collection0 + arguments: + document: *document + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + insert: *collection0Name + documents: + - *document + comment: "comment" + outcome: *initialData diff --git a/test/spec/crud/unified/replaceOne-comment.json b/test/spec/crud/unified/replaceOne-comment.json new file mode 100644 index 0000000000..02fe90a44d --- /dev/null +++ b/test/spec/crud/unified/replaceOne-comment.json @@ -0,0 +1,229 @@ +{ + "description": "replaceOne-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "ReplaceOne with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 22 + }, + "comment": "comment" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "x": 22 + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 22 + }, + "comment": { + "key": "value" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "x": 22 + } + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "ReplaceOne with comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "replaceOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "replacement": { + "x": 22 + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "x": 22 + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/replaceOne-comment.yml b/test/spec/crud/unified/replaceOne-comment.yml new file mode 100644 index 0000000000..c742a96e91 --- /dev/null +++ b/test/spec/crud/unified/replaceOne-comment.yml @@ -0,0 +1,98 @@ +description: "replaceOne-comment" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: + - description: "ReplaceOne with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: replaceOne + object: *collection0 + arguments: + filter: &filter { _id: 1 } + replacement: &replacement { x: 22 } + comment: "comment" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *replacement + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 22 } + + - description: "ReplaceOne with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: replaceOne + object: *collection0 + arguments: + filter: *filter + replacement: *replacement + comment: &comment { key: "value" } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *replacement + comment: *comment + outcome: *outcome + + - description: "ReplaceOne with comment - pre 4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + operations: + - name: replaceOne + object: *collection0 + arguments: + filter: *filter + replacement: *replacement + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *replacement + comment: "comment" + outcome: *initialData diff --git a/test/spec/crud/unified/updateMany-comment.json b/test/spec/crud/unified/updateMany-comment.json new file mode 100644 index 0000000000..26abd92ed4 --- /dev/null +++ b/test/spec/crud/unified/updateMany-comment.json @@ -0,0 +1,244 @@ +{ + "description": "updateMany-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "UpdateMany with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": "comment" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + }, + "multi": true + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateMany with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": { + "key": "value" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + }, + "multi": true + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateMany with comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "updateMany", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + }, + "multi": true + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/updateMany-comment.yml b/test/spec/crud/unified/updateMany-comment.yml new file mode 100644 index 0000000000..bfe1a5d08a --- /dev/null +++ b/test/spec/crud/unified/updateMany-comment.yml @@ -0,0 +1,100 @@ +description: "updateMany-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: + - description: "UpdateMany with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: updateMany + object: *collection0 + arguments: + filter: &filter { _id: 1 } + update: &update { $set: {x: 22} } + comment: "comment" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + multi: true + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 22 } + + - description: "UpdateMany with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: updateMany + object: *collection0 + arguments: + filter: *filter + update: *update + comment: &comment { key: "value" } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + multi: true + comment: *comment + outcome: *outcome + + - description: "UpdateMany with comment - pre 4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + operations: + - name: updateMany + object: *collection0 + arguments: + filter: *filter + update: *update + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + multi: true + comment: "comment" + outcome: *initialData diff --git a/test/spec/crud/unified/updateOne-comment.json b/test/spec/crud/unified/updateOne-comment.json new file mode 100644 index 0000000000..9b3b71d395 --- /dev/null +++ b/test/spec/crud/unified/updateOne-comment.json @@ -0,0 +1,241 @@ +{ + "description": "updateOne-comment", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "crud-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ], + "tests": [ + { + "description": "UpdateOne with string comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": "comment" + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": { + "key": "value" + } + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + } + } + ], + "comment": { + "key": "value" + } + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 22 + } + ] + } + ] + }, + { + "description": "UpdateOne with comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "$set": { + "x": 22 + } + }, + "comment": "comment" + }, + "expectError": { + "isClientError": false + } + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "update": "coll0", + "updates": [ + { + "q": { + "_id": 1 + }, + "u": { + "$set": { + "x": 22 + } + } + } + ], + "comment": "comment" + } + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "coll0", + "databaseName": "crud-tests", + "documents": [ + { + "_id": 1, + "x": 11 + } + ] + } + ] + } + ] +} diff --git a/test/spec/crud/unified/updateOne-comment.yml b/test/spec/crud/unified/updateOne-comment.yml new file mode 100644 index 0000000000..0a879d16d2 --- /dev/null +++ b/test/spec/crud/unified/updateOne-comment.yml @@ -0,0 +1,97 @@ +description: "updateOne-comment" +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + observeEvents: [ commandStartedEvent ] + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name crud-tests + - collection: + id: &collection0 collection0 + database: *database0 + collectionName: &collection0Name coll0 + +initialData: &initialData + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 11 } + +tests: + - description: "UpdateOne with string comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: updateOne + object: *collection0 + arguments: + filter: &filter { _id: 1 } + update: &update { $set: {x: 22} } + comment: "comment" + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + comment: "comment" + outcome: &outcome + - collectionName: *collection0Name + databaseName: *database0Name + documents: + - { _id: 1, x: 22 } + + - description: "UpdateOne with document comment" + runOnRequirements: + - minServerVersion: "4.4" + operations: + - name: updateOne + object: *collection0 + arguments: + filter: *filter + update: *update + comment: &comment { key: "value" } + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + comment: *comment + outcome: *outcome + + - description: "UpdateOne with comment - pre 4.4" + runOnRequirements: + - maxServerVersion: "4.2.99" + operations: + - name: updateOne + object: *collection0 + arguments: + filter: *filter + update: *update + comment: "comment" + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + update: *collection0Name + updates: + - + q: *filter + u: *update + comment: "comment" + outcome: *initialData diff --git a/test/tools/unified-spec-runner/entities.ts b/test/tools/unified-spec-runner/entities.ts index b26c0fccfc..fe8f7e9f5e 100644 --- a/test/tools/unified-spec-runner/entities.ts +++ b/test/tools/unified-spec-runner/entities.ts @@ -18,8 +18,8 @@ import { ConnectionPoolCreatedEvent, ConnectionReadyEvent } from '../../../src/cmap/connection_pool_events'; -import { FindCursor } from '../../../src/cursor/find_cursor'; import { + AbstractCursor, Collection, Db, Document, @@ -38,7 +38,7 @@ import { trace } from './runner'; import type { ClientEntity, EntityDescription } from './schema'; import { makeConnectionString, patchCollectionOptions, patchDbOptions } from './unified-utils'; -interface UnifiedChangeStream extends ChangeStream { +export interface UnifiedChangeStream extends ChangeStream { eventCollector: InstanceType; } @@ -259,7 +259,7 @@ export type Entity = | Db | Collection | ClientSession - | FindCursor + | AbstractCursor | UnifiedChangeStream | GridFSBucket | Document; // Results from operations @@ -270,7 +270,7 @@ export type EntityCtor = | typeof Collection | typeof ClientSession | typeof ChangeStream - | typeof FindCursor + | typeof AbstractCursor | typeof GridFSBucket; export type EntityTypeId = @@ -288,7 +288,7 @@ ENTITY_CTORS.set('db', Db); ENTITY_CTORS.set('collection', Collection); ENTITY_CTORS.set('session', ClientSession); ENTITY_CTORS.set('bucket', GridFSBucket); -ENTITY_CTORS.set('cursor', FindCursor); +ENTITY_CTORS.set('cursor', AbstractCursor); ENTITY_CTORS.set('stream', ChangeStream); export class EntitiesMap extends Map { @@ -304,7 +304,7 @@ export class EntitiesMap extends Map { mapOf(type: 'collection'): EntitiesMap; mapOf(type: 'session'): EntitiesMap; mapOf(type: 'bucket'): EntitiesMap; - mapOf(type: 'cursor'): EntitiesMap; + mapOf(type: 'cursor'): EntitiesMap; mapOf(type: 'stream'): EntitiesMap; mapOf(type: EntityTypeId): EntitiesMap { const ctor = ENTITY_CTORS.get(type); @@ -319,7 +319,7 @@ export class EntitiesMap extends Map { getEntity(type: 'collection', key: string, assertExists?: boolean): Collection; getEntity(type: 'session', key: string, assertExists?: boolean): ClientSession; getEntity(type: 'bucket', key: string, assertExists?: boolean): GridFSBucket; - getEntity(type: 'cursor', key: string, assertExists?: boolean): FindCursor; + getEntity(type: 'cursor', key: string, assertExists?: boolean): AbstractCursor; getEntity(type: 'stream', key: string, assertExists?: boolean): UnifiedChangeStream; getEntity(type: EntityTypeId, key: string, assertExists = true): Entity { const entity = this.get(key); diff --git a/test/tools/unified-spec-runner/match.ts b/test/tools/unified-spec-runner/match.ts index ce0370e79a..1a4c16ddb5 100644 --- a/test/tools/unified-spec-runner/match.ts +++ b/test/tools/unified-spec-runner/match.ts @@ -184,6 +184,16 @@ export function resultCheck( } else if (Long.isLong(expected) && typeof actual === 'number') { // Long requires special equality check expect(expected.equals(actual)).to.be.true; + } else if (Number.isNaN(actual) && Number.isNaN(expected)) { + // in JS, NaN isn't equal to NaN but we want to not fail if we have two NaN + } else if ( + typeof expected === 'number' && + typeof actual === 'number' && + expected === 0 && + actual === 0 + ) { + // case to handle +0 and -0 + expect(Object.is(expected, actual)).to.be.true; } else { expect(actual).to.equal(expected); } diff --git a/test/tools/unified-spec-runner/operations.ts b/test/tools/unified-spec-runner/operations.ts index 6638283e58..76f85d6bb2 100644 --- a/test/tools/unified-spec-runner/operations.ts +++ b/test/tools/unified-spec-runner/operations.ts @@ -14,8 +14,8 @@ import { CommandStartedEvent } from '../../../src/cmap/command_monitoring_events import { ReadConcern } from '../../../src/read_concern'; import { ReadPreference } from '../../../src/read_preference'; import { WriteConcern } from '../../../src/write_concern'; -import { EventCollector } from '../../tools/utils'; -import { EntitiesMap } from './entities'; +import { getSymbolFrom } from '../../tools/utils'; +import { EntitiesMap, UnifiedChangeStream } from './entities'; import { expectErrorCheck, resultCheck } from './match'; import type { OperationDescription } from './schema'; import { translateOptions } from './unified-utils'; @@ -173,8 +173,8 @@ operations.set('assertNumberConnectionsCheckedOut', async ({ entities, operation operations.set('bulkWrite', async ({ entities, operation }) => { const collection = entities.getEntity('collection', operation.object); - const { requests: operations, ...options } = operation.arguments; - return collection.bulkWrite(operations, options); + const { requests, ...opts } = operation.arguments; + return collection.bulkWrite(requests, opts); }); // The entity exists for the name but can potentially have the wrong @@ -200,23 +200,14 @@ operations.set('createChangeStream', async ({ entities, operation }) => { if (!('watch' in watchable)) { throw new Error(`Entity ${operation.object} must be watchable`); } - const changeStream = watchable.watch(operation.arguments.pipeline, { - fullDocument: operation.arguments.fullDocument, - maxAwaitTimeMS: operation.arguments.maxAwaitTimeMS, - resumeAfter: operation.arguments.resumeAfter, - startAfter: operation.arguments.startAfter, - startAtOperationTime: operation.arguments.startAtOperationTime, - batchSize: operation.arguments.batchSize - }); - changeStream.eventCollector = new EventCollector(changeStream, ['init', 'change', 'error']); - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new Error('Change stream never started')); - }, 2000); + const { pipeline, ...args } = operation.arguments; + const changeStream = watchable.watch(pipeline, args); - changeStream.cursor.once('init', () => { - clearTimeout(timeout); + return new Promise((resolve, reject) => { + const init = getSymbolFrom(AbstractCursor.prototype, 'kInit'); + changeStream.cursor[init](err => { + if (err) return reject(err); resolve(changeStream); }); }); @@ -304,17 +295,26 @@ operations.set('insertMany', async ({ entities, operation }) => { }); operations.set('iterateUntilDocumentOrError', async ({ entities, operation }) => { - try { - const changeStream = entities.getEntity('stream', operation.object); - // Either change or error promise will finish - return Promise.race([ - changeStream.eventCollector.waitAndShiftEvent('change'), - changeStream.eventCollector.waitAndShiftEvent('error') - ]); - } catch (e) { - const findCursor = entities.getEntity('cursor', operation.object); - return await findCursor.next(); + function getChangeStream(): UnifiedChangeStream | null { + try { + const changeStream = entities.getEntity('stream', operation.object); + return changeStream; + } catch (e) { + return null; + } } + + const changeStream = getChangeStream(); + if (changeStream == null) { + // iterateUntilDocumentOrError is used for changes streams and regular cursors. + // we have no other way to distinguish which scenario we are testing when we run an + // iterateUntilDocumentOrError operation, so we first try to get the changeStream and + // if that fails, we know we need to get a cursor + const cursor = entities.getEntity('cursor', operation.object); + return await cursor.next(); + } + + return changeStream.cursor.next(); }); operations.set('listCollections', async ({ entities, operation }) => { diff --git a/test/tools/unified-spec-runner/schema.ts b/test/tools/unified-spec-runner/schema.ts index 1c6847c59f..aedfc24fd8 100644 --- a/test/tools/unified-spec-runner/schema.ts +++ b/test/tools/unified-spec-runner/schema.ts @@ -20,9 +20,10 @@ export interface UnifiedSuite { description: string; schemaVersion: string; runOnRequirements?: RunOnRequirement[]; - createEntities?: [EntityDescription, ...EntityDescription[]]; - initialData?: [CollectionData, ...CollectionData[]]; - tests: [Test, ...Test[]]; + createEntities?: EntityDescription[]; + /** Data inserted before **all tests */ + initialData?: CollectionData[]; + tests: Test[]; _yamlAnchors?: Document; } export const TopologyType = Object.freeze({ @@ -126,11 +127,11 @@ export interface CollectionData { } export interface Test { description: string; - runOnRequirements?: [RunOnRequirement, ...RunOnRequirement[]]; + runOnRequirements?: RunOnRequirement[]; skipReason?: string; operations: OperationDescription[]; expectEvents?: ExpectedEventsForClient[]; - outcome?: [CollectionData, ...CollectionData[]]; + outcome?: CollectionData[]; } export interface ExpectedEventsForClient { client: string; diff --git a/test/tools/utils.ts b/test/tools/utils.ts index 1fb6ed6ce7..b21b7e439a 100644 --- a/test/tools/utils.ts +++ b/test/tools/utils.ts @@ -4,6 +4,15 @@ import util from 'util'; import { Logger } from '../../src/logger'; import { deprecateOptions, DeprecateOptionsConfig } from '../../src/utils'; +import { + CollectionData, + EntityDescription, + ExpectedEventsForClient, + OperationDescription, + RunOnRequirement, + Test, + UnifiedSuite +} from './unified-spec-runner/schema'; export function makeTestFunction(config: DeprecateOptionsConfig) { const fn = (options: any) => { @@ -83,22 +92,6 @@ export class EventCollector { this.waitForEventImpl(this, Date.now(), eventName, count, callback); } - /** - * Will only return one event at a time from the front of the list - * Useful for iterating over the events in the order they occurred - */ - waitAndShiftEvent(eventName: string): Promise> { - return new Promise>((resolve, reject) => { - if (this._events[eventName].length > 0) { - return resolve(this._events[eventName].shift()); - } - this.waitForEventImpl(this, Date.now(), eventName, 1, (error: any) => { - if (error) return reject(error); - resolve(this._events[eventName].shift()); - }); - }); - } - reset(eventName: string) { if (eventName == null) { Object.keys(this._events).forEach(eventName => { @@ -286,3 +279,181 @@ export interface FailPoint { appName?: string; }; } + +export class TestBuilder { + private _description: string; + private runOnRequirements: RunOnRequirement[] = []; + private _skipReason?: string; + private _operations: OperationDescription[] = []; + private _expectEvents?: ExpectedEventsForClient[] = []; + private _outcome?: CollectionData[] = []; + + constructor(description: string) { + this._description = description; + } + + operation(operation: OperationDescription): this { + this._operations.push({ + object: 'collection0', + ...operation + }); + return this; + } + + runOnRequirement(requirement: RunOnRequirement): this { + this.runOnRequirements.push(requirement); + return this; + } + + expectEvents(event: ExpectedEventsForClient): this { + this._expectEvents.push(event); + return this; + } + + toJSON(): Test { + const test: Test = { + description: this._description, + runOnRequirements: this.runOnRequirements, + operations: this._operations, + expectEvents: this._expectEvents, + outcome: this._outcome + }; + + if (this._skipReason != null) { + test.skipReason = this._skipReason; + } + + return test; + } +} + +export class UnifiedTestSuiteBuilder { + private _description = 'Default Description'; + private _databaseName = ''; + private _schemaVersion = '1.0'; + private _createEntities: EntityDescription[] = [ + { + client: { + id: 'client0', + useMultipleMongoses: true, + observeEvents: ['commandStartedEvent'] + } + }, + { + database: { + id: 'database0', + client: 'client0', + databaseName: '' + } + }, + { + collection: { + id: 'collection0', + database: 'database0', + collectionName: 'coll0' + } + } + ]; + private _runOnRequirement: RunOnRequirement[] = []; + private _initialData: CollectionData[] = []; + private _tests: Test[] = []; + + constructor(description: string) { + this._description = description; + } + + description(description: string): this { + this._description = description; + return this; + } + + test(test: Test): this; + test(test: Test[]): this; + test(test: Test | Test[]): this { + if (Array.isArray(test)) { + this._tests.push(...test); + } else { + this._tests.push(test); + } + return this; + } + + createEntities(entity: EntityDescription): this; + createEntities(entity: EntityDescription[]): this; + createEntities(entity: EntityDescription | EntityDescription[]): this { + if (Array.isArray(entity)) { + this._createEntities.push(...entity); + } else { + this._createEntities.push(entity); + } + return this; + } + + initialData(data: CollectionData): this; + initialData(data: CollectionData[]): this; + initialData(data: CollectionData | CollectionData[]): this { + if (Array.isArray(data)) { + this._initialData.push(...data); + } else { + this._initialData.push(data); + } + return this; + } + + /** + * sets the database name for the tests + */ + databaseName(name: string): this { + this._databaseName = name; + return this; + } + + runOnRequirement(requirement: RunOnRequirement): this; + runOnRequirement(requirement: RunOnRequirement[]): this; + runOnRequirement(requirement: RunOnRequirement | RunOnRequirement[]): this { + Array.isArray(requirement) + ? this._runOnRequirement.push(...requirement) + : this._runOnRequirement.push(requirement); + return this; + } + + schemaVersion(version: string): this { + this._schemaVersion = version; + return this; + } + + toJSON(): UnifiedSuite { + const databaseName = + this._databaseName !== '' + ? this._databaseName + : this._description + .split(' ') + .filter(s => s.length > 0) + .join('_'); + return { + description: this._description, + schemaVersion: this._schemaVersion, + runOnRequirements: this._runOnRequirement, + createEntities: this._createEntities.map(entity => { + if ('database' in entity) { + return { + database: { ...entity.database, databaseName } + }; + } + + return entity; + }), + initialData: this._initialData.map(data => { + return { + ...data, + databaseName + }; + }), + tests: this._tests + }; + } + + clone(): UnifiedSuite { + return JSON.parse(JSON.stringify(this)); + } +} diff --git a/test/unit/connection_string.test.ts b/test/unit/connection_string.test.ts index b4d45af7c4..989fcd16b4 100644 --- a/test/unit/connection_string.test.ts +++ b/test/unit/connection_string.test.ts @@ -292,7 +292,7 @@ describe('Connection String', function () { describe('resolveSRVRecord()', () => { const resolveSRVRecordAsync = promisify(resolveSRVRecord); - afterEach(async () => { + afterEach(() => { sinon.restore(); }); diff --git a/test/unit/operations/get_more.test.js b/test/unit/operations/get_more.test.js deleted file mode 100644 index 99621a1f64..0000000000 --- a/test/unit/operations/get_more.test.js +++ /dev/null @@ -1,118 +0,0 @@ -'use strict'; - -const sinon = require('sinon'); -const { expect } = require('chai'); -const { Long } = require('../../../src/bson'); -const { GetMoreOperation } = require('../../../src/operations/get_more'); -const { Server } = require('../../../src/sdam/server'); -const { ClientSession } = require('../../../src/sessions'); -const { ReadPreference } = require('../../../src/read_preference'); -const { Aspect } = require('../../../src/operations/operation'); -const { MongoRuntimeError } = require('../../../src/error'); - -describe('GetMoreOperation', function () { - const ns = 'db.coll'; - const cursorId = Object.freeze(Long.fromNumber(1)); - const options = Object.freeze({ - batchSize: 100, - comment: 'test', - maxTimeMS: 500, - readPreference: ReadPreference.primary - }); - - describe('#constructor', function () { - const server = sinon.createStubInstance(Server, {}); - const operation = new GetMoreOperation(ns, cursorId, server, options); - - it('sets the namespace', function () { - expect(operation.ns).to.equal(ns); - }); - - it('sets the cursorId', function () { - expect(operation.cursorId).to.equal(cursorId); - }); - - it('sets the server', function () { - expect(operation.server).to.equal(server); - }); - - it('sets the options', function () { - expect(operation.options).to.deep.equal(options); - }); - }); - - describe('#execute', function () { - context('when the server is the same as the instance', function () { - const getMoreStub = sinon.stub().yields(undefined); - const server = sinon.createStubInstance(Server, { - getMore: getMoreStub - }); - const session = sinon.createStubInstance(ClientSession); - const opts = { ...options, session }; - const operation = new GetMoreOperation(ns, cursorId, server, opts); - - it('executes a getmore on the provided server', function (done) { - const callback = () => { - const call = getMoreStub.getCall(0); - expect(getMoreStub.calledOnce).to.be.true; - expect(call.args[0]).to.equal(ns); - expect(call.args[1]).to.equal(cursorId); - expect(call.args[2]).to.deep.equal(opts); - done(); - }; - operation.execute(server, session, callback); - }); - }); - - context('when the server is not the same as the instance', function () { - const getMoreStub = sinon.stub().yields(undefined); - const server = sinon.createStubInstance(Server, { - getMore: getMoreStub - }); - const newServer = sinon.createStubInstance(Server, { - getMore: getMoreStub - }); - const session = sinon.createStubInstance(ClientSession); - const opts = { ...options, session }; - const operation = new GetMoreOperation(ns, cursorId, server, opts); - - it('errors in the callback', function (done) { - const callback = error => { - expect(error).to.be.instanceOf(MongoRuntimeError); - expect(error.message).to.equal('Getmore must run on the same server operation began on'); - done(); - }; - operation.execute(newServer, session, callback); - }); - }); - }); - - describe('#hasAspect', function () { - const server = sinon.createStubInstance(Server, {}); - const operation = new GetMoreOperation(ns, cursorId, server, options); - - context('when the aspect is cursor iterating', function () { - it('returns true', function () { - expect(operation.hasAspect(Aspect.CURSOR_ITERATING)).to.be.true; - }); - }); - - context('when the aspect is read', function () { - it('returns true', function () { - expect(operation.hasAspect(Aspect.READ_OPERATION)).to.be.true; - }); - }); - - context('when the aspect is write', function () { - it('returns false', function () { - expect(operation.hasAspect(Aspect.WRITE_OPERATION)).to.be.false; - }); - }); - - context('when the aspect is retryable', function () { - it('returns false', function () { - expect(operation.hasAspect(Aspect.RETRYABLE)).to.be.false; - }); - }); - }); -}); diff --git a/test/unit/operations/get_more.test.ts b/test/unit/operations/get_more.test.ts new file mode 100644 index 0000000000..6127c0f33c --- /dev/null +++ b/test/unit/operations/get_more.test.ts @@ -0,0 +1,160 @@ +import { expect } from 'chai'; +import * as sinon from 'sinon'; + +import { Long } from '../../../src/bson'; +import { MongoRuntimeError } from '../../../src/error'; +import { GetMoreOperation } from '../../../src/operations/get_more'; +import { Aspect } from '../../../src/operations/operation'; +import { ReadPreference } from '../../../src/read_preference'; +import { Server } from '../../../src/sdam/server'; +import { ServerDescription } from '../../../src/sdam/server_description'; +import { Topology } from '../../../src/sdam/topology'; +import { ClientSession } from '../../../src/sessions'; +import { MongoDBNamespace } from '../../../src/utils'; + +describe('GetMoreOperation', function () { + const ns = new MongoDBNamespace('db.coll'); + const cursorId = Object.freeze(Long.fromNumber(1)); + const options = { + batchSize: 100, + maxTimeMS: 500, + readPreference: ReadPreference.primary + }; + + describe('#constructor', function () { + const server = new Server(new Topology([], {} as any), new ServerDescription(''), {} as any); + const operation = new GetMoreOperation(ns, cursorId, server, options); + + it('sets the namespace', function () { + expect(operation.ns).to.equal(ns); + }); + + it('sets the cursorId', function () { + expect(operation.cursorId).to.equal(cursorId); + }); + + it('sets the server', function () { + expect(operation.server).to.equal(server); + }); + + context('options', function () { + const optionsWithComment = { + ...options, + comment: 'test' + }; + it('does not set the comment option if the server version is <4', () => { + const server = new Server( + new Topology([], {} as any), + new ServerDescription(''), + {} as any + ); + server.hello = { + maxWireVersion: 8 + }; + const operation = new GetMoreOperation(ns, cursorId, server, optionsWithComment); + const expected = { + batchSize: 100, + maxTimeMS: 500, + readPreference: ReadPreference.primary + }; + expect(operation.options).to.deep.equal(expected); + }); + + it('sets the comment option if the server version is >=4', () => { + const server = new Server( + new Topology([], {} as any), + new ServerDescription(''), + {} as any + ); + server.hello = { + maxWireVersion: 10 + }; + const operation = new GetMoreOperation(ns, cursorId, server, optionsWithComment); + expect(operation.options).to.deep.equal(optionsWithComment); + }); + }); + }); + + describe('#execute', function () { + afterEach(function () { + sinon.restore(); + }); + + context('when the server is the same as the instance', function () { + it('executes a getMore on the provided server', function (done) { + const server = new Server( + new Topology([], {} as any), + new ServerDescription(''), + {} as any + ); + const session = sinon.createStubInstance(ClientSession); + const opts = { ...options, session }; + const operation = new GetMoreOperation(ns, cursorId, server, opts); + const stub = sinon.stub(server, 'getMore').callsFake((_, __, ___, cb) => cb()); + + const callback = () => { + const call = stub.getCall(0); + expect(stub.calledOnce).to.be.true; + expect(call.args[0]).to.equal(ns); + expect(call.args[1]).to.equal(cursorId); + expect(call.args[2]).to.deep.equal(opts); + done(); + }; + operation.execute(server, session, callback); + }); + }); + + context('when the server is not the same as the instance', function () { + it('errors in the callback', function (done) { + const server1 = new Server( + new Topology([], {} as any), + new ServerDescription(''), + {} as any + ); + const server2 = new Server( + new Topology([], {} as any), + new ServerDescription(''), + {} as any + ); + const session = sinon.createStubInstance(ClientSession); + const opts = { ...options, session }; + const operation = new GetMoreOperation(ns, cursorId, server1, opts); + const callback = error => { + expect(error).to.be.instanceOf(MongoRuntimeError); + expect(error.message).to.equal('Getmore must run on the same server operation began on'); + done(); + }; + operation.execute(server2, session, callback); + }); + }); + }); + + describe('#hasAspect', function () { + const server = new Server(new Topology([], {} as any), new ServerDescription(''), {} as any); + const operation = new GetMoreOperation(ns, cursorId, server, options); + + context('when the aspect is cursor iterating', function () { + it('returns true', function () { + expect(operation.hasAspect(Aspect.CURSOR_ITERATING)).to.be.true; + }); + }); + + context('when the aspect is read', function () { + it('returns true', function () { + expect(operation.hasAspect(Aspect.READ_OPERATION)).to.be.true; + }); + }); + + context('when the aspect is write', function () { + it('returns false', function () { + expect(operation.hasAspect(Aspect.WRITE_OPERATION)).to.be.false; + }); + }); + + context('when the aspect is retryable', function () { + it('returns false', function () { + expect(operation.hasAspect(Aspect.RETRYABLE)).to.be.false; + }); + }); + }); +}); diff --git a/test/unit/operations/list_collections.test.js b/test/unit/operations/list_collections.test.js index d63d7c0c16..071f43856d 100644 --- a/test/unit/operations/list_collections.test.js +++ b/test/unit/operations/list_collections.test.js @@ -65,12 +65,37 @@ describe('ListCollectionsOperation', function () { }); describe('#generateCommand', function () { + context('when comment is provided', function () { + context('when the wireVersion < 9', function () { + it('does not set a comment on the command', function () { + const operation = new ListCollectionsOperation( + db, + {}, + { dbName: db, comment: 'test comment' } + ); + const command = operation.generateCommand(8); + expect(command).not.to.haveOwnProperty('comment'); + }); + }); + + context('when the wireVersion >= 9', function () { + it('sets a comment on the command', function () { + const operation = new ListCollectionsOperation( + db, + {}, + { dbName: db, comment: 'test comment' } + ); + const command = operation.generateCommand(9); + expect(command).to.have.property('comment').that.equals('test comment'); + }); + }); + }); context('when nameOnly is provided', function () { context('when nameOnly is true', function () { const operation = new ListCollectionsOperation(db, {}, { nameOnly: true, dbName: db }); it('sets nameOnly to true', function () { - expect(operation.generateCommand()).to.deep.equal({ + expect(operation.generateCommand(8)).to.deep.equal({ listCollections: 1, cursor: {}, filter: {}, @@ -84,7 +109,7 @@ describe('ListCollectionsOperation', function () { const operation = new ListCollectionsOperation(db, {}, { nameOnly: false, dbName: db }); it('sets nameOnly to false', function () { - expect(operation.generateCommand()).to.deep.equal({ + expect(operation.generateCommand(8)).to.deep.equal({ listCollections: 1, cursor: {}, filter: {}, @@ -104,7 +129,7 @@ describe('ListCollectionsOperation', function () { ); it('sets authorizedCollections to true', function () { - expect(operation.generateCommand()).to.deep.equal({ + expect(operation.generateCommand(8)).to.deep.equal({ listCollections: 1, cursor: {}, filter: {}, @@ -122,7 +147,7 @@ describe('ListCollectionsOperation', function () { ); it('sets authorizedCollections to false', function () { - expect(operation.generateCommand()).to.deep.equal({ + expect(operation.generateCommand(8)).to.deep.equal({ listCollections: 1, cursor: {}, filter: {}, @@ -137,7 +162,7 @@ describe('ListCollectionsOperation', function () { const operation = new ListCollectionsOperation(db, {}, { dbName: db }); it('sets nameOnly and authorizedCollections properties to false', function () { - expect(operation.generateCommand()).to.deep.equal({ + expect(operation.generateCommand(8)).to.deep.equal({ listCollections: 1, cursor: {}, filter: {},