diff --git a/src/bulk/common.ts b/src/bulk/common.ts index f7425c691b..078b122c5c 100644 --- a/src/bulk/common.ts +++ b/src/bulk/common.ts @@ -880,13 +880,17 @@ const executeCommandsAsync = promisify(executeCommands); * We would like this logic to simply live inside the BulkWriteOperation class * @internal */ -class BulkWriteShimOperation extends AbstractOperation { +export class BulkWriteShimOperation extends AbstractOperation { bulkOperation: BulkOperationBase; constructor(bulkOperation: BulkOperationBase, options: BulkWriteOptions) { super(options); this.bulkOperation = bulkOperation; } + get commandName(): string { + return 'bulkWrite' as const; + } + execute(_server: Server, session: ClientSession | undefined): Promise { if (this.options.session == null) { // An implicit session could have been created by 'executeOperation' diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index 313bd37f39..93f1cfe157 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -82,6 +82,10 @@ export class AggregateOperation extends CommandOperation { } } + override get commandName() { + return 'aggregate' as const; + } + override get canRetryRead(): boolean { return !this.hasWriteStage; } diff --git a/src/operations/bulk_write.ts b/src/operations/bulk_write.ts index 88771c3c4b..58a143f208 100644 --- a/src/operations/bulk_write.ts +++ b/src/operations/bulk_write.ts @@ -26,6 +26,10 @@ export class BulkWriteOperation extends AbstractOperation { this.operations = operations; } + override get commandName() { + return 'bulkWrite' as const; + } + override async execute( server: Server, session: ClientSession | undefined diff --git a/src/operations/collections.ts b/src/operations/collections.ts index 9a8cdf0e58..5ed9629680 100644 --- a/src/operations/collections.ts +++ b/src/operations/collections.ts @@ -19,6 +19,10 @@ export class CollectionsOperation extends AbstractOperation { this.db = db; } + override get commandName() { + return 'listCollections' as const; + } + override async execute( server: Server, session: ClientSession | undefined diff --git a/src/operations/count.ts b/src/operations/count.ts index f9ad49f0dc..00aae50172 100644 --- a/src/operations/count.ts +++ b/src/operations/count.ts @@ -32,6 +32,10 @@ export class CountOperation extends CommandOperation { this.query = filter; } + override get commandName() { + return 'count' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const options = this.options; const cmd: Document = { diff --git a/src/operations/create_collection.ts b/src/operations/create_collection.ts index 6ea988b8f9..9732e879b1 100644 --- a/src/operations/create_collection.ts +++ b/src/operations/create_collection.ts @@ -120,6 +120,10 @@ export class CreateCollectionOperation extends CommandOperation { this.name = name; } + override get commandName() { + return 'create' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const db = this.db; const name = this.name; diff --git a/src/operations/delete.ts b/src/operations/delete.ts index dc4d0bc13e..39f0fde7bf 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -53,6 +53,10 @@ export class DeleteOperation extends CommandOperation { this.statements = statements; } + override get commandName() { + return 'delete' as const; + } + override get canRetryWrite(): boolean { if (super.canRetryWrite === false) { return false; diff --git a/src/operations/distinct.ts b/src/operations/distinct.ts index 900abd6afa..4fda285d88 100644 --- a/src/operations/distinct.ts +++ b/src/operations/distinct.ts @@ -38,6 +38,10 @@ export class DistinctOperation extends CommandOperation { this.query = query; } + override get commandName() { + return 'distinct' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const coll = this.collection; const key = this.key; diff --git a/src/operations/drop.ts b/src/operations/drop.ts index dbbfa7fea0..41cce4b361 100644 --- a/src/operations/drop.ts +++ b/src/operations/drop.ts @@ -25,6 +25,10 @@ export class DropCollectionOperation extends CommandOperation { this.name = name; } + override get commandName() { + return 'drop' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const db = this.db; const options = this.options; @@ -88,6 +92,10 @@ export class DropDatabaseOperation extends CommandOperation { super(db, options); this.options = options; } + override get commandName() { + return 'dropDatabase' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { await super.executeCommand(server, session, { dropDatabase: 1 }); return true; diff --git a/src/operations/estimated_document_count.ts b/src/operations/estimated_document_count.ts index 44e70bbaef..c1d6c38199 100644 --- a/src/operations/estimated_document_count.ts +++ b/src/operations/estimated_document_count.ts @@ -26,6 +26,10 @@ export class EstimatedDocumentCountOperation extends CommandOperation { this.collectionName = collection.collectionName; } + override get commandName() { + return 'count' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const cmd: Document = { count: this.collectionName }; diff --git a/src/operations/find.ts b/src/operations/find.ts index 278cead138..85ebf1edcd 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -97,6 +97,10 @@ export class FindOperation extends CommandOperation { this.filter = filter != null && filter._bsontype === 'ObjectId' ? { _id: filter } : filter; } + override get commandName() { + return 'find' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { this.server = server; diff --git a/src/operations/find_and_modify.ts b/src/operations/find_and_modify.ts index 0e0d786be3..fe851da0fb 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -121,7 +121,7 @@ function configureFindAndModifyCmdBaseUpdateOpts( } /** @internal */ -class FindAndModifyOperation extends CommandOperation { +export class FindAndModifyOperation extends CommandOperation { override options: FindOneAndReplaceOptions | FindOneAndUpdateOptions | FindOneAndDeleteOptions; cmdBase: FindAndModifyCmdBase; collection: Collection; @@ -178,6 +178,10 @@ class FindAndModifyOperation extends CommandOperation { this.query = query; } + override get commandName() { + return 'findAndModify' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const coll = this.collection; const query = this.query; diff --git a/src/operations/get_more.ts b/src/operations/get_more.ts index 0a3693d57b..b7059a46c4 100644 --- a/src/operations/get_more.ts +++ b/src/operations/get_more.ts @@ -48,6 +48,9 @@ export class GetMoreOperation extends AbstractOperation { this.server = server; } + override get commandName() { + return 'getMore' as const; + } /** * Although there is a server already associated with the get more operation, the signature * for execute passes a server so we will just use that one. diff --git a/src/operations/indexes.ts b/src/operations/indexes.ts index b147b67cbe..cbeb82ff81 100644 --- a/src/operations/indexes.ts +++ b/src/operations/indexes.ts @@ -186,6 +186,10 @@ export class IndexesOperation extends AbstractOperation { this.collection = collection; } + override get commandName() { + return 'listIndexes' as const; + } + override async execute(_server: Server, session: ClientSession | undefined): Promise { const coll = this.collection; const options = this.options; @@ -235,6 +239,10 @@ export class CreateIndexesOperation< }); } + override get commandName() { + return 'createIndexes'; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const options = this.options; const indexes = this.indexes; @@ -272,6 +280,7 @@ export class CreateIndexOperation extends CreateIndexesOperation { ) { super(parent, collectionName, [makeIndexSpec(indexSpec, options)], options); } + override async execute(server: Server, session: ClientSession | undefined): Promise { const indexNames = await super.execute(server, session); return indexNames[0]; @@ -295,6 +304,10 @@ export class EnsureIndexOperation extends CreateIndexOperation { this.collectionName = collectionName; } + override get commandName() { + return 'listIndexes'; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const indexName = this.indexes[0].name; const indexes = await this.db @@ -328,6 +341,10 @@ export class DropIndexOperation extends CommandOperation { this.indexName = indexName; } + override get commandName() { + return 'dropIndexes' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const cmd = { dropIndexes: this.collection.collectionName, index: this.indexName }; return super.executeCommand(server, session, cmd); @@ -360,6 +377,10 @@ export class ListIndexesOperation extends CommandOperation { this.collectionNamespace = collection.s.namespace; } + override get commandName() { + return 'listIndexes' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const serverWireVersion = maxWireVersion(server); @@ -394,6 +415,10 @@ export class IndexExistsOperation extends AbstractOperation { this.indexes = indexes; } + override get commandName() { + return 'listIndexes' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const coll = this.collection; const indexes = this.indexes; @@ -423,6 +448,10 @@ export class IndexInformationOperation extends AbstractOperation { this.name = name; } + override get commandName() { + return 'listIndexes' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const db = this.db; const name = this.name; diff --git a/src/operations/insert.ts b/src/operations/insert.ts index 8a63ad4f4d..27e84debf2 100644 --- a/src/operations/insert.ts +++ b/src/operations/insert.ts @@ -24,6 +24,10 @@ export class InsertOperation extends CommandOperation { this.documents = documents; } + override get commandName() { + return 'insert' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const options = this.options ?? {}; const ordered = typeof options.ordered === 'boolean' ? options.ordered : true; @@ -114,6 +118,10 @@ export class InsertManyOperation extends AbstractOperation { this.docs = docs; } + override get commandName() { + return 'insert' as const; + } + override async execute( server: Server, session: ClientSession | undefined diff --git a/src/operations/is_capped.ts b/src/operations/is_capped.ts index 0a08ff4567..db52ec3bb3 100644 --- a/src/operations/is_capped.ts +++ b/src/operations/is_capped.ts @@ -15,6 +15,10 @@ export class IsCappedOperation extends AbstractOperation { this.collection = collection; } + override get commandName() { + return 'listCollections' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const coll = this.collection; const [collection] = await coll.s.db diff --git a/src/operations/kill_cursors.ts b/src/operations/kill_cursors.ts index b908546456..ffbf0a3ad4 100644 --- a/src/operations/kill_cursors.ts +++ b/src/operations/kill_cursors.ts @@ -25,6 +25,10 @@ export class KillCursorsOperation extends AbstractOperation { this.server = server; } + override get commandName() { + return 'killCursors' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { if (server !== this.server) { throw new MongoRuntimeError('Killcursor must run on the same server operation began on'); diff --git a/src/operations/list_collections.ts b/src/operations/list_collections.ts index 4947ca7862..1bdea119ba 100644 --- a/src/operations/list_collections.ts +++ b/src/operations/list_collections.ts @@ -47,6 +47,10 @@ export class ListCollectionsOperation extends CommandOperation { } } + override get commandName() { + return 'listCollections' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { return super.executeCommand(server, session, this.generateCommand(maxWireVersion(server))); } diff --git a/src/operations/list_databases.ts b/src/operations/list_databases.ts index f51da41bd2..6b7b269fea 100644 --- a/src/operations/list_databases.ts +++ b/src/operations/list_databases.ts @@ -35,6 +35,10 @@ export class ListDatabasesOperation extends CommandOperation { this.trySecondaryWrite = false; } + /** Must match the first key of the command object sent to the server. + Command name should be stateless (should not use 'this' keyword) */ + abstract get commandName(): string; + abstract execute(server: Server, session: ClientSession | undefined): Promise; hasAspect(aspect: symbol): boolean { diff --git a/src/operations/options_operation.ts b/src/operations/options_operation.ts index e985cc0973..c480fe077d 100644 --- a/src/operations/options_operation.ts +++ b/src/operations/options_operation.ts @@ -15,6 +15,9 @@ export class OptionsOperation extends AbstractOperation { this.options = options; this.collection = collection; } + override get commandName() { + return 'listCollections' as const; + } override async execute(server: Server, session: ClientSession | undefined): Promise { const coll = this.collection; diff --git a/src/operations/profiling_level.ts b/src/operations/profiling_level.ts index f96f030376..383062c2a4 100644 --- a/src/operations/profiling_level.ts +++ b/src/operations/profiling_level.ts @@ -16,6 +16,10 @@ export class ProfilingLevelOperation extends CommandOperation { this.options = options; } + override get commandName() { + return 'profile' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const doc = await super.executeCommand(server, session, { profile: -1 }); if (doc.ok === 1) { diff --git a/src/operations/remove_user.ts b/src/operations/remove_user.ts index 042c9925e0..ced8e4e1ca 100644 --- a/src/operations/remove_user.ts +++ b/src/operations/remove_user.ts @@ -18,6 +18,10 @@ export class RemoveUserOperation extends CommandOperation { this.username = username; } + override get commandName() { + return 'dropUser' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { await super.executeCommand(server, session, { dropUser: this.username }); return true; diff --git a/src/operations/rename.ts b/src/operations/rename.ts index ec5ee7c8af..a27d4afe45 100644 --- a/src/operations/rename.ts +++ b/src/operations/rename.ts @@ -25,6 +25,10 @@ export class RenameOperation extends CommandOperation { this.ns = new MongoDBNamespace('admin', '$cmd'); } + override get commandName(): string { + return 'renameCollection' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { // Build the command const renameCollection = this.collection.namespace; diff --git a/src/operations/run_command.ts b/src/operations/run_command.ts index a0e306e6c6..d89d2e229c 100644 --- a/src/operations/run_command.ts +++ b/src/operations/run_command.ts @@ -22,6 +22,10 @@ export class RunCommandOperation extends AbstractOperation { this.ns = parent.s.namespace.withCollection('$cmd'); } + override get commandName() { + return 'runCommand' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { this.server = server; return server.commandAsync(this.ns, this.command, { @@ -44,6 +48,10 @@ export class RunAdminCommandOperation extends AbstractOperation this.ns = new MongoDBNamespace('admin', '$cmd'); } + override get commandName() { + return 'runCommand' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { this.server = server; return server.commandAsync(this.ns, this.command, { diff --git a/src/operations/search_indexes/create.ts b/src/operations/search_indexes/create.ts index 1dedfac5cb..96b38a159b 100644 --- a/src/operations/search_indexes/create.ts +++ b/src/operations/search_indexes/create.ts @@ -25,6 +25,10 @@ export class CreateSearchIndexesOperation extends AbstractOperation { super(); } + override get commandName() { + return 'createSearchIndexes' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const namespace = this.collection.fullNamespace; const command = { diff --git a/src/operations/search_indexes/drop.ts b/src/operations/search_indexes/drop.ts index 5f29308f86..f17e3365e6 100644 --- a/src/operations/search_indexes/drop.ts +++ b/src/operations/search_indexes/drop.ts @@ -12,6 +12,10 @@ export class DropSearchIndexOperation extends AbstractOperation { super(); } + override get commandName() { + return 'dropSearchIndex' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const namespace = this.collection.fullNamespace; diff --git a/src/operations/search_indexes/update.ts b/src/operations/search_indexes/update.ts index a490740ee4..fde2230a7c 100644 --- a/src/operations/search_indexes/update.ts +++ b/src/operations/search_indexes/update.ts @@ -15,6 +15,10 @@ export class UpdateSearchIndexOperation extends AbstractOperation { super(); } + override get commandName() { + return 'updateSearchIndex' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const namespace = this.collection.fullNamespace; const command = { diff --git a/src/operations/set_profiling_level.ts b/src/operations/set_profiling_level.ts index 9c738f2057..9969b2ea3c 100644 --- a/src/operations/set_profiling_level.ts +++ b/src/operations/set_profiling_level.ts @@ -47,6 +47,10 @@ export class SetProfilingLevelOperation extends CommandOperation this.level = level; } + override get commandName() { + return 'profile' as const; + } + override async execute( server: Server, session: ClientSession | undefined diff --git a/src/operations/stats.ts b/src/operations/stats.ts index 4717e3eb38..11b87f25ae 100644 --- a/src/operations/stats.ts +++ b/src/operations/stats.ts @@ -20,6 +20,10 @@ export class DbStatsOperation extends CommandOperation { this.options = options; } + override get commandName() { + return 'dbStats' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const command: Document = { dbStats: true }; if (this.options.scale != null) { diff --git a/src/operations/update.ts b/src/operations/update.ts index fc03424cbc..b1f73f837b 100644 --- a/src/operations/update.ts +++ b/src/operations/update.ts @@ -79,6 +79,10 @@ export class UpdateOperation extends CommandOperation { this.statements = statements; } + override get commandName() { + return 'update' as const; + } + override get canRetryWrite(): boolean { if (super.canRetryWrite === false) { return false; diff --git a/src/operations/validate_collection.ts b/src/operations/validate_collection.ts index 866e3df575..4880a703a7 100644 --- a/src/operations/validate_collection.ts +++ b/src/operations/validate_collection.ts @@ -33,6 +33,10 @@ export class ValidateCollectionOperation extends CommandOperation { this.collectionName = collectionName; } + override get commandName() { + return 'validate' as const; + } + override async execute(server: Server, session: ClientSession | undefined): Promise { const collectionName = this.collectionName; diff --git a/test/integration/crud/abstract_operation.test.ts b/test/integration/crud/abstract_operation.test.ts new file mode 100644 index 0000000000..5e70c531c9 --- /dev/null +++ b/test/integration/crud/abstract_operation.test.ts @@ -0,0 +1,371 @@ +import { expect } from 'chai'; +import * as sinon from 'sinon'; + +import { executeOperation, Long, Server } from '../../mongodb'; +import * as mongodb from '../../mongodb'; +import { topologyWithPlaceholderClient } from '../../tools/utils'; + +describe('abstract operation', async function () { + describe('command name getter', async function () { + interface AbstractOperationSubclasses { + subclassCreator: () => mongodb.AbstractOperation; + subclassType: any; + correctCommandName: string; + } + + const WrapperandServerlessSubclasses = [ + 'RunAdminCommandOperation', + 'RunCommandOperation', + 'OptionsOperation', + 'IsCappedOperation', + 'BulkWriteOperation', + 'IndexExistsOperation', + 'IndexOperation', + 'CollectionsOperation', + 'IndexInformationOperation' + ]; + + const sameServerOnlyOperationSubclasses = ['GetMoreOperation', 'KillCursorsOperation']; + + let client; + let db; + let admin; + let collection; + let constructorServer; + const subclassArray: AbstractOperationSubclasses[] = [ + { + subclassCreator: () => + new mongodb.AggregateOperation(collection.fullNamespace, [{ a: 1 }], {}), + subclassType: mongodb.AggregateOperation, + correctCommandName: 'aggregate' + }, + { + subclassCreator: () => + new mongodb.BulkWriteOperation(collection, [{ insertOne: { document: { a: 1 } } }], {}), + subclassType: mongodb.BulkWriteOperation, + correctCommandName: 'bulkWrite' + }, + { + subclassCreator: () => new mongodb.CollectionsOperation(db, {}), + subclassType: mongodb.CollectionsOperation, + correctCommandName: 'listCollections' + }, + { + subclassCreator: () => new mongodb.CountOperation(collection.fullNamespace, { a: 1 }, {}), + subclassType: mongodb.CountOperation, + correctCommandName: 'count' + }, + { + subclassCreator: () => new mongodb.CountDocumentsOperation(collection, { a: 1 }, {}), + subclassType: mongodb.CountDocumentsOperation, + correctCommandName: 'aggregate' + }, + { + subclassCreator: () => new mongodb.CreateCollectionOperation(db, 'name'), + subclassType: mongodb.CreateCollectionOperation, + correctCommandName: 'create' + }, + { + subclassCreator: () => + new mongodb.DeleteOperation(collection.fullNamespace, [{ q: { a: 1 }, limit: 1 }], {}), + subclassType: mongodb.DeleteOperation, + correctCommandName: 'delete' + }, + { + subclassCreator: () => + new mongodb.DeleteOneOperation(collection, [{ q: { a: 1 }, limit: 1 }], {}), + subclassType: mongodb.DeleteOneOperation, + correctCommandName: 'delete' + }, + { + subclassCreator: () => + new mongodb.DeleteManyOperation(collection, [{ q: { a: 1 }, limit: 1 }], {}), + subclassType: mongodb.DeleteManyOperation, + correctCommandName: 'delete' + }, + { + subclassCreator: () => new mongodb.DistinctOperation(collection, 'a', { a: 1 }), + subclassType: mongodb.DistinctOperation, + correctCommandName: 'distinct' + }, + { + subclassCreator: () => new mongodb.DropCollectionOperation(db, 'collectionName', {}), + subclassType: mongodb.DropCollectionOperation, + correctCommandName: 'drop' + }, + { + subclassCreator: () => new mongodb.DropDatabaseOperation(db, {}), + subclassType: mongodb.DropDatabaseOperation, + correctCommandName: 'dropDatabase' + }, + { + subclassCreator: () => new mongodb.EstimatedDocumentCountOperation(collection, {}), + subclassType: mongodb.EstimatedDocumentCountOperation, + correctCommandName: 'count' + }, + { + subclassCreator: () => new mongodb.FindOperation(collection, collection.fullNamespace), + subclassType: mongodb.FindOperation, + correctCommandName: 'find' + }, + { + subclassCreator: () => new mongodb.FindAndModifyOperation(collection, { a: 1 }, {}), + subclassType: mongodb.FindAndModifyOperation, + correctCommandName: 'findAndModify' + }, + { + subclassCreator: () => new mongodb.FindOneAndDeleteOperation(collection, { a: 1 }, {}), + subclassType: mongodb.FindOneAndDeleteOperation, + correctCommandName: 'findAndModify' + }, + { + subclassCreator: () => + new mongodb.FindOneAndReplaceOperation(collection, { a: 2 }, { a: 1 }, {}), + subclassType: mongodb.FindOneAndReplaceOperation, + correctCommandName: 'findAndModify' + }, + { + subclassCreator: () => + new mongodb.FindOneAndUpdateOperation(collection, { a: 2 }, { $a: 1 }, {}), + subclassType: mongodb.FindOneAndUpdateOperation, + correctCommandName: 'findAndModify' + }, + { + subclassCreator: () => + new mongodb.GetMoreOperation( + collection.fullNamespace, + Long.fromNumber(1), + constructorServer, + {} + ), + subclassType: mongodb.GetMoreOperation, + correctCommandName: 'getMore' + }, + { + subclassCreator: () => new mongodb.IndexesOperation(collection, {}), + subclassType: mongodb.IndexesOperation, + correctCommandName: 'listIndexes' + }, + { + subclassCreator: () => new mongodb.CreateIndexesOperation(db, 'bar', [{ key: { a: 1 } }]), + subclassType: mongodb.CreateIndexesOperation, + correctCommandName: 'createIndexes' + }, + { + subclassCreator: () => + new mongodb.CreateIndexOperation(db, 'collectionName', 'indexDescription'), + subclassType: mongodb.CreateIndexOperation, + correctCommandName: 'createIndexes' + }, + { + subclassCreator: () => + new mongodb.EnsureIndexOperation(db, 'collectionName', 'indexDescription'), + subclassType: mongodb.EnsureIndexOperation, + correctCommandName: 'listIndexes' + }, + { + subclassCreator: () => new mongodb.DropIndexOperation(collection, 'a', {}), + subclassType: mongodb.DropIndexOperation, + correctCommandName: 'dropIndexes' + }, + { + subclassCreator: () => new mongodb.ListIndexesOperation(collection, {}), + subclassType: mongodb.ListIndexesOperation, + correctCommandName: 'listIndexes' + }, + { + subclassCreator: () => new mongodb.IndexExistsOperation(collection, 'a', {}), + subclassType: mongodb.IndexExistsOperation, + correctCommandName: 'listIndexes' + }, + { + subclassCreator: () => new mongodb.IndexInformationOperation(db, 'a', {}), + subclassType: mongodb.IndexInformationOperation, + correctCommandName: 'listIndexes' + }, + { + subclassCreator: () => + new mongodb.InsertOperation(collection.fullNamespace, [{ a: 1 }], {}), + subclassType: mongodb.InsertOperation, + correctCommandName: 'insert' + }, + { + subclassCreator: () => new mongodb.InsertOneOperation(collection, { a: 1 }, {}), + subclassType: mongodb.InsertOneOperation, + correctCommandName: 'insert' + }, + { + subclassCreator: () => new mongodb.InsertManyOperation(collection, [{ a: 1 }], {}), + subclassType: mongodb.InsertManyOperation, + correctCommandName: 'insert' + }, + { + subclassCreator: () => new mongodb.IsCappedOperation(collection, {}), + subclassType: mongodb.IsCappedOperation, + correctCommandName: 'listCollections' + }, + { + subclassCreator: () => + new mongodb.KillCursorsOperation( + Long.fromNumber(1), + collection.fullNamespace, + constructorServer, + {} + ), + subclassType: mongodb.KillCursorsOperation, + correctCommandName: 'killCursors' + }, + { + subclassCreator: () => new mongodb.ListCollectionsOperation(db, { a: 1 }, {}), + subclassType: mongodb.ListCollectionsOperation, + correctCommandName: 'listCollections' + }, + { + subclassCreator: () => new mongodb.ListDatabasesOperation(db, {}), + subclassType: mongodb.ListDatabasesOperation, + correctCommandName: 'listDatabases' + }, + { + subclassCreator: () => new mongodb.OptionsOperation(collection, {}), + subclassType: mongodb.OptionsOperation, + correctCommandName: 'listCollections' + }, + { + subclassCreator: () => new mongodb.ProfilingLevelOperation(db, {}), + subclassType: mongodb.ProfilingLevelOperation, + correctCommandName: 'profile' + }, + { + subclassCreator: () => new mongodb.RemoveUserOperation(db, 'userToDrop', {}), + subclassType: mongodb.RemoveUserOperation, + correctCommandName: 'dropUser' + }, + { + subclassCreator: () => new mongodb.RenameOperation(collection, 'newName', {}), + subclassType: mongodb.RenameOperation, + correctCommandName: 'renameCollection' + }, + { + subclassCreator: () => + new mongodb.RunCommandOperation(db, { dummyCommand: 'dummyCommand' }, {}), + subclassType: mongodb.RunCommandOperation, + correctCommandName: 'runCommand' + }, + { + subclassCreator: () => + new mongodb.RunAdminCommandOperation({ dummyCommand: 'dummyCommand' }, {}), + subclassType: mongodb.RunAdminCommandOperation, + correctCommandName: 'runCommand' + }, + { + subclassCreator: () => + new mongodb.CreateSearchIndexesOperation(collection, [{ definition: { a: 1 } }]), + subclassType: mongodb.CreateSearchIndexesOperation, + correctCommandName: 'createSearchIndexes' + }, + { + subclassCreator: () => new mongodb.DropSearchIndexOperation(collection, 'dummyName'), + subclassType: mongodb.DropSearchIndexOperation, + correctCommandName: 'dropSearchIndex' + }, + { + subclassCreator: () => + new mongodb.UpdateSearchIndexOperation(collection, 'dummyName', { + a: 1 + }), + subclassType: mongodb.UpdateSearchIndexOperation, + correctCommandName: 'updateSearchIndex' + }, + { + subclassCreator: () => new mongodb.SetProfilingLevelOperation(db, 'all', {}), + subclassType: mongodb.SetProfilingLevelOperation, + correctCommandName: 'profile' + }, + { + subclassCreator: () => new mongodb.DbStatsOperation(db, {}), + subclassType: mongodb.DbStatsOperation, + correctCommandName: 'dbStats' + }, + { + subclassCreator: () => + new mongodb.UpdateOperation( + collection.fullNamespace, + [{ q: { a: 1 }, u: { $a: 2 } }], + {} + ), + subclassType: mongodb.UpdateOperation, + correctCommandName: 'update' + }, + { + subclassCreator: () => new mongodb.UpdateOneOperation(collection, { a: 1 }, { $a: 2 }, {}), + subclassType: mongodb.UpdateOneOperation, + correctCommandName: 'update' + }, + { + subclassCreator: () => new mongodb.UpdateManyOperation(collection, { a: 1 }, { $a: 2 }, {}), + subclassType: mongodb.UpdateManyOperation, + correctCommandName: 'update' + }, + { + subclassCreator: () => new mongodb.ReplaceOneOperation(collection, { a: 1 }, { b: 1 }, {}), + subclassType: mongodb.ReplaceOneOperation, + correctCommandName: 'update' + }, + { + subclassCreator: () => new mongodb.ValidateCollectionOperation(admin, 'bar', {}), + subclassType: mongodb.ValidateCollectionOperation, + correctCommandName: 'validate' + } + ]; + + beforeEach(async function () { + client = this.configuration.newClient(); + db = client.db('foo'); + admin = client.db().admin(); + collection = db.collection('bar'); + constructorServer = new Server( + topologyWithPlaceholderClient([], {} as any), + new mongodb.ServerDescription('a:1'), + {} as any + ); + }); + + afterEach(async function () { + db = undefined; + collection = undefined; + constructorServer = undefined; + admin = undefined; + await client.close(); + sinon.restore(); + }); + + for (const { subclassCreator, subclassType, correctCommandName } of subclassArray) { + context(`when subclass is ${subclassType.name}`, async function () { + it(`operation.commandName equals correct string`, async function () { + const subclassInstance = subclassCreator(); + expect(subclassInstance.commandName).to.equal(correctCommandName); + }); + + if (!WrapperandServerlessSubclasses.includes(subclassType.name.toString())) { + it(`operation.commandName equals key in command document`, async function () { + const subclassInstance = subclassCreator(); + const yieldDoc = + subclassType.name === 'ProfilingLevelOperation' ? { ok: 1, was: 1 } : { ok: 1 }; + const cmdCallerStub = sinon + .stub(Server.prototype, 'command') + .yieldsRight(undefined, yieldDoc); + if (sameServerOnlyOperationSubclasses.includes(subclassType.name.toString())) { + await subclassInstance.execute(constructorServer, client.session); + } else { + await executeOperation(client, subclassInstance); + } + expect(cmdCallerStub).to.have.been.calledWith( + sinon.match.any, + sinon.match.hasOwn(subclassInstance.commandName) + ); + }); + } + }); + } + }); +}); diff --git a/test/mongodb.ts b/test/mongodb.ts index df85d2cd64..a530383848 100644 --- a/test/mongodb.ts +++ b/test/mongodb.ts @@ -180,6 +180,9 @@ export * from '../src/operations/profiling_level'; export * from '../src/operations/remove_user'; export * from '../src/operations/rename'; export * from '../src/operations/run_command'; +export * from '../src/operations/search_indexes/create'; +export * from '../src/operations/search_indexes/drop'; +export * from '../src/operations/search_indexes/update'; export * from '../src/operations/set_profiling_level'; export * from '../src/operations/stats'; export * from '../src/operations/update';