//! Conformance results (extensions/conformance-results.md): the committed corpus //! (`conform-result-conformance/vectors.json`) regenerates byte for byte, wai-rs gives every //! vector its stated outcome, every reason of §5 refuses a vector, §5 lists exactly the //! reference's codes, and a record survives no change after it is signed. #![cfg(feature = "conform_result")] use serde_json::{Value, json}; use wai::conform_result::{Reason, check, corpus_document, sign, verdict}; fn wai_dir() -> std::path::PathBuf { std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().to_path_buf() } #[test] fn the_committed_corpus_regenerates_byte_for_byte() { let committed = std::fs::read_to_string(wai_dir().join("conform-result-conformance/vectors.json")).expect("the corpus is committed"); assert!(committed == corpus_document(), "conform-result-conformance/vectors.json differs from what wai_conform_result_vectors gen writes"); } #[test] fn every_vector_reaches_its_stated_outcome_and_every_reason_is_raised() { let doc: Value = serde_json::from_str(&corpus_document()).unwrap(); let vs = doc["vectors"].as_array().unwrap(); let mut seen = std::collections::BTreeSet::new(); for v in vs { let got = verdict(v["record"].as_str().unwrap().as_bytes()); assert_eq!(got, v["expected"].as_str().unwrap(), "{}", v["name"]); if let Some(r) = got.split("reason=").nth(1) { seen.insert(r.to_string()); } } for r in Reason::ALL { assert!(seen.contains(r.code()), "no vector is refused with {}", r.code()); } assert!(vs.iter().any(|v| v["expected"] == "accept conformant=true") && vs.iter().any(|v| v["expected"] == "accept conformant=false")); } #[test] fn section_5_lists_exactly_the_reference_codes() { let text = std::fs::read_to_string(wai_dir().join("extensions/conformance-results.md")).unwrap(); let section = text.split("\n## 5. Reasons").nth(1).expect("§5").split("\n## ").next().unwrap(); let listed: std::collections::BTreeSet = section.lines().filter_map(|l| l.strip_prefix("| `")).filter_map(|l| l.split('`').next()).map(String::from).collect(); let codes: std::collections::BTreeSet = Reason::ALL.iter().map(|r| r.code().to_string()).collect(); assert_eq!(listed, codes); } /// A record signed, then each leaf of it changed in turn: every change is refused, at the /// signature or before it. #[test] fn no_change_after_signing_survives() { let key = ed25519_dalek::SigningKey::from_bytes(&[7u8; 32]); let doc: Value = serde_json::from_str(&corpus_document()).unwrap(); let valid: Value = serde_json::from_str(doc["vectors"][0]["record"].as_str().unwrap()).unwrap(); let mut r = valid.as_object().unwrap().clone(); r.remove("sig"); let r = sign(r, &key).unwrap(); let text = serde_json::to_string(&Value::Object(r.clone())).unwrap(); assert!(check(text.as_bytes()).is_ok()); fn leaves(v: &Value, path: Vec, out: &mut Vec>) { match v { Value::Object(m) => m.iter().for_each(|(k, x)| leaves(x, [path.clone(), vec![k.clone()]].concat(), out)), Value::Array(a) => a.iter().enumerate().for_each(|(i, x)| leaves(x, [path.clone(), vec![i.to_string()]].concat(), out)), _ => out.push(path), } } let mut paths = Vec::new(); leaves(&Value::Object(r.clone()), vec![], &mut paths); for p in paths.iter().filter(|p| p[0] != "sig") { let mut t = Value::Object(r.clone()); let mut at = &mut t; for k in p { at = if let Ok(i) = k.parse::() { &mut at[i] } else { &mut at[k.as_str()] }; } *at = match at.clone() { Value::String(s) => json!(format!("{s}0")), Value::Bool(b) => json!(!b), Value::Number(n) => json!(n.as_u64().unwrap() + 1), Value::Null => json!("x"), other => other, }; let got = check(serde_json::to_string(&t).unwrap().as_bytes()); assert!(got.as_ref().is_err_and(|e| e.step <= 4), "{p:?} changed: {got:?}"); } }