open-standards/wai/wai-rs/tests/influence.rs
Transaction Science 58fcff3a04 wai: influence attribution — conditioning, shares, attribution lines and reports (spec text, reference module and corpus, in progress)
The extension text (extensions/influence.md), the influence module of wai-rs behind the
feature influence, its corpus generator and committed corpus; settlement gains attribution
lines (kind 2) recomputed from a basis, and contribution notes the sibling record.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-21 19:52:19 -04:00

88 lines
4.5 KiB
Rust

//! `wai.media.influence` (extensions/influence.md): the committed corpus
//! `live-conformance/influence/` regenerates byte for byte from its fixed seeds, holds no other
//! file, every case, statement and report in it gets the outcome it states, every reason of §9
//! and every code it reuses refuses a vector, and §9 lists exactly the reasons the reference
//! adds.
#![cfg(feature = "influence")]
use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf;
use wai::influence::{corpus, Reason};
fn wai_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..")
}
fn committed() -> BTreeMap<String, String> {
let dir = wai_dir().join("live-conformance/influence");
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<String, String> = 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_influence_vectors gen`");
}
#[test]
fn every_vector_gets_its_stated_outcome() {
let fails = corpus::verify(&committed());
assert!(fails.is_empty(), "{}", fails.join("\n"));
}
/// Every reason §9 adds and every code it reuses refuses a vector; the accepted vectors are
/// §10's, with the figures each was written for.
#[test]
fn the_corpus_covers_every_reason_and_case() {
let doc: serde_json::Value = serde_json::from_str(&committed()["vectors.json"]).unwrap();
let mut seen: BTreeSet<String> = BTreeSet::new();
for v in doc["cases"].as_array().unwrap() {
if let Some(r) = v["reason"].as_str() {
seen.insert(r.into());
}
}
for section in ["statements", "reports"] {
for v in doc[section].as_array().unwrap() {
if let Some(r) = v["outcome"]["reason"].as_str() {
seen.insert(r.into());
}
}
}
for r in Reason::ALL.iter().chain(Reason::REUSED.iter()) {
assert!(seen.contains(r.code()), "nothing in the corpus is refused {r}");
}
let case = |n: &str| doc["cases"].as_array().unwrap().iter().find(|v| v["name"] == n).unwrap_or_else(|| panic!("no case {n}")).clone();
let style = case("style");
assert_eq!(style["statements"]["tier"], "recomputed", "the conditioned generation regenerates");
assert_eq!(style["statements"]["conditioning"][0]["status"], "recomputed");
assert_eq!(style["statements"]["shares"]["compared"], true);
let whole: u64 = style["statements"]["shares"]["computed"].as_array().unwrap().iter().map(|b| b["units"].as_u64().unwrap()).sum();
assert_eq!(whole, 48 * 1_000_000, "the shares are the whole");
assert_eq!(case("attested_equal")["statements"]["conditioning"][0]["weights"], "equal");
assert_eq!(case("attested_partial")["statements"]["shares"]["compared"], false);
assert!(case("scaling_unknown")["statements"]["shares"]["computed"].as_array().unwrap().iter().any(|b| b["bucket"] == "unknown"), "unknown is never zero");
let st = doc["statements"].as_array().unwrap().iter().find(|v| v["name"] == "publisher_pays_owners").unwrap()["outcome"].clone();
assert_eq!(st["outcome"], "accept");
assert!(st["attribution"].as_array().unwrap().len() >= 3, "an attribution line for each owner of the two outputs");
}
/// §9 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/influence.md")).expect("the extension");
let at = text.find("\n## 9. Reasons").expect("a §9");
let end = text[at + 1..].find("\n## ").map_or(text.len(), |e| at + 1 + e);
let listed: Vec<String> = text[at..end].lines().filter(|l| l.starts_with("| `")).filter_map(|l| l.split('`').nth(1).map(String::from)).collect();
let want: Vec<String> = Reason::ALL.iter().map(|r| r.code().to_string()).collect();
assert_eq!(listed, want, "§9's table and Reason::ALL differ");
}