use super::*;
fn compiled(source: &str) -> OutputValidator {
compile("test.rhai", source).expect("fixture compiles")
}
#[test]
fn a_validator_that_returns_unit_accepts() {
let v = compiled("fn validate(content) { () }");
assert_eq!(validate(&v, "anything"), Verdict::Valid);
}
#[test]
fn an_empty_reason_accepts() {
let v = compiled(r#"fn validate(content) { "" }"#);
assert_eq!(validate(&v, "anything"), Verdict::Valid);
let blank = compiled(r#"fn validate(content) { " " }"#);
assert_eq!(validate(&blank, "anything"), Verdict::Valid);
}
#[test]
fn a_returned_string_is_the_reason_the_agent_sees() {
let v = compiled(r#"fn validate(content) { "the document has no root node" }"#);
assert_eq!(
validate(&v, "{}"),
Verdict::Invalid("the document has no root node".to_string())
);
}
#[test]
fn a_validator_can_inspect_the_content_it_is_given() {
let v = compiled(
r#"
fn validate(content) {
let doc = parse_json(content);
if doc.root == () { return "missing `root`"; }
()
}
"#,
);
assert_eq!(validate(&v, r#"{"root":{"a":1}}"#), Verdict::Valid);
assert_eq!(
validate(&v, r#"{"other":1}"#),
Verdict::Invalid("missing `root`".to_string())
);
}
#[test]
fn a_validator_that_throws_is_unusable_rather_than_a_rejection() {
let v = compiled(r#"fn validate(content) { throw "boom" }"#);
match validate(&v, "anything") {
Verdict::Unusable(reason) => assert!(reason.contains("boom"), "{reason}"),
other => panic!("expected Unusable, got {other:?}"),
}
}
#[test]
fn a_validator_returning_the_wrong_type_is_unusable() {
let v = compiled("fn validate(content) { 42 }");
match validate(&v, "anything") {
Verdict::Unusable(reason) => {
assert!(reason.contains("() or a string"), "{reason}");
}
other => panic!("expected Unusable, got {other:?}"),
}
}
#[test]
fn a_runaway_validator_is_stopped() {
let v = compiled("fn validate(content) { let i = 0; loop { i += 1; } }");
assert!(matches!(validate(&v, "x"), Verdict::Unusable(_)));
}
#[test]
fn a_script_without_validate_is_refused() {
let err = compile("v.rhai", "fn other(x) { () }").expect_err("no validate fn");
assert!(format!("{err}").contains("must define fn validate"));
}
#[test]
fn a_validate_with_the_wrong_arity_is_refused() {
let err = compile("v.rhai", "fn validate(a, b) { () }").expect_err("wrong arity");
assert!(format!("{err}").contains("exactly one parameter"));
}
#[test]
fn a_script_that_does_not_compile_is_refused() {
let err = compile("v.rhai", "fn validate(content) { this is not rhai").expect_err("bad syntax");
assert!(format!("{err}").contains("v.rhai"));
}
#[test]
fn a_validator_cannot_reach_the_filesystem() {
let outcome = compile(
"v.rhai",
r#"fn validate(content) { open_file("/etc/passwd") }"#,
)
.map(|v| validate(&v, "x"));
match outcome {
Err(_) => {}
Ok(Verdict::Unusable(_)) => {}
other => panic!("a validator must not reach the filesystem, got {other:?}"),
}
}