//! Key release (extensions/key-release.md): the committed corpus `live-conformance/keys/` //! regenerates byte for byte from its fixed seeds, holds no other file, every case in it gets //! the outcome it states, and the extension's §11 lists exactly the reasons the reference //! reports. The generator itself asserts that the corpus gives every one of them. #![cfg(feature = "key_release")] use std::collections::BTreeMap; use std::path::PathBuf; use wai::key_release::{corpus, Decision, DeliveryKind, Reason, Scheme}; fn wai_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..") } fn committed() -> BTreeMap { let dir = wai_dir().join("live-conformance/keys"); std::fs::read_dir(&dir) .expect("the committed corpus") .flatten() .map(|e| (e.file_name().to_string_lossy().into_owned(), e.path())) .filter(|(n, _)| n != "README.md") .map(|(n, p)| (n, std::fs::read_to_string(p).expect("a text file"))) .collect() } #[test] fn the_committed_corpus_regenerates_from_its_seeds() { let files = committed(); let generated: BTreeMap = corpus::generate().into_iter().collect(); let extra: Vec<&String> = files.keys().filter(|k| !generated.contains_key(*k)).collect(); let missing: Vec<&String> = generated.keys().filter(|k| !files.contains_key(*k)).collect(); let differ: Vec<&String> = generated.iter().filter(|(k, v)| files.get(*k).is_some_and(|f| f != *v)).map(|(k, _)| k).collect(); assert!(extra.is_empty() && missing.is_empty() && differ.is_empty(), "extra {extra:?}, missing {missing:?}, differ {differ:?}: run `wai_key_release_vectors gen`"); } #[test] fn every_case_gets_its_stated_outcome() { let fails = corpus::verify(&committed()); assert!(fails.is_empty(), "{}", fails.join("\n")); } /// §11 of the extension lists exactly the codes `Reason::ALL` has, each once, in order. #[test] fn the_extension_lists_the_reasons() { let text = std::fs::read_to_string(wai_dir().join("extensions/key-release.md")).expect("the extension"); let at = text.find("\n## 11. Reasons").expect("a §11"); let end = text[at + 1..].find("\n## ").map_or(text.len(), |e| at + 1 + e); let listed: Vec = text[at..end].lines().filter(|l| l.starts_with("| `")).filter_map(|l| l.split('`').nth(1).map(String::from)).collect(); let want: Vec = Reason::ALL.iter().map(|r| r.code().to_string()).collect(); assert_eq!(listed, want, "§11's table and Reason::ALL differ"); } /// The rows of the table under heading `head`, up to the next heading, split on `|`. fn rows(head: &str) -> Vec> { let text = std::fs::read_to_string(wai_dir().join("extensions/key-release.md")).expect("the extension"); let at = text.find(&format!("\n{head}\n")).unwrap_or_else(|| panic!("a heading {head}")); let body = &text[at + head.len() + 2..]; let end = body.find("\n#").unwrap_or(body.len()); body[..end] .lines() .filter(|l| l.starts_with("| ") && !l.starts_with("| ---") && !l.starts_with("|---")) .skip(1) .map(|l| l.trim_matches('|').split('|').map(|c| c.trim().trim_matches('`').to_string()).collect()) .collect() } /// §3.1, §3.2 and §7.2 give each scheme, delivery and decision the code and label the /// reference writes, and no other. #[test] fn the_extension_tables_are_the_reference_codes() { let schemes: Vec<(String, String)> = rows("### 3.1 Schemes").iter().map(|r| (r[0].clone(), r[1].clone())).collect(); let want: Vec<(String, String)> = Scheme::ALL.iter().map(|s| (s.code().to_string(), s.label().to_string())).collect(); assert_eq!(schemes, want, "§3.1"); let deliveries: Vec<(String, String)> = rows("### 3.2 Key deliveries").iter().map(|r| (r[0].clone(), r[1].clone())).collect(); let want: Vec<(String, String)> = DeliveryKind::ALL.iter().map(|d| (d.code().to_string(), d.label().to_string())).collect(); assert_eq!(deliveries, want, "§3.2"); // §7.2's fourteen, and the four of §14.4 that only an entitlement with rules reaches. let mut decisions: Vec<(String, String)> = rows("### 7.2 The checks, in order").iter().chain(rows("### 14.4 The checks").iter()).map(|r| (r[1].clone(), r[2].clone())).collect(); assert_eq!(rows("### 14.4 The checks").iter().map(|r| r[1].clone()).collect::>(), Decision::ALL.iter().filter(|d| d.of_rules()).map(|d| d.label().to_string()).collect::>(), "§14.4"); decisions.sort(); let mut want: Vec<(String, String)> = Decision::ALL.iter().map(|d| (d.label().to_string(), d.code().to_string())).collect(); want.sort(); assert_eq!(decisions, want, "§7.2 and §14.4"); } /// §15.3 and §15.5 give each decision of a key provider its code and label, and each step of /// an exchange's verification its reasons, as the reference has them. #[test] fn the_key_providers_tables_are_the_reference_codes() { use wai::key_release::provider::{Fault, Provision}; let mut decisions: Vec<(String, String)> = rows("### 15.3 The decision and the answer").iter().map(|r| (r[1].clone(), r[2].clone())).collect(); decisions.sort(); let mut want: Vec<(String, String)> = Provision::ALL.iter().map(|d| (d.label().to_string(), d.code().to_string())).collect(); want.sort(); assert_eq!(decisions, want, "§15.3"); let mut reasons: Vec = Vec::new(); for r in rows("### 15.5 Verification") { for code in r[1].split(',').map(|c| c.trim().trim_matches('`').to_string()) { if !reasons.contains(&code) { reasons.push(code); } } } assert_eq!(reasons, Fault::ALL.iter().map(|f| f.code().to_string()).collect::>(), "§15.5"); }