From 2ff6b5388eff351c734b4c65da43ec02164cc900 Mon Sep 17 00:00:00 2001 From: bymyself Date: Sat, 28 Jun 2025 22:30:37 -0700 Subject: [PATCH] [tests] Update API tests to use /api prefix - Simplified api_client fixture to hardcode /api prefix - Updated websocket endpoint test to use /api/ws - Fixed internal endpoint tests to not use /api prefix - Enhanced validation.py to handle OpenAPI nullable syntax --- tests-api/conftest.py | 3 +- tests-api/test_endpoint_existence.py | 11 +++-- tests-api/utils/validation.py | 69 ++++++++++++++++++---------- 3 files changed, 55 insertions(+), 28 deletions(-) diff --git a/tests-api/conftest.py b/tests-api/conftest.py index e124279b6..4e6df7330 100644 --- a/tests-api/conftest.py +++ b/tests-api/conftest.py @@ -93,7 +93,8 @@ def api_client(base_url: str) -> Generator[Optional[requests.Session], None, Non # Helper function to construct URLs def get_url(path: str) -> str: - return urljoin(base_url, path) + # All API endpoints use the /api prefix + return urljoin(base_url, '/api' + path) # Add url helper to the session session.get_url = get_url # type: ignore diff --git a/tests-api/test_endpoint_existence.py b/tests-api/test_endpoint_existence.py index 6cf13c3f5..8340dc2ac 100644 --- a/tests-api/test_endpoint_existence.py +++ b/tests-api/test_endpoint_existence.py @@ -116,7 +116,8 @@ def test_websocket_endpoint_exists(require_server, base_url: str): require_server: Fixture that skips if server is not available base_url: Base server URL """ - ws_url = urljoin(base_url, "/ws") + # WebSocket endpoint uses /api prefix + ws_url = urljoin(base_url, "/api/ws") # For WebSocket, we can't use a normal GET request # Instead, we make a HEAD request to check if the endpoint exists @@ -209,13 +210,14 @@ def test_api_object_info_node_endpoint(require_server, api_client): pytest.fail(f"Failed to process response: {str(e)}") -def test_internal_endpoints_exist(require_server, api_client): +def test_internal_endpoints_exist(require_server, api_client, base_url: str): """ Test that internal endpoints exist Args: require_server: Fixture that skips if server is not available api_client: API client fixture + base_url: Base server URL """ internal_endpoints = [ "/internal/logs", @@ -225,10 +227,11 @@ def test_internal_endpoints_exist(require_server, api_client): ] for endpoint in internal_endpoints: - url = api_client.get_url(endpoint) # type: ignore + # Internal endpoints don't use the /api/ prefix + url = urljoin(base_url, endpoint) try: - response = api_client.get(url) + response = requests.get(url) # We're just checking that the endpoint exists assert response.status_code != 404, f"Endpoint {endpoint} does not exist" diff --git a/tests-api/utils/validation.py b/tests-api/utils/validation.py index 2ed9a5a4e..6fc2195a4 100644 --- a/tests-api/utils/validation.py +++ b/tests-api/utils/validation.py @@ -69,7 +69,7 @@ def get_endpoint_schema( def resolve_schema_refs(schema: Dict[str, Any], spec: Dict[str, Any]) -> Dict[str, Any]: """ - Resolve $ref references in a schema + Resolve $ref references in a schema and convert OpenAPI nullable to JSON Schema Args: schema: Schema that may contain references @@ -83,29 +83,52 @@ def resolve_schema_refs(schema: Dict[str, Any], spec: Dict[str, Any]) -> Dict[st result = {} - for key, value in schema.items(): - if key == '$ref' and isinstance(value, str) and value.startswith('#/'): - # Handle reference - ref_path = value[2:].split('/') - ref_value = spec - for path_part in ref_path: - ref_value = ref_value.get(path_part, {}) + # Check if this schema has nullable: true with a type + if schema.get('nullable') is True and 'type' in schema: + # Convert OpenAPI nullable syntax to JSON Schema oneOf + original_type = schema['type'] + result['oneOf'] = [ + {'type': original_type}, + {'type': 'null'} + ] + # Copy other properties except nullable and type + for key, value in schema.items(): + if key not in ['nullable', 'type']: + if isinstance(value, dict): + result[key] = resolve_schema_refs(value, spec) + elif isinstance(value, list): + result[key] = [ + resolve_schema_refs(item, spec) if isinstance(item, dict) else item + for item in value + ] + else: + result[key] = value + else: + # Normal processing + for key, value in schema.items(): + if key == '$ref' and isinstance(value, str) and value.startswith('#/'): + # Handle reference + ref_path = value[2:].split('/') + ref_value = spec + for path_part in ref_path: + ref_value = ref_value.get(path_part, {}) - # Recursively resolve any refs in the referenced schema - ref_value = resolve_schema_refs(ref_value, spec) - result.update(ref_value) - elif isinstance(value, dict): - # Recursively resolve refs in nested dictionaries - result[key] = resolve_schema_refs(value, spec) - elif isinstance(value, list): - # Recursively resolve refs in list items - result[key] = [ - resolve_schema_refs(item, spec) if isinstance(item, dict) else item - for item in value - ] - else: - # Pass through other values - result[key] = value + # Recursively resolve any refs in the referenced schema + ref_value = resolve_schema_refs(ref_value, spec) + result.update(ref_value) + elif isinstance(value, dict): + # Recursively resolve refs in nested dictionaries + result[key] = resolve_schema_refs(value, spec) + elif isinstance(value, list): + # Recursively resolve refs in list items + result[key] = [ + resolve_schema_refs(item, spec) if isinstance(item, dict) else item + for item in value + ] + else: + # Pass through other values (skip nullable as it's OpenAPI specific) + if key != 'nullable': + result[key] = value return result