From a3c8a5fcf52b1a41d09b2dfe6261de34f1110d8d Mon Sep 17 00:00:00 2001 From: Ankit Jha Date: Tue, 29 Sep 2026 21:06:16 +0530 Subject: [PATCH] test(interactions): follow Google's redesigned Interactions OpenAPI spec tests/unit/interactions/test_openapi_compliance.py compares the SDK to the live spec at ai.google.dev, and Google has reshaped that spec. Four tests now fail on every PR: the request schema CreateModelInteractionParams no longer exists (KeyError), and the item path parameter was renamed from {id} to {interactionsId}, so GET and DELETE /interactions/{id} were not found. The create body is now read off POST /interactions, where the spec wraps an agent variant and a model variant in a oneOf, and the model one is picked by carrying `model`. The GET and DELETE lookups match `/interactions/{param}` by shape, since the parameter's name is cosmetic. Each helper raises if the endpoint disappears, so real drift still fails the test. `input` is no longer listed as required on the model variant, so the test now asserts it is a property. `model` is still asserted required. Signed-off-by: Ankit Jha --- .../interactions/test_openapi_compliance.py | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/tests/unit/interactions/test_openapi_compliance.py b/tests/unit/interactions/test_openapi_compliance.py index d3f1183cea6..433b94f5983 100644 --- a/tests/unit/interactions/test_openapi_compliance.py +++ b/tests/unit/interactions/test_openapi_compliance.py @@ -9,6 +9,7 @@ Run with: pytest tests/unit/interactions/test_openapi_compliance.py -v import json import os +import re from typing import Any, Dict from unittest.mock import MagicMock, patch @@ -44,6 +45,24 @@ def _declared_type_value(variant_schema: Dict[str, Any]) -> Any: return type_property.get("const") or (enum_values[0] if len(enum_values) == 1 else None) +def _schema_named(spec_dict: dict[str, Any], ref: str) -> dict[str, Any]: + return spec_dict["components"]["schemas"][ref.split("/")[-1]] + + +def _create_request_schema(spec_dict: dict[str, Any]) -> dict[str, Any]: + """The model-interaction body POSTed to /interactions, found by endpoint since Google renames + the schema and now wraps it in a oneOf with the agent variant.""" + for path, methods in spec_dict["paths"].items(): + if path.endswith("/interactions") and "post" in methods: + body = methods["post"]["requestBody"]["content"]["application/json"]["schema"] + candidates = body.get("oneOf") or [body] + for candidate in candidates: + schema = _schema_named(spec_dict, candidate["$ref"]) if "$ref" in candidate else candidate + if "model" in schema.get("properties", {}): + return schema + raise AssertionError("no model-interaction request body found on POST /interactions") + + @pytest.fixture(scope="module") def spec_dict() -> Dict[str, Any]: """Load raw spec dict for manual validation.""" @@ -56,16 +75,22 @@ def openapi_spec(spec_dict: Dict[str, Any]) -> OpenAPI: return OpenAPI.from_dict(spec_dict) +def _interaction_item_path(paths: dict[str, Any], method: str) -> Any: + """`/…/interactions/{param}` for a method; the parameter's name is cosmetic and has changed.""" + item = re.compile(r"/interactions/\{[^}/]+\}$") + return next((p for p, methods in paths.items() if item.search(p) and method in methods), None) + + class TestRequestCompliance: """Tests that our request bodies match the OpenAPI spec.""" def test_create_model_interaction_request_schema(self, spec_dict): - """Verify CreateModelInteractionParams schema fields.""" - schema = spec_dict["components"]["schemas"]["CreateModelInteractionParams"] + """Verify the model-interaction request body fields.""" + schema = _create_request_schema(spec_dict) - # Required fields per spec + # `model` is required; `input` is optional now that a call can resume via previous_interaction_id assert "model" in schema["required"] - assert "input" in schema["required"] + assert "input" in schema["properties"] # Check our supported optional fields exist in spec our_optional_fields = [ @@ -88,8 +113,7 @@ class TestRequestCompliance: def test_input_types_match_spec(self, spec_dict): """Verify input field supports string, Content, Content[], Turn[].""" - schema = spec_dict["components"]["schemas"]["CreateModelInteractionParams"] - input_schema = schema["properties"]["input"] + input_schema = _create_request_schema(spec_dict)["properties"]["input"] # The input property may be inline oneOf or a $ref to InteractionsInput if "$ref" in input_schema: @@ -311,11 +335,7 @@ class TestEndpointCompliance: """Verify GET /interactions/{id} endpoint exists.""" paths = spec_dict["paths"] - get_path = None - for path, methods in paths.items(): - if "{id}" in path and "interactions" in path and "get" in methods: - get_path = path - break + get_path = _interaction_item_path(paths, "get") assert get_path is not None, "GET /interactions/{id} endpoint not found" print(f"✓ Get endpoint: GET {get_path}") @@ -324,11 +344,7 @@ class TestEndpointCompliance: """Verify DELETE /interactions/{id} endpoint exists.""" paths = spec_dict["paths"] - delete_path = None - for path, methods in paths.items(): - if "{id}" in path and "interactions" in path and "delete" in methods: - delete_path = path - break + delete_path = _interaction_item_path(paths, "delete") assert delete_path is not None, "DELETE /interactions/{id} endpoint not found" print(f"✓ Delete endpoint: DELETE {delete_path}")