//! `wai.live.metering` (extensions/live-metering.md): the committed corpus //! `live-conformance/metering/vectors.json` regenerates byte for byte from its fixed seeds and //! the reference relay's recorded record, every vector in it gets the outcome it states, every //! reason the extension adds refuses a vector, the large audience's relay record is the size of //! the lesson's, a relay keeping a DVR window signs a version 2 record and every other record //! is version 1, and the extension's §9 lists exactly the reasons the reference reports. #![cfg(feature = "live_metering")] use std::collections::BTreeSet; use std::path::PathBuf; use wai::live_metering::{corpus, Reason}; fn wai_dir() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..") } fn read(p: &str) -> String { std::fs::read_to_string(wai_dir().join(p)).unwrap_or_else(|e| panic!("{p}: {e}")) } fn committed() -> String { read("live-conformance/metering/vectors.json") } #[test] fn the_committed_corpus_regenerates_from_its_seeds() { assert!(committed() == corpus::generate(), "live-conformance/metering/vectors.json differs from `wai_live_metering_vectors gen`"); } #[test] fn every_vector_gets_its_stated_outcome() { let fails = corpus::verify(&committed()); assert!(fails.is_empty(), "{}", fails.join("\n")); } /// Every reason this extension adds refuses a vector; the accepted vectors are the ones §10 /// names; the large audience's relay record is the lesson's size, within the same bound; the /// mixed vector states one total per class; the ceilings vector names two grants past; the /// DVR relay's record is version 2, 48 bytes longer than the lesson relay's under a bound 48 /// bytes higher, with every DVR figure above 0, and every other accepted record is version 1. #[test] fn the_corpus_covers_every_reason_and_case() { let doc: serde_json::Value = serde_json::from_str(&committed()).unwrap(); let vectors = doc["vectors"].as_array().unwrap(); let seen: BTreeSet<&str> = vectors.iter().filter_map(|v| v["reason"].as_str()).collect(); for r in Reason::ALL { assert!(seen.contains(r.code()), "nothing in the corpus is refused {r}"); } let get = |n: &str| vectors.iter().find(|v| v["name"] == n).unwrap_or_else(|| panic!("no vector {n}")); for n in ["lesson", "signed_by_authority", "mixed_classes", "ceilings_past", "large_audience", "relay_recorded", "dvr_relay", "egress"] { assert_eq!(get(n)["outcome"], "accept", "{n}"); } let relay_record = |n: &str| { let v = get(n); let relay = &doc["principals"]["relay"]; let i = v["hops"].as_array().unwrap().iter().position(|h| h["record"].as_str().unwrap()[74..138] == relay.as_str().unwrap()[..]).unwrap(); let rec = v["hops"][i]["record"].as_str().unwrap().len() / 2; v["records"].as_array().unwrap().iter().find(|r| r["size"] == rec).unwrap().clone() }; let (lesson, large) = (relay_record("lesson"), relay_record("large_audience")); assert_eq!(lesson["size"], large["size"], "the record does not grow with the audience"); assert_eq!(lesson["bound"], large["bound"]); assert!(large["size"].as_u64() <= large["bound"].as_u64()); assert!(get("large_audience")["units"]["sessions"].as_u64().unwrap() > 10_000); assert_eq!(get("large_audience")["qoe_cell"]["suppressed"], false); assert_eq!(get("lesson")["qoe_cell"]["suppressed"], true); assert_eq!(get("lesson")["coverage"], "whole"); assert_eq!(get("mixed_classes")["totals"].as_array().unwrap().len(), 3); assert_eq!(get("relay_recorded")["coverage"], "partial"); let past = get("ceilings_past")["past"].as_array().unwrap(); assert_eq!(past.iter().map(|p| p["flags"].as_u64().unwrap()).collect::>(), BTreeSet::from([1, 2])); let dvr = relay_record("dvr_relay"); assert_eq!(dvr["version"], 2); assert_eq!(dvr["size"].as_u64().unwrap(), lesson["size"].as_u64().unwrap() + 48); assert_eq!(dvr["bound"].as_u64().unwrap(), lesson["bound"].as_u64().unwrap() + 48); for k in ["dvr_objects_out", "dvr_bytes_out", "dvr_objects_stored", "dvr_bytes_stored", "dvr_byte_s", "dvr_dropped"] { assert!(dvr[k].as_u64().unwrap() > 0, "{k}"); } for v in vectors.iter().filter(|v| v["outcome"] == "accept" && v["name"] != "dvr_relay") { for r in v["records"].as_array().unwrap() { assert_eq!(r["version"], 1, "{}", v["name"]); assert!(r.get("dvr_byte_s").is_none()); } } for (n, why) in [("dvr_out_exceeds", "record_invalid"), ("dvr_stored_exceeds", "record_invalid"), ("record_unknown_version", "unknown_version"), ("dvr_record_too_large", "record_too_large")] { assert_eq!(get(n)["reason"], why, "{n}"); } } /// A receipt of version 2 (§4.1, §4.4, §4.6): its bytes out are its hops' ledgers' bytes out, /// the sum of its grants'; a grant's funds count its bytes at their price; every receipt of /// version 1 states no bytes; and the controls of version 2 are refused at their places. #[test] fn a_receipt_of_version_2_states_and_prices_the_bytes_out() { let doc: serde_json::Value = serde_json::from_str(&committed()).unwrap(); let vectors = doc["vectors"].as_array().unwrap(); let get = |n: &str| vectors.iter().find(|v| v["name"] == n).unwrap_or_else(|| panic!("no vector {n}")); let v2 = get("bytes_out_priced"); assert_eq!(v2["outcome"], "accept"); assert_eq!(&v2["receipt"].as_str().unwrap()[8..10], "02", "a receipt of version 2"); let bytes = v2["units"]["bytes_out"].as_u64().unwrap(); assert!(bytes > 0); let by_grant: u64 = v2["consumption"].as_array().unwrap().iter().map(|c| c["bytes_out"].as_u64().unwrap()).sum(); assert_eq!(by_grant, bytes, "the grants' bytes out sum to the receipt's"); let lesson = get("lesson"); let mut without = v2["units"].clone(); without.as_object_mut().unwrap().remove("bytes_out"); assert_eq!(without, lesson["units"], "every other unit is version 1's"); assert!( v2["consumption"].as_array().unwrap().iter().zip(lesson["consumption"].as_array().unwrap()).any(|(a, b)| a["funds"].as_u64() > b["funds"].as_u64()), "a grant's funds count its bytes" ); for v in vectors.iter().filter(|v| v["outcome"] == "accept" && v["name"] != "bytes_out_priced" && v["name"] != "egress") { assert!(v["units"].get("bytes_out").is_none(), "{}", v["name"]); } for (n, why, at) in [ ("bytes_out_mismatch", "unit_mismatch", serde_json::json!(["units"])), ("bytes_out_left_out_of_the_consumption", "ceiling_mismatch", serde_json::json!(["ceilings"])), ("bytes_priced_with_no_tariff", "session_invalid", serde_json::json!(["receipt"])), ("receipt_unknown_version", "unknown_version", serde_json::json!(["receipt"])), ] { assert_eq!((&get(n)["reason"], &get(n)["at"]), (&serde_json::json!(why), &at), "{n}"); } } /// An egress (§3.12): a hop of role 4 whose bytes out the receipt of version 2 counts beside /// the relay's, the lesson's session otherwise; and each control of its record refused at the /// egress's entry. #[test] fn an_egress_is_a_hop_whose_bytes_the_receipt_counts() { let doc: serde_json::Value = serde_json::from_str(&committed()).unwrap(); let vectors = doc["vectors"].as_array().unwrap(); let get = |n: &str| vectors.iter().find(|v| v["name"] == n).unwrap_or_else(|| panic!("no vector {n}")); let (egress, lesson) = (get("egress"), get("bytes_out_priced")); assert_eq!(egress["outcome"], "accept"); // The egress's ledger: every body it answered, 66 932 bytes over its out entries. let more = egress["units"]["bytes_out"].as_u64().unwrap() - lesson["units"]["bytes_out"].as_u64().unwrap(); assert_eq!(more, 4_992_000 + 47_840_000 + 14_100_000, "the egress's bytes out, beside the relay's"); let (mut a, mut b) = (egress["units"].clone(), lesson["units"].clone()); a.as_object_mut().unwrap().remove("bytes_out"); b.as_object_mut().unwrap().remove("bytes_out"); assert_eq!(a, b, "no subscriber time and no session of the egress's"); let role_4 = |v: &serde_json::Value| v["hops"].as_array().unwrap().iter().filter(|h| h["record"].as_str().unwrap()[138..140] == *"04").count(); assert_eq!(role_4(egress), 1, "one record of role 4"); for (n, why) in [ ("egress_states_sessions", "record_invalid"), ("egress_states_a_dvr_window", "record_invalid"), ("egress_states_drops_out", "record_invalid"), ("egress_ledger_sessions", "ledger_mismatch"), ("egress_ledger_bytes_in", "ledger_mismatch"), ("egress_not_authorised", "hop_not_authorised"), ] { let v = get(n); assert_eq!(v["reason"], why, "{n}"); assert_eq!(v["at"][0], "hop", "{n}: refused at a hop's record"); // The relay's key signs a record of role 4 in one of them: the record is still of role 4. assert_eq!(role_4(v), 1, "{n}"); } assert_eq!((&get("egress_bytes_left_out")["reason"], &get("egress_bytes_left_out")["at"]), (&serde_json::json!("unit_mismatch"), &serde_json::json!(["units"]))); } /// §9 of the extension lists exactly the codes `Reason::ALL` has, each once, in order. #[test] fn the_extension_lists_the_reasons() { let text = read("extensions/live-metering.md"); 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 = 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, "§9's table and Reason::ALL differ"); }