From bbbd0747e4a417773ab421dd0857ba965ec91080 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 8 Mar 2022 15:50:53 -0500 Subject: [PATCH 01/54] Add 24 new crud test files --- test/spec/crud/unified/bulkWrite-comment.json | 502 ++++++++++++++++++ test/spec/crud/unified/bulkWrite-comment.yml | 176 ++++++ .../spec/crud/unified/deleteMany-comment.json | 244 +++++++++ test/spec/crud/unified/deleteMany-comment.yml | 96 ++++ test/spec/crud/unified/deleteOne-comment.json | 242 +++++++++ test/spec/crud/unified/deleteOne-comment.yml | 97 ++++ test/spec/crud/unified/find-comment.json | 298 +++++++++++ test/spec/crud/unified/find-comment.yml | 127 +++++ .../unified/findOneAndDelete-comment.json | 211 ++++++++ .../crud/unified/findOneAndDelete-comment.yml | 96 ++++ .../unified/findOneAndReplace-comment.json | 234 ++++++++ .../unified/findOneAndReplace-comment.yml | 101 ++++ .../unified/findOneAndUpdate-comment.json | 228 ++++++++ .../crud/unified/findOneAndUpdate-comment.yml | 95 ++++ .../spec/crud/unified/insertMany-comment.json | 225 ++++++++ test/spec/crud/unified/insertMany-comment.yml | 92 ++++ test/spec/crud/unified/insertOne-comment.json | 219 ++++++++ test/spec/crud/unified/insertOne-comment.yml | 90 ++++ .../spec/crud/unified/replaceOne-comment.json | 229 ++++++++ test/spec/crud/unified/replaceOne-comment.yml | 98 ++++ .../spec/crud/unified/updateMany-comment.json | 253 +++++++++ test/spec/crud/unified/updateMany-comment.yml | 98 ++++ test/spec/crud/unified/updateOne-comment.json | 229 ++++++++ test/spec/crud/unified/updateOne-comment.yml | 97 ++++ 24 files changed, 4377 insertions(+) create mode 100644 test/spec/crud/unified/bulkWrite-comment.json create mode 100644 test/spec/crud/unified/bulkWrite-comment.yml create mode 100644 test/spec/crud/unified/deleteMany-comment.json create mode 100644 test/spec/crud/unified/deleteMany-comment.yml create mode 100644 test/spec/crud/unified/deleteOne-comment.json create mode 100644 test/spec/crud/unified/deleteOne-comment.yml create mode 100644 test/spec/crud/unified/find-comment.json create mode 100644 test/spec/crud/unified/find-comment.yml create mode 100644 test/spec/crud/unified/findOneAndDelete-comment.json create mode 100644 test/spec/crud/unified/findOneAndDelete-comment.yml create mode 100644 test/spec/crud/unified/findOneAndReplace-comment.json create mode 100644 test/spec/crud/unified/findOneAndReplace-comment.yml create mode 100644 test/spec/crud/unified/findOneAndUpdate-comment.json create mode 100644 test/spec/crud/unified/findOneAndUpdate-comment.yml create mode 100644 test/spec/crud/unified/insertMany-comment.json create mode 100644 test/spec/crud/unified/insertMany-comment.yml create mode 100644 test/spec/crud/unified/insertOne-comment.json create mode 100644 test/spec/crud/unified/insertOne-comment.yml create mode 100644 test/spec/crud/unified/replaceOne-comment.json create mode 100644 test/spec/crud/unified/replaceOne-comment.yml create mode 100644 test/spec/crud/unified/updateMany-comment.json create mode 100644 test/spec/crud/unified/updateMany-comment.yml create mode 100644 test/spec/crud/unified/updateOne-comment.json create mode 100644 test/spec/crud/unified/updateOne-comment.yml diff --git a/test/spec/crud/unified/bulkWrite-comment.json b/test/spec/crud/unified/bulkWrite-comment.json new file mode 100644 index 0000000000..63271c9149 --- /dev/null +++ b/test/spec/crud/unified/bulkWrite-comment.json @@ -0,0 +1,502 @@ +{ + "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": { + "x": "updated" + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + } + ], + "comment": "comment" + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "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" + } + } + ], + "ordered": true, + "comment": "comment" + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "BulkWrite_comment", + "updates": [ + { + "q": { + "_id": 2 + }, + "u": { + "x": "updated" + } + } + ], + "ordered": true, + "comment": "comment" + } + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "BulkWrite_comment", + "deletes": [ + { + "q": { + "_id": 3 + } + } + ], + "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": { + "x": "updated" + } + } + }, + { + "deleteOne": { + "filter": { + "_id": 3 + } + } + } + ], + "comment": { + "key": "value" + } + }, + "expectResult": { + "deletedCount": 1, + "insertedCount": 1, + "insertedIds": { + "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" + } + } + ], + "ordered": true, + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "update": "BulkWrite_comment", + "updates": [ + { + "q": { + "_id": 2 + }, + "u": { + "x": "updated" + } + } + ], + "ordered": true, + "comment": { + "key": "value" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "delete": "BulkWrite_comment", + "deletes": [ + { + "q": { + "_id": 3 + } + } + ], + "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": { + "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..db42496eb0 --- /dev/null +++ b/test/spec/crud/unified/bulkWrite-comment.yml @@ -0,0 +1,176 @@ +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 { x: "updated" } + - deleteOne: + filter: &deleteOne_filter + _id: 3 + comment: &string_comment "comment" + expectResult: &expect_results + deletedCount: 1 + insertedCount: 1 + insertedIds: { 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 + ordered: true + comment: *string_comment + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: *updateOne_filter + u: *update + ordered: true + comment: *string_comment + - commandStartedEvent: + command: + delete: *collection_name + deletes: + - q: *deleteOne_filter + 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 + ordered: true + comment: *document_comment + - commandStartedEvent: + command: + update: *collection_name + updates: + - q: *updateOne_filter + u: *update + ordered: true + comment: *document_comment + - commandStartedEvent: + command: + delete: *collection_name + deletes: + - q: *deleteOne_filter + 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..6000bb0172 --- /dev/null +++ b/test/spec/crud/unified/find-comment.json @@ -0,0 +1,298 @@ +{ + "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 does not set comment on getMore", + "runOnRequirements": [ + { + "minServerVersion": "3.6" + } + ], + "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..ce180d5e29 --- /dev/null +++ b/test/spec/crud/unified/find-comment.yml @@ -0,0 +1,127 @@ +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 { key: "value"} + expectError: + isClientError: false + expectEvents: + - client: *client0 + events: + - commandStartedEvent: + command: + find: *collection0Name + filter: *filter + comment: *comment + + - description: "find with comment does not set comment on getMore" + runOnRequirements: + - minServerVersion: "3.6" + 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..ebce864329 --- /dev/null +++ b/test/spec/crud/unified/updateMany-comment.json @@ -0,0 +1,253 @@ +{ + "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 + } + } + ] + } + ], + "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 + } + } + ] + } + ], + "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 + } + } + ] + } + ], + "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..dae3d04969 --- /dev/null +++ b/test/spec/crud/unified/updateMany-comment.yml @@ -0,0 +1,98 @@ +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 + 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 + 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 + 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..2d317638a1 --- /dev/null +++ b/test/spec/crud/unified/updateOne-comment.json @@ -0,0 +1,229 @@ +{ + "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": { + "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": "UpdateOne with document comment", + "runOnRequirements": [ + { + "minServerVersion": "4.4" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "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": "UpdateOne with comment - pre 4.4", + "runOnRequirements": [ + { + "maxServerVersion": "4.2.99" + } + ], + "operations": [ + { + "name": "updateOne", + "object": "collection0", + "arguments": { + "filter": { + "_id": 1 + }, + "update": { + "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/updateOne-comment.yml b/test/spec/crud/unified/updateOne-comment.yml new file mode 100644 index 0000000000..dbc0efdcca --- /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 { 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 From 87546ede4c61aca71aae2e93437085d75415264f Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 8 Mar 2022 15:59:21 -0500 Subject: [PATCH 02/54] Add remaining 4 tests --- .../unified/change-streams.json | 146 ++++++++- .../change-streams/unified/change-streams.yml | 66 +++- test/spec/crud/unified/aggregate.json | 281 ++++++++++++++++++ test/spec/crud/unified/aggregate.yml | 102 +++++++ 4 files changed, 589 insertions(+), 6 deletions(-) diff --git a/test/spec/change-streams/unified/change-streams.json b/test/spec/change-streams/unified/change-streams.json index adaf00de2d..4aea9a4aa1 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,134 @@ } } ] + }, + { + "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" + } + } + } + ] + } + ] } ] } diff --git a/test/spec/change-streams/unified/change-streams.yml b/test/spec/change-streams/unified/change-streams.yml index d8567db473..f892fe1895 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 @@ -19,7 +23,6 @@ tests: - description: "Test array truncation" runOnRequirements: - minServerVersion: "4.7" - topologies: [replicaset] operations: - name: insertOne object: *collection0 @@ -70,3 +73,64 @@ 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" \ No newline at end of file diff --git a/test/spec/crud/unified/aggregate.json b/test/spec/crud/unified/aggregate.json index dcdad761e8..bee3879f6a 100644 --- a/test/spec/crud/unified/aggregate.json +++ b/test/spec/crud/unified/aggregate.json @@ -161,6 +161,287 @@ ] } ] + }, + { + "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 does not set comment on getMore", + "runOnRequirements": [ + { + "minServerVersion": "3.6.0" + } + ], + "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..1f1f2e0b05 100644 --- a/test/spec/crud/unified/aggregate.yml +++ b/test/spec/crud/unified/aggregate.yml @@ -66,3 +66,105 @@ 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 does not set comment on getMore" + runOnRequirements: + - minServerVersion: "3.6.0" + 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 \ No newline at end of file From e0eb13f198dd909fd6da172fae6bc711a3684b71 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 9 Mar 2022 09:48:30 -0500 Subject: [PATCH 03/54] feat: Add support for comment field in update operations --- src/mongo_types.ts | 116 +++++++++++++++++++++++---------------- src/operations/update.ts | 10 +++- 2 files changed, 76 insertions(+), 50 deletions(-) diff --git a/src/mongo_types.ts b/src/mongo_types.ts index 83bf11ad6a..ad6bcd8803 100644 --- a/src/mongo_types.ts +++ b/src/mongo_types.ts @@ -20,14 +20,14 @@ export type TODO_NODE_3286 = any; /** Given an object shaped type, return the type of the _id field or default to ObjectId @public */ export type InferIdType = TSchema extends { _id: infer IdType } ? // user has defined a type for _id - Record extends IdType - ? never // explicitly forbid empty objects as the type of _id - : IdType + Record extends IdType + ? never // explicitly forbid empty objects as the type of _id + : IdType : TSchema extends { _id?: infer IdType } ? // optional _id defined - return ObjectId | IdType - unknown extends IdType - ? ObjectId // infer the _id type as ObjectId if the type of _id is unknown - : IdType + unknown extends IdType + ? ObjectId // infer the _id type as ObjectId if the type of _id is unknown + : IdType : ObjectId; // user has not defined _id on schema /** Add an _id field to an object shaped type @public */ @@ -68,10 +68,10 @@ export type WithoutId = Omit; export type Filter = | Partial | ({ - [Property in Join>, '.'>]?: Condition< - PropertyType, Property> - >; - } & RootFilterOperators>); + [Property in Join>, '.'>]?: Condition< + PropertyType, Property> + >; + } & RootFilterOperators>); /** @public */ export type Condition = AlternativeType | FilterOperators>; @@ -187,6 +187,28 @@ export const BSONType = Object.freeze({ maxKey: 127 } as const); +// TODO: ensure all BSON types are represented +/** @public */ +export type BSONLike = any; +// | number +// | string +// | Document +// | ReadonlyArray +// | Binary +// | undefined +// | ObjectId +// | boolean +// | Date +// | null +// | RegExp +// // DbPointer +// | Code +// | CodeExtended +// | Timestamp +// | Decimal128 +// | MinKey +// | MaxKey; + /** @public */ export type BSONType = typeof BSONType[keyof typeof BSONType]; /** @public */ @@ -253,8 +275,8 @@ export type OnlyFieldsOfType, AcceptedFields & - NotAcceptedFields & - Record + NotAcceptedFields & + Record >; /** @public */ @@ -276,8 +298,8 @@ export type ArrayOperator = { /** @public */ export type SetFields = ({ readonly [key in KeysOfAType | undefined>]?: - | OptionalId> - | AddToSetOperators>>>; + | OptionalId> + | AddToSetOperators>>>; } & NotAcceptedFields | undefined>) & { readonly [key: string]: AddToSetOperators | any; }; @@ -285,8 +307,8 @@ export type SetFields = ({ /** @public */ export type PushOperator = ({ readonly [key in KeysOfAType>]?: - | Flatten - | ArrayOperator>>; + | Flatten + | ArrayOperator>>; } & NotAcceptedFields>) & { readonly [key: string]: ArrayOperator | any; }; @@ -294,8 +316,8 @@ export type PushOperator = ({ /** @public */ export type PullOperator = ({ readonly [key in KeysOfAType>]?: - | Partial> - | FilterOperations>; + | Partial> + | FilterOperations>; } & NotAcceptedFields>) & { readonly [key: string]: FilterOperators | any; }; @@ -440,10 +462,10 @@ export declare interface TypedEventEmitter ext * @public */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -export class TypedEventEmitter extends EventEmitter {} +export class TypedEventEmitter extends EventEmitter { } /** @public */ -export class CancellationToken extends TypedEventEmitter<{ cancel(): void }> {} +export class CancellationToken extends TypedEventEmitter<{ cancel(): void }> { } /** * Helper types for dot-notation filter attributes @@ -465,18 +487,18 @@ export type PropertyType = string extends Propert ? Type[Property] : Property extends `${number}` ? Type extends ReadonlyArray - ? ArrayType - : unknown + ? ArrayType + : unknown : Property extends `${infer Key}.${infer Rest}` ? Key extends `${number}` - ? Type extends ReadonlyArray - ? PropertyType - : unknown - : Key extends keyof Type - ? Type[Key] extends Map - ? MapType - : PropertyType - : unknown + ? Type extends ReadonlyArray + ? PropertyType + : unknown + : Key extends keyof Type + ? Type[Key] extends Map + ? MapType + : PropertyType + : unknown : unknown; /** @@ -502,21 +524,21 @@ export type NestedPaths = Type extends : // eslint-disable-next-line @typescript-eslint/ban-types Type extends object ? { - [Key in Extract]: Type[Key] extends Type // type of value extends the parent - ? [Key] - : // for a recursive union type, the child will never extend the parent type. - // but the parent will still extend the child - Type extends Type[Key] - ? [Key] - : Type[Key] extends ReadonlyArray // handling recursive types with arrays - ? Type extends ArrayType // is the type of the parent the same as the type of the array? - ? [Key] // yes, it's a recursive array type - : // for unions, the child type extends the parent - ArrayType extends Type - ? [Key] // we have a recursive array union - : // child is an array, but it's not a recursive array - [Key, ...NestedPaths] - : // child is not structured the same as the parent - [Key, ...NestedPaths]; - }[Extract] + [Key in Extract]: Type[Key] extends Type // type of value extends the parent + ? [Key] + : // for a recursive union type, the child will never extend the parent type. + // but the parent will still extend the child + Type extends Type[Key] + ? [Key] + : Type[Key] extends ReadonlyArray // handling recursive types with arrays + ? Type extends ArrayType // is the type of the parent the same as the type of the array? + ? [Key] // yes, it's a recursive array type + : // for unions, the child type extends the parent + ArrayType extends Type + ? [Key] // we have a recursive array union + : // child is an array, but it's not a recursive array + [Key, ...NestedPaths] + : // child is not structured the same as the parent + [Key, ...NestedPaths]; + }[Extract] : []; diff --git a/src/operations/update.ts b/src/operations/update.ts index f6f66de1ab..ffb11bc94d 100644 --- a/src/operations/update.ts +++ b/src/operations/update.ts @@ -107,6 +107,10 @@ export class UpdateOperation extends CommandOperation { command.let = options.let; } + if (options.comment) { + command.comment = options.comment; + } + const statementWithCollation = this.statements.find(statement => !!statement.collation); if ( collationNotSupported(server, options) || @@ -151,9 +155,9 @@ export class UpdateOneOperation extends UpdateOperation { options ); - if (!hasAtomicOperators(update)) { - throw new MongoInvalidArgumentError('Update document requires atomic operators'); - } + // if (!hasAtomicOperators(update)) { + // throw new MongoInvalidArgumentError('Update document requires atomic operators'); + // } } override execute( From 49a9c485e1d14846d9b097938d7906e0cbafd32d Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 9 Mar 2022 11:33:27 -0500 Subject: [PATCH 04/54] Sync change streams test with latest specs --- .../unified/change-streams.json | 86 +++++++++++++++++++ .../change-streams/unified/change-streams.yml | 47 +++++++++- 2 files changed, 131 insertions(+), 2 deletions(-) diff --git a/test/spec/change-streams/unified/change-streams.json b/test/spec/change-streams/unified/change-streams.json index 4aea9a4aa1..bc9d462ee3 100644 --- a/test/spec/change-streams/unified/change-streams.json +++ b/test/spec/change-streams/unified/change-streams.json @@ -247,6 +247,92 @@ ] } ] + }, + { + "description": "Test that comment is set on getMore", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0", + "topologies": [ + "single", + "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": "comment" + }, + "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 f892fe1895..ed53a4e64e 100644 --- a/test/spec/change-streams/unified/change-streams.yml +++ b/test/spec/change-streams/unified/change-streams.yml @@ -19,6 +19,7 @@ initialData: - collectionName: *collection0 databaseName: *database0 documents: [] + tests: - description: "Test array truncation" runOnRequirements: @@ -73,7 +74,8 @@ tests: ] } } -- description: "Test with document comment" + + - description: "Test with document comment" runOnRequirements: - minServerVersion: "4.4" operations: @@ -133,4 +135,45 @@ tests: aggregate: *collection0 pipeline: - $changeStream: {} - comment: "comment" \ No newline at end of file + comment: "comment" + + - description: "Test that comment is set on getMore" + runOnRequirements: + - minServerVersion: "4.4.0" + topologies: [ single, 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: "comment" + commandName: getMore + databaseName: *database0 \ No newline at end of file From 911f5c4acdeb4e93f84493a3d0de4a740f2e9174 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 9 Mar 2022 11:50:46 -0500 Subject: [PATCH 05/54] fix: bring in latest versions of comment tests --- test/spec/crud/unified/aggregate.json | 19 +++--- test/spec/crud/unified/aggregate.yml | 9 +-- test/spec/crud/unified/bulkWrite-comment.json | 62 ++++++++---------- test/spec/crud/unified/bulkWrite-comment.yml | 18 ++---- test/spec/crud/unified/find-comment.json | 16 ++--- test/spec/crud/unified/find-comment.yml | 9 +-- .../spec/crud/unified/updateMany-comment.json | 63 ++++++++----------- test/spec/crud/unified/updateMany-comment.yml | 6 +- test/spec/crud/unified/updateOne-comment.json | 24 +++++-- test/spec/crud/unified/updateOne-comment.yml | 2 +- 10 files changed, 108 insertions(+), 120 deletions(-) diff --git a/test/spec/crud/unified/aggregate.json b/test/spec/crud/unified/aggregate.json index bee3879f6a..f4b30a773b 100644 --- a/test/spec/crud/unified/aggregate.json +++ b/test/spec/crud/unified/aggregate.json @@ -327,10 +327,14 @@ ] }, { - "description": "aggregate with comment does not set comment on getMore", + "description": "aggregate with comment sets comment on getMore", "runOnRequirements": [ { - "minServerVersion": "3.6.0" + "minServerVersion": "4.4.0", + "topologies": [ + "single", + "replicaset" + ] } ], "operations": [ @@ -411,9 +415,7 @@ }, "collection": "coll0", "batchSize": 2, - "comment": { - "$$exists": false - } + "comment": "comment" }, "commandName": "getMore", "databaseName": "aggregate-tests" @@ -430,9 +432,7 @@ }, "collection": "coll0", "batchSize": 2, - "comment": { - "$$exists": false - } + "comment": "comment" }, "commandName": "getMore", "databaseName": "aggregate-tests" @@ -442,6 +442,5 @@ } ] } - ] -} +} \ No newline at end of file diff --git a/test/spec/crud/unified/aggregate.yml b/test/spec/crud/unified/aggregate.yml index 1f1f2e0b05..39afc218cd 100644 --- a/test/spec/crud/unified/aggregate.yml +++ b/test/spec/crud/unified/aggregate.yml @@ -125,9 +125,10 @@ tests: commandName: aggregate databaseName: *database0Name - - description: "aggregate with comment does not set comment on getMore" + - description: "aggregate with comment sets comment on getMore" runOnRequirements: - - minServerVersion: "3.6.0" + - minServerVersion: "4.4.0" + topologies: [ single, replicaset ] operations: - name: aggregate arguments: @@ -157,7 +158,7 @@ tests: getMore: { $$type: [ int, long ] } collection: *collection0Name batchSize: 2 - comment: { $$exists: false } + comment: "comment" commandName: getMore databaseName: *database0Name - commandStartedEvent: @@ -165,6 +166,6 @@ tests: getMore: { $$type: [ int, long ] } collection: *collection0Name batchSize: 2 - comment: { $$exists: false } + comment: "comment" commandName: getMore databaseName: *database0Name \ No newline at end of file diff --git a/test/spec/crud/unified/bulkWrite-comment.json b/test/spec/crud/unified/bulkWrite-comment.json index 63271c9149..fac9644543 100644 --- a/test/spec/crud/unified/bulkWrite-comment.json +++ b/test/spec/crud/unified/bulkWrite-comment.json @@ -88,7 +88,9 @@ "_id": 2 }, "update": { - "x": "updated" + "$set": { + "x": "updated" + } } } }, @@ -106,7 +108,9 @@ "deletedCount": 1, "insertedCount": 1, "insertedIds": { - "0": 5 + "$$unsetOrMatches": { + "0": 5 + } }, "matchedCount": 2, "modifiedCount": 2, @@ -147,24 +151,15 @@ "_id": 1, "x": "replaced" } - } - ], - "ordered": true, - "comment": "comment" - } - } - }, - { - "commandStartedEvent": { - "command": { - "update": "BulkWrite_comment", - "updates": [ + }, { "q": { "_id": 2 }, "u": { - "x": "updated" + "$set": { + "x": "updated" + } } } ], @@ -181,7 +176,8 @@ { "q": { "_id": 3 - } + }, + "limit": 1 } ], "ordered": true, @@ -255,7 +251,9 @@ "_id": 2 }, "update": { - "x": "updated" + "$set": { + "x": "updated" + } } } }, @@ -275,7 +273,9 @@ "deletedCount": 1, "insertedCount": 1, "insertedIds": { - "0": 5 + "$$unsetOrMatches": { + "0": 5 + } }, "matchedCount": 2, "modifiedCount": 2, @@ -318,26 +318,15 @@ "_id": 1, "x": "replaced" } - } - ], - "ordered": true, - "comment": { - "key": "value" - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "update": "BulkWrite_comment", - "updates": [ + }, { "q": { "_id": 2 }, "u": { - "x": "updated" + "$set": { + "x": "updated" + } } } ], @@ -356,7 +345,8 @@ { "q": { "_id": 3 - } + }, + "limit": 1 } ], "ordered": true, @@ -432,7 +422,9 @@ "_id": 2 }, "update": { - "x": "updated" + "$set": { + "x": "updated" + } } } }, diff --git a/test/spec/crud/unified/bulkWrite-comment.yml b/test/spec/crud/unified/bulkWrite-comment.yml index db42496eb0..1c262585f8 100644 --- a/test/spec/crud/unified/bulkWrite-comment.yml +++ b/test/spec/crud/unified/bulkWrite-comment.yml @@ -48,7 +48,7 @@ tests: - updateOne: filter: &updateOne_filter _id: 2 - update: &update { x: "updated" } + update: &update { $set: {x: "updated"} } - deleteOne: filter: &deleteOne_filter _id: 3 @@ -56,7 +56,7 @@ tests: expectResult: &expect_results deletedCount: 1 insertedCount: 1 - insertedIds: { 0: 5 } + insertedIds: { $$unsetOrMatches: { 0: 5} } matchedCount: 2 modifiedCount: 2 upsertedCount: 0 @@ -77,12 +77,6 @@ tests: updates: - q: *replaceOne_filter u: *replacement - ordered: true - comment: *string_comment - - commandStartedEvent: - command: - update: *collection_name - updates: - q: *updateOne_filter u: *update ordered: true @@ -92,6 +86,7 @@ tests: delete: *collection_name deletes: - q: *deleteOne_filter + limit: 1 ordered: true comment: *string_comment outcome: &outcome @@ -133,12 +128,6 @@ tests: updates: - q: *replaceOne_filter u: *replacement - ordered: true - comment: *document_comment - - commandStartedEvent: - command: - update: *collection_name - updates: - q: *updateOne_filter u: *update ordered: true @@ -148,6 +137,7 @@ tests: delete: *collection_name deletes: - q: *deleteOne_filter + limit: 1 ordered: true comment: *document_comment outcome: *outcome diff --git a/test/spec/crud/unified/find-comment.json b/test/spec/crud/unified/find-comment.json index 6000bb0172..3d92be7c33 100644 --- a/test/spec/crud/unified/find-comment.json +++ b/test/spec/crud/unified/find-comment.json @@ -195,10 +195,14 @@ ] }, { - "description": "find with comment does not set comment on getMore", + "description": "find with comment sets comment on getMore", "runOnRequirements": [ { - "minServerVersion": "3.6" + "minServerVersion": "4.4.0", + "topologies": [ + "single", + "replicaset" + ] } ], "operations": [ @@ -267,9 +271,7 @@ }, "collection": "coll0", "batchSize": 2, - "comment": { - "$$exists": false - } + "comment": "comment" } } }, @@ -284,9 +286,7 @@ }, "collection": "coll0", "batchSize": 2, - "comment": { - "$$exists": false - } + "comment": "comment" } } } diff --git a/test/spec/crud/unified/find-comment.yml b/test/spec/crud/unified/find-comment.yml index ce180d5e29..72009722a8 100644 --- a/test/spec/crud/unified/find-comment.yml +++ b/test/spec/crud/unified/find-comment.yml @@ -88,9 +88,10 @@ tests: filter: *filter comment: *comment - - description: "find with comment does not set comment on getMore" + - description: "find with comment sets comment on getMore" runOnRequirements: - - minServerVersion: "3.6" + - minServerVersion: "4.4.0" + topologies: [ single, replicaset ] operations: - name: find object: *collection0 @@ -118,10 +119,10 @@ tests: getMore: { $$type: [ int, long ] } collection: *collection0Name batchSize: 2 - comment: { $$exists: false } + comment: "comment" - commandStartedEvent: command: getMore: { $$type: [ int, long ] } collection: *collection0Name batchSize: 2 - comment: { $$exists: false } + comment: "comment" diff --git a/test/spec/crud/unified/updateMany-comment.json b/test/spec/crud/unified/updateMany-comment.json index ebce864329..26abd92ed4 100644 --- a/test/spec/crud/unified/updateMany-comment.json +++ b/test/spec/crud/unified/updateMany-comment.json @@ -53,13 +53,11 @@ "filter": { "_id": 1 }, - "update": [ - { - "$set": { - "x": 22 - } + "update": { + "$set": { + "x": 22 } - ], + }, "comment": "comment" } } @@ -77,13 +75,12 @@ "q": { "_id": 1 }, - "u": [ - { - "$set": { - "x": 22 - } + "u": { + "$set": { + "x": 22 } - ] + }, + "multi": true } ], "comment": "comment" @@ -121,13 +118,11 @@ "filter": { "_id": 1 }, - "update": [ - { - "$set": { - "x": 22 - } + "update": { + "$set": { + "x": 22 } - ], + }, "comment": { "key": "value" } @@ -147,13 +142,12 @@ "q": { "_id": 1 }, - "u": [ - { - "$set": { - "x": 22 - } + "u": { + "$set": { + "x": 22 } - ] + }, + "multi": true } ], "comment": { @@ -193,13 +187,11 @@ "filter": { "_id": 1 }, - "update": [ - { - "$set": { - "x": 22 - } + "update": { + "$set": { + "x": 22 } - ], + }, "comment": "comment" }, "expectError": { @@ -220,13 +212,12 @@ "q": { "_id": 1 }, - "u": [ - { - "$set": { - "x": 22 - } + "u": { + "$set": { + "x": 22 } - ] + }, + "multi": true } ], "comment": "comment" diff --git a/test/spec/crud/unified/updateMany-comment.yml b/test/spec/crud/unified/updateMany-comment.yml index dae3d04969..bfe1a5d08a 100644 --- a/test/spec/crud/unified/updateMany-comment.yml +++ b/test/spec/crud/unified/updateMany-comment.yml @@ -29,8 +29,7 @@ tests: object: *collection0 arguments: filter: &filter { _id: 1 } - update: &update - - $set: { x: 22 } + update: &update { $set: {x: 22} } comment: "comment" expectEvents: - client: *client0 @@ -42,6 +41,7 @@ tests: - q: *filter u: *update + multi: true comment: "comment" outcome: &outcome - collectionName: *collection0Name @@ -69,6 +69,7 @@ tests: - q: *filter u: *update + multi: true comment: *comment outcome: *outcome @@ -94,5 +95,6 @@ tests: - 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 index 2d317638a1..9b3b71d395 100644 --- a/test/spec/crud/unified/updateOne-comment.json +++ b/test/spec/crud/unified/updateOne-comment.json @@ -54,7 +54,9 @@ "_id": 1 }, "update": { - "x": 22 + "$set": { + "x": 22 + } }, "comment": "comment" } @@ -74,7 +76,9 @@ "_id": 1 }, "u": { - "x": 22 + "$set": { + "x": 22 + } } } ], @@ -114,7 +118,9 @@ "_id": 1 }, "update": { - "x": 22 + "$set": { + "x": 22 + } }, "comment": { "key": "value" @@ -136,7 +142,9 @@ "_id": 1 }, "u": { - "x": 22 + "$set": { + "x": 22 + } } } ], @@ -178,7 +186,9 @@ "_id": 1 }, "update": { - "x": 22 + "$set": { + "x": 22 + } }, "comment": "comment" }, @@ -201,7 +211,9 @@ "_id": 1 }, "u": { - "x": 22 + "$set": { + "x": 22 + } } } ], diff --git a/test/spec/crud/unified/updateOne-comment.yml b/test/spec/crud/unified/updateOne-comment.yml index dbc0efdcca..0a879d16d2 100644 --- a/test/spec/crud/unified/updateOne-comment.yml +++ b/test/spec/crud/unified/updateOne-comment.yml @@ -29,7 +29,7 @@ tests: object: *collection0 arguments: filter: &filter { _id: 1 } - update: &update { x: 22 } + update: &update { $set: {x: 22} } comment: "comment" expectEvents: - client: *client0 From ac9fd81c83ba5f5328b1c7926d3c131f0b81d519 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 9 Mar 2022 13:04:35 -0500 Subject: [PATCH 06/54] fix: uncomment atomic operators check in update.ts --- src/operations/update.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/operations/update.ts b/src/operations/update.ts index ffb11bc94d..b620ae8bf4 100644 --- a/src/operations/update.ts +++ b/src/operations/update.ts @@ -155,9 +155,9 @@ export class UpdateOneOperation extends UpdateOperation { options ); - // if (!hasAtomicOperators(update)) { - // throw new MongoInvalidArgumentError('Update document requires atomic operators'); - // } + if (!hasAtomicOperators(update)) { + throw new MongoInvalidArgumentError('Update document requires atomic operators'); + } } override execute( From a5e4de69c7fd2d143f51dfb84b38f04b54d468d6 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 14 Mar 2022 12:09:44 -0400 Subject: [PATCH 07/54] resolve merge conflicts --- src/cmap/connection.ts | 4 + src/index.ts | 1 + src/mongo_types.ts | 94 ++++++++++---------- src/operations/aggregate.ts | 8 ++ src/operations/delete.ts | 8 +- src/operations/find_and_modify.ts | 6 ++ test/tools/unified-spec-runner/operations.ts | 7 +- 7 files changed, 74 insertions(+), 54 deletions(-) diff --git a/src/cmap/connection.ts b/src/cmap/connection.ts index c15d0fadfb..1e2d2140cb 100644 --- a/src/cmap/connection.ts +++ b/src/cmap/connection.ts @@ -575,6 +575,10 @@ export class Connection extends TypedEventEmitter { getMoreCmd.maxTimeMS = options.maxAwaitTimeMS; } + if (options.comment) { + getMoreCmd.comment = options.comment; + } + const commandOptions = Object.assign( { returnFieldSelector: null, diff --git a/src/index.ts b/src/index.ts index da8cc2614d..409ae29c5b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -278,6 +278,7 @@ export type { AlternativeType, ArrayOperator, BitwiseFilter, + BSONLike, BSONTypeAlias, Condition, EnhancedOmit, diff --git a/src/mongo_types.ts b/src/mongo_types.ts index ad6bcd8803..ee33fb4bf2 100644 --- a/src/mongo_types.ts +++ b/src/mongo_types.ts @@ -20,14 +20,14 @@ export type TODO_NODE_3286 = any; /** Given an object shaped type, return the type of the _id field or default to ObjectId @public */ export type InferIdType = TSchema extends { _id: infer IdType } ? // user has defined a type for _id - Record extends IdType - ? never // explicitly forbid empty objects as the type of _id - : IdType + Record extends IdType + ? never // explicitly forbid empty objects as the type of _id + : IdType : TSchema extends { _id?: infer IdType } ? // optional _id defined - return ObjectId | IdType - unknown extends IdType - ? ObjectId // infer the _id type as ObjectId if the type of _id is unknown - : IdType + unknown extends IdType + ? ObjectId // infer the _id type as ObjectId if the type of _id is unknown + : IdType : ObjectId; // user has not defined _id on schema /** Add an _id field to an object shaped type @public */ @@ -68,10 +68,10 @@ export type WithoutId = Omit; export type Filter = | Partial | ({ - [Property in Join>, '.'>]?: Condition< - PropertyType, Property> - >; - } & RootFilterOperators>); + [Property in Join>, '.'>]?: Condition< + PropertyType, Property> + >; + } & RootFilterOperators>); /** @public */ export type Condition = AlternativeType | FilterOperators>; @@ -275,8 +275,8 @@ export type OnlyFieldsOfType, AcceptedFields & - NotAcceptedFields & - Record + NotAcceptedFields & + Record >; /** @public */ @@ -298,8 +298,8 @@ export type ArrayOperator = { /** @public */ export type SetFields = ({ readonly [key in KeysOfAType | undefined>]?: - | OptionalId> - | AddToSetOperators>>>; + | OptionalId> + | AddToSetOperators>>>; } & NotAcceptedFields | undefined>) & { readonly [key: string]: AddToSetOperators | any; }; @@ -307,8 +307,8 @@ export type SetFields = ({ /** @public */ export type PushOperator = ({ readonly [key in KeysOfAType>]?: - | Flatten - | ArrayOperator>>; + | Flatten + | ArrayOperator>>; } & NotAcceptedFields>) & { readonly [key: string]: ArrayOperator | any; }; @@ -316,8 +316,8 @@ export type PushOperator = ({ /** @public */ export type PullOperator = ({ readonly [key in KeysOfAType>]?: - | Partial> - | FilterOperations>; + | Partial> + | FilterOperations>; } & NotAcceptedFields>) & { readonly [key: string]: FilterOperators | any; }; @@ -462,10 +462,10 @@ export declare interface TypedEventEmitter ext * @public */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -export class TypedEventEmitter extends EventEmitter { } +export class TypedEventEmitter extends EventEmitter {} /** @public */ -export class CancellationToken extends TypedEventEmitter<{ cancel(): void }> { } +export class CancellationToken extends TypedEventEmitter<{ cancel(): void }> {} /** * Helper types for dot-notation filter attributes @@ -487,18 +487,18 @@ export type PropertyType = string extends Propert ? Type[Property] : Property extends `${number}` ? Type extends ReadonlyArray - ? ArrayType - : unknown + ? ArrayType + : unknown : Property extends `${infer Key}.${infer Rest}` ? Key extends `${number}` - ? Type extends ReadonlyArray - ? PropertyType - : unknown - : Key extends keyof Type - ? Type[Key] extends Map - ? MapType - : PropertyType - : unknown + ? Type extends ReadonlyArray + ? PropertyType + : unknown + : Key extends keyof Type + ? Type[Key] extends Map + ? MapType + : PropertyType + : unknown : unknown; /** @@ -524,21 +524,21 @@ export type NestedPaths = Type extends : // eslint-disable-next-line @typescript-eslint/ban-types Type extends object ? { - [Key in Extract]: Type[Key] extends Type // type of value extends the parent - ? [Key] - : // for a recursive union type, the child will never extend the parent type. - // but the parent will still extend the child - Type extends Type[Key] - ? [Key] - : Type[Key] extends ReadonlyArray // handling recursive types with arrays - ? Type extends ArrayType // is the type of the parent the same as the type of the array? - ? [Key] // yes, it's a recursive array type - : // for unions, the child type extends the parent - ArrayType extends Type - ? [Key] // we have a recursive array union - : // child is an array, but it's not a recursive array - [Key, ...NestedPaths] - : // child is not structured the same as the parent - [Key, ...NestedPaths]; - }[Extract] + [Key in Extract]: Type[Key] extends Type // type of value extends the parent + ? [Key] + : // for a recursive union type, the child will never extend the parent type. + // but the parent will still extend the child + Type extends Type[Key] + ? [Key] + : Type[Key] extends ReadonlyArray // handling recursive types with arrays + ? Type extends ArrayType // is the type of the parent the same as the type of the array? + ? [Key] // yes, it's a recursive array type + : // for unions, the child type extends the parent + ArrayType extends Type + ? [Key] // we have a recursive array union + : // child is an array, but it's not a recursive array + [Key, ...NestedPaths] + : // child is not structured the same as the parent + [Key, ...NestedPaths]; + }[Extract] : []; diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index fb18d969f6..f8ec9a8c53 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -1,5 +1,6 @@ import type { Document } from '../bson'; import { MongoInvalidArgumentError } from '../error'; +import type { BSONLike } from '../mongo_types'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback } from '../utils'; @@ -31,6 +32,9 @@ 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; + /** Comment to apply to the operation. */ + comment?: BSONLike; + out?: string; } @@ -121,6 +125,10 @@ export class AggregateOperation extends CommandOperation { command.let = options.let; } + if (options.comment) { + command.comment = options.comment; + } + command.cursor = options.cursor || {}; if (options.batchSize && !this.hasWriteStage) { command.cursor.batchSize = options.batchSize; diff --git a/src/operations/delete.ts b/src/operations/delete.ts index 5891c1028c..f166c50299 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -80,6 +80,10 @@ export class DeleteOperation extends CommandOperation { command.let = options.let; } + if (options.comment) { + command.comment = options.comment; + } + if (options.explain != null && maxWireVersion(server) < 3) { return callback ? callback( @@ -175,10 +179,6 @@ export function makeDeleteStatement( op.hint = options.hint; } - if (options.comment) { - op.comment = options.comment; - } - return op; } diff --git a/src/operations/find_and_modify.ts b/src/operations/find_and_modify.ts index 2e2a776d70..7a6b369cce 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -1,6 +1,7 @@ import type { Document } from '../bson'; import type { Collection } from '../collection'; import { MongoCompatibilityError, MongoInvalidArgumentError } from '../error'; +import type { BSONLike } from '../mongo_types'; import { ReadPreference } from '../read_preference'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; @@ -82,6 +83,7 @@ interface FindAndModifyCmdBase { maxTimeMS?: number; let?: Document; writeConcern?: WriteConcern | WriteConcernSettings; + comment?: BSONLike; } function configureFindAndModifyCmdBaseUpdateOpts( @@ -140,6 +142,10 @@ class FindAndModifyOperation extends CommandOperation { this.cmdBase.let = options.let; } + if (options.comment) { + this.cmdBase.comment = options.comment; + } + // force primary read preference this.readPreference = ReadPreference.primary; diff --git a/test/tools/unified-spec-runner/operations.ts b/test/tools/unified-spec-runner/operations.ts index 6638283e58..f9532654eb 100644 --- a/test/tools/unified-spec-runner/operations.ts +++ b/test/tools/unified-spec-runner/operations.ts @@ -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 @@ -206,7 +206,8 @@ operations.set('createChangeStream', async ({ entities, operation }) => { resumeAfter: operation.arguments.resumeAfter, startAfter: operation.arguments.startAfter, startAtOperationTime: operation.arguments.startAtOperationTime, - batchSize: operation.arguments.batchSize + batchSize: operation.arguments.batchSize, + comment: operation.arguments.comment }); changeStream.eventCollector = new EventCollector(changeStream, ['init', 'change', 'error']); From 704764a03a424f966a61fdc42285099f5719993d Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 17 Mar 2022 13:59:40 -0400 Subject: [PATCH 08/54] finish conflict --- src/change_stream.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/change_stream.ts b/src/change_stream.ts index b73a2fa6fc..767dc6f4c8 100644 --- a/src/change_stream.ts +++ b/src/change_stream.ts @@ -411,6 +411,9 @@ export interface ChangeStreamCursorOptions extends AbstractCursorOptions { startAtOperationTime?: OperationTime; resumeAfter?: ResumeToken; startAfter?: boolean; + + /** todo: add comment */ + comment?: any; } /** @internal */ From 07df766b20fa78cddab6dcabd7f8793c0faecd29 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 14 Mar 2022 11:15:38 -0400 Subject: [PATCH 09/54] fix: skip failing change stream tests --- test/integration/change-streams/change_streams.spec.test.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/integration/change-streams/change_streams.spec.test.js b/test/integration/change-streams/change_streams.spec.test.js index 436e7f493f..ff2c5a70a1 100644 --- a/test/integration/change-streams/change_streams.spec.test.js +++ b/test/integration/change-streams/change_streams.spec.test.js @@ -9,7 +9,11 @@ const { LEGACY_HELLO_COMMAND } = require('../../../src/constants'); const { delay, setupDatabase } = require('../shared'); describe('Change Streams Spec - Unified', function () { - runUnifiedSuite(loadSpecTests(path.join('change-streams', 'unified'))); + runUnifiedSuite(loadSpecTests(path.join('change-streams', 'unified')), [ + 'Test with document comment', + 'Test with string comment', + 'Test that comment is set on getMore' + ]); }); // TODO: NODE-3819: Unskip flaky MacOS tests. From d7d7d28b8bf254368cdaf7a8985bbd7042486810 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 14 Mar 2022 12:06:51 -0400 Subject: [PATCH 10/54] fix: address linting issues --- src/cursor/abstract_cursor.ts | 2 +- src/index.ts | 1 - src/mongo_types.ts | 22 ---------------------- src/operations/aggregate.ts | 3 +-- src/operations/find_and_modify.ts | 5 +++-- 5 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index 1ef82f13f0..f7cf9a8602 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -77,7 +77,7 @@ export interface AbstractCursorOptions extends BSONSerializeOptions { readConcern?: ReadConcernLike; batchSize?: number; maxTimeMS?: number; - comment?: Document | string; + comment?: any; tailable?: boolean; awaitData?: boolean; noCursorTimeout?: boolean; diff --git a/src/index.ts b/src/index.ts index 409ae29c5b..da8cc2614d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -278,7 +278,6 @@ export type { AlternativeType, ArrayOperator, BitwiseFilter, - BSONLike, BSONTypeAlias, Condition, EnhancedOmit, diff --git a/src/mongo_types.ts b/src/mongo_types.ts index ee33fb4bf2..83bf11ad6a 100644 --- a/src/mongo_types.ts +++ b/src/mongo_types.ts @@ -187,28 +187,6 @@ export const BSONType = Object.freeze({ maxKey: 127 } as const); -// TODO: ensure all BSON types are represented -/** @public */ -export type BSONLike = any; -// | number -// | string -// | Document -// | ReadonlyArray -// | Binary -// | undefined -// | ObjectId -// | boolean -// | Date -// | null -// | RegExp -// // DbPointer -// | Code -// | CodeExtended -// | Timestamp -// | Decimal128 -// | MinKey -// | MaxKey; - /** @public */ export type BSONType = typeof BSONType[keyof typeof BSONType]; /** @public */ diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index f8ec9a8c53..ce03878628 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -1,6 +1,5 @@ import type { Document } from '../bson'; import { MongoInvalidArgumentError } from '../error'; -import type { BSONLike } from '../mongo_types'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import type { Callback } from '../utils'; @@ -33,7 +32,7 @@ export interface AggregateOptions extends CommandOperationOptions { /** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */ let?: Document; /** Comment to apply to the operation. */ - comment?: BSONLike; + comment?: any; out?: string; } diff --git a/src/operations/find_and_modify.ts b/src/operations/find_and_modify.ts index 7a6b369cce..0dadc8d156 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -1,7 +1,6 @@ import type { Document } from '../bson'; import type { Collection } from '../collection'; import { MongoCompatibilityError, MongoInvalidArgumentError } from '../error'; -import type { BSONLike } from '../mongo_types'; import { ReadPreference } from '../read_preference'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; @@ -83,7 +82,9 @@ interface FindAndModifyCmdBase { maxTimeMS?: number; let?: Document; writeConcern?: WriteConcern | WriteConcernSettings; - comment?: BSONLike; + + // TODO: add comment + comment?: any; } function configureFindAndModifyCmdBaseUpdateOpts( From 6bb15ecdbdd94cef6220ca04272e4d2f21fe378d Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 14 Mar 2022 12:08:39 -0400 Subject: [PATCH 11/54] fix: skip change streams pre 4.4 test --- test/integration/change-streams/change_streams.spec.test.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/integration/change-streams/change_streams.spec.test.js b/test/integration/change-streams/change_streams.spec.test.js index ff2c5a70a1..f740e5ece8 100644 --- a/test/integration/change-streams/change_streams.spec.test.js +++ b/test/integration/change-streams/change_streams.spec.test.js @@ -12,7 +12,8 @@ describe('Change Streams Spec - Unified', function () { runUnifiedSuite(loadSpecTests(path.join('change-streams', 'unified')), [ 'Test with document comment', 'Test with string comment', - 'Test that comment is set on getMore' + 'Test that comment is set on getMore', + 'Test with document comment - pre 4.4' ]); }); From fec0040d5d72efec5daca28324051bbaa93ce773 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 14 Mar 2022 15:05:08 -0400 Subject: [PATCH 12/54] docs: add docstring comments to 'comment' option --- src/change_stream.ts | 10 ++++++++-- src/cmap/connection.ts | 10 +++++++++- src/cursor/abstract_cursor.ts | 8 ++++++++ src/operations/aggregate.ts | 9 ++++++++- src/operations/command.ts | 11 +++++++++-- src/operations/delete.ts | 22 ++++++++++++++++++---- src/operations/find.ts | 11 +++++++++-- src/operations/find_and_modify.ts | 10 ++++++++-- src/operations/get_more.ts | 11 +++++++++-- 9 files changed, 86 insertions(+), 16 deletions(-) diff --git a/src/change_stream.ts b/src/change_stream.ts index 767dc6f4c8..9c338f325b 100644 --- a/src/change_stream.ts +++ b/src/change_stream.ts @@ -411,8 +411,14 @@ export interface ChangeStreamCursorOptions extends AbstractCursorOptions { startAtOperationTime?: OperationTime; resumeAfter?: ResumeToken; startAfter?: boolean; - - /** todo: add comment */ + /** + * Comment to apply to the operation. + * + * In server versions <4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions >=4.4, 'comment' can be any valid BSON type. + */ comment?: any; } diff --git a/src/cmap/connection.ts b/src/cmap/connection.ts index 1e2d2140cb..be70cc7000 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 <4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions >=4.4, 'comment' can be any valid BSON type. + */ + comment?: any; } /** @public */ diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index f7cf9a8602..ec53eed41f 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -77,6 +77,14 @@ export interface AbstractCursorOptions extends BSONSerializeOptions { readConcern?: ReadConcernLike; batchSize?: number; maxTimeMS?: number; + /** + * Comment to apply to the operation. + * + * In server versions <4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions >=4.4, 'comment' can be any valid BSON type. + */ comment?: any; tailable?: boolean; awaitData?: boolean; diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index ce03878628..43a134920d 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -31,7 +31,14 @@ 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; - /** Comment to apply to the operation. */ + /** + * Comment to apply to the operation. + * + * In server versions <4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions >=4.4, 'comment' can be any valid BSON type. + */ comment?: any; out?: string; diff --git a/src/operations/command.ts b/src/operations/command.ts index b67979d422..f5d4e0d044 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 <4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions >=4.4, 'comment' can be any valid BSON type. + */ + comment?: any; /** Should retry failed writes */ retryWrites?: boolean; diff --git a/src/operations/delete.ts b/src/operations/delete.ts index f166c50299..9c10647b0d 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -12,8 +12,15 @@ 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; + /** + * Comment to apply to the operation. + * + * In server versions <4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions >=4.4, 'comment' can be any valid BSON type. + */ + comment?: any; /** 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 +50,15 @@ 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; + /** + * Comment to apply to the operation. + * + * In server versions <4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions >=4.4, 'comment' can be any valid BSON type. + */ + comment?: any; } /** @internal */ diff --git a/src/operations/find.ts b/src/operations/find.ts index f3e618596b..7a32f79b50 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -46,8 +46,15 @@ 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; + /** + * Comment to apply to the operation. + * + * In server versions <4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions >=4.4, 'comment' can be any valid BSON type. + */ + comment?: any; /** 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 */ diff --git a/src/operations/find_and_modify.ts b/src/operations/find_and_modify.ts index 0dadc8d156..6bb0923783 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -82,8 +82,14 @@ interface FindAndModifyCmdBase { maxTimeMS?: number; let?: Document; writeConcern?: WriteConcern | WriteConcernSettings; - - // TODO: add comment + /** + * Comment to apply to the operation. + * + * In server versions <4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions >=4.4, 'comment' can be any valid BSON type. + */ comment?: any; } diff --git a/src/operations/get_more.ts b/src/operations/get_more.ts index e0d1bd5430..10aa48b87f 100644 --- a/src/operations/get_more.ts +++ b/src/operations/get_more.ts @@ -12,8 +12,15 @@ 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. + * + * In server versions <4.4, 'comment' must be string. A server + * error will be thrown if any other type is provided. + * + * In server versions >=4.4, 'comment' can be any valid BSON type. + */ + comment?: any; /** Number of milliseconds to wait before aborting the query. */ maxTimeMS?: number; } From c3e7fc1e04dcfc3f536158096a6f866b462a7c20 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 14 Mar 2022 15:11:44 -0400 Subject: [PATCH 13/54] fix: fix lint error in doc string --- src/change_stream.ts | 4 ++-- src/cmap/connection.ts | 4 ++-- src/cursor/abstract_cursor.ts | 4 ++-- src/operations/aggregate.ts | 4 ++-- src/operations/command.ts | 4 ++-- src/operations/delete.ts | 8 ++++---- src/operations/find.ts | 4 ++-- src/operations/find_and_modify.ts | 4 ++-- src/operations/get_more.ts | 4 ++-- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/change_stream.ts b/src/change_stream.ts index 9c338f325b..4f88339405 100644 --- a/src/change_stream.ts +++ b/src/change_stream.ts @@ -414,10 +414,10 @@ export interface ChangeStreamCursorOptions extends AbstractCursorOptions { /** * Comment to apply to the operation. * - * In server versions <4.4, 'comment' must be string. A server + * 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, 'comment' can be any valid BSON type. + * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ comment?: any; } diff --git a/src/cmap/connection.ts b/src/cmap/connection.ts index be70cc7000..90468c88c6 100644 --- a/src/cmap/connection.ts +++ b/src/cmap/connection.ts @@ -127,10 +127,10 @@ export interface GetMoreOptions extends CommandOptions { /** * Comment to apply to the operation. * - * In server versions <4.4, 'comment' must be string. A server + * 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, 'comment' can be any valid BSON type. + * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ comment?: any; } diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index ec53eed41f..597d8daa18 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -80,10 +80,10 @@ export interface AbstractCursorOptions extends BSONSerializeOptions { /** * Comment to apply to the operation. * - * In server versions <4.4, 'comment' must be string. A server + * 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, 'comment' can be any valid BSON type. + * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ comment?: any; tailable?: boolean; diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index 43a134920d..1311fe2b1a 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -34,10 +34,10 @@ export interface AggregateOptions extends CommandOperationOptions { /** * Comment to apply to the operation. * - * In server versions <4.4, 'comment' must be string. A server + * 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, 'comment' can be any valid BSON type. + * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ comment?: any; diff --git a/src/operations/command.ts b/src/operations/command.ts index f5d4e0d044..f80ba26bf1 100644 --- a/src/operations/command.ts +++ b/src/operations/command.ts @@ -48,10 +48,10 @@ export interface CommandOperationOptions /** * Comment to apply to the operation. * - * In server versions <4.4, 'comment' must be string. A server + * 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, 'comment' can be any valid BSON type. + * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ comment?: any; /** Should retry failed writes */ diff --git a/src/operations/delete.ts b/src/operations/delete.ts index 9c10647b0d..d1ade9fe17 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -15,10 +15,10 @@ export interface DeleteOptions extends CommandOperationOptions, WriteConcernOpti /** * Comment to apply to the operation. * - * In server versions <4.4, 'comment' must be string. A server + * 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, 'comment' can be any valid BSON type. + * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ comment?: any; /** Specifies the collation to use for the operation */ @@ -53,10 +53,10 @@ export interface DeleteStatement { /** * Comment to apply to the operation. * - * In server versions <4.4, 'comment' must be string. A server + * 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, 'comment' can be any valid BSON type. + * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ comment?: any; } diff --git a/src/operations/find.ts b/src/operations/find.ts index 7a32f79b50..52469346e5 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -49,10 +49,10 @@ export interface FindOptions extends Comman /** * Comment to apply to the operation. * - * In server versions <4.4, 'comment' must be string. A server + * 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, 'comment' can be any valid BSON type. + * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ comment?: any; /** Number of milliseconds to wait before aborting the query. */ diff --git a/src/operations/find_and_modify.ts b/src/operations/find_and_modify.ts index 6bb0923783..ebce5f5d63 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -85,10 +85,10 @@ interface FindAndModifyCmdBase { /** * Comment to apply to the operation. * - * In server versions <4.4, 'comment' must be string. A server + * 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, 'comment' can be any valid BSON type. + * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ comment?: any; } diff --git a/src/operations/get_more.ts b/src/operations/get_more.ts index 10aa48b87f..102b58cc93 100644 --- a/src/operations/get_more.ts +++ b/src/operations/get_more.ts @@ -15,10 +15,10 @@ export interface GetMoreOptions extends OperationOptions { /** * Comment to apply to the operation. * - * In server versions <4.4, 'comment' must be string. A server + * 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, 'comment' can be any valid BSON type. + * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ comment?: any; /** Number of milliseconds to wait before aborting the query. */ From f95e8f27ec9ea3ce662df6a7426f73f15c3e9348 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 15 Mar 2022 16:27:41 -0400 Subject: [PATCH 14/54] fix: check for 'undefined' in place of a falsy value --- src/cmap/connection.ts | 3 ++- src/cursor/abstract_cursor.ts | 3 ++- src/operations/aggregate.ts | 3 ++- src/operations/delete.ts | 3 ++- src/operations/find.ts | 3 ++- src/operations/find_and_modify.ts | 3 ++- src/operations/update.ts | 3 ++- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/cmap/connection.ts b/src/cmap/connection.ts index 90468c88c6..709167aa20 100644 --- a/src/cmap/connection.ts +++ b/src/cmap/connection.ts @@ -583,7 +583,8 @@ export class Connection extends TypedEventEmitter { getMoreCmd.maxTimeMS = options.maxAwaitTimeMS; } - if (options.comment) { + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { getMoreCmd.comment = options.comment; } diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index 597d8daa18..11f885ac80 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -170,7 +170,8 @@ export abstract class AbstractCursor< this[kOptions].batchSize = options.batchSize; } - if (options.comment != null) { + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { this[kOptions].comment = options.comment; } diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index 1311fe2b1a..516da0f278 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -131,7 +131,8 @@ export class AggregateOperation extends CommandOperation { command.let = options.let; } - if (options.comment) { + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { command.comment = options.comment; } diff --git a/src/operations/delete.ts b/src/operations/delete.ts index d1ade9fe17..4ffaaa3a10 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -94,7 +94,8 @@ export class DeleteOperation extends CommandOperation { command.let = options.let; } - if (options.comment) { + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { command.comment = options.comment; } diff --git a/src/operations/find.ts b/src/operations/find.ts index 52469346e5..a41acbae5a 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -248,7 +248,8 @@ function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOp findCommand.singleBatch = options.singleBatch; } - if (options.comment) { + // 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 ebce5f5d63..b8bf3d2c28 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -149,7 +149,8 @@ class FindAndModifyOperation extends CommandOperation { this.cmdBase.let = options.let; } - if (options.comment) { + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { this.cmdBase.comment = options.comment; } diff --git a/src/operations/update.ts b/src/operations/update.ts index b620ae8bf4..fcb0ae6fef 100644 --- a/src/operations/update.ts +++ b/src/operations/update.ts @@ -107,7 +107,8 @@ export class UpdateOperation extends CommandOperation { command.let = options.let; } - if (options.comment) { + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { command.comment = options.comment; } From 097a5f1be752f3f5c234bf7cad60c154031666f4 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 16 Mar 2022 17:02:38 -0400 Subject: [PATCH 15/54] fix: remove comment from DeleteStatement --- src/operations/delete.ts | 9 --------- src/operations/insert.ts | 3 ++- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/operations/delete.ts b/src/operations/delete.ts index 4ffaaa3a10..1248f4903d 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -50,15 +50,6 @@ export interface DeleteStatement { collation?: CollationOptions; /** A document or string that specifies the index to use to support the query predicate. */ hint?: Hint; - /** - * 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?: any; } /** @internal */ diff --git a/src/operations/insert.ts b/src/operations/insert.ts index 3762835914..fc9bddb029 100644 --- a/src/operations/insert.ts +++ b/src/operations/insert.ts @@ -41,7 +41,8 @@ export class InsertOperation extends CommandOperation { command.bypassDocumentValidation = options.bypassDocumentValidation; } - if (options.comment != null) { + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { command.comment = options.comment; } From 8433c67b81545db8e3511c31df56d33a26bba42a Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 17 Mar 2022 15:09:02 -0400 Subject: [PATCH 16/54] fix: support change streams tests --- .../change_streams.spec.test.js | 7 +- .../node-specific/change-streams.json | 309 ++++++++++++++++++ 2 files changed, 314 insertions(+), 2 deletions(-) create mode 100644 test/spec/change-streams/node-specific/change-streams.json diff --git a/test/integration/change-streams/change_streams.spec.test.js b/test/integration/change-streams/change_streams.spec.test.js index f740e5ece8..953d1bc82f 100644 --- a/test/integration/change-streams/change_streams.spec.test.js +++ b/test/integration/change-streams/change_streams.spec.test.js @@ -12,11 +12,14 @@ describe('Change Streams Spec - Unified', function () { runUnifiedSuite(loadSpecTests(path.join('change-streams', 'unified')), [ 'Test with document comment', 'Test with string comment', - 'Test that comment is set on getMore', - 'Test with document comment - pre 4.4' + 'Test that comment is set on getMore' ]); }); +describe('Change Streams Spec - Node-Specific', function () { + runUnifiedSuite(loadSpecTests(path.join('change-streams', 'node-specific'))); +}); + // TODO: NODE-3819: Unskip flaky MacOS tests. const maybeDescribe = process.platform === 'darwin' ? describe.skip : describe; maybeDescribe('Change Stream Spec - v1', function () { diff --git a/test/spec/change-streams/node-specific/change-streams.json b/test/spec/change-streams/node-specific/change-streams.json new file mode 100644 index 0000000000..557f48f0bd --- /dev/null +++ b/test/spec/change-streams/node-specific/change-streams.json @@ -0,0 +1,309 @@ +{ + "description": "change-streams", + "schemaVersion": "1.0", + "runOnRequirements": [ + { + "topologies": [ + "replicaset", + "sharded-replicaset" + ] + } + ], + "createEntities": [ + { + "client": { + "id": "client0", + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "database0" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "collection0" + } + } + ], + "initialData": [ + { + "collectionName": "collection0", + "databaseName": "database0", + "documents": [] + } + ], + "tests": [ + { + "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" + } + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection0", + "comment": { + "name": "test1" + } + }, + "commandName": "getMore", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "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" + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection0", + "comment": "comment" + }, + "commandName": "getMore", + "databaseName": "database0" + } + } + ] + } + ] + }, + { + "description": "Test that comment is set on getMore", + "runOnRequirements": [ + { + "minServerVersion": "4.4.0", + "topologies": [ + "single", + "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": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection0", + "comment": "comment" + }, + "commandName": "getMore", + "databaseName": "database0" + } + }, + { + "commandStartedEvent": { + "command": { + "insert": "collection0", + "documents": [ + { + "_id": 1, + "a": 1 + } + ] + } + } + }, + { + "commandStartedEvent": { + "command": { + "getMore": { + "$$type": [ + "int", + "long" + ] + }, + "collection": "collection0", + "comment": "comment" + }, + "commandName": "getMore", + "databaseName": "database0" + } + } + ] + } + ] + } + ] +} \ No newline at end of file From a1137c4db551a1acf61839d7bb197ce1f07e8e68 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 17 Mar 2022 15:11:06 -0400 Subject: [PATCH 17/54] feat: support comment in change streams --- src/change_stream.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/change_stream.ts b/src/change_stream.ts index 4f88339405..1255964551 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; From 0cfbc2a2b6201cb8cf192c41091cf06968f75b8d Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 17 Mar 2022 15:24:51 -0400 Subject: [PATCH 18/54] convert change stream node specific test to TS --- .../change_streams.spec.test.js | 3 +- .../node-specific/change-streams.json | 309 ------------------ .../node-specific/change-streams.ts | 291 +++++++++++++++++ 3 files changed, 293 insertions(+), 310 deletions(-) delete mode 100644 test/spec/change-streams/node-specific/change-streams.json create mode 100644 test/spec/change-streams/node-specific/change-streams.ts diff --git a/test/integration/change-streams/change_streams.spec.test.js b/test/integration/change-streams/change_streams.spec.test.js index 953d1bc82f..49ba850993 100644 --- a/test/integration/change-streams/change_streams.spec.test.js +++ b/test/integration/change-streams/change_streams.spec.test.js @@ -7,6 +7,7 @@ const { runUnifiedSuite } = require('../../tools/unified-spec-runner/runner'); const camelCase = require('lodash.camelcase'); const { LEGACY_HELLO_COMMAND } = require('../../../src/constants'); const { delay, setupDatabase } = require('../shared'); +const { suite } = require('../../spec/change-streams/node-specific/change-streams'); describe('Change Streams Spec - Unified', function () { runUnifiedSuite(loadSpecTests(path.join('change-streams', 'unified')), [ @@ -17,7 +18,7 @@ describe('Change Streams Spec - Unified', function () { }); describe('Change Streams Spec - Node-Specific', function () { - runUnifiedSuite(loadSpecTests(path.join('change-streams', 'node-specific'))); + runUnifiedSuite([suite]); }); // TODO: NODE-3819: Unskip flaky MacOS tests. diff --git a/test/spec/change-streams/node-specific/change-streams.json b/test/spec/change-streams/node-specific/change-streams.json deleted file mode 100644 index 557f48f0bd..0000000000 --- a/test/spec/change-streams/node-specific/change-streams.json +++ /dev/null @@ -1,309 +0,0 @@ -{ - "description": "change-streams", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "topologies": [ - "replicaset", - "sharded-replicaset" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0" - } - } - ], - "initialData": [ - { - "collectionName": "collection0", - "databaseName": "database0", - "documents": [] - } - ], - "tests": [ - { - "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" - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "collection0", - "comment": { - "name": "test1" - } - }, - "commandName": "getMore", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "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" - } - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "collection0", - "comment": "comment" - }, - "commandName": "getMore", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "Test that comment is set on getMore", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0", - "topologies": [ - "single", - "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": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "collection0", - "comment": "comment" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "collection0", - "documents": [ - { - "_id": 1, - "a": 1 - } - ] - } - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "collection0", - "comment": "comment" - }, - "commandName": "getMore", - "databaseName": "database0" - } - } - ] - } - ] - } - ] -} \ No newline at end of file diff --git a/test/spec/change-streams/node-specific/change-streams.ts b/test/spec/change-streams/node-specific/change-streams.ts new file mode 100644 index 0000000000..c7366a85f6 --- /dev/null +++ b/test/spec/change-streams/node-specific/change-streams.ts @@ -0,0 +1,291 @@ +import { UnifiedSuite } from '../../../tools/unified-spec-runner/schema'; + +export const suite: UnifiedSuite = { + description: 'change-streams', + schemaVersion: '1.0', + runOnRequirements: [ + { + topologies: ['replicaset', 'sharded-replicaset'] + } + ], + createEntities: [ + { + client: { + id: 'client0', + observeEvents: ['commandStartedEvent'] + } + }, + { + database: { + id: 'database0', + client: 'client0', + databaseName: 'database0' + } + }, + { + collection: { + id: 'collection0', + database: 'database0', + collectionName: 'collection0' + } + } + ], + initialData: [ + { + collectionName: 'collection0', + databaseName: 'database0', + documents: [] + } + ], + tests: [ + { + 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' + } + } + } + }, + { + commandStartedEvent: { + command: { + getMore: { + $$type: ['int', 'long'] + }, + collection: 'collection0', + comment: { + name: 'test1' + } + }, + commandName: 'getMore', + databaseName: 'database0' + } + } + ] + } + ] + }, + { + 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' + } + } + }, + { + commandStartedEvent: { + command: { + getMore: { + $$type: ['int', 'long'] + }, + collection: 'collection0', + comment: 'comment' + }, + commandName: 'getMore', + databaseName: 'database0' + } + } + ] + } + ] + }, + { + description: 'Test that comment is set on getMore', + runOnRequirements: [ + { + minServerVersion: '4.4.0', + topologies: ['single', '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: { + getMore: { + $$type: ['int', 'long'] + }, + collection: 'collection0', + comment: 'comment' + }, + commandName: 'getMore', + databaseName: 'database0' + } + }, + { + commandStartedEvent: { + command: { + insert: 'collection0', + documents: [ + { + _id: 1, + a: 1 + } + ] + } + } + }, + { + commandStartedEvent: { + command: { + getMore: { + $$type: ['int', 'long'] + }, + collection: 'collection0', + comment: 'comment' + }, + commandName: 'getMore', + databaseName: 'database0' + } + } + ] + } + ] + } + ] +}; From 71f17756dee2e344e904cb6986692f8707f7c065 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 18 Mar 2022 12:00:24 -0400 Subject: [PATCH 19/54] fix: only add comment to getMore on server versions 4.4 and above --- src/operations/get_more.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/operations/get_more.ts b/src/operations/get_more.ts index 102b58cc93..15d475eecc 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'; /** @@ -15,10 +15,7 @@ export interface GetMoreOptions extends OperationOptions { /** * 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. + * getMore only supports 'comment' in server versions 4.4 and above. */ comment?: any; /** Number of milliseconds to wait before aborting the query. */ @@ -33,6 +30,12 @@ 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 && 'comment' in this.options) { + delete this.options.comment; + } + this.ns = ns; this.cursorId = cursorId; this.server = server; From 8d4267c59b1c7dee19c43349809549d159ee3bbc Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 18 Mar 2022 12:01:12 -0400 Subject: [PATCH 20/54] fix: remove comment from get more for failiing test --- test/spec/change-streams/node-specific/change-streams.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/spec/change-streams/node-specific/change-streams.ts b/test/spec/change-streams/node-specific/change-streams.ts index c7366a85f6..ec6d81db8f 100644 --- a/test/spec/change-streams/node-specific/change-streams.ts +++ b/test/spec/change-streams/node-specific/change-streams.ts @@ -183,8 +183,7 @@ export const suite: UnifiedSuite = { getMore: { $$type: ['int', 'long'] }, - collection: 'collection0', - comment: 'comment' + collection: 'collection0' }, commandName: 'getMore', databaseName: 'database0' From 2cdbf3bc4cf92012b352c1f3cdcce6e6e94bb653 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 21 Mar 2022 16:28:00 -0400 Subject: [PATCH 21/54] Fix broken unified change stream tests Unified change stream tests were failing because our implementation of `createChangeStream` was out of spec compliance. Rather than simply performing the initial aggregate command, it would perform the aggregate and the following getMore. This caused extra CommandStarted events to be emitted for the Node driver during change stream tests. This commit refactors AbstractCursor to have an `initialize` method that performs cursor initialization. This allows the UnifiedTestRunner to call `initialize` in createChangeStream. --- src/cursor/abstract_cursor.ts | 117 ++++++++++-------- .../change_streams.spec.test.js | 11 +- .../unified_test_format.spec.test.ts | 5 +- test/tools/unified-spec-runner/entities.ts | 14 +-- test/tools/unified-spec-runner/operations.ts | 51 ++++---- test/tools/utils.ts | 14 ++- 6 files changed, 116 insertions(+), 96 deletions(-) diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index 11f885ac80..0a80a9d923 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -629,94 +629,105 @@ export abstract class AbstractCursor< executeOperation(this, getMoreOperation, callback); } -} - -function nextDocument(cursor: AbstractCursor): T | null | undefined { - if (cursor[kDocuments] == null || !cursor[kDocuments].length) { - return null; - } - - const doc = cursor[kDocuments].shift(); - if (doc) { - const transform = cursor[kTransform]; - if (transform) { - return transform(doc) as T; - } - return doc; - } - - return null; -} - -function next(cursor: AbstractCursor, blocking: boolean, callback: Callback): void { - const cursorId = cursor[kId]; - if (cursor.closed) { - return callback(undefined, null); - } - - if (cursor[kDocuments] && cursor[kDocuments].length) { - callback(undefined, nextDocument(cursor)); - return; - } - - if (cursorId == null) { - // All cursors must operate within a session, one must be made implicitly if not explicitly provided - if (cursor[kSession] == null) { - if (cursor[kTopology].shouldCheckForSessionSupport()) { - return cursor[kTopology].selectServer(ReadPreference.primaryPreferred, {}, err => { + /** @internal */ + initialize(callback: Callback): void { + if (this[kSession] == null) { + if (this[kTopology].shouldCheckForSessionSupport()) { + return this[kTopology].selectServer(ReadPreference.primaryPreferred, {}, err => { if (err) return callback(err); - return next(cursor, blocking, callback); + return this.initialize(callback); }); - } else if (cursor[kTopology].hasSessionSupport()) { - cursor[kSession] = cursor[kTopology].startSession({ owner: cursor, explicit: false }); + } else if (this[kTopology].hasSessionSupport()) { + this[kSession] = this[kTopology].startSession({ owner: this, explicit: false }); } } - cursor._initialize(cursor[kSession], (err, state) => { + this._initialize(this[kSession], (err, state) => { if (state) { const response = state.response; - cursor[kServer] = state.server; - cursor[kSession] = state.session; + this[kServer] = state.server; + this[kSession] = state.session; if (response.cursor) { - cursor[kId] = + this[kId] = typeof response.cursor.id === 'number' ? Long.fromNumber(response.cursor.id) : response.cursor.id; if (response.cursor.ns) { - cursor[kNamespace] = ns(response.cursor.ns); + this[kNamespace] = ns(response.cursor.ns); } - cursor[kDocuments] = response.cursor.firstBatch; + this[kDocuments] = response.cursor.firstBatch; } else { // NOTE: This is for support of older servers (<3.2) which do not use commands - cursor[kId] = + this[kId] = typeof response.cursorId === 'number' ? Long.fromNumber(response.cursorId) : response.cursorId; - cursor[kDocuments] = response.documents; + this[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; + if (this[kId] == null) { + this[kId] = Long.ZERO; // TODO(NODE-3286): ExecutionResult needs to accept a generic parameter - cursor[kDocuments] = [state.response as TODO_NODE_3286]; + this[kDocuments] = [state.response as TODO_NODE_3286]; } } // the cursor is now initialized, even if an error occurred or it is dead - cursor[kInitialized] = true; + this[kInitialized] = true; - if (err || cursorIsDead(cursor)) { - return cleanupCursor(cursor, { error: err }, () => callback(err, nextDocument(cursor))); + if (err || cursorIsDead(this)) { + return cleanupCursor(this, { error: err }, () => callback(err, nextDocument(this))); } - next(cursor, blocking, callback); + callback(); + }); + } +} + +function nextDocument(cursor: AbstractCursor): T | null | undefined { + if (cursor[kDocuments] == null || !cursor[kDocuments].length) { + return null; + } + + const doc = cursor[kDocuments].shift(); + if (doc) { + const transform = cursor[kTransform]; + if (transform) { + return transform(doc) as T; + } + + return doc; + } + + return null; +} + +function next(cursor: AbstractCursor, blocking: boolean, callback: Callback): void { + const cursorId = cursor[kId]; + if (cursor.closed) { + return callback(undefined, null); + } + + if (cursor[kDocuments] && cursor[kDocuments].length) { + callback(undefined, nextDocument(cursor)); + return; + } + + if (cursorId == null) { + // All cursors must operate within a session, one must be made implicitly if not explicitly provided + cursor.initialize((err, value) => { + if (err) return callback(err); + if (value) { + return callback(undefined, value); + } + return next(cursor, blocking, callback); }); return; diff --git a/test/integration/change-streams/change_streams.spec.test.js b/test/integration/change-streams/change_streams.spec.test.js index 49ba850993..436e7f493f 100644 --- a/test/integration/change-streams/change_streams.spec.test.js +++ b/test/integration/change-streams/change_streams.spec.test.js @@ -7,18 +7,9 @@ const { runUnifiedSuite } = require('../../tools/unified-spec-runner/runner'); const camelCase = require('lodash.camelcase'); const { LEGACY_HELLO_COMMAND } = require('../../../src/constants'); const { delay, setupDatabase } = require('../shared'); -const { suite } = require('../../spec/change-streams/node-specific/change-streams'); describe('Change Streams Spec - Unified', function () { - runUnifiedSuite(loadSpecTests(path.join('change-streams', 'unified')), [ - 'Test with document comment', - 'Test with string comment', - 'Test that comment is set on getMore' - ]); -}); - -describe('Change Streams Spec - Node-Specific', function () { - runUnifiedSuite([suite]); + runUnifiedSuite(loadSpecTests(path.join('change-streams', 'unified'))); }); // TODO: NODE-3819: Unskip flaky MacOS tests. 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..c4de5f78e8 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-4051): fix change stream resume logic + 'Test consecutive resume' ].concat(process.env.AUTH === 'auth' ? FAILING_TESTS_AUTH_ENABLED : []); describe('Unified test format runner', function unifiedTestRunner() { diff --git a/test/tools/unified-spec-runner/entities.ts b/test/tools/unified-spec-runner/entities.ts index b26c0fccfc..7002176c17 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/operations.ts b/test/tools/unified-spec-runner/operations.ts index f9532654eb..0298230f80 100644 --- a/test/tools/unified-spec-runner/operations.ts +++ b/test/tools/unified-spec-runner/operations.ts @@ -1,21 +1,13 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { expect } from 'chai'; -import { - AbstractCursor, - Collection, - Db, - Document, - GridFSFile, - MongoClient, - ObjectId -} from '../../../src'; +import { Collection, Db, Document, GridFSFile, MongoClient, ObjectId } from '../../../src'; 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 { EntitiesMap, UnifiedChangeStream } from './entities'; import { expectErrorCheck, resultCheck } from './match'; import type { OperationDescription } from './schema'; import { translateOptions } from './unified-utils'; @@ -209,15 +201,12 @@ operations.set('createChangeStream', async ({ entities, operation }) => { batchSize: operation.arguments.batchSize, comment: operation.arguments.comment }); - changeStream.eventCollector = new EventCollector(changeStream, ['init', 'change', 'error']); - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - reject(new Error('Change stream never started')); - }, 2000); + changeStream.eventCollector = new EventCollector(changeStream, ['init', 'error']); - changeStream.cursor.once('init', () => { - clearTimeout(timeout); + return new Promise((resolve, reject) => { + changeStream.cursor.initialize(err => { + if (err) return reject(err); resolve(changeStream); }); }); @@ -305,16 +294,32 @@ 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 + 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(); + } + + if (changeStream.cursorStream == null) { + return changeStream.cursor.next(); + } else { return Promise.race([ changeStream.eventCollector.waitAndShiftEvent('change'), changeStream.eventCollector.waitAndShiftEvent('error') ]); - } catch (e) { - const findCursor = entities.getEntity('cursor', operation.object); - return await findCursor.next(); } }); diff --git a/test/tools/utils.ts b/test/tools/utils.ts index 1fb6ed6ce7..fe5f32e887 100644 --- a/test/tools/utils.ts +++ b/test/tools/utils.ts @@ -87,8 +87,18 @@ export class EventCollector { * 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) => { + waitAndShiftEvent(eventName) { + return new Promise((resolve, reject) => { + if (!this._events[eventName]) { + return reject( + `Error: attempted to listen for ${eventName} but no listener handler is set` + eventName + ); + } + if (!this._events[eventName]) { + return reject( + `Error: attempted to listen for ${eventName} but no listener handler is set` + eventName + ); + } if (this._events[eventName].length > 0) { return resolve(this._events[eventName].shift()); } From d9d09ee3c7be38f41f1cbcf58fdcc24f1a3c2e4b Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 21 Mar 2022 16:55:23 -0400 Subject: [PATCH 22/54] fix: address lint warning in entities.ts --- test/tools/unified-spec-runner/entities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tools/unified-spec-runner/entities.ts b/test/tools/unified-spec-runner/entities.ts index 7002176c17..fe8f7e9f5e 100644 --- a/test/tools/unified-spec-runner/entities.ts +++ b/test/tools/unified-spec-runner/entities.ts @@ -259,7 +259,7 @@ export type Entity = | Db | Collection | ClientSession - | AbstractCursor + | AbstractCursor | UnifiedChangeStream | GridFSBucket | Document; // Results from operations From 9744897403fe61475fe18f13461cd649979a55c7 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 22 Mar 2022 12:36:40 -0400 Subject: [PATCH 23/54] fix: fix unit tests for get_more opreation --- src/operations/get_more.ts | 13 ++- test/unit/connection_string.test.ts | 9 +- test/unit/operations/get_more.test.js | 118 -------------------- test/unit/operations/get_more.test.ts | 149 ++++++++++++++++++++++++++ 4 files changed, 163 insertions(+), 126 deletions(-) delete mode 100644 test/unit/operations/get_more.test.js create mode 100644 test/unit/operations/get_more.test.ts diff --git a/src/operations/get_more.ts b/src/operations/get_more.ts index 15d475eecc..a1ca99a0e8 100644 --- a/src/operations/get_more.ts +++ b/src/operations/get_more.ts @@ -22,6 +22,13 @@ export interface GetMoreOptions extends OperationOptions { maxTimeMS?: number; } +function filterOptions( + options: Record, + invalidOptions: Set +): Record { + return Object.fromEntries(Object.entries(options).filter(([key]) => !invalidOptions.has(key))); +} + /** @internal */ export class GetMoreOperation extends AbstractOperation { cursorId: Long; @@ -29,12 +36,10 @@ 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 && 'comment' in this.options) { - delete this.options.comment; - } + const invalidOptions = maxWireVersion(server) < 9 ? new Set(['comment']) : new Set(); + this.options = filterOptions(options, invalidOptions); this.ns = ns; this.cursorId = cursorId; diff --git a/test/unit/connection_string.test.ts b/test/unit/connection_string.test.ts index b4d45af7c4..63f3292722 100644 --- a/test/unit/connection_string.test.ts +++ b/test/unit/connection_string.test.ts @@ -291,9 +291,10 @@ describe('Connection String', function () { describe('resolveSRVRecord()', () => { const resolveSRVRecordAsync = promisify(resolveSRVRecord); + const sandbox = sinon.createSandbox(); - afterEach(async () => { - sinon.restore(); + afterEach(() => { + sandbox.restore(); }); function makeStub(txtRecord: string) { @@ -310,11 +311,11 @@ describe('Connection String', function () { // first call is for stubbing resolveSrv // second call is for stubbing resolveTxt - sinon.stub(dns, 'resolveSrv').callsFake((address, callback) => { + sandbox.stub(dns, 'resolveSrv').callsFake((address, callback) => { return process.nextTick(callback, null, mockAddress); }); - sinon.stub(dns, 'resolveTxt').callsFake((address, whatWeTest) => { + sandbox.stub(dns, 'resolveTxt').callsFake((address, whatWeTest) => { whatWeTest(null, mockRecord); }); } 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..36dc3b194d --- /dev/null +++ b/test/unit/operations/get_more.test.ts @@ -0,0 +1,149 @@ +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 () { + context('when the server is the same as the instance', function () { + 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) => { + console.error('executing'); + cb(); + }); + + it('executes a getMore on the provided server', function (done) { + 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 () { + 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); + + 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(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; + }); + }); + }); +}); From 960e887b75aa63d09f407463e86c397af328605c Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 22 Mar 2022 12:38:59 -0400 Subject: [PATCH 24/54] chore: remove old node_specific change stream tests --- .../node-specific/change-streams.ts | 290 ------------------ 1 file changed, 290 deletions(-) delete mode 100644 test/spec/change-streams/node-specific/change-streams.ts diff --git a/test/spec/change-streams/node-specific/change-streams.ts b/test/spec/change-streams/node-specific/change-streams.ts deleted file mode 100644 index ec6d81db8f..0000000000 --- a/test/spec/change-streams/node-specific/change-streams.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { UnifiedSuite } from '../../../tools/unified-spec-runner/schema'; - -export const suite: UnifiedSuite = { - description: 'change-streams', - schemaVersion: '1.0', - runOnRequirements: [ - { - topologies: ['replicaset', 'sharded-replicaset'] - } - ], - createEntities: [ - { - client: { - id: 'client0', - observeEvents: ['commandStartedEvent'] - } - }, - { - database: { - id: 'database0', - client: 'client0', - databaseName: 'database0' - } - }, - { - collection: { - id: 'collection0', - database: 'database0', - collectionName: 'collection0' - } - } - ], - initialData: [ - { - collectionName: 'collection0', - databaseName: 'database0', - documents: [] - } - ], - tests: [ - { - 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' - } - } - } - }, - { - commandStartedEvent: { - command: { - getMore: { - $$type: ['int', 'long'] - }, - collection: 'collection0', - comment: { - name: 'test1' - } - }, - commandName: 'getMore', - databaseName: 'database0' - } - } - ] - } - ] - }, - { - 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' - } - } - }, - { - commandStartedEvent: { - command: { - getMore: { - $$type: ['int', 'long'] - }, - collection: 'collection0' - }, - commandName: 'getMore', - databaseName: 'database0' - } - } - ] - } - ] - }, - { - description: 'Test that comment is set on getMore', - runOnRequirements: [ - { - minServerVersion: '4.4.0', - topologies: ['single', '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: { - getMore: { - $$type: ['int', 'long'] - }, - collection: 'collection0', - comment: 'comment' - }, - commandName: 'getMore', - databaseName: 'database0' - } - }, - { - commandStartedEvent: { - command: { - insert: 'collection0', - documents: [ - { - _id: 1, - a: 1 - } - ] - } - } - }, - { - commandStartedEvent: { - command: { - getMore: { - $$type: ['int', 'long'] - }, - collection: 'collection0', - comment: 'comment' - }, - commandName: 'getMore', - databaseName: 'database0' - } - } - ] - } - ] - } - ] -}; From 016652aa2b615618abc4eabe269a5b79d4ffe478 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 22 Mar 2022 13:05:09 -0400 Subject: [PATCH 25/54] fix: misc changes before re-request review --- src/cursor/abstract_cursor.ts | 14 ++++++++++---- test/tools/unified-spec-runner/operations.ts | 2 +- test/tools/utils.ts | 4 +--- test/unit/operations/get_more.test.ts | 5 +---- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index 0a80a9d923..cc7188f98a 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -630,13 +630,19 @@ export abstract class AbstractCursor< executeOperation(this, getMoreOperation, callback); } - /** @internal */ - initialize(callback: Callback): void { + /** + * @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. + */ + __initialize(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.initialize(callback); + return this.__initialize(callback); }); } else if (this[kTopology].hasSessionSupport()) { this[kSession] = this[kTopology].startSession({ owner: this, explicit: false }); @@ -722,7 +728,7 @@ function next(cursor: AbstractCursor, blocking: boolean, callback: Callback { + cursor.__initialize((err, value) => { if (err) return callback(err); if (value) { return callback(undefined, value); diff --git a/test/tools/unified-spec-runner/operations.ts b/test/tools/unified-spec-runner/operations.ts index 0298230f80..51a178f302 100644 --- a/test/tools/unified-spec-runner/operations.ts +++ b/test/tools/unified-spec-runner/operations.ts @@ -205,7 +205,7 @@ operations.set('createChangeStream', async ({ entities, operation }) => { changeStream.eventCollector = new EventCollector(changeStream, ['init', 'error']); return new Promise((resolve, reject) => { - changeStream.cursor.initialize(err => { + changeStream.cursor.__initialize(err => { if (err) return reject(err); resolve(changeStream); }); diff --git a/test/tools/utils.ts b/test/tools/utils.ts index fe5f32e887..7202f41926 100644 --- a/test/tools/utils.ts +++ b/test/tools/utils.ts @@ -90,9 +90,7 @@ export class EventCollector { waitAndShiftEvent(eventName) { return new Promise((resolve, reject) => { if (!this._events[eventName]) { - return reject( - `Error: attempted to listen for ${eventName} but no listener handler is set` + eventName - ); + return reject(`Error: attempted to listen for ${eventName} but no listener handler is set`); } if (!this._events[eventName]) { return reject( diff --git a/test/unit/operations/get_more.test.ts b/test/unit/operations/get_more.test.ts index 36dc3b194d..a114228ade 100644 --- a/test/unit/operations/get_more.test.ts +++ b/test/unit/operations/get_more.test.ts @@ -82,10 +82,7 @@ describe('GetMoreOperation', function () { const opts = { ...options, session }; const operation = new GetMoreOperation(ns, cursorId, server, opts); - const stub = sinon.stub(server, 'getMore').callsFake((_, __, ___, cb) => { - console.error('executing'); - cb(); - }); + const stub = sinon.stub(server, 'getMore').callsFake((_, __, ___, cb) => cb()); it('executes a getMore on the provided server', function (done) { const callback = () => { From 2c705ea2541d89e89f82ffac5a2cfa0ae643e357 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 25 Mar 2022 14:40:22 -0400 Subject: [PATCH 26/54] test: sync change stream, aggregate and find spec tests https://github.com/mongodb/specifications/pull/1161 updates the specs to only send `comment` on getMore commands if the server version is >= 4.4. Although this functionality was already implemented as a part of this PR, the spec tests were not. This commit brings in the spec affected spec tests. --- .../unified/change-streams.json | 97 ++++++++++++- .../change-streams/unified/change-streams.yml | 49 ++++++- test/spec/crud/unified/aggregate.json | 135 +++++++++++++++++- test/spec/crud/unified/aggregate.yml | 52 ++++++- test/spec/crud/unified/find-comment.json | 117 ++++++++++++++- test/spec/crud/unified/find-comment.yml | 46 +++++- 6 files changed, 470 insertions(+), 26 deletions(-) diff --git a/test/spec/change-streams/unified/change-streams.json b/test/spec/change-streams/unified/change-streams.json index bc9d462ee3..5fd2544ce0 100644 --- a/test/spec/change-streams/unified/change-streams.json +++ b/test/spec/change-streams/unified/change-streams.json @@ -254,7 +254,98 @@ { "minServerVersion": "4.4.0", "topologies": [ - "single", + "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" ] } @@ -324,7 +415,9 @@ ] }, "collection": "collection0", - "comment": "comment" + "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 ed53a4e64e..3bd8a4aa80 100644 --- a/test/spec/change-streams/unified/change-streams.yml +++ b/test/spec/change-streams/unified/change-streams.yml @@ -140,7 +140,50 @@ tests: - description: "Test that comment is set on getMore" runOnRequirements: - minServerVersion: "4.4.0" - topologies: [ single, replicaset ] + 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 @@ -174,6 +217,6 @@ tests: command: getMore: { $$type: [ int, long ] } collection: *collection0 - comment: "comment" + comment: { $$exists: false } commandName: getMore - databaseName: *database0 \ No newline at end of file + databaseName: *database0 diff --git a/test/spec/crud/unified/aggregate.json b/test/spec/crud/unified/aggregate.json index f4b30a773b..0cbfb4e6e9 100644 --- a/test/spec/crud/unified/aggregate.json +++ b/test/spec/crud/unified/aggregate.json @@ -330,12 +330,129 @@ "description": "aggregate with comment sets comment on getMore", "runOnRequirements": [ { - "minServerVersion": "4.4.0", - "topologies": [ - "single", - "replicaset" + "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": [ { @@ -415,7 +532,9 @@ }, "collection": "coll0", "batchSize": 2, - "comment": "comment" + "comment": { + "$$exists": false + } }, "commandName": "getMore", "databaseName": "aggregate-tests" @@ -432,7 +551,9 @@ }, "collection": "coll0", "batchSize": 2, - "comment": "comment" + "comment": { + "$$exists": false + } }, "commandName": "getMore", "databaseName": "aggregate-tests" @@ -443,4 +564,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/test/spec/crud/unified/aggregate.yml b/test/spec/crud/unified/aggregate.yml index 39afc218cd..032aece0fa 100644 --- a/test/spec/crud/unified/aggregate.yml +++ b/test/spec/crud/unified/aggregate.yml @@ -128,7 +128,51 @@ tests: - description: "aggregate with comment sets comment on getMore" runOnRequirements: - minServerVersion: "4.4.0" - topologies: [ single, replicaset ] + 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: @@ -158,7 +202,7 @@ tests: getMore: { $$type: [ int, long ] } collection: *collection0Name batchSize: 2 - comment: "comment" + comment: { $$exists: false } commandName: getMore databaseName: *database0Name - commandStartedEvent: @@ -166,6 +210,6 @@ tests: getMore: { $$type: [ int, long ] } collection: *collection0Name batchSize: 2 - comment: "comment" + comment: { $$exists: false } commandName: getMore - databaseName: *database0Name \ No newline at end of file + databaseName: *database0Name diff --git a/test/spec/crud/unified/find-comment.json b/test/spec/crud/unified/find-comment.json index 3d92be7c33..600a3723f1 100644 --- a/test/spec/crud/unified/find-comment.json +++ b/test/spec/crud/unified/find-comment.json @@ -198,13 +198,114 @@ "description": "find with comment sets comment on getMore", "runOnRequirements": [ { - "minServerVersion": "4.4.0", - "topologies": [ - "single", - "replicaset" + "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", @@ -271,7 +372,9 @@ }, "collection": "coll0", "batchSize": 2, - "comment": "comment" + "comment": { + "$$exists": false + } } } }, @@ -286,7 +389,9 @@ }, "collection": "coll0", "batchSize": 2, - "comment": "comment" + "comment": { + "$$exists": false + } } } } diff --git a/test/spec/crud/unified/find-comment.yml b/test/spec/crud/unified/find-comment.yml index 72009722a8..905241ad0e 100644 --- a/test/spec/crud/unified/find-comment.yml +++ b/test/spec/crud/unified/find-comment.yml @@ -76,7 +76,7 @@ tests: object: *collection0 arguments: filter: *filter - comment: &comment { key: "value"} + comment: *comment expectError: isClientError: false expectEvents: @@ -91,7 +91,45 @@ tests: - description: "find with comment sets comment on getMore" runOnRequirements: - minServerVersion: "4.4.0" - topologies: [ single, replicaset ] + 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 @@ -119,10 +157,10 @@ tests: getMore: { $$type: [ int, long ] } collection: *collection0Name batchSize: 2 - comment: "comment" + comment: { $$exists: false } - commandStartedEvent: command: getMore: { $$type: [ int, long ] } collection: *collection0Name batchSize: 2 - comment: "comment" + comment: { $$exists: false } From d999bcc5953bf5d4d1a03ed4af16db46ba082337 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Sun, 27 Mar 2022 18:46:42 -0400 Subject: [PATCH 27/54] misc changes --- src/cmap/connection.ts | 6 ++--- src/cursor/abstract_cursor.ts | 15 ++++++------ src/db.ts | 4 ++-- src/operations/aggregate.ts | 8 ++----- src/operations/delete.ts | 13 +++++++---- src/operations/find.ts | 6 ++--- src/operations/find_and_modify.ts | 13 +++++++---- src/operations/get_more.ts | 12 ++-------- src/operations/insert.ts | 7 ++---- src/operations/update.ts | 6 ++--- src/utils.ts | 24 +++++++++++++++++++- test/tools/unified-spec-runner/operations.ts | 3 ++- 12 files changed, 62 insertions(+), 55 deletions(-) diff --git a/src/cmap/connection.ts b/src/cmap/connection.ts index 709167aa20..a112fea49d 100644 --- a/src/cmap/connection.ts +++ b/src/cmap/connection.ts @@ -24,6 +24,7 @@ import { CancellationToken, TypedEventEmitter } from '../mongo_types'; import { ReadPreference, ReadPreferenceLike } from '../read_preference'; import { applySession, ClientSession, updateSessionFromResponse } from '../sessions'; import { + applyComment, calculateDurationInMs, Callback, ClientMetadata, @@ -583,10 +584,7 @@ export class Connection extends TypedEventEmitter { getMoreCmd.maxTimeMS = options.maxAwaitTimeMS; } - // eslint-disable-next-line no-restricted-syntax - if (options.comment !== undefined) { - getMoreCmd.comment = options.comment; - } + applyComment(options, getMoreCmd); const commandOptions = Object.assign( { diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index cc7188f98a..2c23e210bb 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -18,7 +18,7 @@ import { ReadPreference, ReadPreferenceLike } from '../read_preference'; import type { Server } from '../sdam/server'; import type { Topology } from '../sdam/topology'; import { ClientSession, maybeClearPinnedConnection } from '../sessions'; -import { Callback, maybePromise, MongoDBNamespace, ns } from '../utils'; +import { applyComment, Callback, maybePromise, MongoDBNamespace, ns } from '../utils'; /** @internal */ const kId = Symbol('id'); @@ -42,6 +42,8 @@ const kInitialized = Symbol('initialized'); const kClosed = Symbol('closed'); /** @internal */ const kKilled = Symbol('killed'); +/** @internal */ +const kInit = Symbol.for('kInit'); /** @public */ export const CURSOR_FLAGS = [ @@ -170,10 +172,7 @@ export abstract class AbstractCursor< this[kOptions].batchSize = options.batchSize; } - // eslint-disable-next-line no-restricted-syntax - if (options.comment !== undefined) { - this[kOptions].comment = options.comment; - } + applyComment(options, this[kOptions]); if (typeof options.maxTimeMS === 'number') { this[kOptions].maxTimeMS = options.maxTimeMS; @@ -637,12 +636,12 @@ export abstract class AbstractCursor< * operation. We cannot refactor to use the abstract _initialize method without * a significant refactor. */ - __initialize(callback: Callback): void { + [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.__initialize(callback); + return this[kInit](callback); }); } else if (this[kTopology].hasSessionSupport()) { this[kSession] = this[kTopology].startSession({ owner: this, explicit: false }); @@ -728,7 +727,7 @@ function next(cursor: AbstractCursor, blocking: boolean, callback: Callback { + cursor[kInit]((err, value) => { if (err) return callback(err); if (value) { return callback(undefined, value); diff --git a/src/db.ts b/src/db.ts index 539cbb745d..505ea34372 100644 --- a/src/db.ts +++ b/src/db.ts @@ -44,9 +44,9 @@ import { DbStatsOperation, DbStatsOptions } from './operations/stats'; import { ReadConcern } from './read_concern'; import { ReadPreference, ReadPreferenceLike } from './read_preference'; import { + allowOptions, Callback, DEFAULT_PK_FACTORY, - filterOptions, getTopology, MongoDBNamespace, resolveOptions @@ -149,7 +149,7 @@ export class Db { options = options ?? {}; // Filter the options - options = filterOptions(options, DB_OPTIONS_ALLOW_LIST); + options = allowOptions(options, DB_OPTIONS_ALLOW_LIST); // Ensure we have a valid db name validateDatabaseName(databaseName); diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index 516da0f278..f6d81586bf 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 { applyComment, Callback, maxWireVersion, MongoDBNamespace } from '../utils'; import { CollationOptions, CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects, Hint } from './operation'; @@ -131,10 +130,7 @@ export class AggregateOperation extends CommandOperation { command.let = options.let; } - // eslint-disable-next-line no-restricted-syntax - if (options.comment !== undefined) { - command.comment = options.comment; - } + applyComment(options, command); command.cursor = options.cursor || {}; if (options.batchSize && !this.hasWriteStage) { diff --git a/src/operations/delete.ts b/src/operations/delete.ts index 1248f4903d..ecfd560c5a 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -3,7 +3,13 @@ import type { Collection } from '../collection'; import { MongoCompatibilityError, MongoServerError } from '../error'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; -import { Callback, collationNotSupported, maxWireVersion, MongoDBNamespace } from '../utils'; +import { + applyComment, + Callback, + collationNotSupported, + maxWireVersion, + MongoDBNamespace +} from '../utils'; import type { WriteConcernOptions } from '../write_concern'; import { CollationOptions, CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects, Hint } from './operation'; @@ -85,10 +91,7 @@ export class DeleteOperation extends CommandOperation { command.let = options.let; } - // eslint-disable-next-line no-restricted-syntax - if (options.comment !== undefined) { - command.comment = options.comment; - } + applyComment(options, command); if (options.explain != null && maxWireVersion(server) < 3) { return callback diff --git a/src/operations/find.ts b/src/operations/find.ts index a41acbae5a..254f807f58 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -7,6 +7,7 @@ import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { formatSort, Sort } from '../sort'; import { + applyComment, Callback, decorateWithExplain, maxWireVersion, @@ -248,10 +249,7 @@ function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOp findCommand.singleBatch = options.singleBatch; } - // eslint-disable-next-line no-restricted-syntax - if (options.comment !== undefined) { - findCommand.comment = options.comment; - } + applyComment(options, findCommand); if (typeof options.maxTimeMS === 'number') { findCommand.maxTimeMS = options.maxTimeMS; diff --git a/src/operations/find_and_modify.ts b/src/operations/find_and_modify.ts index b8bf3d2c28..8957f125bd 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -5,7 +5,13 @@ import { ReadPreference } from '../read_preference'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { formatSort, Sort, SortForCmd } from '../sort'; -import { Callback, decorateWithCollation, hasAtomicOperators, maxWireVersion } from '../utils'; +import { + applyComment, + Callback, + decorateWithCollation, + hasAtomicOperators, + maxWireVersion +} from '../utils'; import type { WriteConcern, WriteConcernSettings } from '../write_concern'; import { CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; @@ -149,10 +155,7 @@ class FindAndModifyOperation extends CommandOperation { this.cmdBase.let = options.let; } - // eslint-disable-next-line no-restricted-syntax - if (options.comment !== undefined) { - this.cmdBase.comment = options.comment; - } + applyComment(options, this.cmdBase); // force primary read preference this.readPreference = ReadPreference.primary; diff --git a/src/operations/get_more.ts b/src/operations/get_more.ts index a1ca99a0e8..db441048a6 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 { Callback, maxWireVersion, MongoDBNamespace } from '../utils'; +import { Callback, disallowOptions, maxWireVersion, MongoDBNamespace } from '../utils'; import { AbstractOperation, Aspect, defineAspects, OperationOptions } from './operation'; /** @@ -22,13 +22,6 @@ export interface GetMoreOptions extends OperationOptions { maxTimeMS?: number; } -function filterOptions( - options: Record, - invalidOptions: Set -): Record { - return Object.fromEntries(Object.entries(options).filter(([key]) => !invalidOptions.has(key))); -} - /** @internal */ export class GetMoreOperation extends AbstractOperation { cursorId: Long; @@ -38,8 +31,7 @@ export class GetMoreOperation extends AbstractOperation { super(options); // comment on getMore is only supported for server versions 4.4 and above - const invalidOptions = maxWireVersion(server) < 9 ? new Set(['comment']) : new Set(); - this.options = filterOptions(options, invalidOptions); + this.options = maxWireVersion(server) < 9 ? disallowOptions(options, ['comment']) : options; this.ns = ns; this.cursorId = cursorId; diff --git a/src/operations/insert.ts b/src/operations/insert.ts index fc9bddb029..51d1b0158d 100644 --- a/src/operations/insert.ts +++ b/src/operations/insert.ts @@ -5,7 +5,7 @@ import { MongoInvalidArgumentError, MongoServerError } from '../error'; import type { InferIdType } from '../mongo_types'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; -import type { Callback, MongoDBNamespace } from '../utils'; +import { applyComment, Callback, MongoDBNamespace } from '../utils'; import { WriteConcern } from '../write_concern'; import { BulkWriteOperation } from './bulk_write'; import { CommandOperation, CommandOperationOptions } from './command'; @@ -41,10 +41,7 @@ export class InsertOperation extends CommandOperation { command.bypassDocumentValidation = options.bypassDocumentValidation; } - // eslint-disable-next-line no-restricted-syntax - if (options.comment !== undefined) { - command.comment = options.comment; - } + applyComment(options, command); super.executeCommand(server, session, command, callback); } diff --git a/src/operations/update.ts b/src/operations/update.ts index fcb0ae6fef..ba14c40472 100644 --- a/src/operations/update.ts +++ b/src/operations/update.ts @@ -4,6 +4,7 @@ import { MongoCompatibilityError, MongoInvalidArgumentError, MongoServerError } import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { + applyComment, Callback, collationNotSupported, hasAtomicOperators, @@ -107,10 +108,7 @@ export class UpdateOperation extends CommandOperation { command.let = options.let; } - // eslint-disable-next-line no-restricted-syntax - if (options.comment !== undefined) { - command.comment = options.comment; - } + applyComment(options, command); const statementWithCollation = this.statements.find(statement => !!statement.collation); if ( diff --git a/src/utils.ts b/src/utils.ts index 129d3995b7..52317148e8 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -176,7 +176,7 @@ export function mergeOptions(target: T, source: S): T & S { } /** @internal */ -export function filterOptions(options: AnyOptions, names: string[]): AnyOptions { +export function allowOptions(options: AnyOptions, names: string[]): AnyOptions { const filterOptions: AnyOptions = {}; for (const name in options) { @@ -189,6 +189,28 @@ export function filterOptions(options: AnyOptions, names: string[]): AnyOptions return filterOptions; } +/** @internal */ +export function disallowOptions(options: AnyOptions, denylist: string[]): AnyOptions { + const accumulatedOptions: AnyOptions = {}; + + for (const name in options) { + if (!denylist.includes(name)) { + accumulatedOptions[name] = options[name]; + } + } + + // Filtered options + return accumulatedOptions; +} + +/** @internal */ +export function applyComment(options: AnyOptions, command: Document) { + // eslint-disable-next-line no-restricted-syntax + if (options.comment !== undefined) { + command.comment = options.comment; + } +} + interface HasRetryableWrites { retryWrites?: boolean; } diff --git a/test/tools/unified-spec-runner/operations.ts b/test/tools/unified-spec-runner/operations.ts index 51a178f302..1a65f794b2 100644 --- a/test/tools/unified-spec-runner/operations.ts +++ b/test/tools/unified-spec-runner/operations.ts @@ -205,7 +205,8 @@ operations.set('createChangeStream', async ({ entities, operation }) => { changeStream.eventCollector = new EventCollector(changeStream, ['init', 'error']); return new Promise((resolve, reject) => { - changeStream.cursor.__initialize(err => { + const init = Symbol.for('kInit'); + changeStream.cursor[init](err => { if (err) return reject(err); resolve(changeStream); }); From 48cd535d3add0e8549a8c7ec65366a424fbc3aa7 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Sun, 27 Mar 2022 18:47:01 -0400 Subject: [PATCH 28/54] test: add test for comments with falsy value --- .../comment_with_falsy_values.test.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 test/integration/node-specific/comment_with_falsy_values.test.ts 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..225d7ac686 --- /dev/null +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -0,0 +1,29 @@ +import { expect } from 'chai'; + +import { Collection, Db } from '../../../src'; +import { MongoClient } from '../../../src/mongo_client'; + +describe('comment option tests', function () { + let client: MongoClient; + let db: Db; + let collection: Collection<{ _id: number }>; + + beforeEach(async function () { + client = await this.configuration.newClient({ monitorCommands: true }).connect(); + db = client.db('comment_with_falsy_values'); + collection = db.collection<{ _id: number }>('test'); + await collection.insertMany([{ _id: 0 }]); + }); + + afterEach(async function () { + await db.dropDatabase(); + await client.close(); + }); + + it(`should allow falsy values for the comment field post 4.4`, async function () { + let command = null; + client.on('commandStarted', ({ command: _command }) => (command = _command)); + await collection.find({ _id: 0 }, { comment: 0 }).toArray(); + expect(command.comment).to.equal(0); + }); +}); From d16f651122f36d2cece6e388b547ce7ca35596b2 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 28 Mar 2022 10:43:32 -0400 Subject: [PATCH 29/54] fix: add run-on requirements to falsy values test --- .../node-specific/comment_with_falsy_values.test.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts index 225d7ac686..07e6211dc0 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -20,10 +20,13 @@ describe('comment option tests', function () { await client.close(); }); - it(`should allow falsy values for the comment field post 4.4`, async function () { - let command = null; - client.on('commandStarted', ({ command: _command }) => (command = _command)); - await collection.find({ _id: 0 }, { comment: 0 }).toArray(); - expect(command.comment).to.equal(0); + it(`should allow falsy values for the comment field post 4.4`, { + metadata: { requires: { topology: '!server', mongodb: '>=4.4' } }, + test: async function () { + let command = null; + client.on('commandStarted', ({ command: _command }) => (command = _command)); + await collection.find({ _id: 0 }, { comment: 0 }).toArray(); + expect(command.comment).to.equal(0); + } }); }); From 0da642f3fc2e6b02a25fd8d19bb498abf9abe80e Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 28 Mar 2022 10:44:51 -0400 Subject: [PATCH 30/54] fix: remove legacy (<3.2) logic from cursors --- src/cursor/abstract_cursor.ts | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index 2c23e210bb..d5bac7f5a2 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -654,26 +654,17 @@ export abstract class AbstractCursor< 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[kId] = + typeof response.cursor.id === 'number' + ? Long.fromNumber(response.cursor.id) + : response.cursor.id; - this[kDocuments] = response.cursor.firstBatch; - } else { - // NOTE: This is for support of older servers (<3.2) which do not use commands - this[kId] = - typeof response.cursorId === 'number' - ? Long.fromNumber(response.cursorId) - : response.cursorId; - this[kDocuments] = response.documents; + 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 From 14f2ea22f961c3d44ba170a5d0bb42c98218af4e Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 28 Mar 2022 11:00:08 -0400 Subject: [PATCH 31/54] fix: update comment falsy values tests with more falsy values --- .../comment_with_falsy_values.test.ts | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts index 07e6211dc0..12c1ba7707 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -3,7 +3,7 @@ import { expect } from 'chai'; import { Collection, Db } from '../../../src'; import { MongoClient } from '../../../src/mongo_client'; -describe('comment option tests', function () { +describe('comment option w/ falsy values', function () { let client: MongoClient; let db: Db; let collection: Collection<{ _id: number }>; @@ -20,8 +20,8 @@ describe('comment option tests', function () { await client.close(); }); - it(`should allow falsy values for the comment field post 4.4`, { - metadata: { requires: { topology: '!server', mongodb: '>=4.4' } }, + it(`should allow 0 for comment option`, { + metadata: { requires: { mongodb: '>=4.4' } }, test: async function () { let command = null; client.on('commandStarted', ({ command: _command }) => (command = _command)); @@ -29,4 +29,24 @@ describe('comment option tests', function () { expect(command.comment).to.equal(0); } }); + + it(`should allow the empty string ('') for comment option`, { + metadata: { requires: { mongodb: '>=4.4' } }, + test: async function () { + let command = null; + client.on('commandStarted', ({ command: _command }) => (command = _command)); + await collection.find({ _id: 0 }, { comment: '' }).toArray(); + expect(command.comment).to.equal(''); + } + }); + + it(`should allow false for comment option`, { + metadata: { requires: { mongodb: '>=4.4' } }, + test: async function () { + let command = null; + client.on('commandStarted', ({ command: _command }) => (command = _command)); + await collection.find({ _id: 0 }, { comment: false }).toArray(); + expect(command.comment).to.equal(false); + } + }); }); From 2f810ac0798a8b7749d8fb9d579fc8e84fc9b848 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 28 Mar 2022 12:34:46 -0400 Subject: [PATCH 32/54] fix: Symbol() instead of Symbol.for() --- src/cursor/abstract_cursor.ts | 2 +- test/tools/unified-spec-runner/operations.ts | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index d5bac7f5a2..a7bd6aa4e7 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -43,7 +43,7 @@ const kClosed = Symbol('closed'); /** @internal */ const kKilled = Symbol('killed'); /** @internal */ -const kInit = Symbol.for('kInit'); +const kInit = Symbol('kInit'); /** @public */ export const CURSOR_FLAGS = [ diff --git a/test/tools/unified-spec-runner/operations.ts b/test/tools/unified-spec-runner/operations.ts index 1a65f794b2..1b2514241c 100644 --- a/test/tools/unified-spec-runner/operations.ts +++ b/test/tools/unified-spec-runner/operations.ts @@ -1,12 +1,20 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { expect } from 'chai'; -import { Collection, Db, Document, GridFSFile, MongoClient, ObjectId } from '../../../src'; +import { + AbstractCursor, + Collection, + Db, + Document, + GridFSFile, + MongoClient, + ObjectId +} from '../../../src'; 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 { EventCollector, getSymbolFrom } from '../../tools/utils'; import { EntitiesMap, UnifiedChangeStream } from './entities'; import { expectErrorCheck, resultCheck } from './match'; import type { OperationDescription } from './schema'; @@ -205,7 +213,7 @@ operations.set('createChangeStream', async ({ entities, operation }) => { changeStream.eventCollector = new EventCollector(changeStream, ['init', 'error']); return new Promise((resolve, reject) => { - const init = Symbol.for('kInit'); + const init = getSymbolFrom(AbstractCursor.prototype, 'kInit'); changeStream.cursor[init](err => { if (err) return reject(err); resolve(changeStream); From 127f71bf3fd99745940c4bf8365cd94d61d85564 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 28 Mar 2022 13:34:47 -0400 Subject: [PATCH 33/54] fix: add back if statement for failing tests --- src/cursor/abstract_cursor.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index a7bd6aa4e7..1c4e157130 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -654,17 +654,19 @@ export abstract class AbstractCursor< this[kServer] = state.server; this[kSession] = state.session; - this[kId] = - typeof response.cursor.id === 'number' - ? Long.fromNumber(response.cursor.id) - : response.cursor.id; + 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); + } - if (response.cursor.ns) { - this[kNamespace] = ns(response.cursor.ns); + this[kDocuments] = response.cursor.firstBatch; } - 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 From eb5069d0128bdb1bb300d947836d592dc67ec33f Mon Sep 17 00:00:00 2001 From: Neal Beeken Date: Mon, 28 Mar 2022 15:11:21 -0400 Subject: [PATCH 34/54] Add programatic unified test run for falsey comments Co-authored-by: Bailey Pearson --- .../comment_with_falsy_values.test.ts | 156 ++++++++++++------ test/tools/unified-spec-runner/match.ts | 3 + test/tools/unified-spec-runner/schema.ts | 5 +- 3 files changed, 114 insertions(+), 50 deletions(-) diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts index 12c1ba7707..043611717c 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -1,52 +1,112 @@ -import { expect } from 'chai'; - -import { Collection, Db } from '../../../src'; -import { MongoClient } from '../../../src/mongo_client'; - -describe('comment option w/ falsy values', function () { - let client: MongoClient; - let db: Db; - let collection: Collection<{ _id: number }>; - - beforeEach(async function () { - client = await this.configuration.newClient({ monitorCommands: true }).connect(); - db = client.db('comment_with_falsy_values'); - collection = db.collection<{ _id: number }>('test'); - await collection.insertMany([{ _id: 0 }]); - }); - - afterEach(async function () { - await db.dropDatabase(); - await client.close(); - }); - - it(`should allow 0 for comment option`, { - metadata: { requires: { mongodb: '>=4.4' } }, - test: async function () { - let command = null; - client.on('commandStarted', ({ command: _command }) => (command = _command)); - await collection.find({ _id: 0 }, { comment: 0 }).toArray(); - expect(command.comment).to.equal(0); +import { Long } from '../../../src'; +import { runUnifiedSuite } from '../../tools/unified-spec-runner/runner'; +import * as uni from '../../tools/unified-spec-runner/schema'; + +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* test() { + for (const [name, args] of [ + // ['replaceOne', {}], + // ['deleteMany', {}], + ['find', { filter: { _id: 1 } }] as const, + ['aggregate', { pipeline: [] }] as const, + // ['updateMany', {}], + // ['bulkWrite', {}], + // ['insertOne', {}], + ['insertMany', { documents: [{ name: 'john' }] }] as const, + // ['deleteOne', {}], + // ['updateOne', {}], + // ['findOneAndDelete', {}], + // ['findOneAndUpdate', {}], + ['findOneAndReplace', { filter: { _id: 1 }, replacement: { x: 12 } }] as const + ]) { + for (const falsyValue of falsyValues) { + yield { name, args: { ...args, comment: falsyValue } }; } - }); - - it(`should allow the empty string ('') for comment option`, { - metadata: { requires: { mongodb: '>=4.4' } }, - test: async function () { - let command = null; - client.on('commandStarted', ({ command: _command }) => (command = _command)); - await collection.find({ _id: 0 }, { comment: '' }).toArray(); - expect(command.comment).to.equal(''); + } +} + +const operations = Array.from(test()); + +const unifiedTestBase: uni.UnifiedSuite = { + description: 'comment', + schemaVersion: '1.0', + createEntities: [ + { + client: { + id: 'client0', + useMultipleMongoses: true, + observeEvents: ['commandStartedEvent'] + } + }, + { + database: { + id: 'database0', + client: 'client0', + databaseName: 'comment-falsy-values-tests' + } + }, + { + collection: { + id: 'collection0', + database: 'database0', + collectionName: 'coll0' + } } - }); - - it(`should allow false for comment option`, { - metadata: { requires: { mongodb: '>=4.4' } }, - test: async function () { - let command = null; - client.on('commandStarted', ({ command: _command }) => (command = _command)); - await collection.find({ _id: 0 }, { comment: false }).toArray(); - expect(command.comment).to.equal(false); + ], + initialData: [ + { + collectionName: 'coll0', + databaseName: 'comment-falsy-values-tests', + documents: [ + { _id: 1, x: 11 }, + { _id: 2, toBeDeleted: true } // This should only be used by the delete test + ] } - }); + ], + tests: operations.map(({ name, args }) => ({ + description: `${name} should pass falsy value ${falsyToString( + args.comment + )} for comment parameter`, + operations: [ + { + name, + object: 'collection0', + arguments: args + } + ], + expectEvents: [ + { + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + comment: args.comment + } + } + } + ] + } + ] + })) +}; + +describe('falsy values tests', () => { + runUnifiedSuite([unifiedTestBase]); }); diff --git a/test/tools/unified-spec-runner/match.ts b/test/tools/unified-spec-runner/match.ts index ce0370e79a..07f05da4a8 100644 --- a/test/tools/unified-spec-runner/match.ts +++ b/test/tools/unified-spec-runner/match.ts @@ -184,6 +184,9 @@ 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 + return; } else { expect(actual).to.equal(expected); } diff --git a/test/tools/unified-spec-runner/schema.ts b/test/tools/unified-spec-runner/schema.ts index 1c6847c59f..8c6ee518b5 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[]]; + createEntities?: EntityDescription[]; + /** Data inserted before **all tests */ initialData?: [CollectionData, ...CollectionData[]]; - tests: [Test, ...Test[]]; + tests: Test[]; _yamlAnchors?: Document; } export const TopologyType = Object.freeze({ From b160c55fdf6f67b576417f981ac8d27b0140e756 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 28 Mar 2022 15:57:43 -0400 Subject: [PATCH 35/54] fix: revert usage of applyComment --- src/cmap/connection.ts | 8 +++++--- src/cursor/abstract_cursor.ts | 8 ++++++-- src/operations/aggregate.ts | 8 ++++++-- src/operations/delete.ts | 14 ++++++-------- src/operations/find.ts | 7 +++++-- src/operations/find_and_modify.ts | 14 ++++++-------- src/operations/insert.ts | 8 ++++++-- src/operations/update.ts | 7 +++++-- src/utils.ts | 9 --------- 9 files changed, 45 insertions(+), 38 deletions(-) diff --git a/src/cmap/connection.ts b/src/cmap/connection.ts index a112fea49d..da565ae116 100644 --- a/src/cmap/connection.ts +++ b/src/cmap/connection.ts @@ -24,7 +24,6 @@ import { CancellationToken, TypedEventEmitter } from '../mongo_types'; import { ReadPreference, ReadPreferenceLike } from '../read_preference'; import { applySession, ClientSession, updateSessionFromResponse } from '../sessions'; import { - applyComment, calculateDurationInMs, Callback, ClientMetadata, @@ -583,8 +582,11 @@ export class Connection extends TypedEventEmitter { if (typeof options.maxAwaitTimeMS === 'number') { getMoreCmd.maxTimeMS = options.maxAwaitTimeMS; } - - applyComment(options, getMoreCmd); + // 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 1c4e157130..1703dca6b3 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -18,7 +18,7 @@ import { ReadPreference, ReadPreferenceLike } from '../read_preference'; import type { Server } from '../sdam/server'; import type { Topology } from '../sdam/topology'; import { ClientSession, maybeClearPinnedConnection } from '../sessions'; -import { applyComment, Callback, maybePromise, MongoDBNamespace, ns } from '../utils'; +import { Callback, maybePromise, MongoDBNamespace, ns } from '../utils'; /** @internal */ const kId = Symbol('id'); @@ -172,7 +172,11 @@ export abstract class AbstractCursor< this[kOptions].batchSize = options.batchSize; } - applyComment(options, this[kOptions]); + // 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; + } if (typeof options.maxTimeMS === 'number') { this[kOptions].maxTimeMS = options.maxTimeMS; diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index f6d81586bf..44ef8a7417 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -2,7 +2,7 @@ import type { Document } from '../bson'; import { MongoInvalidArgumentError } from '../error'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; -import { applyComment, Callback, maxWireVersion, MongoDBNamespace } from '../utils'; +import { Callback, maxWireVersion, MongoDBNamespace } from '../utils'; import { CollationOptions, CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects, Hint } from './operation'; @@ -130,7 +130,11 @@ export class AggregateOperation extends CommandOperation { command.let = options.let; } - applyComment(options, command); + // 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) { diff --git a/src/operations/delete.ts b/src/operations/delete.ts index ecfd560c5a..8bf229d9e2 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -3,13 +3,7 @@ import type { Collection } from '../collection'; import { MongoCompatibilityError, MongoServerError } from '../error'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; -import { - applyComment, - Callback, - collationNotSupported, - maxWireVersion, - MongoDBNamespace -} from '../utils'; +import { Callback, collationNotSupported, maxWireVersion, MongoDBNamespace } from '../utils'; import type { WriteConcernOptions } from '../write_concern'; import { CollationOptions, CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects, Hint } from './operation'; @@ -91,7 +85,11 @@ export class DeleteOperation extends CommandOperation { command.let = options.let; } - applyComment(options, command); + // 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 diff --git a/src/operations/find.ts b/src/operations/find.ts index 254f807f58..5d89e02c82 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -7,7 +7,6 @@ import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { formatSort, Sort } from '../sort'; import { - applyComment, Callback, decorateWithExplain, maxWireVersion, @@ -249,7 +248,11 @@ function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOp findCommand.singleBatch = options.singleBatch; } - applyComment(options, findCommand); + // 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; + } if (typeof options.maxTimeMS === 'number') { findCommand.maxTimeMS = options.maxTimeMS; diff --git a/src/operations/find_and_modify.ts b/src/operations/find_and_modify.ts index 8957f125bd..ba40da0090 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -5,13 +5,7 @@ import { ReadPreference } from '../read_preference'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { formatSort, Sort, SortForCmd } from '../sort'; -import { - applyComment, - Callback, - decorateWithCollation, - hasAtomicOperators, - maxWireVersion -} from '../utils'; +import { Callback, decorateWithCollation, hasAtomicOperators, maxWireVersion } from '../utils'; import type { WriteConcern, WriteConcernSettings } from '../write_concern'; import { CommandOperation, CommandOperationOptions } from './command'; import { Aspect, defineAspects } from './operation'; @@ -155,7 +149,11 @@ class FindAndModifyOperation extends CommandOperation { this.cmdBase.let = options.let; } - applyComment(options, this.cmdBase); + // 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/insert.ts b/src/operations/insert.ts index 51d1b0158d..7584322925 100644 --- a/src/operations/insert.ts +++ b/src/operations/insert.ts @@ -5,7 +5,7 @@ import { MongoInvalidArgumentError, MongoServerError } from '../error'; import type { InferIdType } from '../mongo_types'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; -import { applyComment, Callback, MongoDBNamespace } from '../utils'; +import { Callback, MongoDBNamespace } from '../utils'; import { WriteConcern } from '../write_concern'; import { BulkWriteOperation } from './bulk_write'; import { CommandOperation, CommandOperationOptions } from './command'; @@ -41,7 +41,11 @@ export class InsertOperation extends CommandOperation { command.bypassDocumentValidation = options.bypassDocumentValidation; } - applyComment(options, command); + // 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; + } super.executeCommand(server, session, command, callback); } diff --git a/src/operations/update.ts b/src/operations/update.ts index ba14c40472..a389878a14 100644 --- a/src/operations/update.ts +++ b/src/operations/update.ts @@ -4,7 +4,6 @@ import { MongoCompatibilityError, MongoInvalidArgumentError, MongoServerError } import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; import { - applyComment, Callback, collationNotSupported, hasAtomicOperators, @@ -108,7 +107,11 @@ export class UpdateOperation extends CommandOperation { command.let = options.let; } - applyComment(options, command); + // 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 ( diff --git a/src/utils.ts b/src/utils.ts index 52317148e8..12d6130e92 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -202,15 +202,6 @@ export function disallowOptions(options: AnyOptions, denylist: string[]): AnyOpt // Filtered options return accumulatedOptions; } - -/** @internal */ -export function applyComment(options: AnyOptions, command: Document) { - // eslint-disable-next-line no-restricted-syntax - if (options.comment !== undefined) { - command.comment = options.comment; - } -} - interface HasRetryableWrites { retryWrites?: boolean; } From d3c20c5c518b18f03fdf1278b456ca6597ba5fd1 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Mon, 28 Mar 2022 16:02:46 -0400 Subject: [PATCH 36/54] test: use unified runner for falsy values test --- .../comment_with_falsy_values.test.ts | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts index 043611717c..90bf64eb2b 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -19,20 +19,12 @@ const falsyToString = (value: typeof falsyValues[number]) => { return JSON.stringify(value); }; -function* test() { +function* generateTestCombinations() { for (const [name, args] of [ - // ['replaceOne', {}], - // ['deleteMany', {}], ['find', { filter: { _id: 1 } }] as const, ['aggregate', { pipeline: [] }] as const, - // ['updateMany', {}], - // ['bulkWrite', {}], - // ['insertOne', {}], ['insertMany', { documents: [{ name: 'john' }] }] as const, - // ['deleteOne', {}], - // ['updateOne', {}], - // ['findOneAndDelete', {}], - // ['findOneAndUpdate', {}], + ['deleteOne', { filter: { toBeDeleted: true } }] as const, ['findOneAndReplace', { filter: { _id: 1 }, replacement: { x: 12 } }] as const ]) { for (const falsyValue of falsyValues) { @@ -41,7 +33,7 @@ function* test() { } } -const operations = Array.from(test()); +const operations = Array.from(generateTestCombinations()); const unifiedTestBase: uni.UnifiedSuite = { description: 'comment', @@ -82,7 +74,7 @@ const unifiedTestBase: uni.UnifiedSuite = { tests: operations.map(({ name, args }) => ({ description: `${name} should pass falsy value ${falsyToString( args.comment - )} for comment parameter`, + )} for comment option`, operations: [ { name, @@ -107,6 +99,6 @@ const unifiedTestBase: uni.UnifiedSuite = { })) }; -describe('falsy values tests', () => { +describe('comment w/ falsy values ', () => { runUnifiedSuite([unifiedTestBase]); }); From 056ddaebf52b23f4cbea3f135f1a01bb5739f208 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 29 Mar 2022 09:03:59 -0400 Subject: [PATCH 37/54] fix: address lint issue --- src/operations/insert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/operations/insert.ts b/src/operations/insert.ts index 7584322925..4a31017bff 100644 --- a/src/operations/insert.ts +++ b/src/operations/insert.ts @@ -5,7 +5,7 @@ import { MongoInvalidArgumentError, MongoServerError } from '../error'; import type { InferIdType } from '../mongo_types'; import type { Server } from '../sdam/server'; import type { ClientSession } from '../sessions'; -import { Callback, MongoDBNamespace } from '../utils'; +import type { Callback, MongoDBNamespace } from '../utils'; import { WriteConcern } from '../write_concern'; import { BulkWriteOperation } from './bulk_write'; import { CommandOperation, CommandOperationOptions } from './command'; From cf145bda909f9d9b8beb5cfc1e25325fecbae5fa Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 29 Mar 2022 09:07:42 -0400 Subject: [PATCH 38/54] fix: add runonrequirement to falsy values tests --- test/integration/node-specific/comment_with_falsy_values.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts index 90bf64eb2b..794b410424 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -38,6 +38,7 @@ const operations = Array.from(generateTestCombinations()); const unifiedTestBase: uni.UnifiedSuite = { description: 'comment', schemaVersion: '1.0', + runOnRequirements: [{ minServerVersion: '4.4' }], createEntities: [ { client: { From 0a7e38dddf71ead6ada0b3fac04f5a70662370c9 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 29 Mar 2022 14:35:11 -0400 Subject: [PATCH 39/54] chore: working builder - needs fixup --- .../comment_with_falsy_values.test.ts | 88 +++++++------- test/tools/unified-spec-runner/schema.ts | 2 +- test/tools/utils.ts | 115 ++++++++++++++++++ 3 files changed, 159 insertions(+), 46 deletions(-) diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts index 794b410424..0774bd53d7 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -1,6 +1,6 @@ import { Long } from '../../../src'; import { runUnifiedSuite } from '../../tools/unified-spec-runner/runner'; -import * as uni from '../../tools/unified-spec-runner/schema'; +import { UnifiedTestSuite } from '../../tools/utils'; const falsyValues = [0, false, '', Long.ZERO, null, NaN] as const; const falsyToString = (value: typeof falsyValues[number]) => { @@ -35,11 +35,9 @@ function* generateTestCombinations() { const operations = Array.from(generateTestCombinations()); -const unifiedTestBase: uni.UnifiedSuite = { - description: 'comment', - schemaVersion: '1.0', - runOnRequirements: [{ minServerVersion: '4.4' }], - createEntities: [ +const test = new UnifiedTestSuite('Comment with Falsy Values') + .runOnRequirement({ minServerVersion: '4.0' }) + .createEntities([ { client: { id: 'client0', @@ -51,7 +49,7 @@ const unifiedTestBase: uni.UnifiedSuite = { database: { id: 'database0', client: 'client0', - databaseName: 'comment-falsy-values-tests' + databaseName: '' } }, { @@ -61,45 +59,45 @@ const unifiedTestBase: uni.UnifiedSuite = { collectionName: 'coll0' } } - ], - initialData: [ - { - collectionName: 'coll0', - databaseName: 'comment-falsy-values-tests', - documents: [ - { _id: 1, x: 11 }, - { _id: 2, toBeDeleted: true } // This should only be used by the delete test - ] - } - ], - tests: operations.map(({ name, args }) => ({ - description: `${name} should pass falsy value ${falsyToString( - args.comment - )} for comment option`, - operations: [ - { - name, - object: 'collection0', - arguments: args - } - ], - expectEvents: [ - { - client: 'client0', - events: [ - { - commandStartedEvent: { - command: { - comment: args.comment + ]) + .initialData({ + collectionName: 'coll0', + databaseName: 'comment-falsy-values-tests', + documents: [ + { _id: 1, x: 11 }, + { _id: 2, toBeDeleted: true } // This should only be used by the delete test + ] + }) + .test( + operations.map(({ name, args }) => ({ + description: `${name} should pass falsy value ${falsyToString( + args.comment + )} for comment option`, + operations: [ + { + name, + object: 'collection0', + arguments: args + } + ], + expectEvents: [ + { + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + comment: args.comment + } } } - } - ] - } - ] - })) -}; + ] + } + ] + })) + ) + .build(); -describe('comment w/ falsy values ', () => { - runUnifiedSuite([unifiedTestBase]); +describe.only('comment w/ falsy values ', () => { + runUnifiedSuite([test]); }); diff --git a/test/tools/unified-spec-runner/schema.ts b/test/tools/unified-spec-runner/schema.ts index 8c6ee518b5..8c6bf040d6 100644 --- a/test/tools/unified-spec-runner/schema.ts +++ b/test/tools/unified-spec-runner/schema.ts @@ -22,7 +22,7 @@ export interface UnifiedSuite { runOnRequirements?: RunOnRequirement[]; createEntities?: EntityDescription[]; /** Data inserted before **all tests */ - initialData?: [CollectionData, ...CollectionData[]]; + initialData?: CollectionData[]; tests: Test[]; _yamlAnchors?: Document; } diff --git a/test/tools/utils.ts b/test/tools/utils.ts index 7202f41926..b493c7079f 100644 --- a/test/tools/utils.ts +++ b/test/tools/utils.ts @@ -4,6 +4,13 @@ import util from 'util'; import { Logger } from '../../src/logger'; import { deprecateOptions, DeprecateOptionsConfig } from '../../src/utils'; +import { + CollectionData, + EntityDescription, + RunOnRequirement, + Test, + UnifiedSuite +} from './unified-spec-runner/schema'; export function makeTestFunction(config: DeprecateOptionsConfig) { const fn = (options: any) => { @@ -294,3 +301,111 @@ export interface FailPoint { appName?: string; }; } + +export class UnifiedTestSuite { + /** + * TODO: add node ticket to complete the entitity work + */ + private _anchors = {}; + private _description = 'Default Description'; + private _databaseName = ''; + private _schemaVersion = '1.0'; + private _createEntities: EntityDescription[] = []; + 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; + } + + build(): UnifiedSuite { + const databaseName = + this._databaseName !== '' ? this._databaseName : this._description.replaceAll(' ', '_'); + return { + description: this._description, + schemaVersion: this._schemaVersion, + runOnRequirements: this._runOnRequirement, + createEntities: this._createEntities.map((entity: any) => { + if (entity.database) { + return { + database: { ...entity.database, databaseName } + } as EntityDescription; + } + + return entity as EntityDescription; + }), + initialData: this._initialData.map(data => { + return { + ...data, + databaseName + }; + }), + tests: this._tests + }; + } + + clone(): UnifiedSuite { + return JSON.parse(JSON.stringify(this.build())); + } +} From fba5e13786fdcbd8c23ae67e51ee7749525a03e2 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 29 Mar 2022 15:43:55 -0400 Subject: [PATCH 40/54] fix: allow falsy values in change stream options --- src/change_stream.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/change_stream.ts b/src/change_stream.ts index 1255964551..5e8153831c 100644 --- a/src/change_stream.ts +++ b/src/change_stream.ts @@ -627,7 +627,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]; } } From d0dbaabf55561eda272724bd52637d6496991b20 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Tue, 29 Mar 2022 15:44:32 -0400 Subject: [PATCH 41/54] fix: add change stream falsy tests --- .../comment_with_falsy_values.test.ts | 177 ++++++++++++------ test/tools/unified-spec-runner/schema.ts | 4 +- test/tools/utils.ts | 82 +++++++- 3 files changed, 200 insertions(+), 63 deletions(-) diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts index 0774bd53d7..9c1bac5092 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -1,6 +1,8 @@ +import { ObjectId } from 'bson'; + import { Long } from '../../../src'; import { runUnifiedSuite } from '../../tools/unified-spec-runner/runner'; -import { UnifiedTestSuite } from '../../tools/utils'; +import { TestBuilder, UnifiedTestSuiteBuilder } from '../../tools/utils'; const falsyValues = [0, false, '', Long.ZERO, null, NaN] as const; const falsyToString = (value: typeof falsyValues[number]) => { @@ -33,71 +35,140 @@ function* generateTestCombinations() { } } -const operations = Array.from(generateTestCombinations()); +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 test = new UnifiedTestSuite('Comment with Falsy Values') - .runOnRequirement({ minServerVersion: '4.0' }) - .createEntities([ - { - client: { - id: 'client0', - useMultipleMongoses: true, - observeEvents: ['commandStartedEvent'] - } - }, - { - database: { - id: 'database0', - client: 'client0', - databaseName: '' - } - }, - { - collection: { - id: 'collection0', - database: 'database0', - collectionName: 'coll0' - } - } - ]) +const testSuite = new UnifiedTestSuiteBuilder('Comment with Falsy Values') + .runOnRequirement({ minServerVersion: '4.4' }) .initialData({ collectionName: 'coll0', - databaseName: 'comment-falsy-values-tests', + databaseName: '', documents: [ { _id: 1, x: 11 }, { _id: 2, toBeDeleted: true } // This should only be used by the delete test ] }) - .test( - operations.map(({ name, args }) => ({ - description: `${name} should pass falsy value ${falsyToString( - args.comment - )} for comment option`, - operations: [ + .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: [ { - name, - object: 'collection0', - arguments: args + commandStartedEvent: { + command: { + comment: falsyValue + } + } + } + ] + }) + .toJSON(); +}); + +const testsForGetMore = 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' + }) + .operation({ + name: 'insertOne', + object: 'collection0', + arguments: { + document: { + a: 1 } - ], - expectEvents: [ + } + }) + .operation({ + name: 'iterateUntilDocumentOrError', + object: 'changeStream0', + arguments: {} + }) + .expectEvents({ + client: 'client0', + events: [ + { + commandStartedEvent: { + command: { + comment: falsyValue + } + } + }, { - client: 'client0', - events: [ - { - commandStartedEvent: { - command: { - comment: args.comment - } - } + commandStartedEvent: {} + }, + { + commandStartedEvent: { + command: { + comment: falsyValue } - ] + } } ] - })) - ) - .build(); + }) + .toJSON(); +}); + +const changeStreamTestSuite = new UnifiedTestSuiteBuilder( + 'Change Streams - Comment with Falsy Values' +) + .schemaVersion('1.0') + .runOnRequirement({ minServerVersion: '4.4' }) + .runOnRequirement({ topologies: ['replicaset', 'sharded-replicaset'] }) + .test(testsForChangeStreamsAggregate) + .test(testsForGetMore) + .toJSON(); -describe.only('comment w/ falsy values ', () => { - runUnifiedSuite([test]); +describe('comment w/ falsy values ', () => { + runUnifiedSuite([testSuite]); + runUnifiedSuite([changeStreamTestSuite]); }); diff --git a/test/tools/unified-spec-runner/schema.ts b/test/tools/unified-spec-runner/schema.ts index 8c6bf040d6..aedfc24fd8 100644 --- a/test/tools/unified-spec-runner/schema.ts +++ b/test/tools/unified-spec-runner/schema.ts @@ -127,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 b493c7079f..e1a51b39ac 100644 --- a/test/tools/utils.ts +++ b/test/tools/utils.ts @@ -7,6 +7,8 @@ import { deprecateOptions, DeprecateOptionsConfig } from '../../src/utils'; import { CollectionData, EntityDescription, + ExpectedEventsForClient, + OperationDescription, RunOnRequirement, Test, UnifiedSuite @@ -302,7 +304,49 @@ export interface FailPoint { }; } -export class UnifiedTestSuite { +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; + } + + 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 { /** * TODO: add node ticket to complete the entitity work */ @@ -310,7 +354,29 @@ export class UnifiedTestSuite { private _description = 'Default Description'; private _databaseName = ''; private _schemaVersion = '1.0'; - private _createEntities: EntityDescription[] = []; + 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[] = []; @@ -379,21 +445,21 @@ export class UnifiedTestSuite { return this; } - build(): UnifiedSuite { + toJSON(): UnifiedSuite { const databaseName = this._databaseName !== '' ? this._databaseName : this._description.replaceAll(' ', '_'); return { description: this._description, schemaVersion: this._schemaVersion, runOnRequirements: this._runOnRequirement, - createEntities: this._createEntities.map((entity: any) => { - if (entity.database) { + createEntities: this._createEntities.map(entity => { + if ('database' in entity) { return { database: { ...entity.database, databaseName } - } as EntityDescription; + }; } - return entity as EntityDescription; + return entity; }), initialData: this._initialData.map(data => { return { @@ -406,6 +472,6 @@ export class UnifiedTestSuite { } clone(): UnifiedSuite { - return JSON.parse(JSON.stringify(this.build())); + return JSON.parse(JSON.stringify(this)); } } From f1b15f9dce49310344ff1f939943540412fac2d0 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 30 Mar 2022 11:53:13 -0400 Subject: [PATCH 42/54] wip - address comments pt 1 --- .../comment_with_falsy_values.test.ts | 2 -- .../unified_test_format.spec.test.ts | 2 +- test/tools/unified-spec-runner/operations.ts | 23 ++++--------------- test/tools/utils.ts | 10 ++++---- 4 files changed, 11 insertions(+), 26 deletions(-) diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts index 9c1bac5092..9d0105a4b5 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -1,5 +1,3 @@ -import { ObjectId } from 'bson'; - import { Long } from '../../../src'; import { runUnifiedSuite } from '../../tools/unified-spec-runner/runner'; import { TestBuilder, UnifiedTestSuiteBuilder } from '../../tools/utils'; 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 c4de5f78e8..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 @@ -25,7 +25,7 @@ const SKIPPED_TESTS = [ // TODO(NODE-3308): 'A successful find event with a getmore and the server kills the cursor', - // TODO(NODE-4051): fix change stream resume logic + // 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 : []); diff --git a/test/tools/unified-spec-runner/operations.ts b/test/tools/unified-spec-runner/operations.ts index 1b2514241c..76f85d6bb2 100644 --- a/test/tools/unified-spec-runner/operations.ts +++ b/test/tools/unified-spec-runner/operations.ts @@ -14,7 +14,7 @@ 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, getSymbolFrom } from '../../tools/utils'; +import { getSymbolFrom } from '../../tools/utils'; import { EntitiesMap, UnifiedChangeStream } from './entities'; import { expectErrorCheck, resultCheck } from './match'; import type { OperationDescription } from './schema'; @@ -200,17 +200,9 @@ 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, - comment: operation.arguments.comment - }); - changeStream.eventCollector = new EventCollector(changeStream, ['init', 'error']); + const { pipeline, ...args } = operation.arguments; + const changeStream = watchable.watch(pipeline, args); return new Promise((resolve, reject) => { const init = getSymbolFrom(AbstractCursor.prototype, 'kInit'); @@ -322,14 +314,7 @@ operations.set('iterateUntilDocumentOrError', async ({ entities, operation }) => return await cursor.next(); } - if (changeStream.cursorStream == null) { - return changeStream.cursor.next(); - } else { - return Promise.race([ - changeStream.eventCollector.waitAndShiftEvent('change'), - changeStream.eventCollector.waitAndShiftEvent('error') - ]); - } + return changeStream.cursor.next(); }); operations.set('listCollections', async ({ entities, operation }) => { diff --git a/test/tools/utils.ts b/test/tools/utils.ts index e1a51b39ac..15d1cb5144 100644 --- a/test/tools/utils.ts +++ b/test/tools/utils.ts @@ -98,9 +98,6 @@ export class EventCollector { */ waitAndShiftEvent(eventName) { return new Promise((resolve, reject) => { - if (!this._events[eventName]) { - return reject(`Error: attempted to listen for ${eventName} but no listener handler is set`); - } if (!this._events[eventName]) { return reject( `Error: attempted to listen for ${eventName} but no listener handler is set` + eventName @@ -447,7 +444,12 @@ export class UnifiedTestSuiteBuilder { toJSON(): UnifiedSuite { const databaseName = - this._databaseName !== '' ? this._databaseName : this._description.replaceAll(' ', '_'); + this._databaseName !== '' + ? this._databaseName + : this._description + .split(' ') + .filter(s => s.length > 0) + .join('_'); return { description: this._description, schemaVersion: this._schemaVersion, From a74b5304853c433bd66cea2b9e6fb4006a61e2cd Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 30 Mar 2022 14:45:17 -0400 Subject: [PATCH 43/54] feat: add comment support to listCollections and listIndexes --- src/operations/indexes.ts | 29 ++--- src/operations/list_collections.ts | 73 +++---------- .../enumerate_databases.test.ts | 100 ++++++++++++++++++ test/integration/index_management.test.ts | 95 +++++++++++++++++ .../comment_with_falsy_values.test.ts | 4 +- test/tools/utils.ts | 7 +- test/unit/operations/list_collections.test.js | 34 +++++- 7 files changed, 259 insertions(+), 83 deletions(-) create mode 100644 test/integration/index_management.test.ts 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/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/test/integration/enumerate_databases/enumerate_databases.test.ts b/test/integration/enumerate_databases/enumerate_databases.test.ts index d25323d61d..3c80a31dc0 100644 --- a/test/integration/enumerate_databases/enumerate_databases.test.ts +++ b/test/integration/enumerate_databases/enumerate_databases.test.ts @@ -1,6 +1,8 @@ import { expect } from 'chai'; 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,101 @@ describe('listDatabases() authorizedDatabases flag', function () { } ); }); + +const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment option') + .initialData({ + collectionName: 'coll0', + databaseName: '', + documents: [ + { _id: 1, x: 11 }, + { _id: 2, toBeDeleted: true } // This should only be used by the delete test + ] + }) + .databaseName('listCollections-with-falsy-values') + .test( + new TestBuilder('listCollections should not send comment for server versions < 4.4') + .runOnRequirement({ maxServerVersion: '4.4' }) + .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/index_management.test.ts b/test/integration/index_management.test.ts new file mode 100644 index 0000000000..2f740a5e0f --- /dev/null +++ b/test/integration/index_management.test.ts @@ -0,0 +1,95 @@ +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 }, + { _id: 2, toBeDeleted: true } // This should only be used by the delete test + ] + }) + .databaseName('listIndexes-with-falsy-values') + .test( + new TestBuilder('listIndexes should not send comment for server versions < 4.4') + .runOnRequirement({ maxServerVersion: '4.4' }) + .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 index 9d0105a4b5..0c3563a4a6 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -59,7 +59,7 @@ const tests = Array.from(generateTestCombinations()).map(({ name, args }) => { }); const testSuite = new UnifiedTestSuiteBuilder('Comment with Falsy Values') - .runOnRequirement({ minServerVersion: '4.4' }) + .runOnRequirement({ minServerVersion: '4.4.0' }) .initialData({ collectionName: 'coll0', databaseName: '', @@ -160,7 +160,7 @@ const changeStreamTestSuite = new UnifiedTestSuiteBuilder( 'Change Streams - Comment with Falsy Values' ) .schemaVersion('1.0') - .runOnRequirement({ minServerVersion: '4.4' }) + .runOnRequirement({ minServerVersion: '4.4.0' }) .runOnRequirement({ topologies: ['replicaset', 'sharded-replicaset'] }) .test(testsForChangeStreamsAggregate) .test(testsForGetMore) diff --git a/test/tools/utils.ts b/test/tools/utils.ts index 15d1cb5144..0fc1382dc9 100644 --- a/test/tools/utils.ts +++ b/test/tools/utils.ts @@ -303,7 +303,7 @@ export interface FailPoint { export class TestBuilder { private _description: string; - private runOnRequirements: RunOnRequirement[]; + private runOnRequirements: RunOnRequirement[] = []; private _skipReason?: string; private _operations: OperationDescription[] = []; private _expectEvents?: ExpectedEventsForClient[] = []; @@ -321,6 +321,11 @@ export class TestBuilder { return this; } + runOnRequirement(requirement: RunOnRequirement): this { + this.runOnRequirements.push(requirement); + return this; + } + expectEvents(event: ExpectedEventsForClient): this { this._expectEvents.push(event); return this; diff --git a/test/unit/operations/list_collections.test.js b/test/unit/operations/list_collections.test.js index d63d7c0c16..1c5ea9fbde 100644 --- a/test/unit/operations/list_collections.test.js +++ b/test/unit/operations/list_collections.test.js @@ -65,12 +65,36 @@ 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('does not set 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 +108,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 +128,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 +146,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 +161,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: {}, From f1800641ddf278cdb2ad294473fc8750b1d0a4c7 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 30 Mar 2022 14:55:44 -0400 Subject: [PATCH 44/54] fix: reorganize tests && add tests for listDatabases --- src/operations/list_databases.ts | 8 +- .../integration/enumerate_collections.test.ts | 100 ++++++++++++++++++ .../enumerate_databases.test.ts | 36 +++---- ...ment.test.ts => enumerate_indexes.test.ts} | 0 4 files changed, 125 insertions(+), 19 deletions(-) create mode 100644 test/integration/enumerate_collections.test.ts rename test/integration/{enumerate_databases => }/enumerate_databases.test.ts (87%) rename test/integration/{index_management.test.ts => enumerate_indexes.test.ts} (100%) 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/test/integration/enumerate_collections.test.ts b/test/integration/enumerate_collections.test.ts new file mode 100644 index 0000000000..51f71aa66e --- /dev/null +++ b/test/integration/enumerate_collections.test.ts @@ -0,0 +1,100 @@ +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 }, + { _id: 2, toBeDeleted: true } // This should only be used by the delete test + ] + }) + .databaseName('listCollections-with-falsy-values') + .test( + new TestBuilder('listCollections should not send comment for server versions < 4.4') + .runOnRequirement({ maxServerVersion: '4.4' }) + .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.test.ts b/test/integration/enumerate_databases.test.ts similarity index 87% rename from test/integration/enumerate_databases/enumerate_databases.test.ts rename to test/integration/enumerate_databases.test.ts index 3c80a31dc0..9ae8862e14 100644 --- a/test/integration/enumerate_databases/enumerate_databases.test.ts +++ b/test/integration/enumerate_databases.test.ts @@ -1,8 +1,8 @@ import { expect } from 'chai'; -import { AddUserOptions, MongoClient, MongoServerError } from '../../../src'; -import { runUnifiedSuite } from '../../tools/unified-spec-runner/runner'; -import { TestBuilder, UnifiedTestSuiteBuilder } from '../../tools/utils'; +import { AddUserOptions, MongoClient, MongoServerError } from '../../src'; +import { runUnifiedSuite } from '../tools/unified-spec-runner/runner'; +import { TestBuilder, UnifiedTestSuiteBuilder } from '../tools/utils'; const metadata: MongoDBMetadataUI = { requires: { @@ -143,7 +143,7 @@ describe('listDatabases() authorizedDatabases flag', function () { ); }); -const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment option') +const testSuite = new UnifiedTestSuiteBuilder('listDatabases with comment option') .initialData({ collectionName: 'coll0', databaseName: '', @@ -152,17 +152,17 @@ const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment opti { _id: 2, toBeDeleted: true } // This should only be used by the delete test ] }) - .databaseName('listCollections-with-falsy-values') + .databaseName('listDatabases-with-falsy-values') .test( - new TestBuilder('listCollections should not send comment for server versions < 4.4') + new TestBuilder('listDatabases should not send comment for server versions < 4.4') .runOnRequirement({ maxServerVersion: '4.4' }) .operation({ - name: 'listCollections', + name: 'listDatabases', arguments: { filter: {}, comment: 'string value' }, - object: 'database0' + object: 'client0' }) .expectEvents({ client: 'client0', @@ -170,7 +170,7 @@ const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment opti { commandStartedEvent: { command: { - listCollections: 1 + listDatabases: 1 } } } @@ -179,15 +179,15 @@ const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment opti .toJSON() ) .test( - new TestBuilder('listCollections should send string comment for server versions >= 4.4') + new TestBuilder('listDatabases should send string comment for server versions >= 4.4') .runOnRequirement({ minServerVersion: '4.4.0' }) .operation({ - name: 'listCollections', + name: 'listDatabases', arguments: { filter: {}, comment: 'string value' }, - object: 'database0' + object: 'client0' }) .expectEvents({ client: 'client0', @@ -195,7 +195,7 @@ const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment opti { commandStartedEvent: { command: { - listCollections: 1, + listDatabases: 1, comment: 'string value' } } @@ -205,10 +205,10 @@ const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment opti .toJSON() ) .test( - new TestBuilder('listCollections should send non-string comment for server versions >= 4.4') + new TestBuilder('listDatabases should send non-string comment for server versions >= 4.4') .runOnRequirement({ minServerVersion: '4.4.0' }) .operation({ - name: 'listCollections', + name: 'listDatabases', arguments: { filter: {}, @@ -216,7 +216,7 @@ const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment opti key: 'value' } }, - object: 'database0' + object: 'client0' }) .expectEvents({ client: 'client0', @@ -224,7 +224,7 @@ const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment opti { commandStartedEvent: { command: { - listCollections: 1, + listDatabases: 1, comment: { key: 'value' } @@ -237,6 +237,6 @@ const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment opti ) .toJSON(); -describe('listCollections w/ comment option', () => { +describe('listDatabases w/ comment option', () => { runUnifiedSuite([testSuite]); }); diff --git a/test/integration/index_management.test.ts b/test/integration/enumerate_indexes.test.ts similarity index 100% rename from test/integration/index_management.test.ts rename to test/integration/enumerate_indexes.test.ts From 42147eea1c62e5516ba04afb3ac3230c73ab2ab7 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 30 Mar 2022 15:32:03 -0400 Subject: [PATCH 45/54] fix: move sinon stubbing into tests and revert changes to connection string tests --- test/unit/connection_string.test.ts | 7 ++--- test/unit/operations/get_more.test.ts | 40 ++++++++++++++++++--------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/test/unit/connection_string.test.ts b/test/unit/connection_string.test.ts index 63f3292722..989fcd16b4 100644 --- a/test/unit/connection_string.test.ts +++ b/test/unit/connection_string.test.ts @@ -291,10 +291,9 @@ describe('Connection String', function () { describe('resolveSRVRecord()', () => { const resolveSRVRecordAsync = promisify(resolveSRVRecord); - const sandbox = sinon.createSandbox(); afterEach(() => { - sandbox.restore(); + sinon.restore(); }); function makeStub(txtRecord: string) { @@ -311,11 +310,11 @@ describe('Connection String', function () { // first call is for stubbing resolveSrv // second call is for stubbing resolveTxt - sandbox.stub(dns, 'resolveSrv').callsFake((address, callback) => { + sinon.stub(dns, 'resolveSrv').callsFake((address, callback) => { return process.nextTick(callback, null, mockAddress); }); - sandbox.stub(dns, 'resolveTxt').callsFake((address, whatWeTest) => { + sinon.stub(dns, 'resolveTxt').callsFake((address, whatWeTest) => { whatWeTest(null, mockRecord); }); } diff --git a/test/unit/operations/get_more.test.ts b/test/unit/operations/get_more.test.ts index a114228ade..6127c0f33c 100644 --- a/test/unit/operations/get_more.test.ts +++ b/test/unit/operations/get_more.test.ts @@ -76,15 +76,22 @@ describe('GetMoreOperation', function () { }); describe('#execute', function () { - context('when the server is the same as the instance', function () { - 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()); + 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; @@ -98,13 +105,20 @@ describe('GetMoreOperation', function () { }); context('when the server is not the same as the instance', function () { - 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); - 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'); From 0bcdfc04e2000c10422b70aacd5030cfe5a94601 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 30 Mar 2022 16:20:35 -0400 Subject: [PATCH 46/54] fix: address comments --- src/change_stream.ts | 11 +---------- src/cmap/connection.ts | 2 +- src/cursor/abstract_cursor.ts | 2 +- src/db.ts | 4 ++-- src/operations/aggregate.ts | 9 --------- src/operations/command.ts | 2 +- src/operations/delete.ts | 9 --------- src/operations/find.ts | 9 --------- src/operations/find_and_modify.ts | 2 +- src/operations/get_more.ts | 10 +++++++--- src/utils.ts | 15 +-------------- test/tools/utils.ts | 25 ------------------------- 12 files changed, 15 insertions(+), 85 deletions(-) diff --git a/src/change_stream.ts b/src/change_stream.ts index 5e8153831c..df0f0466fe 100644 --- a/src/change_stream.ts +++ b/src/change_stream.ts @@ -411,16 +411,7 @@ export class ChangeStream extends TypedEven export interface ChangeStreamCursorOptions extends AbstractCursorOptions { startAtOperationTime?: OperationTime; resumeAfter?: ResumeToken; - startAfter?: boolean; - /** - * 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?: any; + startAfter?: ResumeToken; } /** @internal */ diff --git a/src/cmap/connection.ts b/src/cmap/connection.ts index da565ae116..bbc3981561 100644 --- a/src/cmap/connection.ts +++ b/src/cmap/connection.ts @@ -132,7 +132,7 @@ export interface GetMoreOptions extends CommandOptions { * * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ - comment?: any; + comment?: unknown; } /** @public */ diff --git a/src/cursor/abstract_cursor.ts b/src/cursor/abstract_cursor.ts index 1703dca6b3..eb19198674 100644 --- a/src/cursor/abstract_cursor.ts +++ b/src/cursor/abstract_cursor.ts @@ -87,7 +87,7 @@ export interface AbstractCursorOptions extends BSONSerializeOptions { * * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ - comment?: any; + comment?: unknown; tailable?: boolean; awaitData?: boolean; noCursorTimeout?: boolean; diff --git a/src/db.ts b/src/db.ts index 505ea34372..539cbb745d 100644 --- a/src/db.ts +++ b/src/db.ts @@ -44,9 +44,9 @@ import { DbStatsOperation, DbStatsOptions } from './operations/stats'; import { ReadConcern } from './read_concern'; import { ReadPreference, ReadPreferenceLike } from './read_preference'; import { - allowOptions, Callback, DEFAULT_PK_FACTORY, + filterOptions, getTopology, MongoDBNamespace, resolveOptions @@ -149,7 +149,7 @@ export class Db { options = options ?? {}; // Filter the options - options = allowOptions(options, DB_OPTIONS_ALLOW_LIST); + options = filterOptions(options, DB_OPTIONS_ALLOW_LIST); // Ensure we have a valid db name validateDatabaseName(databaseName); diff --git a/src/operations/aggregate.ts b/src/operations/aggregate.ts index 44ef8a7417..1acc6ac628 100644 --- a/src/operations/aggregate.ts +++ b/src/operations/aggregate.ts @@ -30,15 +30,6 @@ 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; - /** - * 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?: any; out?: string; } diff --git a/src/operations/command.ts b/src/operations/command.ts index f80ba26bf1..a9110592d6 100644 --- a/src/operations/command.ts +++ b/src/operations/command.ts @@ -53,7 +53,7 @@ export interface CommandOperationOptions * * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ - comment?: any; + comment?: unknown; /** Should retry failed writes */ retryWrites?: boolean; diff --git a/src/operations/delete.ts b/src/operations/delete.ts index 8bf229d9e2..ccdfd1c80b 100644 --- a/src/operations/delete.ts +++ b/src/operations/delete.ts @@ -12,15 +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; - /** - * 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?: any; /** Specifies the collation to use for the operation */ collation?: CollationOptions; /** Specify that the update query should only consider plans using the hinted index */ diff --git a/src/operations/find.ts b/src/operations/find.ts index 5d89e02c82..50bc5236bf 100644 --- a/src/operations/find.ts +++ b/src/operations/find.ts @@ -46,15 +46,6 @@ export interface FindOptions extends Comman min?: Document; /** The exclusive upper bound for a specific index */ max?: 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?: any; /** 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 */ diff --git a/src/operations/find_and_modify.ts b/src/operations/find_and_modify.ts index ba40da0090..12d7ba5044 100644 --- a/src/operations/find_and_modify.ts +++ b/src/operations/find_and_modify.ts @@ -90,7 +90,7 @@ interface FindAndModifyCmdBase { * * In server versions 4.4 and above, 'comment' can be any valid BSON type. */ - comment?: any; + comment?: unknown; } function configureFindAndModifyCmdBaseUpdateOpts( diff --git a/src/operations/get_more.ts b/src/operations/get_more.ts index db441048a6..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 { Callback, disallowOptions, maxWireVersion, MongoDBNamespace } from '../utils'; +import { Callback, maxWireVersion, MongoDBNamespace } from '../utils'; import { AbstractOperation, Aspect, defineAspects, OperationOptions } from './operation'; /** @@ -17,7 +17,7 @@ export interface GetMoreOptions extends OperationOptions { * * getMore only supports 'comment' in server versions 4.4 and above. */ - comment?: any; + comment?: unknown; /** Number of milliseconds to wait before aborting the query. */ maxTimeMS?: number; } @@ -30,8 +30,12 @@ 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 - this.options = maxWireVersion(server) < 9 ? disallowOptions(options, ['comment']) : options; + if (maxWireVersion(server) < 9) { + delete this.options.comment; + } this.ns = ns; this.cursorId = cursorId; diff --git a/src/utils.ts b/src/utils.ts index 12d6130e92..129d3995b7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -176,7 +176,7 @@ export function mergeOptions(target: T, source: S): T & S { } /** @internal */ -export function allowOptions(options: AnyOptions, names: string[]): AnyOptions { +export function filterOptions(options: AnyOptions, names: string[]): AnyOptions { const filterOptions: AnyOptions = {}; for (const name in options) { @@ -189,19 +189,6 @@ export function allowOptions(options: AnyOptions, names: string[]): AnyOptions { return filterOptions; } -/** @internal */ -export function disallowOptions(options: AnyOptions, denylist: string[]): AnyOptions { - const accumulatedOptions: AnyOptions = {}; - - for (const name in options) { - if (!denylist.includes(name)) { - accumulatedOptions[name] = options[name]; - } - } - - // Filtered options - return accumulatedOptions; -} interface HasRetryableWrites { retryWrites?: boolean; } diff --git a/test/tools/utils.ts b/test/tools/utils.ts index 0fc1382dc9..b21b7e439a 100644 --- a/test/tools/utils.ts +++ b/test/tools/utils.ts @@ -92,27 +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) { - return new Promise((resolve, reject) => { - if (!this._events[eventName]) { - return reject( - `Error: attempted to listen for ${eventName} but no listener handler is set` + eventName - ); - } - 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 => { @@ -349,10 +328,6 @@ export class TestBuilder { } export class UnifiedTestSuiteBuilder { - /** - * TODO: add node ticket to complete the entitity work - */ - private _anchors = {}; private _description = 'Default Description'; private _databaseName = ''; private _schemaVersion = '1.0'; From a5523e05d59d39e00c90f7f47c76c18230cee530 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 30 Mar 2022 16:31:01 -0400 Subject: [PATCH 47/54] chore: add test case for positive and negative zero --- test/tools/unified-spec-runner/match.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/tools/unified-spec-runner/match.ts b/test/tools/unified-spec-runner/match.ts index 07f05da4a8..f97ac3ef59 100644 --- a/test/tools/unified-spec-runner/match.ts +++ b/test/tools/unified-spec-runner/match.ts @@ -187,6 +187,15 @@ export function resultCheck( } 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 return; + } 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; + return; } else { expect(actual).to.equal(expected); } From 459344db4e0bd48c6066960191af0e88871f57ed Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 30 Mar 2022 16:39:33 -0400 Subject: [PATCH 48/54] fix: run on requireemnts in change stream falsy tests --- .../node-specific/comment_with_falsy_values.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts index 0c3563a4a6..1cdd35e4bc 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -160,8 +160,7 @@ const changeStreamTestSuite = new UnifiedTestSuiteBuilder( 'Change Streams - Comment with Falsy Values' ) .schemaVersion('1.0') - .runOnRequirement({ minServerVersion: '4.4.0' }) - .runOnRequirement({ topologies: ['replicaset', 'sharded-replicaset'] }) + .runOnRequirement({ minServerVersion: '4.4.0', topologies: ['replicaset', 'sharded-replicaset'] }) .test(testsForChangeStreamsAggregate) .test(testsForGetMore) .toJSON(); From 8aac554e53b53430e2a42531595bcca55de274db Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 30 Mar 2022 16:50:05 -0400 Subject: [PATCH 49/54] fix: use correct run on requirement for list* tests --- test/integration/enumerate_collections.test.ts | 2 +- test/integration/enumerate_databases.test.ts | 2 +- test/integration/enumerate_indexes.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/integration/enumerate_collections.test.ts b/test/integration/enumerate_collections.test.ts index 51f71aa66e..debfc47fd9 100644 --- a/test/integration/enumerate_collections.test.ts +++ b/test/integration/enumerate_collections.test.ts @@ -13,7 +13,7 @@ const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment opti .databaseName('listCollections-with-falsy-values') .test( new TestBuilder('listCollections should not send comment for server versions < 4.4') - .runOnRequirement({ maxServerVersion: '4.4' }) + .runOnRequirement({ maxServerVersion: '4.4.0' }) .operation({ name: 'listCollections', arguments: { diff --git a/test/integration/enumerate_databases.test.ts b/test/integration/enumerate_databases.test.ts index 9ae8862e14..bff9488235 100644 --- a/test/integration/enumerate_databases.test.ts +++ b/test/integration/enumerate_databases.test.ts @@ -155,7 +155,7 @@ const testSuite = new UnifiedTestSuiteBuilder('listDatabases with comment option .databaseName('listDatabases-with-falsy-values') .test( new TestBuilder('listDatabases should not send comment for server versions < 4.4') - .runOnRequirement({ maxServerVersion: '4.4' }) + .runOnRequirement({ maxServerVersion: '4.4.0' }) .operation({ name: 'listDatabases', arguments: { diff --git a/test/integration/enumerate_indexes.test.ts b/test/integration/enumerate_indexes.test.ts index 2f740a5e0f..6405c4ea40 100644 --- a/test/integration/enumerate_indexes.test.ts +++ b/test/integration/enumerate_indexes.test.ts @@ -13,7 +13,7 @@ const testSuite = new UnifiedTestSuiteBuilder('listIndexes with comment option') .databaseName('listIndexes-with-falsy-values') .test( new TestBuilder('listIndexes should not send comment for server versions < 4.4') - .runOnRequirement({ maxServerVersion: '4.4' }) + .runOnRequirement({ maxServerVersion: '4.4.0' }) .operation({ name: 'listIndexes', arguments: { From ece725da9f0a436914a8a1a3614a9039eee21cd8 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Wed, 30 Mar 2022 18:49:25 -0400 Subject: [PATCH 50/54] wip --- test/integration/enumerate_databases.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/integration/enumerate_databases.test.ts b/test/integration/enumerate_databases.test.ts index bff9488235..cf959b7cdd 100644 --- a/test/integration/enumerate_databases.test.ts +++ b/test/integration/enumerate_databases.test.ts @@ -159,8 +159,7 @@ const testSuite = new UnifiedTestSuiteBuilder('listDatabases with comment option .operation({ name: 'listDatabases', arguments: { - filter: {}, - comment: 'string value' + filter: {} }, object: 'client0' }) From 9def648adbd7fd537a54f2eb96c6fbd6f6e32e30 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 31 Mar 2022 09:52:52 -0400 Subject: [PATCH 51/54] fix: don't run chnage stream tests on shareded topology --- .../node-specific/comment_with_falsy_values.test.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts index 1cdd35e4bc..08bbf9ff20 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -105,9 +105,10 @@ const testsForChangeStreamsAggregate = falsyValues.map(falsyValue => { const testsForGetMore = falsyValues.map(falsyValue => { const description = `ChangeStreams should pass falsy value ${falsyToString( falsyValue - )} for comment option on initial aggregate`; + )} for comment option on getMore`; return new TestBuilder(description) + .runOnRequirement({ topologies: ['replicaset'] }) .operation({ name: 'createChangeStream', object: 'collection0', @@ -157,15 +158,21 @@ const testsForGetMore = falsyValues.map(falsyValue => { }); const changeStreamTestSuite = new UnifiedTestSuiteBuilder( - 'Change Streams - Comment with Falsy Values' + '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 ', () => { +describe.only('comment w/ falsy values ', () => { runUnifiedSuite([testSuite]); runUnifiedSuite([changeStreamTestSuite]); }); From cd2707db80b67effc3cf3c66314c9df810cb4499 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 31 Mar 2022 10:07:30 -0400 Subject: [PATCH 52/54] fix: remove only --- .../integration/node-specific/comment_with_falsy_values.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integration/node-specific/comment_with_falsy_values.test.ts b/test/integration/node-specific/comment_with_falsy_values.test.ts index 08bbf9ff20..016430457d 100644 --- a/test/integration/node-specific/comment_with_falsy_values.test.ts +++ b/test/integration/node-specific/comment_with_falsy_values.test.ts @@ -172,7 +172,7 @@ const changeStreamTestSuite = new UnifiedTestSuiteBuilder( .test(testsForGetMore) .toJSON(); -describe.only('comment w/ falsy values ', () => { +describe('comment w/ falsy values ', () => { runUnifiedSuite([testSuite]); runUnifiedSuite([changeStreamTestSuite]); }); From 8518dcdc898a1c5de2ae87cddfd561bd08721df3 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Thu, 31 Mar 2022 14:20:13 -0400 Subject: [PATCH 53/54] fix: address Neal's latest comments --- .../{enumerate_databases => }/enumerate_databases.prose.test.ts | 2 +- test/tools/unified-spec-runner/match.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) rename test/integration/{enumerate_databases => }/enumerate_databases.prose.test.ts (98%) 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/tools/unified-spec-runner/match.ts b/test/tools/unified-spec-runner/match.ts index f97ac3ef59..1a4c16ddb5 100644 --- a/test/tools/unified-spec-runner/match.ts +++ b/test/tools/unified-spec-runner/match.ts @@ -186,7 +186,6 @@ export function resultCheck( 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 - return; } else if ( typeof expected === 'number' && typeof actual === 'number' && @@ -195,7 +194,6 @@ export function resultCheck( ) { // case to handle +0 and -0 expect(Object.is(expected, actual)).to.be.true; - return; } else { expect(actual).to.equal(expected); } From 157c75dc62ca6486b9dce2898704af005096ba98 Mon Sep 17 00:00:00 2001 From: Bailey Pearson Date: Fri, 1 Apr 2022 10:20:15 -0400 Subject: [PATCH 54/54] fix: address Daria's comments --- test/integration/enumerate_collections.test.ts | 7 ++----- test/integration/enumerate_databases.test.ts | 7 ++----- test/integration/enumerate_indexes.test.ts | 7 ++----- test/unit/operations/list_collections.test.js | 3 ++- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/test/integration/enumerate_collections.test.ts b/test/integration/enumerate_collections.test.ts index debfc47fd9..c376be5d50 100644 --- a/test/integration/enumerate_collections.test.ts +++ b/test/integration/enumerate_collections.test.ts @@ -5,15 +5,12 @@ const testSuite = new UnifiedTestSuiteBuilder('listCollections with comment opti .initialData({ collectionName: 'coll0', databaseName: '', - documents: [ - { _id: 1, x: 11 }, - { _id: 2, toBeDeleted: true } // This should only be used by the delete test - ] + 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.4.0' }) + .runOnRequirement({ maxServerVersion: '4.3.99' }) .operation({ name: 'listCollections', arguments: { diff --git a/test/integration/enumerate_databases.test.ts b/test/integration/enumerate_databases.test.ts index cf959b7cdd..f1f295c76c 100644 --- a/test/integration/enumerate_databases.test.ts +++ b/test/integration/enumerate_databases.test.ts @@ -147,15 +147,12 @@ const testSuite = new UnifiedTestSuiteBuilder('listDatabases with comment option .initialData({ collectionName: 'coll0', databaseName: '', - documents: [ - { _id: 1, x: 11 }, - { _id: 2, toBeDeleted: true } // This should only be used by the delete test - ] + 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.4.0' }) + .runOnRequirement({ maxServerVersion: '4.3.99' }) .operation({ name: 'listDatabases', arguments: { diff --git a/test/integration/enumerate_indexes.test.ts b/test/integration/enumerate_indexes.test.ts index 6405c4ea40..d7fec608cc 100644 --- a/test/integration/enumerate_indexes.test.ts +++ b/test/integration/enumerate_indexes.test.ts @@ -5,15 +5,12 @@ const testSuite = new UnifiedTestSuiteBuilder('listIndexes with comment option') .initialData({ collectionName: 'coll0', databaseName: '', - documents: [ - { _id: 1, x: 11 }, - { _id: 2, toBeDeleted: true } // This should only be used by the delete test - ] + 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.4.0' }) + .runOnRequirement({ maxServerVersion: '4.3.99' }) .operation({ name: 'listIndexes', arguments: { diff --git a/test/unit/operations/list_collections.test.js b/test/unit/operations/list_collections.test.js index 1c5ea9fbde..071f43856d 100644 --- a/test/unit/operations/list_collections.test.js +++ b/test/unit/operations/list_collections.test.js @@ -77,8 +77,9 @@ describe('ListCollectionsOperation', function () { expect(command).not.to.haveOwnProperty('comment'); }); }); + context('when the wireVersion >= 9', function () { - it('does not set a comment on the command', function () { + it('sets a comment on the command', function () { const operation = new ListCollectionsOperation( db, {},