#![cfg(feature = "validate-schema")]
#![allow(missing_docs)]
use noyalib::{Value, from_str, schema_for, validate_against_schema, validate_against_schema_str};
fn parse(s: &str) -> Value {
from_str(s).unwrap()
}
#[test]
fn end_to_end_required_and_types() {
let schema = parse(
"type: object
required: [port, host]
properties:
port:
type: integer
host:
type: string
",
);
assert!(validate_against_schema(&parse("port: 8080\nhost: localhost\n"), &schema).is_ok());
assert!(validate_against_schema(&parse("port: 8080\n"), &schema).is_err());
assert!(validate_against_schema(&parse("port: x\nhost: y\n"), &schema).is_err());
}
#[test]
fn enum_constraint_with_string_values() {
let schema = parse(
"type: object
properties:
level:
enum: [trace, debug, info, warn, error]
",
);
assert!(validate_against_schema(&parse("level: warn\n"), &schema).is_ok());
assert!(validate_against_schema(&parse("level: ULTRA\n"), &schema).is_err());
}
#[test]
fn integer_bounds_lower_and_upper() {
let schema = parse(
"type: object
properties:
port:
type: integer
minimum: 0
maximum: 65535
",
);
assert!(validate_against_schema(&parse("port: 8080\n"), &schema).is_ok());
assert!(validate_against_schema(&parse("port: 0\n"), &schema).is_ok());
assert!(validate_against_schema(&parse("port: 65535\n"), &schema).is_ok());
assert!(validate_against_schema(&parse("port: -1\n"), &schema).is_err());
assert!(validate_against_schema(&parse("port: 70000\n"), &schema).is_err());
}
#[test]
fn nested_objects_validated_recursively() {
let schema = parse(
"type: object
properties:
db:
type: object
required: [host, port]
properties:
host: { type: string }
port: { type: integer }
",
);
let good = parse("db:\n host: localhost\n port: 5432\n");
let bad = parse("db:\n host: localhost\n");
assert!(validate_against_schema(&good, &schema).is_ok());
assert!(validate_against_schema(&bad, &schema).is_err());
}
#[test]
fn array_min_items_constraint() {
let schema = parse(
"type: object
properties:
tags:
type: array
minItems: 1
items: { type: string }
",
);
assert!(validate_against_schema(&parse("tags: [yaml, serde]\n"), &schema).is_ok());
assert!(validate_against_schema(&parse("tags: []\n"), &schema).is_err());
}
#[test]
fn validate_against_schema_str_handles_yaml_inputs() {
let schema = "\
type: object
required: [port]
properties:
port:
type: integer
";
assert!(validate_against_schema_str("port: 8080\n", schema).is_ok());
assert!(validate_against_schema_str("port: hello\n", schema).is_err());
}
#[test]
fn aggregated_violations_list_all_paths() {
let schema = parse(
"type: object
required: [port, host, role]
properties:
port: { type: integer }
host: { type: string }
role: { type: string }
",
);
let value = parse("port: not-int\n");
let err = validate_against_schema(&value, &schema).unwrap_err();
let msg = err.to_string();
assert!(msg.contains("schema violations"), "got: {msg}");
assert!(msg.contains("port"));
assert!(msg.contains("host"));
assert!(msg.contains("role"));
}
#[test]
fn malformed_schema_distinguished_from_data_error() {
let schema = parse("type: 99\n");
let value = parse("any: 1\n");
let err = validate_against_schema(&value, &schema).unwrap_err();
assert!(
err.to_string().contains("not a valid JSON Schema"),
"got: {err}"
);
}
#[test]
fn validate_data_against_schemars_emitted_schema() {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, noyalib::JsonSchema)]
#[allow(dead_code)]
struct ServerConfig {
port: u16,
host: String,
#[serde(default)]
tls: bool,
}
let schema = schema_for::<ServerConfig>().unwrap();
let good = parse("port: 8080\nhost: localhost\n");
assert!(validate_against_schema(&good, &schema).is_ok());
let good_tls = parse("port: 8443\nhost: api.example.com\ntls: true\n");
assert!(validate_against_schema(&good_tls, &schema).is_ok());
let bad_missing = parse("host: localhost\n");
let err = validate_against_schema(&bad_missing, &schema).unwrap_err();
assert!(err.to_string().contains("port"));
let bad_range = parse("port: 70000\nhost: localhost\n");
let err = validate_against_schema(&bad_range, &schema).unwrap_err();
assert!(err.to_string().contains("port"));
}