From b0e4ef315849aa78e74ea0f3373617cf6dedda05 Mon Sep 17 00:00:00 2001 From: Miguel Athie <141064846+GreyCrossX@users.noreply.github.com> Date: Sat, 27 Dec 2025 02:18:51 -0600 Subject: [PATCH] test(core): add regression test for list-index `$ref` resolution (#34097) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR adds a regression test covering the JSON Schema `$ref` pattern found in MCP-style schemas, where a `$ref` points into a list-based structure such as: #/properties/body/anyOf/1/properties/Message/properties/bccRecipients/items This pattern historically failed due to incorrect handling of numeric list components in `_retrieve_ref`. The underlying bug has since been fixed, and this test ensures coverage so we don't regress on list-index `$ref` resolution. The new test (`test_dereference_refs_list_index_items_ref_mcp_like`) verifies: - correct traversal into `anyOf[1]` - proper dereferencing of `items.$ref` - no errors thrown - `ccRecipients.items` is identical to the resolved schema of `bccRecipients.items` No code changes are included, just the one test — this PR adds coverage to preserve the expected behavior and documents support for this real-world MCP schema pattern. Related to #32012. --------- Co-authored-by: Mason Daugherty --- .../unit_tests/utils/test_json_schema.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/libs/core/tests/unit_tests/utils/test_json_schema.py b/libs/core/tests/unit_tests/utils/test_json_schema.py index 68a711f42d6..a6eafdfd83e 100644 --- a/libs/core/tests/unit_tests/utils/test_json_schema.py +++ b/libs/core/tests/unit_tests/utils/test_json_schema.py @@ -452,6 +452,84 @@ def test_dereference_refs_list_index() -> None: assert actual_dict_key == expected_dict_key +def test_dereference_refs_list_index_items_ref_mcp_like() -> None: + """Regression test: MCP-style list index ref into array items.""" + schema = { + "type": "object", + "properties": { + "body": { + "anyOf": [ + {"type": "string"}, + { + "type": "object", + "properties": { + "Message": { + "type": "object", + "properties": { + "bccRecipients": { + "type": "array", + "items": { + "type": "object", + "properties": { + "emailAddress": { + "type": "object", + "properties": { + "address": {"type": "string"}, + "name": {"type": "string"}, + }, + "required": ["address"], + } + }, + }, + "description": ( + "The Bcc: recipients for the message." + ), + }, + "ccRecipients": { + "type": "array", + "items": { + "$ref": ( + "#/properties/body/anyOf/1/" + "properties/Message/properties/" + "bccRecipients/items" + ) + }, + "description": ( + "The Cc: recipients for the message." + ), + }, + }, + "additionalProperties": False, + }, + "SaveToSentItems": { + "type": ["boolean", "null"], + "default": False, + }, + }, + "additionalProperties": False, + }, + ] + } + }, + "required": ["body"], + "additionalProperties": False, + } + + resolved = dereference_refs(schema) + + message_props = resolved["properties"]["body"]["anyOf"][1]["properties"]["Message"][ + "properties" + ] + + bcc_items = message_props["bccRecipients"]["items"] + cc_items = message_props["ccRecipients"]["items"] + + # $ref should be fully resolved in ccRecipients.items + assert "$ref" not in cc_items + # And ccRecipients.items should match bccRecipients.items + assert cc_items == bcc_items + + def test_dereference_refs_mixed_ref_with_properties() -> None: """Test dereferencing refs that have $ref plus other properties.""" # This pattern can cause infinite recursion if not handled correctly