fix(repository-api): reject overlapping oneof literals

This commit is contained in:
2026-09-18 20:53:48 +09:00
parent e6d3905dc6
commit b0ad3689d0
@@ -404,6 +404,7 @@ fn render_schema(value: &Value, context: &str) -> Result<String, GenerationError
)));
}
let mut rendered = Vec::with_capacity(branches.len());
let mut seen_literals = BTreeSet::new();
for branch in branches {
let branch_object = object(branch, context)?;
if !branch_object.contains_key("enum") && !branch_object.contains_key("const") {
@@ -411,6 +412,32 @@ fn render_schema(value: &Value, context: &str) -> Result<String, GenerationError
"{context} contains unsupported ambiguous oneOf semantics"
)));
}
let literals = if let Some(constant) = branch_object.get("const") {
std::slice::from_ref(constant)
} else {
branch_object
.get("enum")
.and_then(Value::as_array)
.ok_or_else(|| {
GenerationError::invalid(format!(
"{context} contains a non-array oneOf enum"
))
})?
.as_slice()
};
for literal in literals {
render_literal(literal, context)?;
let normalized = serde_json::to_string(literal).map_err(|error| {
GenerationError::invalid(format!(
"failed to normalize {context} oneOf literal: {error}"
))
})?;
if !seen_literals.insert(normalized) {
return Err(GenerationError::invalid(format!(
"{context} contains overlapping oneOf literal branches"
)));
}
}
if let Some(kind) = outer_type {
if !literal_schema_matches_type(branch_object, kind) {
return Err(GenerationError::invalid(format!(
@@ -800,6 +827,14 @@ mod tests {
.to_string()
.contains("oneOf branch does not match outer type")
);
let mut document: Value = serde_json::from_str(OPENAPI).unwrap();
document["components"]["schemas"]["RepositorySourceKind"]["oneOf"]
.as_array_mut()
.unwrap()
.push(serde_json::json!({"const": "invalid", "type": "string"}));
let error = generate_repository_typescript(&document.to_string()).unwrap_err();
assert!(error.to_string().contains("overlapping oneOf literal"));
}
#[test]