use serde_json::Value;
use std::path::PathBuf;
const DEFAULT_DIR: &str = "/Users/fabianodicheti/Documents/maned/capi-freeze/conformance";
fn corpus_dir() -> PathBuf {
match std::env::var_os("MANED_CONFORMANCE_DIR") {
Some(d) if !d.is_empty() => PathBuf::from(d),
_ => PathBuf::from(DEFAULT_DIR),
}
}
struct Case {
name: String,
script: PathBuf,
request: Value,
expect: Value,
}
fn cases() -> Vec<Case> {
let dir = corpus_dir();
let entries = std::fs::read_dir(&dir).unwrap_or_else(|e| {
panic!(
"cannot read the conformance corpus at {}: {e}\n\
Set MANED_CONFORMANCE_DIR to the `conformance/` directory of an \
unpacked maned-capi tarball.",
dir.display()
)
});
let mut names: Vec<String> = entries
.filter_map(|e| e.ok())
.map(|e| e.path())
.filter(|p| p.extension().is_some_and(|x| x == "mnd"))
.filter_map(|p| p.file_stem().map(|s| s.to_string_lossy().into_owned()))
.collect();
names.sort();
names
.into_iter()
.map(|name| {
let read = |suffix: &str| -> Value {
let p = dir.join(format!("{name}{suffix}"));
let text =
std::fs::read_to_string(&p).unwrap_or_else(|e| panic!("{}: {e}", p.display()));
serde_json::from_str(&text)
.unwrap_or_else(|e| panic!("{}: not JSON: {e}", p.display()))
};
Case {
script: dir.join(format!("{name}.mnd")),
request: read(".request.json"),
expect: read(".expect.json"),
name,
}
})
.collect()
}
#[test]
fn every_fixture_matches_byte_for_byte() {
let cases = cases();
assert!(
cases.len() >= 9,
"expected the whole corpus, found {} case(s) in {}",
cases.len(),
corpus_dir().display()
);
for case in &cases {
let mut builder = maned::builder(&case.script);
if let Some(flags) = case.request.get("flags").and_then(Value::as_array) {
for flag in flags {
match flag.as_str() {
Some("synthesize_outputs") => {
builder = builder.synthesize_outputs(true);
}
Some("no_vm_fallback") => {
builder = builder.no_vm_fallback(true);
}
other => panic!("{}: unknown flag {other:?}", case.name),
}
}
}
let mut script = builder
.open()
.unwrap_or_else(|e| panic!("{}: {e}", case.name));
let method = case.request["method"].as_str().expect("method");
let target = case.request["target"].as_str().expect("target");
let body = case.request["body"].as_str().expect("body");
let content_type = case.request.get("content_type").and_then(Value::as_str);
let response = script
.request(method, target, content_type, body.as_bytes())
.unwrap_or_else(|e| panic!("{}: {e}", case.name));
let want_status = case.expect["status"].as_u64().expect("status") as u16;
let want_type = case.expect["content_type"].as_str().expect("content_type");
let want_body = case.expect["body"].as_str().expect("body");
assert_eq!(response.status(), want_status, "{}: status", case.name);
assert_eq!(
response.content_type(),
want_type,
"{}: content_type",
case.name
);
assert_eq!(
response.body(),
want_body.as_bytes(),
"{}: body\n want: {want_body}\n got: {}",
case.name,
String::from_utf8_lossy(response.body())
);
}
}
#[test]
fn error_fixtures_map_to_the_right_error_variants() {
use maned::Error;
for case in cases() {
let status = case.expect["status"].as_u64().unwrap() as u16;
if (200..300).contains(&status) {
continue;
}
let mut script = maned::builder(&case.script).open().unwrap();
let body = case.request["body"].as_str().unwrap();
let payload: Value = serde_json::from_str(body).unwrap_or(Value::Null);
if case.request["target"] != "/run.json" {
continue;
}
let err = script.run(&payload).expect_err(&case.name);
assert_eq!(err.status(), Some(status), "{}: {err}", case.name);
match (status, &err) {
(400, Error::Input { .. }) => {}
(404 | 405, Error::Route { .. }) => {}
(500, Error::Runtime { .. }) => {}
(501, Error::Unsupported { .. }) => {}
_ => panic!("{}: status {status} mapped to {err:?}", case.name),
}
let want_body = case.expect["body"].as_str().unwrap();
assert!(want_body.ends_with('\n'), "{}: fixture", case.name);
assert!(
err.to_string().ends_with(want_body.trim_end()),
"{}: {err}",
case.name
);
}
}
#[test]
fn health_answers_structurally() {
let case = cases().into_iter().next().expect("at least one case");
let mut script = maned::builder(&case.script).open().unwrap();
let r = script.request("GET", "/health", None, b"").unwrap();
assert_eq!(r.status(), 200);
let v: Value = r.json().unwrap();
assert!(v.get("script").is_some(), "{v}");
}