open-standards/wai/wai-rs/tests/cli_dx.rs
Transaction Science 7ca9e889b9 SPEC §3.1: pin by default; wai verify reports an unpinned envelope, and a decode's record names the set it read
An encoder SHOULD pin the parameter set a payload was coded against wherever a
capability that decodes it is registered as decoding against one, whatever the
intent (SPEC §3.1, Pin by default).
An envelope that pins nothing there still conforms in 1.x; a verifier reports it
(prior_unpinned), informatively: Wai::check_unpinned, WaiMulti::check_unpinned and
codecs::pin_by_default. wai verify now reads the envelopes under each path, refusing
one whose pin breaks SPEC §3.1 and noting prior_unpinned; wai inspect and wai wrap note
it too. pin-conformance/unpinned.json holds the vectors, reached by wai_pin_vectors and
by pin_verify.py, which reads the classes from the published registry export.

A reconstruct-session decoder entry's pin (jwp-receipts §2.4.12) now names the set an
unpinned decode read, by the digest a pin over those bytes names, with the tier none.
The reference sink's base decode gives it (staged::BaseDecode::set, from
IntParams::read_pin), and stage_measure's sessions carry it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-04 12:29:01 -05:00

186 lines
9.3 KiB
Rust

//! The developer's path through the `wai` binary, as a person runs it: `wai hello` writes an
//! object, its receipt index and its group's receipt file, and `wai verify` checks them
//! offline and refuses each when it is altered; `wai create` writes each example of
//! `wai/examples/` as a project, file for file and byte for byte, and never into a directory
//! that holds anything. `wai verify` also reads the envelopes under a path: a planted envelope
//! that pins nothing where its capability decodes against a parameter set is reported
//! `prior_unpinned`, and still verifies; one whose pin breaks SPEC §3.1 does not.
#![cfg(feature = "quickstart")]
use std::path::{Path, PathBuf};
use std::process::{Command, Output};
fn tmp(name: &str) -> PathBuf {
let d = std::env::temp_dir().join(format!("wai-dx-{name}-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&d);
std::fs::create_dir_all(&d).unwrap();
d
}
fn wai(dir: &Path, args: &[&str]) -> Output {
Command::new(env!("CARGO_BIN_EXE_wai")).current_dir(dir).args(args).output().expect("wai runs")
}
fn text(o: &Output) -> String {
format!("{}{}", String::from_utf8_lossy(&o.stdout), String::from_utf8_lossy(&o.stderr))
}
/// The one file under `dir` whose name ends with `suffix`.
fn one(dir: &Path, suffix: &str) -> PathBuf {
let mut v: Vec<PathBuf> = std::fs::read_dir(dir).unwrap().flatten().map(|e| e.path()).filter(|p| p.to_string_lossy().ends_with(suffix)).collect();
assert_eq!(v.len(), 1, "one {suffix} in {}", dir.display());
v.remove(0)
}
#[test]
fn hello_runs_the_four_steps_and_verify_checks_what_it_wrote() {
let d = tmp("hello");
let o = wai(&d, &["hello", "h"]);
assert!(o.status.success(), "{}", text(&o));
let t = text(&o);
for step in ["1 Name", "2 Dispatch", "3 Rebuild", "4 Receipt", "byte for byte", "delivered-verified"] {
assert!(t.contains(step), "hello says {step}: {t}");
}
let h = d.join("h");
let v = wai(&d, &["verify", "h"]);
assert!(v.status.success(), "{}", text(&v));
// The object is an envelope there too, and its pins are read.
assert!(text(&v).contains("delivered-verified") && text(&v).contains("1 receipt index and 1 envelope verified"), "{}", text(&v));
// A byte of the object changed: its content hash is not the receipt's.
let obj = one(&h, ".wai");
let good = std::fs::read(&obj).unwrap();
let mut bad = good.clone();
let n = bad.len();
bad[n - 1] ^= 1;
std::fs::write(&obj, &bad).unwrap();
let v = wai(&d, &["verify", "h"]);
assert_eq!(v.status.code(), Some(1), "{}", text(&v));
assert!(text(&v).contains("FAIL"), "{}", text(&v));
std::fs::write(&obj, &good).unwrap();
// A byte of the group's receipt file changed: its name is no longer its link hash, or its
// signature no longer verifies.
let rcpt = one(&h.join("rcpt"), ".json");
let g = std::fs::read_to_string(&rcpt).unwrap();
let i = g.find("\"sig\":\"").expect("a signature") + 7;
let mut b = g.into_bytes();
b[i] = if b[i] == b'0' { b'1' } else { b'0' };
std::fs::write(&rcpt, &b).unwrap();
let v = wai(&d, &["verify", "h"]);
assert_eq!(v.status.code(), Some(1), "{}", text(&v));
// Nothing to verify is an error, not a pass.
std::fs::create_dir_all(d.join("empty")).unwrap();
let v = wai(&d, &["verify", "empty"]);
assert_eq!(v.status.code(), Some(2), "{}", text(&v));
// And hello never writes into a directory of someone else's files.
std::fs::write(d.join("empty/mine.txt"), "mine").unwrap();
assert_eq!(wai(&d, &["hello", "empty"]).status.code(), Some(2));
let _ = std::fs::remove_dir_all(&d);
}
/// SPEC §3.1, *Pin by default*: `wai verify` reports `prior_unpinned` on a planted envelope
/// that pins nothing over a capability that decodes against a parameter set, and the envelope
/// still verifies; it reports nothing on one over a standard codec or on a pinned one; and it
/// refuses one whose pin a verifier can tell is wrong without holding a set.
#[test]
fn verify_reports_an_unpinned_envelope() {
use wai::container::{Conditioning, Manifest, ModelRequirement, Prior, Wai};
let d = tmp("unpinned");
let p = d.join("planted");
std::fs::create_dir_all(&p).unwrap();
std::fs::write(d.join("payload.bin"), b"opaque test bytes").unwrap();
let wrap = |cap: &str, out: &str| {
let o = wai(&d, &["wrap", cap, "payload.bin", "-o", out, "--intent", "improve"]);
assert!(o.status.success(), "{}", text(&o));
o
};
let w = wrap("wai.neural.bmshj2018", "planted/unpinned.wai");
assert!(text(&w).contains("note prior_unpinned: wai.neural.bmshj2018"), "wrap says it pinned nothing: {}", text(&w));
wrap("wai.image.png", "planted/png.wai");
let pin = Prior::new("int_mlicpp/test", "7ef44f3b1fc4a2df6c3e3b68460f595cae3d31cb7f9018699e29e738340272a4", "int-rans/p16+int-mlicpp/i64");
let pinned = Wai::new(Manifest::new("image", "improve", ModelRequirement::new("wai.neural.int_mlicpp"),
Conditioning::new("int_mlicpp").with_prior(pin.clone())), b"M".to_vec());
std::fs::write(p.join("pinned.wai"), pinned.to_bytes().unwrap()).unwrap();
let v = wai(&d, &["verify", "planted"]);
let t = text(&v);
assert!(v.status.success(), "a report is not a refusal: {t}");
assert!(t.contains("note prior_unpinned: wai.neural.bmshj2018 decodes against a parameter set"), "{t}");
assert_eq!(t.matches("prior_unpinned").count(), 1, "the PNG and the pinned envelope are not reported: {t}");
assert!(t.contains("3 envelopes verified"), "{t}");
let v = wai(&d, &["verify", "planted/unpinned.wai", "--json"]);
assert!(v.status.success() && text(&v).contains("prior_unpinned"), "{}", text(&v));
// A pin over a capability that reads no set: the envelope does not conform.
let bad = Wai::new(Manifest::new("image", "replicate", ModelRequirement::new("wai.image.png"),
Conditioning::new("png").with_prior(pin)), b"PNG".to_vec());
std::fs::write(p.join("bad.wai"), bad.to_bytes().unwrap()).unwrap();
let v = wai(&d, &["verify", "planted"]);
assert_eq!(v.status.code(), Some(1), "{}", text(&v));
assert!(text(&v).contains("FAIL WAI1 wai.image.png: prior_not_applicable"), "{}", text(&v));
let _ = std::fs::remove_dir_all(&d);
}
/// Every file of an example but its `test/` directory, relative to it, sorted.
fn example_files(dir: &Path) -> Vec<String> {
fn walk(root: &Path, p: &Path, out: &mut Vec<String>) {
for e in std::fs::read_dir(p).unwrap().flatten() {
let q = e.path();
let rel = q.strip_prefix(root).unwrap().to_string_lossy().replace('\\', "/");
if rel == "test" || rel.starts_with("test/") || rel.ends_with(".DS_Store") || rel == "target" || rel.starts_with("target/") {
continue;
}
if q.is_dir() {
walk(root, &q, out);
} else {
out.push(rel);
}
}
}
let mut v = Vec::new();
walk(dir, dir, &mut v);
v.sort();
v
}
#[test]
fn create_writes_every_example_as_it_is_in_the_repository() {
let d = tmp("create");
let examples = Path::new(env!("CARGO_MANIFEST_DIR")).join("../examples");
let o = wai(&d, &["create", "--list", "--json"]);
assert!(o.status.success(), "{}", text(&o));
// The JSON envelope (envelope/1): the list is its data.
let env: serde_json::Value = serde_json::from_slice(&o.stdout).unwrap();
assert_eq!((env["ok"].as_bool(), env["op"].as_str()), (Some(true), Some("project.create")), "{env}");
let list = env["data"].as_array().unwrap();
let names: Vec<&str> = list.iter().map(|t| t["template"].as_str().unwrap()).collect();
// Every example directory is a template, and every template an example.
let mut dirs: Vec<String> = std::fs::read_dir(&examples).unwrap().flatten().filter(|e| e.path().is_dir()).map(|e| e.file_name().to_string_lossy().into_owned()).collect();
dirs.sort();
let mut sorted = names.clone();
sorted.sort();
assert_eq!(sorted, dirs, "the templates are the examples");
for t in list {
let name = t["template"].as_str().unwrap();
let generated: Vec<&str> = t["files"].as_array().unwrap().iter().map(|f| f.as_str().unwrap()).filter(|f| !examples.join(name).join(f).exists()).collect();
let o = wai(&d, &["create", name, "--template", name]);
assert!(o.status.success(), "{name}: {}", text(&o));
let made = example_files(&d.join(name));
let mut want = example_files(&examples.join(name));
want.extend(generated.iter().map(|g| g.to_string()));
want.sort();
assert_eq!(made, want, "{name}: the project holds the example's files, and those made for it");
for f in example_files(&examples.join(name)) {
assert_eq!(std::fs::read(d.join(name).join(&f)).unwrap(), std::fs::read(examples.join(name).join(&f)).unwrap(), "{name}/{f}: byte for byte");
}
}
// Never into a directory that holds anything.
let o = wai(&d, &["create", "video-room", "--template", "video-room"]);
assert_eq!(o.status.code(), Some(2), "{}", text(&o));
assert!(text(&o).contains("not empty"));
let o = wai(&d, &["create", "x", "--template", "no-such"]);
assert_eq!(o.status.code(), Some(2));
let _ = std::fs::remove_dir_all(&d);
}