use std::path::Path;
use apimock_routing::{Respond, RuleSet};
use crate::workspace::validate::respond_node_validation;
fn rule_set_in(dir: &Path) -> RuleSet {
let rs_path = dir.join("apimock-rule-set.toml");
std::fs::write(
&rs_path,
"[[rules]]\nwhen.request.url_path = \"/probe\"\nrespond.text = \"ok\"\n",
)
.expect("write rule-set file");
RuleSet::new(rs_path.to_str().unwrap(), dir.to_str().unwrap(), 0).expect("RuleSet::new")
}
struct Case {
name: &'static str,
respond: Respond,
file: Option<(&'static str, &'static str)>,
expect_ok: bool,
}
fn respond_with(f: impl FnOnce(&mut Respond)) -> Respond {
let mut r = Respond::default();
f(&mut r);
r
}
fn corpus() -> Vec<Case> {
vec![
Case {
name: "empty respond (no field set) is rejected",
respond: Respond::default(),
file: None,
expect_ok: false,
},
Case {
name: "status alone is accepted",
respond: respond_with(|r| r.status = Some(204)),
file: None,
expect_ok: true,
},
Case {
name: "text alone is accepted",
respond: respond_with(|r| r.text = Some("hi".to_owned())),
file: None,
expect_ok: true,
},
Case {
name: "json alone is accepted",
respond: respond_with(|r| r.json = Some(r#"{"a":1}"#.to_owned())),
file: None,
expect_ok: true,
},
Case {
name: "file_path alone (existing file) is accepted",
respond: respond_with(|r| r.file_path = Some("data.txt".to_owned())),
file: Some(("data.txt", "hello")),
expect_ok: true,
},
Case {
name: "json + text together are rejected",
respond: respond_with(|r| {
r.json = Some(r#"{"a":1}"#.to_owned());
r.text = Some("hi".to_owned());
}),
file: None,
expect_ok: false,
},
Case {
name: "json + file_path together are rejected",
respond: respond_with(|r| {
r.json = Some(r#"{"a":1}"#.to_owned());
r.file_path = Some("data.json".to_owned());
}),
file: Some(("data.json", r#"{"a":1}"#)),
expect_ok: false,
},
Case {
name: "file_path + text together are rejected",
respond: respond_with(|r| {
r.file_path = Some("data.txt".to_owned());
r.text = Some("hi".to_owned());
}),
file: Some(("data.txt", "hello")),
expect_ok: false,
},
Case {
name: "file_path + status together are rejected",
respond: respond_with(|r| {
r.file_path = Some("data.txt".to_owned());
r.status = Some(200);
}),
file: Some(("data.txt", "hello")),
expect_ok: false,
},
Case {
name: "json + status together are accepted",
respond: respond_with(|r| {
r.json = Some(r#"{"error":"nope"}"#.to_owned());
r.status = Some(404);
}),
file: None,
expect_ok: true,
},
Case {
name: "malformed inline json is rejected",
respond: respond_with(|r| r.json = Some("{not json".to_owned())),
file: None,
expect_ok: false,
},
Case {
name: "referenced valid .json file is accepted",
respond: respond_with(|r| r.file_path = Some("good.json".to_owned())),
file: Some(("good.json", r#"{"a":1}"#)),
expect_ok: true,
},
Case {
name: "referenced malformed .json file is rejected",
respond: respond_with(|r| r.file_path = Some("bad.json".to_owned())),
file: Some(("bad.json", "{not json")),
expect_ok: false,
},
Case {
name: "missing file_path is rejected",
respond: respond_with(|r| r.file_path = Some("does-not-exist.json".to_owned())),
file: None,
expect_ok: false,
},
]
}
#[test]
fn both_validators_agree_on_every_case_in_the_shared_corpus() {
for case in corpus() {
let dir = tempfile::tempdir().expect("tempdir");
if let Some((name, content)) = case.file {
std::fs::write(dir.path().join(name), content).expect("write case file");
}
let rule_set = rule_set_in(dir.path());
let routing_ok = case
.respond
.validate(dir.path().to_str().unwrap(), 0, 0)
.is_ok();
let config_ok = respond_node_validation(&case.respond, &rule_set, 0, 0).ok;
assert_eq!(
routing_ok, config_ok,
"{}: Respond::validate says ok={routing_ok}, respond_node_validation says ok={config_ok} — the two validators disagree",
case.name
);
assert_eq!(
routing_ok, case.expect_ok,
"{}: both validators agree with each other (ok={routing_ok}) but not with the expected verdict (ok={})",
case.name, case.expect_ok
);
}
}