Fix mock server nested body matching and array response tests.

Recursively ignore read-only fields in nested request objects and
regenerate dashboards/streaming integration tests for list responses.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin 2026-07-28 12:40:21 +01:00
parent f1f280d48c
commit 39517ce6d1
4 changed files with 54 additions and 4 deletions

View File

@ -50,8 +50,17 @@ def _normalize_json(value: Any) -> Any:
def _json_body_matches(expected: Any, actual: Any) -> bool:
if isinstance(expected, dict) and isinstance(actual, dict):
filtered_expected = {key: expected[key] for key in actual if key in expected}
return _normalize_json(filtered_expected) == _normalize_json(actual)
for key in actual:
if key not in expected:
return False
if not _json_body_matches(expected[key], actual[key]):
return False
return True
if isinstance(expected, list) and isinstance(actual, list):
if len(expected) != len(actual):
return False
return all(_json_body_matches(expected_item, actual_item)
for expected_item, actual_item in zip(expected, actual))
return _normalize_json(expected) == _normalize_json(actual)

View File

@ -214,3 +214,38 @@ def test_mock_server_accepts_equivalent_iso8601_datetime_formats(manifest):
)
assert status == 201
assert json.loads(body.decode("utf-8")) == {"ruleId": "1"}
def test_mock_server_ignores_readonly_fields_in_nested_request_objects(manifest):
nested_manifest = {
**manifest,
"createAlertRule": OperationExpectation(
operation_id="createAlertRule",
method="POST",
path="/alerts/rules",
request_body_example={
"ruleName": "Example",
"widgets": [
{
"title": "Widget Title",
"id": "read-only-id",
"embedUrl": "https://example.com/embed",
}
],
},
success_status=201,
success_body={"ruleId": "1"},
),
}
with MockApiServer(nested_manifest) as server:
status, body = _request(
server,
method="POST",
path="/alerts/rules",
body={
"ruleName": "Example",
"widgets": [{"title": "Widget Title"}],
},
)
assert status == 201
assert json.loads(body.decode("utf-8")) == {"ruleId": "1"}

View File

@ -2916,7 +2916,10 @@ class TestDashboardsApiIntegration(IntegrationTestBase):
_headers=self.te_headers("get_dashboards"),
)
assert_constructed_model_matches_example_json(response, expected_response)
self.assertIsInstance(response, list)
self.assertEqual(len(response), len(expected_response))
for index, element in enumerate(response):
assert_constructed_model_matches_example_json(element, expected_response[index])
def test_get_dashboards_error_400(self) -> None:

View File

@ -907,7 +907,10 @@ class TestStreamingApiIntegration(IntegrationTestBase):
_headers=self.te_headers("get_streams"),
)
assert_constructed_model_matches_example_json(response, expected_response)
self.assertIsInstance(response, list)
self.assertEqual(len(response), len(expected_response))
for index, element in enumerate(response):
assert_constructed_model_matches_example_json(element, expected_response[index])
def test_get_streams_error_400(self) -> None: