feat(import): resolve references and schema composition

This commit is contained in:
2026-08-29 08:54:26 +03:00
parent 6c2a3712d8
commit 55209a9bbc
46 changed files with 4848 additions and 437 deletions
@@ -161,6 +161,58 @@ paths:
assert!(ir.operations[0].request_body_schema.is_some());
}
#[test]
fn operation_parameters_override_path_parameters_by_name_and_location() {
let openapi = r#"
openapi: 3.1.0
info: { title: OAS parameter overrides }
servers: [{ url: https://example.test }]
paths:
/items:
parameters:
- { name: page, in: query, description: path, schema: { type: integer } }
get:
operationId: listItems
parameters:
- { name: page, in: query, description: operation, schema: { type: string } }
responses: { '200': { description: ok } }
"#;
let ir = normalize_document(openapi, &NormalizationConfig::default()).unwrap();
assert_eq!(ir.operations[0].parameters.len(), 1);
assert_eq!(
ir.operations[0].parameters[0].description.as_deref(),
Some("operation")
);
assert_eq!(
ir.operations[0].parameters[0].source_location.pointer,
"/paths/~1items/get/parameters/0"
);
let swagger = r#"
swagger: '2.0'
info: { title: Swagger parameter overrides }
paths:
/items:
parameters:
- { name: page, in: query, description: path, type: integer }
get:
operationId: listItems
parameters:
- { name: page, in: query, description: operation, type: string }
responses: { '200': { description: ok } }
"#;
let ir = normalize_document(swagger, &NormalizationConfig::default()).unwrap();
assert_eq!(ir.operations[0].parameters.len(), 1);
assert_eq!(
ir.operations[0].parameters[0].description.as_deref(),
Some("operation")
);
assert_eq!(
ir.operations[0].parameters[0].source_location.pointer,
"/paths/~1items/get/parameters/0"
);
}
#[test]
fn openapi_parameter_omissions_are_errors_at_the_dropped_item_pointer() {
let document = r#"
@@ -220,10 +272,6 @@ components:
"/paths/~1items~1{id}/parameters/1",
),
("invalid_parameter", "/paths/~1items~1{id}/parameters/2",),
(
"unresolved_parameter_reference",
"/paths/~1items~1{id}/parameters/3",
),
(
"missing_parameter_name",
"/paths/~1items~1{id}/get/parameters/0",
@@ -336,10 +384,6 @@ parameters:
"/paths/~1items~1{id}/parameters/1",
),
("invalid_parameter", "/paths/~1items~1{id}/parameters/2",),
(
"unresolved_parameter_reference",
"/paths/~1items~1{id}/parameters/3",
),
(
"missing_parameter_name",
"/paths/~1items~1{id}/get/parameters/0",
@@ -688,7 +732,7 @@ paths:
}
#[test]
fn preserves_every_unresolved_reference_from_the_full_source_tree() {
fn resolves_local_reference_graph_and_preserves_external_blocker() {
let document = r#"
openapi: 3.1.0
info: { title: References }
@@ -722,32 +766,24 @@ components:
serde_json::to_vec(&first).unwrap(),
serde_json::to_vec(&second).unwrap()
);
assert_eq!(first.unresolved_references.len(), 10);
assert_eq!(first.unresolved_references.len(), 1);
let references = first
.unresolved_references
.iter()
.map(|reference| (reference.uri.as_str(), reference.location.pointer.as_str()))
.collect::<std::collections::BTreeSet<_>>();
assert!(references.contains(&(
"#/components/parameters/Id",
"/paths/~1ok/parameters/0/$ref"
)));
assert!(references.contains(&(
"#/components/requestBodies/Body",
"/paths/~1ok/get/requestBody/$ref"
)));
assert!(references.contains(&(
"#/components/responses/Ok",
"/paths/~1ok/get/responses/200/$ref"
)));
assert!(references.contains(&("#/components/pathItems/Reusable", "/paths/~1reused/$ref")));
assert!(
references.contains(&("#/components/schemas/Loop", "/components/schemas/Loop/$ref"))
);
assert!(references.contains(&(
"https://example.test/schema.json#/Remote",
"/components/schemas/Remote/$ref"
)));
assert!(first.reference_graph.edges.len() >= 5);
assert!(
first
.reference_graph
.edges
.iter()
.any(|edge| edge.recursive)
);
assert!(first.unresolved_references.iter().all(|reference| {
reference.construct_id
== format!(