//! WAI `live-conformance/e2ee/` (confidentiality §14): the committed `vectors.json` //! regenerates byte for byte from its fixed seeds, the reference gives every vector its //! stated outcome, and §13 of the extension lists exactly the reference's codes. use joule_moq::secure_objects::Refusal; use joule_moq_mls::corpus; const DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../../../wai/live-conformance/e2ee/"); const SPEC: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../../../wai/extensions/confidentiality.md"); #[test] fn the_committed_corpus_regenerates_byte_for_byte() { let on_disk = std::fs::read(format!("{DIR}{}", corpus::FILE)).expect("vectors.json"); assert!(on_disk == corpus::to_bytes(&corpus::generate()), "vectors.json is not what the generator writes; run joule_moq_e2ee_vectors gen"); } /// The writer's key order is its own: members inserted out of key order, at every depth, /// come out in key order. In a build where serde_json keeps insertion order (the /// conformance job runs this crate in one) a writer that left the order to the build would /// fail here and in the test above. #[test] fn the_writer_puts_every_object_in_key_order() { let mut inner = serde_json::Map::new(); inner.insert("z".into(), serde_json::json!(0)); inner.insert("y".into(), serde_json::json!(1)); let mut outer = serde_json::Map::new(); outer.insert("b".into(), serde_json::Value::Array(vec![serde_json::Value::Object(inner)])); outer.insert("a".into(), serde_json::json!(2)); let text = String::from_utf8(corpus::to_bytes(&serde_json::Value::Object(outer))).unwrap(); assert_eq!(text, "{\n \"a\": 2,\n \"b\": [\n {\n \"y\": 1,\n \"z\": 0\n }\n ]\n}\n"); } /// The positive control of the conformance job's step that runs these tests in a build with /// serde_json's `preserve_order`: with `JOULE_MOQ_EXPECT_INSERTION_ORDER=1` the build must /// keep members in the order they were inserted. #[test] fn the_build_keeps_the_order_its_job_says() { joule_moq::json_order::check_the_order_the_job_expects(); } #[test] fn the_reference_gives_every_vector_its_outcome() { let v: serde_json::Value = serde_json::from_slice(&std::fs::read(format!("{DIR}{}", corpus::FILE)).unwrap()).unwrap(); corpus::check(&v).unwrap(); } #[test] fn a_mutated_corpus_fails() { let v: serde_json::Value = serde_json::from_slice(&std::fs::read(format!("{DIR}{}", corpus::FILE)).unwrap()).unwrap(); for (path, value) in [ ("/records/0/outcome", serde_json::json!("bad_signature")), ("/objects/0/derived/nonce", serde_json::json!("00")), ("/objects/6/outcome", serde_json::json!("opened")), ("/rooms/0/deliveries/0/deliveries/0/outcome", serde_json::json!("bad_tag")), ("/rooms/0/rosters/1/roster/1", serde_json::json!("00")), ("/rooms/1/outcome/refuse", serde_json::json!("commit_mismatch")), ("/key_schedule/0/moq_key", serde_json::json!("00")), ] { let mut m = v.clone(); *m.pointer_mut(path).unwrap_or_else(|| panic!("{path}")) = value; assert!(corpus::check(&m).is_err(), "{path} mutated and still checks"); } } #[test] fn section_13_lists_exactly_the_references_codes() { let spec = std::fs::read_to_string(SPEC).unwrap(); let start = spec.find("## 13. Reasons").expect("§13"); let end = spec[start..].find("\n## ").map(|e| start + e).unwrap_or(spec.len()); let mut listed = Vec::new(); for line in spec[start..end].lines().filter(|l| l.starts_with("| `")) { let cell = line.split('|').nth(1).unwrap(); listed.extend(cell.split('`').skip(1).step_by(2).map(str::to_string)); } let codes: Vec = Refusal::ALL.iter().map(|r| r.code().to_string()).collect(); assert_eq!(listed, codes, "§13's table and Refusal::ALL, in order"); }