diff --git a/.evergreen/resync-specs.sh b/.evergreen/resync-specs.sh index 489ff28b3a..e2dbe058cd 100755 --- a/.evergreen/resync-specs.sh +++ b/.evergreen/resync-specs.sh @@ -97,7 +97,12 @@ do ;; cmap|CMAP|connection-monitoring-and-pooling) cpjson connection-monitoring-and-pooling/tests cmap - rm $PYMONGO/test/cmap/wait-queue-fairness.json # PYTHON-1873 + rm $PYMONGO/test/cmap/cmap-format/wait-queue-fairness.json # PYTHON-1873 + rm $PYMONGO/test/cmap/cmap-format/pool-clear-interrupt-immediately.json # PYTHON-3175 + rm $PYMONGO/test/cmap/cmap-format/pool-clear-interrupting-pending-connections.json # PYTHON-3175 + rm $PYMONGO/test/cmap/cmap-format/pool-clear-schedule-run-interruptInUseConnections-false.json # PYTHON-3175 + rm $PYMONGO/test/cmap/unified/connection-logging.json # PYTHON-3113 + rm $PYMONGO/test/cmap/unified/connection-pool-options-logging.json # PYTHON-3113 ;; apm|APM|command-monitoring|command_monitoring) cpjson command-logging-and-monitoring/tests/monitoring command_monitoring diff --git a/pymongo/pool.py b/pymongo/pool.py index 6355692ac9..303e23ded7 100644 --- a/pymongo/pool.py +++ b/pymongo/pool.py @@ -1357,6 +1357,8 @@ def connect(self, handler=None): try: sock = _configured_socket(self.address, self.opts) except BaseException as error: + if handler: + handler.handle(type(error), error) if self.enabled_for_cmap: listeners.publish_connection_closed( self.address, conn_id, ConnectionClosedReason.ERROR @@ -1376,7 +1378,9 @@ def connect(self, handler=None): handler.contribute_socket(sock_info, completed_handshake=False) sock_info.authenticate() - except BaseException: + except BaseException as error: + if handler: + handler.handle(type(error), error) sock_info.close_socket(ConnectionClosedReason.ERROR) raise diff --git a/test/cmap/connection-must-have-id.json b/test/cmap/cmap-format/connection-must-have-id.json similarity index 100% rename from test/cmap/connection-must-have-id.json rename to test/cmap/cmap-format/connection-must-have-id.json diff --git a/test/cmap/connection-must-order-ids.json b/test/cmap/cmap-format/connection-must-order-ids.json similarity index 100% rename from test/cmap/connection-must-order-ids.json rename to test/cmap/cmap-format/connection-must-order-ids.json diff --git a/test/cmap/pool-checkin-destroy-closed.json b/test/cmap/cmap-format/pool-checkin-destroy-closed.json similarity index 100% rename from test/cmap/pool-checkin-destroy-closed.json rename to test/cmap/cmap-format/pool-checkin-destroy-closed.json diff --git a/test/cmap/pool-checkin-destroy-stale.json b/test/cmap/cmap-format/pool-checkin-destroy-stale.json similarity index 100% rename from test/cmap/pool-checkin-destroy-stale.json rename to test/cmap/cmap-format/pool-checkin-destroy-stale.json diff --git a/test/cmap/pool-checkin-make-available.json b/test/cmap/cmap-format/pool-checkin-make-available.json similarity index 100% rename from test/cmap/pool-checkin-make-available.json rename to test/cmap/cmap-format/pool-checkin-make-available.json diff --git a/test/cmap/pool-checkin.json b/test/cmap/cmap-format/pool-checkin.json similarity index 100% rename from test/cmap/pool-checkin.json rename to test/cmap/cmap-format/pool-checkin.json diff --git a/test/cmap/pool-checkout-connection.json b/test/cmap/cmap-format/pool-checkout-connection.json similarity index 100% rename from test/cmap/pool-checkout-connection.json rename to test/cmap/cmap-format/pool-checkout-connection.json diff --git a/test/cmap/pool-checkout-custom-maxConnecting-is-enforced.json b/test/cmap/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.json similarity index 100% rename from test/cmap/pool-checkout-custom-maxConnecting-is-enforced.json rename to test/cmap/cmap-format/pool-checkout-custom-maxConnecting-is-enforced.json diff --git a/test/cmap/cmap-format/pool-checkout-error-auth.json b/test/cmap/cmap-format/pool-checkout-error-auth.json new file mode 100644 index 0000000000..328da95c24 --- /dev/null +++ b/test/cmap/cmap-format/pool-checkout-error-auth.json @@ -0,0 +1,68 @@ +{ + "version": 1, + "style": "integration", + "description": "must properly handle auth error on check out", + "runOn": [ + { + "minServerVersion": "4.9.0", + "auth": true + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "appName": "poolCheckOutAuthErrorTest", + "errorCode": 18 + } + }, + "poolOptions": { + "appName": "poolCheckOutAuthErrorTest" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut" + } + ], + "error": { + "type": "AuthenticationError" + }, + "events": [ + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCreated", + "connectionId": 1, + "address": 42 + }, + { + "type": "ConnectionPoolCleared", + "address": 42 + }, + { + "type": "ConnectionClosed", + "connectionId": 1, + "address": 42, + "reason": "error" + }, + { + "type": "ConnectionCheckOutFailed", + "address": 42, + "reason": "connectionError" + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionPoolReady" + ] +} diff --git a/test/cmap/cmap-format/pool-checkout-error-checkin.json b/test/cmap/cmap-format/pool-checkout-error-checkin.json new file mode 100644 index 0000000000..51a9cc18b1 --- /dev/null +++ b/test/cmap/cmap-format/pool-checkout-error-checkin.json @@ -0,0 +1,97 @@ +{ + "version": 1, + "style": "integration", + "description": "checking in a connection after checkout fails closes connection", + "runOn": [ + { + "minServerVersion": "4.9.0", + "auth": true + } + ], + "poolOptions": { + "appName": "poolCheckOutErrorCheckInTest" + }, + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "skip": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "closeConnection": true, + "appName": "poolCheckOutErrorCheckInTest" + } + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut", + "label": "conn" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutFailed", + "count": 1 + }, + { + "name": "checkIn", + "connection": "conn" + } + ], + "events": [ + { + "type": "ConnectionCheckOutStarted" + }, + { + "type": "ConnectionCreated" + }, + { + "type": "ConnectionReady" + }, + { + "type": "ConnectionCheckedOut" + }, + { + "type": "ConnectionCheckOutStarted" + }, + { + "type": "ConnectionCreated" + }, + { + "type": "ConnectionPoolCleared" + }, + { + "type": "ConnectionClosed", + "reason": "error", + "connectionId": 2 + }, + { + "type": "ConnectionCheckOutFailed", + "reason": "connectionError" + }, + { + "type": "ConnectionCheckedIn" + }, + { + "type": "ConnectionClosed", + "connectionId": 1, + "reason": "stale" + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionPoolReady" + ] +} diff --git a/test/cmap/cmap-format/pool-checkout-error-clears-waitqueue.json b/test/cmap/cmap-format/pool-checkout-error-clears-waitqueue.json new file mode 100644 index 0000000000..8fef127e7d --- /dev/null +++ b/test/cmap/cmap-format/pool-checkout-error-clears-waitqueue.json @@ -0,0 +1,105 @@ +{ + "version": 1, + "style": "integration", + "description": "connection error during checkout clears wait queue", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 50 + }, + "data": { + "failCommands": [ + "hello", + "isMaster" + ], + "appName": "poolCheckOutErrorWaitQueueTest", + "closeConnection": true, + "blockConnection": true, + "blockTimeMS": 1000 + } + }, + "poolOptions": { + "maxPoolSize": 1, + "waitQueueTimeoutMS": 30000, + "appName": "poolCheckOutErrorWaitQueueTest" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "start", + "target": "thread2" + }, + { + "name": "checkOut", + "thread": "thread2" + }, + { + "name": "start", + "target": "thread3" + }, + { + "name": "checkOut", + "thread": "thread3" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutStarted", + "count": 3 + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutFailed", + "count": 3, + "timeout": 2000 + } + ], + "events": [ + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionPoolCleared", + "address": 42 + }, + { + "type": "ConnectionCheckOutFailed", + "reason": "connectionError", + "address": 42 + }, + { + "type": "ConnectionCheckOutFailed", + "reason": "connectionError", + "address": 42 + }, + { + "type": "ConnectionCheckOutFailed", + "reason": "connectionError", + "address": 42 + } + ], + "ignore": [ + "ConnectionPoolReady", + "ConnectionCreated", + "ConnectionPoolCreated", + "ConnectionClosed" + ] +} diff --git a/test/cmap/pool-checkout-error-closed.json b/test/cmap/cmap-format/pool-checkout-error-closed.json similarity index 100% rename from test/cmap/pool-checkout-error-closed.json rename to test/cmap/cmap-format/pool-checkout-error-closed.json diff --git a/test/cmap/cmap-format/pool-checkout-error-concurrent.json b/test/cmap/cmap-format/pool-checkout-error-concurrent.json new file mode 100644 index 0000000000..c69c5580ee --- /dev/null +++ b/test/cmap/cmap-format/pool-checkout-error-concurrent.json @@ -0,0 +1,82 @@ +{ + "version": 1, + "style": "integration", + "description": "must properly handle multiple concurrent network errors on check out", + "runOn": [ + { + "minServerVersion": "4.9.0" + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 50 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": true, + "appName": "poolCheckOutErrorTest" + } + }, + "poolOptions": { + "appName": "poolCheckOutErrorTest" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "start", + "target": "thread1" + }, + { + "name": "start", + "target": "thread2" + }, + { + "name": "start", + "target": "thread3" + }, + { + "name": "checkOut", + "thread": "thread1" + }, + { + "name": "checkOut", + "thread": "thread2" + }, + { + "name": "checkOut", + "thread": "thread3" + }, + { + "name": "waitForEvent", + "event": "ConnectionCheckOutFailed", + "count": 3 + }, + { + "name": "ready" + } + ], + "events": [ + { + "type": "ConnectionPoolReady" + }, + { + "type": "ConnectionPoolCleared" + }, + { + "type": "ConnectionPoolReady" + } + ], + "ignore": [ + "ConnectionCheckOutStarted", + "ConnectionCheckOutFailed", + "ConnectionPoolCreated", + "ConnectionCreated", + "ConnectionClosed" + ] +} diff --git a/test/cmap/cmap-format/pool-checkout-error-network.json b/test/cmap/cmap-format/pool-checkout-error-network.json new file mode 100644 index 0000000000..ee04600d67 --- /dev/null +++ b/test/cmap/cmap-format/pool-checkout-error-network.json @@ -0,0 +1,67 @@ +{ + "version": 1, + "style": "integration", + "description": "must properly handle network error on check out", + "runOn": [ + { + "minServerVersion": "4.9.0" + } + ], + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 50 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": true, + "appName": "poolCheckOutErrorTest" + } + }, + "poolOptions": { + "appName": "poolCheckOutErrorTest" + }, + "operations": [ + { + "name": "ready" + }, + { + "name": "checkOut" + } + ], + "error": { + "type": "NetworkError" + }, + "events": [ + { + "type": "ConnectionCheckOutStarted", + "address": 42 + }, + { + "type": "ConnectionCreated", + "address": 42 + }, + { + "type": "ConnectionPoolCleared", + "address": 42 + }, + { + "type": "ConnectionClosed", + "address": 42, + "connectionId": 1, + "reason": "error" + }, + { + "type": "ConnectionCheckOutFailed", + "address": 42, + "reason": "connectionError" + } + ], + "ignore": [ + "ConnectionPoolCreated", + "ConnectionPoolReady" + ] +} diff --git a/test/cmap/pool-checkout-maxConnecting-is-enforced.json b/test/cmap/cmap-format/pool-checkout-maxConnecting-is-enforced.json similarity index 100% rename from test/cmap/pool-checkout-maxConnecting-is-enforced.json rename to test/cmap/cmap-format/pool-checkout-maxConnecting-is-enforced.json diff --git a/test/cmap/pool-checkout-maxConnecting-timeout.json b/test/cmap/cmap-format/pool-checkout-maxConnecting-timeout.json similarity index 100% rename from test/cmap/pool-checkout-maxConnecting-timeout.json rename to test/cmap/cmap-format/pool-checkout-maxConnecting-timeout.json diff --git a/test/cmap/pool-checkout-minPoolSize-connection-maxConnecting.json b/test/cmap/cmap-format/pool-checkout-minPoolSize-connection-maxConnecting.json similarity index 100% rename from test/cmap/pool-checkout-minPoolSize-connection-maxConnecting.json rename to test/cmap/cmap-format/pool-checkout-minPoolSize-connection-maxConnecting.json diff --git a/test/cmap/pool-checkout-multiple.json b/test/cmap/cmap-format/pool-checkout-multiple.json similarity index 100% rename from test/cmap/pool-checkout-multiple.json rename to test/cmap/cmap-format/pool-checkout-multiple.json diff --git a/test/cmap/pool-checkout-no-idle.json b/test/cmap/cmap-format/pool-checkout-no-idle.json similarity index 100% rename from test/cmap/pool-checkout-no-idle.json rename to test/cmap/cmap-format/pool-checkout-no-idle.json diff --git a/test/cmap/pool-checkout-no-stale.json b/test/cmap/cmap-format/pool-checkout-no-stale.json similarity index 100% rename from test/cmap/pool-checkout-no-stale.json rename to test/cmap/cmap-format/pool-checkout-no-stale.json diff --git a/test/cmap/pool-checkout-returned-connection-maxConnecting.json b/test/cmap/cmap-format/pool-checkout-returned-connection-maxConnecting.json similarity index 100% rename from test/cmap/pool-checkout-returned-connection-maxConnecting.json rename to test/cmap/cmap-format/pool-checkout-returned-connection-maxConnecting.json diff --git a/test/cmap/pool-clear-clears-waitqueue.json b/test/cmap/cmap-format/pool-clear-clears-waitqueue.json similarity index 100% rename from test/cmap/pool-clear-clears-waitqueue.json rename to test/cmap/cmap-format/pool-clear-clears-waitqueue.json diff --git a/test/cmap/pool-clear-min-size.json b/test/cmap/cmap-format/pool-clear-min-size.json similarity index 100% rename from test/cmap/pool-clear-min-size.json rename to test/cmap/cmap-format/pool-clear-min-size.json diff --git a/test/cmap/pool-clear-paused.json b/test/cmap/cmap-format/pool-clear-paused.json similarity index 100% rename from test/cmap/pool-clear-paused.json rename to test/cmap/cmap-format/pool-clear-paused.json diff --git a/test/cmap/pool-clear-ready.json b/test/cmap/cmap-format/pool-clear-ready.json similarity index 100% rename from test/cmap/pool-clear-ready.json rename to test/cmap/cmap-format/pool-clear-ready.json diff --git a/test/cmap/pool-close-destroy-conns.json b/test/cmap/cmap-format/pool-close-destroy-conns.json similarity index 100% rename from test/cmap/pool-close-destroy-conns.json rename to test/cmap/cmap-format/pool-close-destroy-conns.json diff --git a/test/cmap/pool-close.json b/test/cmap/cmap-format/pool-close.json similarity index 100% rename from test/cmap/pool-close.json rename to test/cmap/cmap-format/pool-close.json diff --git a/test/cmap/pool-create-max-size.json b/test/cmap/cmap-format/pool-create-max-size.json similarity index 100% rename from test/cmap/pool-create-max-size.json rename to test/cmap/cmap-format/pool-create-max-size.json diff --git a/test/cmap/pool-create-min-size-error.json b/test/cmap/cmap-format/pool-create-min-size-error.json similarity index 100% rename from test/cmap/pool-create-min-size-error.json rename to test/cmap/cmap-format/pool-create-min-size-error.json index 1c744b850c..509b2a2356 100644 --- a/test/cmap/pool-create-min-size-error.json +++ b/test/cmap/cmap-format/pool-create-min-size-error.json @@ -49,15 +49,15 @@ "type": "ConnectionCreated", "address": 42 }, + { + "type": "ConnectionPoolCleared", + "address": 42 + }, { "type": "ConnectionClosed", "address": 42, "connectionId": 42, "reason": "error" - }, - { - "type": "ConnectionPoolCleared", - "address": 42 } ], "ignore": [ diff --git a/test/cmap/pool-create-min-size.json b/test/cmap/cmap-format/pool-create-min-size.json similarity index 100% rename from test/cmap/pool-create-min-size.json rename to test/cmap/cmap-format/pool-create-min-size.json diff --git a/test/cmap/pool-create-with-options.json b/test/cmap/cmap-format/pool-create-with-options.json similarity index 100% rename from test/cmap/pool-create-with-options.json rename to test/cmap/cmap-format/pool-create-with-options.json diff --git a/test/cmap/pool-create.json b/test/cmap/cmap-format/pool-create.json similarity index 100% rename from test/cmap/pool-create.json rename to test/cmap/cmap-format/pool-create.json diff --git a/test/cmap/pool-ready-ready.json b/test/cmap/cmap-format/pool-ready-ready.json similarity index 100% rename from test/cmap/pool-ready-ready.json rename to test/cmap/cmap-format/pool-ready-ready.json diff --git a/test/cmap/pool-ready.json b/test/cmap/cmap-format/pool-ready.json similarity index 100% rename from test/cmap/pool-ready.json rename to test/cmap/cmap-format/pool-ready.json diff --git a/test/cmap/wait-queue-timeout.json b/test/cmap/cmap-format/wait-queue-timeout.json similarity index 100% rename from test/cmap/wait-queue-timeout.json rename to test/cmap/cmap-format/wait-queue-timeout.json diff --git a/test/cmap/unified/auth-error.json b/test/cmap/unified/auth-error.json new file mode 100644 index 0000000000..6704a54627 --- /dev/null +++ b/test/cmap/unified/auth-error.json @@ -0,0 +1,172 @@ +{ + "description": "cmap-auth-error", + "schemaVersion": "1.10", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ], + "auth": true + } + ], + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "cmap-network-error", + "databaseName": "cmap-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Pool is cleared after auth error during checkout", + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "errorCode": 18, + "appName": "cmapAuthErrorTest" + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "connectionCheckOutStartedEvent", + "connectionCheckedOutEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedInEvent", + "connectionClosedEvent", + "poolClearedEvent", + "connectionReadyEvent", + "connectionCreatedEvent" + ], + "uriOptions": { + "retryWrites": false, + "retryReads": false, + "appname": "cmapAuthErrorTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "cmap-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "cmap-network-error" + } + } + ] + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionCheckOutFailedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCreatedEvent": {} + }, + { + "poolClearedEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + }, + { + "connectionCheckOutFailedEvent": { + "reason": "connectionError" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "cmap-network-error", + "databaseName": "cmap-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + } + ] +} diff --git a/test/cmap/unified/network-error.json b/test/cmap/unified/network-error.json new file mode 100644 index 0000000000..2db0396e25 --- /dev/null +++ b/test/cmap/unified/network-error.json @@ -0,0 +1,469 @@ +{ + "description": "cmap-network-error", + "schemaVersion": "1.10", + "createEntities": [ + { + "client": { + "id": "setupClient", + "useMultipleMongoses": false + } + } + ], + "initialData": [ + { + "collectionName": "cmap-network-error", + "databaseName": "cmap-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ], + "tests": [ + { + "description": "Pool properly handles network error on checked out connection", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "replicaset", + "sharded" + ] + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "find" + ], + "closeConnection": true, + "appName": "cmapNetworkErrorTest" + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "connectionCheckOutStartedEvent", + "connectionCheckedOutEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedInEvent", + "connectionClosedEvent", + "poolClearedEvent" + ], + "uriOptions": { + "retryWrites": false, + "retryReads": false, + "appname": "cmapNetworkErrorTest" + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "cmap-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "cmap-network-error" + } + } + ] + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionClosedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCheckedOutEvent": {} + }, + { + "poolClearedEvent": {} + }, + { + "connectionCheckedInEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "cmap-network-error", + "databaseName": "cmap-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "Pool properly handles network error during checkout", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "sharded" + ] + } + ], + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "connectionCheckOutStartedEvent", + "connectionCheckedOutEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedInEvent", + "connectionClosedEvent", + "poolClearedEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "poolReadyEvent" + ], + "uriOptions": { + "retryWrites": false, + "retryReads": false, + "appName": "cmapNetworkErrorCheckOutTest", + "heartbeatFrequencyMS": 30000 + } + } + }, + { + "database": { + "id": "database", + "client": "client", + "databaseName": "cmap-tests" + } + }, + { + "collection": { + "id": "collection", + "database": "database", + "collectionName": "cmap-network-error" + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + }, + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "isMaster", + "hello" + ], + "closeConnection": true, + "appName": "cmapNetworkErrorCheckOutTest" + } + } + } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": { + "_id": 1 + } + }, + "expectError": { + "isError": true + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionCheckOutFailedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "poolReadyEvent": {} + }, + { + "connectionCheckOutStartedEvent": {} + }, + { + "connectionCreatedEvent": {} + }, + { + "poolClearedEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + }, + { + "connectionCheckOutFailedEvent": { + "reason": "connectionError" + } + } + ] + } + ], + "outcome": [ + { + "collectionName": "cmap-network-error", + "databaseName": "cmap-tests", + "documents": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] + } + ] + }, + { + "description": "Pool properly handles network error during minPoolSize background connection creation", + "runOnRequirements": [ + { + "minServerVersion": "4.4", + "serverless": "forbid", + "topologies": [ + "single", + "sharded" + ], + "auth": true + } + ], + "operations": [ + { + "name": "failPoint", + "object": "testRunner", + "arguments": { + "client": "setupClient", + "failPoint": { + "configureFailPoint": "failCommand", + "mode": { + "times": 1 + }, + "data": { + "failCommands": [ + "saslContinue" + ], + "appName": "CMAPminPoolSizeError", + "closeConnection": true + } + } + } + }, + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "useMultipleMongoses": false, + "observeEvents": [ + "poolClearedEvent", + "poolReadyEvent", + "connectionCreatedEvent", + "connectionReadyEvent", + "connectionClosedEvent", + "connectionCheckOutStartedEvent", + "connectionCheckedOutEvent", + "connectionCheckOutFailedEvent", + "connectionCheckedInEvent" + ], + "uriOptions": { + "heartbeatFrequencyMS": 500, + "appname": "CMAPminPoolSizeError", + "minPoolSize": 1 + } + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionClosedEvent": { + "reason": "error" + } + }, + "count": 1 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "poolReadyEvent": {} + }, + "count": 2 + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "connectionReadyEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "cmap", + "events": [ + { + "poolReadyEvent": {} + }, + { + "connectionCreatedEvent": {} + }, + { + "poolClearedEvent": {} + }, + { + "connectionClosedEvent": { + "reason": "error" + } + }, + { + "poolReadyEvent": {} + }, + { + "connectionCreatedEvent": {} + }, + { + "connectionReadyEvent": {} + } + ] + } + ] + } + ] +} diff --git a/test/test_cmap.py b/test/test_cmap.py index 360edef0e8..c46557e343 100644 --- a/test/test_cmap.py +++ b/test/test_cmap.py @@ -22,6 +22,7 @@ from test import IntegrationTest, client_knobs, unittest from test.pymongo_mocks import DummyMonitor +from test.unified_format import generate_test_classes from test.utils import ( CMAPListener, TestCreator, @@ -84,7 +85,7 @@ class TestCMAP(IntegrationTest): # Location of JSON test specifications. - TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "cmap") + TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "cmap", "cmap-format") # Test operations: @@ -198,9 +199,10 @@ def check_events(self, events, ignore): self.fail("missing events: %r" % (events[len(actual_events) :],)) def check_error(self, actual, expected): - message = expected.pop("message") + message = expected.pop("message", "") self.check_object(actual, expected) - self.assertIn(message, str(actual)) + if message: + self.assertIn(message, str(actual)) def _set_fail_point(self, client, command_args): cmd = SON([("configureFailPoint", "failCommand")]) @@ -405,9 +407,11 @@ def test_5_check_out_fails_auth_error(self): self.assertIsInstance(listener.events[2], ConnectionCheckOutStartedEvent) self.assertIsInstance(listener.events[3], ConnectionCreatedEvent) # Error happens here. - self.assertIsInstance(listener.events[4], ConnectionClosedEvent) - self.assertIsInstance(listener.events[5], ConnectionCheckOutFailedEvent) - self.assertEqual(listener.events[5].reason, ConnectionCheckOutFailedReason.CONN_ERROR) + self.assertIsInstance(listener.events[4], PoolClearedEvent) + self.assertIsInstance(listener.events[5], ConnectionClosedEvent) + self.assertEqual(listener.events[5].reason, ConnectionClosedReason.ERROR) + self.assertIsInstance(listener.events[6], ConnectionCheckOutFailedEvent) + self.assertEqual(listener.events[6].reason, ConnectionCheckOutFailedReason.CONN_ERROR) # # Extra non-spec tests @@ -468,6 +472,11 @@ def tests(self, scenario_def): test_creator = CMAPTestCreator(create_test, TestCMAP, TestCMAP.TEST_PATH) test_creator.create_tests() +TEST_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "cmap", "unified") + +# Generate unified tests. +globals().update(generate_test_classes(TEST_PATH, module=__name__)) + if __name__ == "__main__": unittest.main()