diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index 272851a3b..482291e44 100755 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -32,6 +32,9 @@ AUTH=${AUTH:-noauth} SSL=${SSL:-nossl} TEST_ARGS="${*:1}" PYTHON=$(which python) +# TODO: Remove when we drop PyPy 3.8 support. +OLD_PYPY=$(python -c "import sys; print(sys.implementation.name.lower() == 'pypy' and sys.implementation.version < (7, 3, 12))") + export PIP_QUIET=1 # Quiet by default export PIP_PREFER_BINARY=1 # Prefer binary dists by default @@ -110,6 +113,9 @@ fi if [ "$COMPRESSORS" = "snappy" ]; then python -m pip install '.[snappy]' + if [ "$OLD_PYPY" == "True" ]; then + pip install "python-snappy<0.7.0" + fi PYTHON=python elif [ "$COMPRESSORS" = "zstd" ]; then python -m pip install zstandard @@ -250,6 +256,9 @@ fi if [ -n "$GREEN_FRAMEWORK" ]; then # Install all optional deps to ensure lazy imports are getting patched. python -m pip install -q ".[aws,encryption,gssapi,ocsp,snappy,zstd]" + if [ "$OLD_PYPY" == "True" ]; then + pip install "python-snappy<0.7.0" + fi python -m pip install $GREEN_FRAMEWORK fi diff --git a/pyproject.toml b/pyproject.toml index e3a6a521b..3cdb03a1b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,7 +71,7 @@ ocsp = [ "service_identity>=18.1.0", ] snappy = [ - "python-snappy", + "python-snappy" ] # PYTHON-3423 Removed in 4.3 but kept here to avoid pip warnings. srv = [] diff --git a/test/command_logging/unacknowledged-write.json b/test/command_logging/unacknowledged-write.json index 21e247df6..dad0c0a36 100644 --- a/test/command_logging/unacknowledged-write.json +++ b/test/command_logging/unacknowledged-write.json @@ -1,6 +1,6 @@ { "description": "unacknowledged-write", - "schemaVersion": "1.13", + "schemaVersion": "1.16", "createEntities": [ { "client": { @@ -53,11 +53,27 @@ "_id": 2 } } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1 + }, + { + "_id": 2 + } + ] } ], "expectLogMessages": [ { "client": "client", + "ignoreExtraMessages": true, "messages": [ { "level": "debug", diff --git a/test/command_monitoring/unacknowledgedBulkWrite.json b/test/command_monitoring/unacknowledgedBulkWrite.json index 4c16d6df1..782cb84a5 100644 --- a/test/command_monitoring/unacknowledgedBulkWrite.json +++ b/test/command_monitoring/unacknowledgedBulkWrite.json @@ -1,6 +1,6 @@ { "description": "unacknowledgedBulkWrite", - "schemaVersion": "1.0", + "schemaVersion": "1.7", "createEntities": [ { "client": { @@ -64,11 +64,29 @@ ], "ordered": false } + }, + { + "name": "find", + "object": "collection", + "arguments": { + "filter": {} + }, + "expectResult": [ + { + "_id": 1, + "x": 11 + }, + { + "_id": "unorderedBulkWriteInsertW0", + "x": 44 + } + ] } ], "expectEvents": [ { "client": "client", + "ignoreExtraEvents": true, "events": [ { "commandStartedEvent": { diff --git a/test/performance/perf_test.py b/test/performance/perf_test.py index 6646d4eb4..43b4da786 100644 --- a/test/performance/perf_test.py +++ b/test/performance/perf_test.py @@ -43,6 +43,7 @@ import multiprocessing as mp import os import sys import tempfile +import threading import time import warnings from typing import Any, List, Optional @@ -101,10 +102,19 @@ class Timer: self.interval = self.end - self.start +def threaded(n_threads, func): + threads = [threading.Thread(target=func) for _ in range(n_threads)] + for t in threads: + t.start() + for t in threads: + t.join() + + class PerformanceTest: dataset: str data_size: int fail: Any + n_threads: int = 1 @classmethod def setUpClass(cls): @@ -118,7 +128,7 @@ class PerformanceTest: # Remove "Test" so that TestFlatEncoding is reported as "FlatEncoding". name = self.__class__.__name__[4:] median = self.percentile(50) - megabytes_per_sec = self.data_size / median / 1000000 + megabytes_per_sec = (self.data_size * self.n_threads) / median / 1000000 print( f"Completed {self.__class__.__name__} {megabytes_per_sec:.3f} MB/s, MEDIAN={self.percentile(50):.3f}s, " f"total time={duration:.3f}s, iterations={len(self.results)}" @@ -128,7 +138,7 @@ class PerformanceTest: "info": { "test_name": name, "args": { - "threads": 1, + "threads": self.n_threads, }, }, "metrics": [ @@ -163,7 +173,10 @@ class PerformanceTest: i += 1 self.before() with Timer() as timer: - self.do_task() + if self.n_threads == 1: + self.do_task() + else: + threaded(self.n_threads, self.do_task) self.after() results.append(timer.interval) duration = time.monotonic() - start @@ -308,6 +321,10 @@ class TestRunCommand(PerformanceTest, unittest.TestCase): command("hello", True) +class TestRunCommand8Threads(TestRunCommand): + n_threads = 8 + + class TestDocument(PerformanceTest): def setUp(self): super().setUp() @@ -356,6 +373,10 @@ class TestFindOneByID(FindTest, unittest.TestCase): find_one({"_id": _id}) +class TestFindOneByID8Threads(TestFindOneByID): + n_threads = 8 + + class SmallDocInsertTest(TestDocument): dataset = "small_doc.json" @@ -395,6 +416,10 @@ class TestFindManyAndEmptyCursor(FindTest, unittest.TestCase): list(self.corpus.find()) +class TestFindManyAndEmptyCursor8Threads(TestFindManyAndEmptyCursor): + n_threads = 8 + + class TestSmallDocBulkInsert(SmallDocInsertTest, unittest.TestCase): def do_task(self): self.corpus.insert_many(self.documents, ordered=True) diff --git a/test/unified-test-format/invalid/entity-client-observeLogMessages-minProperties.json b/test/unified-test-format/invalid/entity-client-observeLogMessages-minProperties.json new file mode 100644 index 000000000..87cbd2112 --- /dev/null +++ b/test/unified-test-format/invalid/entity-client-observeLogMessages-minProperties.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-observeLogMessages-minProperties", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0", + "observeLogMessages": {} + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/test/unified-test-format/invalid/entity-client-observeLogMessages-property-type.json b/test/unified-test-format/invalid/entity-client-observeLogMessages-property-type.json new file mode 100644 index 000000000..fed0accd6 --- /dev/null +++ b/test/unified-test-format/invalid/entity-client-observeLogMessages-property-type.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-observeLogMessages-property-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0", + "observeLogMessages": { + "command": {} + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/test/unified-test-format/invalid/entity-client-observeLogMessages-property-value.json b/test/unified-test-format/invalid/entity-client-observeLogMessages-property-value.json new file mode 100644 index 000000000..f14b18d6d --- /dev/null +++ b/test/unified-test-format/invalid/entity-client-observeLogMessages-property-value.json @@ -0,0 +1,20 @@ +{ + "description": "entity-client-observeLogMessages-property-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0", + "observeLogMessages": { + "command": "notALogLevel" + } + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/test/unified-test-format/invalid/entity-client-observeLogMessages-type.json b/test/unified-test-format/invalid/entity-client-observeLogMessages-type.json new file mode 100644 index 000000000..8a277034e --- /dev/null +++ b/test/unified-test-format/invalid/entity-client-observeLogMessages-type.json @@ -0,0 +1,18 @@ +{ + "description": "entity-client-observeLogMessages-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0", + "observeLogMessages": 0 + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type.json b/test/unified-test-format/invalid/expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type.json index e25955a0e..de5931882 100644 --- a/test/unified-test-format/invalid/expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type.json +++ b/test/unified-test-format/invalid/expectedCmapEvent-poolClearedEvent-interruptInUseConnections-type.json @@ -20,4 +20,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/test/unified-test-format/invalid/expectedCommandEvent-commandFailedEvent-databaseName-type.json b/test/unified-test-format/invalid/expectedCommandEvent-commandFailedEvent-databaseName-type.json new file mode 100644 index 000000000..f6a305b89 --- /dev/null +++ b/test/unified-test-format/invalid/expectedCommandEvent-commandFailedEvent-databaseName-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandFailedEvent-databaseName-type", + "schemaVersion": "1.15", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandFailedEvent": { + "databaseName": 0 + } + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedCommandEvent-commandSucceededEvent-databaseName-type.json b/test/unified-test-format/invalid/expectedCommandEvent-commandSucceededEvent-databaseName-type.json new file mode 100644 index 000000000..47b8c8bb9 --- /dev/null +++ b/test/unified-test-format/invalid/expectedCommandEvent-commandSucceededEvent-databaseName-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedCommandEvent-commandSucceededEvent-databaseName-type", + "schemaVersion": "1.15", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandSucceededEvent": { + "databaseName": 0 + } + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessage-additionalProperties.json b/test/unified-test-format/invalid/expectedLogMessage-additionalProperties.json new file mode 100644 index 000000000..cd7cf8726 --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessage-additionalProperties.json @@ -0,0 +1,24 @@ +{ + "description": "expectedLogMessage-additionalProperties", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [], + "foo": 0 + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessage-component-enum.json b/test/unified-test-format/invalid/expectedLogMessage-component-enum.json new file mode 100644 index 000000000..2283e9b24 --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessage-component-enum.json @@ -0,0 +1,29 @@ +{ + "description": "expectedLogMessage-component-enum", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "component": "foo", + "data": {} + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessage-component-required.json b/test/unified-test-format/invalid/expectedLogMessage-component-required.json new file mode 100644 index 000000000..f3a157787 --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessage-component-required.json @@ -0,0 +1,28 @@ +{ + "description": "expectedLogMessage-component-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "data": {} + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessage-component-type.json b/test/unified-test-format/invalid/expectedLogMessage-component-type.json new file mode 100644 index 000000000..af8f71157 --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessage-component-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedLogMessage-component-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "component": 0, + "data": {} + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessage-data-required.json b/test/unified-test-format/invalid/expectedLogMessage-data-required.json new file mode 100644 index 000000000..7e8152ddd --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessage-data-required.json @@ -0,0 +1,28 @@ +{ + "description": "expectedLogMessage-data-required", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "component": "command" + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessage-data-type.json b/test/unified-test-format/invalid/expectedLogMessage-data-type.json new file mode 100644 index 000000000..4f81fb627 --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessage-data-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedLogMessage-data-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "component": "command", + "data": 0 + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessage-failureIsRedacted-type.json b/test/unified-test-format/invalid/expectedLogMessage-failureIsRedacted-type.json new file mode 100644 index 000000000..190748a18 --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessage-failureIsRedacted-type.json @@ -0,0 +1,30 @@ +{ + "description": "expectedLogMessage-failureIsRedacted-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "debug", + "component": "command", + "failureIsRedacted": 0, + "data": {} + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessage-level-enum.json b/test/unified-test-format/invalid/expectedLogMessage-level-enum.json new file mode 100644 index 000000000..f4c886bb6 --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessage-level-enum.json @@ -0,0 +1,29 @@ +{ + "description": "expectedLogMessage-level-enum", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": "foo", + "component": "command", + "data": {} + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessage-level-required.json b/test/unified-test-format/invalid/expectedLogMessage-level-required.json new file mode 100644 index 000000000..27c9c7a6c --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessage-level-required.json @@ -0,0 +1,28 @@ +{ + "description": "expectedLogMessage-level-required", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "component": "command", + "data": {} + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessage-level-type.json b/test/unified-test-format/invalid/expectedLogMessage-level-type.json new file mode 100644 index 000000000..180d7afcd --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessage-level-type.json @@ -0,0 +1,29 @@ +{ + "description": "expectedLogMessage-level-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + { + "level": 0, + "component": "command", + "data": {} + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessagesForClient-additionalProperties.json b/test/unified-test-format/invalid/expectedLogMessagesForClient-additionalProperties.json new file mode 100644 index 000000000..306b78b44 --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessagesForClient-additionalProperties.json @@ -0,0 +1,24 @@ +{ + "description": "expectedLogMessagesForClient-additionalProperties", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [], + "foo": 0 + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessagesForClient-client-required.json b/test/unified-test-format/invalid/expectedLogMessagesForClient-client-required.json new file mode 100644 index 000000000..d8e1100be --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessagesForClient-client-required.json @@ -0,0 +1,22 @@ +{ + "description": "expectedLogMessagesForClient-client-required", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "messages": [] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessagesForClient-client-type.json b/test/unified-test-format/invalid/expectedLogMessagesForClient-client-type.json new file mode 100644 index 000000000..5399cac02 --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessagesForClient-client-type.json @@ -0,0 +1,23 @@ +{ + "description": "expectedEventsForClient-client-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": 0, + "messages": [] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessagesForClient-ignoreExtraMessages-type.json b/test/unified-test-format/invalid/expectedLogMessagesForClient-ignoreExtraMessages-type.json new file mode 100644 index 000000000..a9f2da9bc --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessagesForClient-ignoreExtraMessages-type.json @@ -0,0 +1,24 @@ +{ + "description": "expectedLogMessagesForClient-ignoreExtraMessages-type", + "schemaVersion": "1.16", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "ignoreExtraMessages": "true", + "messages": [] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessagesForClient-ignoreMessages-items.json b/test/unified-test-format/invalid/expectedLogMessagesForClient-ignoreMessages-items.json new file mode 100644 index 000000000..345faf41f --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessagesForClient-ignoreMessages-items.json @@ -0,0 +1,26 @@ +{ + "description": "expectedLogMessagesForClient-ignoreMessages-items", + "schemaVersion": "1.16", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [], + "ignoreMessages": [ + 0 + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessagesForClient-ignoreMessages-type.json b/test/unified-test-format/invalid/expectedLogMessagesForClient-ignoreMessages-type.json new file mode 100644 index 000000000..4bc2d41db --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessagesForClient-ignoreMessages-type.json @@ -0,0 +1,24 @@ +{ + "description": "expectedLogMessagesForClient-ignoreMessages-type", + "schemaVersion": "1.16", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [], + "ignoreMessages": 0 + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessagesForClient-messages-items.json b/test/unified-test-format/invalid/expectedLogMessagesForClient-messages-items.json new file mode 100644 index 000000000..9788d8fe5 --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessagesForClient-messages-items.json @@ -0,0 +1,25 @@ +{ + "description": "expectedLogMessagesForClient-messages-items", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": [ + 0 + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessagesForClient-messages-required.json b/test/unified-test-format/invalid/expectedLogMessagesForClient-messages-required.json new file mode 100644 index 000000000..85d070672 --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessagesForClient-messages-required.json @@ -0,0 +1,22 @@ +{ + "description": "expectedLogMessagesForClient-messages-required", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0" + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedLogMessagesForClient-messages-type.json b/test/unified-test-format/invalid/expectedLogMessagesForClient-messages-type.json new file mode 100644 index 000000000..27531667c --- /dev/null +++ b/test/unified-test-format/invalid/expectedLogMessagesForClient-messages-type.json @@ -0,0 +1,23 @@ +{ + "description": "expectedLogMessagesForClient-messages-type", + "schemaVersion": "1.13", + "createEntities": [ + { + "client": { + "id": "client0" + } + } + ], + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + { + "client": "client0", + "messages": 0 + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties.json b/test/unified-test-format/invalid/expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties.json new file mode 100644 index 000000000..ef2686e93 --- /dev/null +++ b/test/unified-test-format/invalid/expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties.json @@ -0,0 +1,23 @@ +{ + "description": "expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties", + "schemaVersion": "1.14", + "tests": [ + { + "description": "foo", + "operations": [], + "expectEvents": [ + { + "client": "client0", + "eventType": "sdam", + "events": [ + { + "topologyDescriptionChangedEvent": { + "foo": "bar" + } + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/invalid/runOnRequirement-authMechanism-type.json b/test/unified-test-format/invalid/runOnRequirement-authMechanism-type.json new file mode 100644 index 000000000..b97654a74 --- /dev/null +++ b/test/unified-test-format/invalid/runOnRequirement-authMechanism-type.json @@ -0,0 +1,17 @@ +{ + "description": "runOnRequirement-authMechanism-type", + "schemaVersion": "1.19", + "runOnRequirements": [ + { + "authMechanism": 0 + } + ], + "tests": [ + { + "description": "foo", + "operations": [ + + ] + } + ] +} diff --git a/test/unified-test-format/invalid/test-expectLogMessages-items.json b/test/unified-test-format/invalid/test-expectLogMessages-items.json new file mode 100644 index 000000000..be4a609c5 --- /dev/null +++ b/test/unified-test-format/invalid/test-expectLogMessages-items.json @@ -0,0 +1,13 @@ +{ + "description": "test-expectLogMessages-items", + "schemaVersion": "1.13", + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [ + 0 + ] + } + ] +} diff --git a/test/unified-test-format/invalid/test-expectLogMessages-minItems.json b/test/unified-test-format/invalid/test-expectLogMessages-minItems.json new file mode 100644 index 000000000..d7a07c2e7 --- /dev/null +++ b/test/unified-test-format/invalid/test-expectLogMessages-minItems.json @@ -0,0 +1,11 @@ +{ + "description": "test-expectLogMessages-minItems", + "schemaVersion": "1.11", + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": [] + } + ] +} diff --git a/test/unified-test-format/invalid/test-expectLogMessages-type.json b/test/unified-test-format/invalid/test-expectLogMessages-type.json new file mode 100644 index 000000000..9a8d6fcdf --- /dev/null +++ b/test/unified-test-format/invalid/test-expectLogMessages-type.json @@ -0,0 +1,11 @@ +{ + "description": "test-expectLogMessages-type", + "schemaVersion": "1.13", + "tests": [ + { + "description": "foo", + "operations": [], + "expectLogMessages": 0 + } + ] +} diff --git a/test/unified-test-format/valid-pass/entity-cursor-iterateOnce.json b/test/unified-test-format/valid-pass/entity-cursor-iterateOnce.json index 88fc28e34..b17ae78b9 100644 --- a/test/unified-test-format/valid-pass/entity-cursor-iterateOnce.json +++ b/test/unified-test-format/valid-pass/entity-cursor-iterateOnce.json @@ -93,7 +93,10 @@ "commandStartedEvent": { "command": { "getMore": { - "$$type": "long" + "$$type": [ + "int", + "long" + ] }, "collection": "coll0" }, diff --git a/test/unified-test-format/valid-pass/entity-find-cursor.json b/test/unified-test-format/valid-pass/entity-find-cursor.json index 85b8f69d7..6f955d81f 100644 --- a/test/unified-test-format/valid-pass/entity-find-cursor.json +++ b/test/unified-test-format/valid-pass/entity-find-cursor.json @@ -109,7 +109,10 @@ "reply": { "cursor": { "id": { - "$$type": "long" + "$$type": [ + "int", + "long" + ] }, "ns": { "$$type": "string" @@ -126,7 +129,10 @@ "commandStartedEvent": { "command": { "getMore": { - "$$type": "long" + "$$type": [ + "int", + "long" + ] }, "collection": "coll0" }, @@ -138,7 +144,10 @@ "reply": { "cursor": { "id": { - "$$type": "long" + "$$type": [ + "int", + "long" + ] }, "ns": { "$$type": "string" diff --git a/test/unified-test-format/valid-pass/expectedEventsForClient-topologyDescriptionChangedEvent.json b/test/unified-test-format/valid-pass/expectedEventsForClient-topologyDescriptionChangedEvent.json new file mode 100644 index 000000000..cf7bd6082 --- /dev/null +++ b/test/unified-test-format/valid-pass/expectedEventsForClient-topologyDescriptionChangedEvent.json @@ -0,0 +1,68 @@ +{ + "description": "expectedEventsForClient-topologyDescriptionChangedEvent", + "schemaVersion": "1.20", + "runOnRequirements": [ + { + "topologies": [ + "replicaset" + ], + "minServerVersion": "4.4" + } + ], + "tests": [ + { + "description": "can assert on values of newDescription and previousDescription fields", + "operations": [ + { + "name": "createEntities", + "object": "testRunner", + "arguments": { + "entities": [ + { + "client": { + "id": "client", + "uriOptions": { + "directConnection": true + }, + "observeEvents": [ + "topologyDescriptionChangedEvent" + ] + } + } + ] + } + }, + { + "name": "waitForEvent", + "object": "testRunner", + "arguments": { + "client": "client", + "event": { + "topologyDescriptionChangedEvent": {} + }, + "count": 1 + } + } + ], + "expectEvents": [ + { + "client": "client", + "eventType": "sdam", + "ignoreExtraEvents": true, + "events": [ + { + "topologyDescriptionChangedEvent": { + "previousDescription": { + "type": "Unknown" + }, + "newDescription": { + "type": "Single" + } + } + } + ] + } + ] + } + ] +} diff --git a/test/unified-test-format/valid-pass/poc-crud.json b/test/unified-test-format/valid-pass/poc-crud.json index 0790d9b78..94e4ec568 100644 --- a/test/unified-test-format/valid-pass/poc-crud.json +++ b/test/unified-test-format/valid-pass/poc-crud.json @@ -322,7 +322,7 @@ "minServerVersion": "4.1.0", "topologies": [ "replicaset", - "sharded-replicaset" + "sharded" ], "serverless": "forbid" } diff --git a/test/unified-test-format/valid-pass/poc-sessions.json b/test/unified-test-format/valid-pass/poc-sessions.json index 75f348942..117c9e7d0 100644 --- a/test/unified-test-format/valid-pass/poc-sessions.json +++ b/test/unified-test-format/valid-pass/poc-sessions.json @@ -264,7 +264,7 @@ { "minServerVersion": "4.1.8", "topologies": [ - "sharded-replicaset" + "sharded" ] } ], diff --git a/test/unified-test-format/valid-pass/poc-transactions-convenient-api.json b/test/unified-test-format/valid-pass/poc-transactions-convenient-api.json index 820ed6592..9ab44a9c5 100644 --- a/test/unified-test-format/valid-pass/poc-transactions-convenient-api.json +++ b/test/unified-test-format/valid-pass/poc-transactions-convenient-api.json @@ -11,7 +11,7 @@ { "minServerVersion": "4.1.8", "topologies": [ - "sharded-replicaset" + "sharded" ] } ], diff --git a/test/unified-test-format/valid-pass/poc-transactions-mongos-pin-auto.json b/test/unified-test-format/valid-pass/poc-transactions-mongos-pin-auto.json index a0b297d59..de08edec4 100644 --- a/test/unified-test-format/valid-pass/poc-transactions-mongos-pin-auto.json +++ b/test/unified-test-format/valid-pass/poc-transactions-mongos-pin-auto.json @@ -5,7 +5,7 @@ { "minServerVersion": "4.1.8", "topologies": [ - "sharded-replicaset" + "sharded" ] } ], diff --git a/test/unified-test-format/valid-pass/poc-transactions.json b/test/unified-test-format/valid-pass/poc-transactions.json index 0355ca206..2055a3b70 100644 --- a/test/unified-test-format/valid-pass/poc-transactions.json +++ b/test/unified-test-format/valid-pass/poc-transactions.json @@ -11,7 +11,7 @@ { "minServerVersion": "4.1.8", "topologies": [ - "sharded-replicaset" + "sharded" ] } ], @@ -93,7 +93,7 @@ "minServerVersion": "4.3.4", "topologies": [ "replicaset", - "sharded-replicaset" + "sharded" ] } ], @@ -203,7 +203,7 @@ "minServerVersion": "4.3.4", "topologies": [ "replicaset", - "sharded-replicaset" + "sharded" ] } ], diff --git a/test/unified_format.py b/test/unified_format.py index 93ef10090..4ae37353c 100644 --- a/test/unified_format.py +++ b/test/unified_format.py @@ -814,84 +814,79 @@ class MatchEvaluatorUtil: self.test.assertEqual(expectation, actual) return None - def assertHasDatabaseName(self, spec, actual): - if "databaseName" in spec: - self.test.assertEqual(spec["databaseName"], actual.database_name) - - def assertHasServiceId(self, spec, actual): - if "hasServiceId" in spec: - if spec.get("hasServiceId"): - self.test.assertIsNotNone(actual.service_id) - self.test.assertIsInstance(actual.service_id, ObjectId) - else: - self.test.assertIsNone(actual.service_id) - - def assertHasInterruptInUseConnections(self, spec, actual): - if "interruptInUseConnections" in spec: - self.test.assertEqual( - spec.get("interruptInUseConnections"), actual.interrupt_connections - ) - else: - self.test.assertIsInstance(actual.interrupt_connections, bool) - - def assertHasServerConnectionId(self, spec, actual): - if "hasServerConnectionId" in spec: - if spec.get("hasServerConnectionId"): - self.test.assertIsNotNone(actual.server_connection_id) - self.test.assertIsInstance(actual.server_connection_id, int) - else: - self.test.assertIsNone(actual.server_connection_id) - def match_server_description(self, actual: ServerDescription, spec: dict) -> None: - if "type" in spec: - self.test.assertEqual(actual.server_type_name, spec["type"]) - if "error" in spec: - self.test.process_error(actual.error, spec["error"]) - if "minWireVersion" in spec: - self.test.assertEqual(actual.min_wire_version, spec["minWireVersion"]) - if "maxWireVersion" in spec: - self.test.assertEqual(actual.max_wire_version, spec["maxWireVersion"]) - if "topologyVersion" in spec: - self.test.assertEqual(actual.topology_version, spec["topologyVersion"]) + for field, expected in spec.items(): + field = camel_to_snake(field) + if field == "type": + field = "server_type_name" + self.test.assertEqual(getattr(actual, field), expected) - def match_event(self, event_type, expectation, actual): + def match_topology_description(self, actual: TopologyDescription, spec: dict) -> None: + for field, expected in spec.items(): + field = camel_to_snake(field) + if field == "type": + field = "topology_type_name" + self.test.assertEqual(getattr(actual, field), expected) + + def match_event_fields(self, actual: Any, spec: dict) -> None: + for field, expected in spec.items(): + if field == "command" and isinstance(actual, CommandStartedEvent): + command = spec["command"] + if command: + self.match_result(command, actual.command) + continue + if field == "reply" and isinstance(actual, CommandSucceededEvent): + reply = spec["reply"] + if reply: + self.match_result(reply, actual.reply) + continue + if field == "hasServiceId": + if spec["hasServiceId"]: + self.test.assertIsNotNone(actual.service_id) + self.test.assertIsInstance(actual.service_id, ObjectId) + else: + self.test.assertIsNone(actual.service_id) + continue + if field == "hasServerConnectionId": + if spec["hasServerConnectionId"]: + self.test.assertIsNotNone(actual.server_connection_id) + self.test.assertIsInstance(actual.server_connection_id, int) + else: + self.test.assertIsNone(actual.server_connection_id) + continue + if field in ("previousDescription", "newDescription"): + if isinstance(actual, ServerDescriptionChangedEvent): + self.match_server_description( + getattr(actual, camel_to_snake(field)), spec[field] + ) + continue + if isinstance(actual, TopologyDescriptionChangedEvent): + self.match_topology_description( + getattr(actual, camel_to_snake(field)), spec[field] + ) + continue + + if field == "interruptInUseConnections": + field = "interrupt_connections" + else: + field = camel_to_snake(field) + self.test.assertEqual(getattr(actual, field), expected) + + def match_event(self, expectation, actual): name, spec = next(iter(expectation.items())) - - # every command event has the commandName field - if event_type == "command": - command_name = spec.get("commandName") - if command_name: - self.test.assertEqual(command_name, actual.command_name) - if name == "commandStartedEvent": self.test.assertIsInstance(actual, CommandStartedEvent) - command = spec.get("command") - if command: - self.match_result(command, actual.command) - self.assertHasDatabaseName(spec, actual) - self.assertHasServiceId(spec, actual) - self.assertHasServerConnectionId(spec, actual) elif name == "commandSucceededEvent": self.test.assertIsInstance(actual, CommandSucceededEvent) - reply = spec.get("reply") - if reply: - self.match_result(reply, actual.reply) - self.assertHasDatabaseName(spec, actual) - self.assertHasServiceId(spec, actual) - self.assertHasServerConnectionId(spec, actual) elif name == "commandFailedEvent": self.test.assertIsInstance(actual, CommandFailedEvent) - self.assertHasServiceId(spec, actual) - self.assertHasDatabaseName(spec, actual) - self.assertHasServerConnectionId(spec, actual) elif name == "poolCreatedEvent": self.test.assertIsInstance(actual, PoolCreatedEvent) elif name == "poolReadyEvent": self.test.assertIsInstance(actual, PoolReadyEvent) elif name == "poolClearedEvent": self.test.assertIsInstance(actual, PoolClearedEvent) - self.assertHasServiceId(spec, actual) - self.assertHasInterruptInUseConnections(spec, actual) + self.test.assertIsInstance(actual.interrupt_connections, bool) elif name == "poolClosedEvent": self.test.assertIsInstance(actual, PoolClosedEvent) elif name == "connectionCreatedEvent": @@ -900,43 +895,29 @@ class MatchEvaluatorUtil: self.test.assertIsInstance(actual, ConnectionReadyEvent) elif name == "connectionClosedEvent": self.test.assertIsInstance(actual, ConnectionClosedEvent) - if "reason" in spec: - self.test.assertEqual(actual.reason, spec["reason"]) elif name == "connectionCheckOutStartedEvent": self.test.assertIsInstance(actual, ConnectionCheckOutStartedEvent) elif name == "connectionCheckOutFailedEvent": self.test.assertIsInstance(actual, ConnectionCheckOutFailedEvent) - if "reason" in spec: - self.test.assertEqual(actual.reason, spec["reason"]) elif name == "connectionCheckedOutEvent": self.test.assertIsInstance(actual, ConnectionCheckedOutEvent) elif name == "connectionCheckedInEvent": self.test.assertIsInstance(actual, ConnectionCheckedInEvent) elif name == "serverDescriptionChangedEvent": self.test.assertIsInstance(actual, ServerDescriptionChangedEvent) - if "previousDescription" in spec: - self.match_server_description( - actual.previous_description, spec["previousDescription"] - ) - if "newDescription" in spec: - self.match_server_description(actual.new_description, spec["newDescription"]) elif name == "serverHeartbeatStartedEvent": self.test.assertIsInstance(actual, ServerHeartbeatStartedEvent) - if "awaited" in spec: - self.test.assertEqual(actual.awaited, spec["awaited"]) elif name == "serverHeartbeatSucceededEvent": self.test.assertIsInstance(actual, ServerHeartbeatSucceededEvent) - if "awaited" in spec: - self.test.assertEqual(actual.awaited, spec["awaited"]) elif name == "serverHeartbeatFailedEvent": self.test.assertIsInstance(actual, ServerHeartbeatFailedEvent) - if "awaited" in spec: - self.test.assertEqual(actual.awaited, spec["awaited"]) elif name == "topologyDescriptionChangedEvent": self.test.assertIsInstance(actual, TopologyDescriptionChangedEvent) else: raise Exception(f"Unsupported event type {name}") + self.match_event_fields(actual, spec) + def coerce_result(opname, result): """Convert a pymongo result into the spec's result format.""" @@ -972,7 +953,7 @@ class UnifiedSpecTestMixinV1(IntegrationTest): a class attribute ``TEST_SPEC``. """ - SCHEMA_VERSION = Version.from_string("1.19") + SCHEMA_VERSION = Version.from_string("1.20") RUN_ON_LOAD_BALANCER = True RUN_ON_SERVERLESS = True TEST_SPEC: Any @@ -1068,6 +1049,10 @@ class UnifiedSpecTestMixinV1(IntegrationTest): if "unpin after TransientTransactionError error on" in spec["description"]: self.skipTest("Skipping TransientTransactionError pending PYTHON-4227") + if "withTransaction commits after callback returns" in spec["description"]: + self.skipTest("Skipping TransientTransactionError pending PYTHON-4303") + if "unpin on successful abort" in spec["description"]: + self.skipTest("Skipping TransientTransactionError pending PYTHON-4227") if "unpin after non-transient error on abort" in spec["description"]: if client_context.version[0] == 8: @@ -1606,7 +1591,7 @@ class UnifiedSpecTestMixinV1(IntegrationTest): count = 0 for actual in actual_events: try: - self.match_evaluator.match_event("all", event, actual) + self.match_evaluator.match_event(event, actual) except AssertionError: continue else: @@ -1763,10 +1748,17 @@ class UnifiedSpecTestMixinV1(IntegrationTest): self.assertEqual(actual_events, []) continue - self.assertEqual(len(actual_events), len(events), actual_events) + if len(actual_events) != len(events): + expected = "\n".join(str(e) for e in events) + actual = "\n".join(str(a) for a in actual_events) + self.assertEqual( + len(actual_events), + len(events), + f"expected events:\n{expected}\nactual events:\n{actual}", + ) for idx, expected_event in enumerate(events): - self.match_evaluator.match_event(event_type, expected_event, actual_events[idx]) + self.match_evaluator.match_event(expected_event, actual_events[idx]) if has_server_connection_id: assert server_connection_id is not None @@ -1800,6 +1792,8 @@ class UnifiedSpecTestMixinV1(IntegrationTest): clientid = self.entity_map[client["client"]]._topology_settings._topology_id actual_logs = formatted_logs[clientid] actual_logs = [log for log in actual_logs if log["component"] in components] + if client.get("ignoreExtraMessages", False): + actual_logs = actual_logs[: len(client["messages"])] self.assertEqual(len(client["messages"]), len(actual_logs)) for expected_msg, actual_msg in zip(client["messages"], actual_logs): expected_data, actual_data = expected_msg.pop("data"), actual_msg.pop("data")