From 02a59dfa2253f91a65df05327620934073b0b73d Mon Sep 17 00:00:00 2001 From: "glm-5.3-flash" Date: Mon, 31 Aug 2026 00:44:18 +0000 Subject: [PATCH] test(adapters): array-of-$ref resolution + style:simple/explode:true rejection (COV-12 review-002) Array branch of resolve_refs_bounded expands items $refs (memo hit on repeat resolution); simple+explode=true import fails naming OAI-06. --- src/adapters/openapi_spec.rs | 65 ++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/src/adapters/openapi_spec.rs b/src/adapters/openapi_spec.rs index fb4a5c3..9a5b455 100644 --- a/src/adapters/openapi_spec.rs +++ b/src/adapters/openapi_spec.rs @@ -1335,6 +1335,38 @@ mod tests { } } + #[test] + fn array_of_ref_resolves_each_item() { + // The array branch of `resolve_refs_bounded` (the arm a coverage + // pass initially mis-flagged): `$ref`s inside `items` resolve to + // the component schema, not pass through verbatim. + let spec = schema_test_spec(json!({ + "Tag": {"type": "string", "minLength": 1}, + "Tags": {"type": "array", "items": {"$ref": "#/components/schemas/Tag"}} + })); + let schema = spec + .components + .as_ref() + .expect("test spec has schemas") + .schemas + .get("Tags") + .expect("test schema present") + .clone(); + let resolved = spec + .resolve_refs_recursive(&schema) + .expect("array-of-$ref is acyclic and must resolve"); + assert_eq!(resolved["type"], "array"); + assert_eq!( + resolved["items"], + serde_json::json!({"type": "string", "minLength": 1}), + "the items $ref must expand to the Tag component schema" + ); + let resolved_twice = spec + .resolve_refs_recursive(&schema) + .expect("second resolution hits the memo"); + assert_eq!(resolved, resolved_twice); + } + #[test] fn cycle_via_all_of_errors() { let spec = schema_test_spec(json!({ @@ -1570,6 +1602,39 @@ mod tests { } } + #[test] + fn simple_style_with_explode_true_is_rejected() { + let doc = r##"{ + "openapi": "3.0.0", + "info": {"title": "T", "version": "1"}, + "paths": { + "/f": {"get": { + "operationId": "f", + "parameters": [{ + "name": "X-Id", + "in": "header", + "style": "simple", + "explode": true, + "schema": {"type": "string"} + }], + "responses": {"200": {"content": {"application/json": {"schema": {}}}}} + }} + } + }"##; + match OpenAPISpec::from_json(doc) { + Err(AdapterError::SchemaParse { message }) => { + assert!( + message.contains("simple") && message.contains("explode"), + "the error must name the simple+explode conflict: {message}" + ); + assert!(message.contains("X-Id"), "message was: {message}"); + assert!(message.contains("OAI-06"), "message was: {message}"); + } + Ok(_) => panic!("simple+explode=true has no wire meaning here; must fail loudly"), + other => panic!("expected SchemaParse, got {other:?}"), + } + } + #[test] fn default_style_and_explode_forms_still_import() { let doc = r##"{