PYTHON-4299 Add support for TopologyDescriptionChangedEvent to expectEvents (#1572)

This commit is contained in:
Shane Harvey 2024-04-10 17:16:20 -07:00 committed by GitHub
parent efe8cc38a6
commit 4e5813c07b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 868 additions and 95 deletions

View File

@ -0,0 +1,18 @@
{
"description": "entity-client-observeLogMessages-minProperties",
"schemaVersion": "1.13",
"createEntities": [
{
"client": {
"id": "client0",
"observeLogMessages": {}
}
}
],
"tests": [
{
"description": "foo",
"operations": []
}
]
}

View File

@ -0,0 +1,20 @@
{
"description": "entity-client-observeLogMessages-property-type",
"schemaVersion": "1.13",
"createEntities": [
{
"client": {
"id": "client0",
"observeLogMessages": {
"command": {}
}
}
}
],
"tests": [
{
"description": "foo",
"operations": []
}
]
}

View File

@ -0,0 +1,20 @@
{
"description": "entity-client-observeLogMessages-property-type",
"schemaVersion": "1.13",
"createEntities": [
{
"client": {
"id": "client0",
"observeLogMessages": {
"command": "notALogLevel"
}
}
}
],
"tests": [
{
"description": "foo",
"operations": []
}
]
}

View File

@ -0,0 +1,18 @@
{
"description": "entity-client-observeLogMessages-type",
"schemaVersion": "1.13",
"createEntities": [
{
"client": {
"id": "client0",
"observeLogMessages": 0
}
}
],
"tests": [
{
"description": "foo",
"operations": []
}
]
}

View File

@ -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
}
}
]
}
]
}
]
}

View File

@ -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
}
}
]
}
]
}
]
}

View File

@ -0,0 +1,24 @@
{
"description": "expectedLogMessage-additionalProperties",
"schemaVersion": "1.13",
"createEntities": [
{
"client": {
"id": "client0"
}
}
],
"tests": [
{
"description": "foo",
"operations": [],
"expectLogMessages": [
{
"client": "client0",
"messages": [],
"foo": 0
}
]
}
]
}

View File

@ -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": {}
}
]
}
]
}
]
}

View File

@ -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": {}
}
]
}
]
}
]
}

View File

@ -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": {}
}
]
}
]
}
]
}

View File

@ -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"
}
]
}
]
}
]
}

View File

@ -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
}
]
}
]
}
]
}

View File

@ -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": {}
}
]
}
]
}
]
}

View File

@ -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": {}
}
]
}
]
}
]
}

View File

@ -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": {}
}
]
}
]
}
]
}

View File

@ -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": {}
}
]
}
]
}
]
}

View File

@ -0,0 +1,24 @@
{
"description": "expectedLogMessagesForClient-additionalProperties",
"schemaVersion": "1.13",
"createEntities": [
{
"client": {
"id": "client0"
}
}
],
"tests": [
{
"description": "foo",
"operations": [],
"expectLogMessages": [
{
"client": "client0",
"messages": [],
"foo": 0
}
]
}
]
}

View File

@ -0,0 +1,22 @@
{
"description": "expectedLogMessagesForClient-client-required",
"schemaVersion": "1.13",
"createEntities": [
{
"client": {
"id": "client0"
}
}
],
"tests": [
{
"description": "foo",
"operations": [],
"expectLogMessages": [
{
"messages": []
}
]
}
]
}

View File

@ -0,0 +1,23 @@
{
"description": "expectedEventsForClient-client-type",
"schemaVersion": "1.13",
"createEntities": [
{
"client": {
"id": "client0"
}
}
],
"tests": [
{
"description": "foo",
"operations": [],
"expectLogMessages": [
{
"client": 0,
"messages": []
}
]
}
]
}

View File

@ -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": []
}
]
}
]
}

View File

@ -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
]
}
]
}
]
}

View File

@ -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
}
]
}
]
}

View File

@ -0,0 +1,25 @@
{
"description": "expectedLogMessagesForClient-messages-items",
"schemaVersion": "1.13",
"createEntities": [
{
"client": {
"id": "client0"
}
}
],
"tests": [
{
"description": "foo",
"operations": [],
"expectLogMessages": [
{
"client": "client0",
"messages": [
0
]
}
]
}
]
}

View File

@ -0,0 +1,22 @@
{
"description": "expectedLogMessagesForClient-messages-required",
"schemaVersion": "1.13",
"createEntities": [
{
"client": {
"id": "client0"
}
}
],
"tests": [
{
"description": "foo",
"operations": [],
"expectLogMessages": [
{
"client": "client0"
}
]
}
]
}

View File

@ -0,0 +1,23 @@
{
"description": "expectedLogMessagesForClient-messages-type",
"schemaVersion": "1.13",
"createEntities": [
{
"client": {
"id": "client0"
}
}
],
"tests": [
{
"description": "foo",
"operations": [],
"expectLogMessages": [
{
"client": "client0",
"messages": 0
}
]
}
]
}

View File

@ -0,0 +1,23 @@
{
"description": "expectedSdamEvent-topologyDescriptionChangedEvent-additionalProperties",
"schemaVersion": "1.14",
"tests": [
{
"description": "foo",
"operations": [],
"expectEvents": [
{
"client": "client0",
"eventType": "sdam",
"events": [
{
"topologyDescriptionChangedEvent": {
"foo": "bar"
}
}
]
}
]
}
]
}

View File

@ -0,0 +1,17 @@
{
"description": "runOnRequirement-authMechanism-type",
"schemaVersion": "1.19",
"runOnRequirements": [
{
"authMechanism": 0
}
],
"tests": [
{
"description": "foo",
"operations": [
]
}
]
}

View File

@ -0,0 +1,13 @@
{
"description": "test-expectLogMessages-items",
"schemaVersion": "1.13",
"tests": [
{
"description": "foo",
"operations": [],
"expectLogMessages": [
0
]
}
]
}

View File

@ -0,0 +1,11 @@
{
"description": "test-expectLogMessages-minItems",
"schemaVersion": "1.11",
"tests": [
{
"description": "foo",
"operations": [],
"expectLogMessages": []
}
]
}

View File

@ -0,0 +1,11 @@
{
"description": "test-expectLogMessages-type",
"schemaVersion": "1.13",
"tests": [
{
"description": "foo",
"operations": [],
"expectLogMessages": 0
}
]
}

View File

@ -93,7 +93,10 @@
"commandStartedEvent": {
"command": {
"getMore": {
"$$type": "long"
"$$type": [
"int",
"long"
]
},
"collection": "coll0"
},

View File

@ -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"

View File

@ -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"
}
}
}
]
}
]
}
]
}

View File

@ -322,7 +322,7 @@
"minServerVersion": "4.1.0",
"topologies": [
"replicaset",
"sharded-replicaset"
"sharded"
],
"serverless": "forbid"
}

View File

@ -264,7 +264,7 @@
{
"minServerVersion": "4.1.8",
"topologies": [
"sharded-replicaset"
"sharded"
]
}
],

View File

@ -11,7 +11,7 @@
{
"minServerVersion": "4.1.8",
"topologies": [
"sharded-replicaset"
"sharded"
]
}
],

View File

@ -5,7 +5,7 @@
{
"minServerVersion": "4.1.8",
"topologies": [
"sharded-replicaset"
"sharded"
]
}
],

View File

@ -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"
]
}
],

View File

@ -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
@ -1606,7 +1587,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 +1744,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