#![cfg(feature = "validate-schema")]
use noyalib::validate_against_schema_str;
fn nested_schema(depth: usize) -> String {
let mut s = String::from(r#"{"type":"object","properties":{"a":"#);
for _ in 0..depth {
s.push_str(r#"{"type":"object","properties":{"a":"#);
}
s.push_str(r#"{"type":"integer"}"#);
for _ in 0..depth {
s.push_str("}}");
}
s.push_str("}}");
s
}
#[test]
fn external_http_ref_is_refused_not_fetched() {
let schema = r#"{"$ref": "https://example.com/schema.json"}"#;
let err = validate_against_schema_str("x: 1", schema)
.expect_err("an external $ref must not be resolved");
let msg = err.to_string();
assert!(
msg.contains("resolve-http") || msg.contains("not present in a registry"),
"expected a refusal to resolve externally, got: {msg}"
);
}
#[test]
fn external_ref_refusal_is_fast() {
let schema = r#"{"$ref": "https://example.com/schema.json"}"#;
let start = std::time::Instant::now();
let _ = validate_against_schema_str("x: 1", schema);
let elapsed = start.elapsed();
assert!(
elapsed < std::time::Duration::from_secs(2),
"refusing an external $ref took {elapsed:?}; that suggests a network attempt"
);
}
#[test]
fn deeply_nested_schemas_are_bounded_not_stack_overflowing() {
for depth in [500usize, 2_000, 10_000] {
let start = std::time::Instant::now();
let result = validate_against_schema_str("a: 1", &nested_schema(depth));
let elapsed = start.elapsed();
assert!(
result.is_err(),
"depth {depth} must be refused, not accepted"
);
assert!(
elapsed < std::time::Duration::from_secs(5),
"depth {depth} took {elapsed:?}; the depth bound is not holding"
);
}
}
#[test]
fn the_depth_bound_reports_itself() {
let err = validate_against_schema_str("a: 1", &nested_schema(2_000))
.expect_err("a 2000-deep schema must be refused");
let msg = err.to_string();
assert!(
msg.contains("recursion depth limit"),
"expected the refusal to name the depth bound, got: {msg}"
);
}
#[test]
fn ordinary_schemas_are_unaffected() {
let schema =
r#"{"type":"object","properties":{"port":{"type":"integer"}},"required":["port"]}"#;
validate_against_schema_str("port: 8080", schema).expect("a normal schema still validates");
let _ = validate_against_schema_str("port: nope", schema)
.expect_err("a violation is still reported");
}
#[test]
fn local_defs_refs_still_work() {
let schema = r##"{
"$defs": {"port": {"type": "integer"}},
"type": "object",
"properties": {"p": {"$ref": "#/$defs/port"}}
}"##;
validate_against_schema_str("p: 8080", schema).expect("local $ref must resolve");
let _ = validate_against_schema_str("p: text", schema)
.expect_err("local $ref must still enforce its type");
}