diff --git a/crates/ailang-core/src/hash.rs b/crates/ailang-core/src/hash.rs index 403daba..3001255 100644 --- a/crates/ailang-core/src/hash.rs +++ b/crates/ailang-core/src/hash.rs @@ -267,4 +267,70 @@ mod tests { "Type::Forall serialised must omit `constraints` when empty; got: {s}" ); } + + /// Iter ct.4 (canonical-type-names milestone close): pin the + /// canonical-form hashes of the two cross-module fixtures + /// migrated by `ail migrate-canonical-types` in ct.1. These + /// hashes are the post-migration state; pre-migration values + /// (when both fixtures carried bare cross-module Type::Con) + /// are no longer reproducible because the ct.1 validator + /// rejects that shape upstream. + #[test] + fn ct4_migrated_fixtures_have_canonical_form_hashes() { + let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let examples = manifest_dir.join("../../examples"); + + let ord_src = std::fs::read(examples.join("ordering_match.ail.json")) + .expect("examples/ordering_match.ail.json present"); + let ord_mod: crate::ast::Module = serde_json::from_slice(&ord_src).unwrap(); + let main_def = ord_mod.defs.iter().find(|d| d.name() == "main").unwrap(); + assert_eq!( + def_hash(main_def), + "8d17235aa3d2e127", + "ordering_match::main canonical hash must match captured post-migration value" + ); + + let dup_a_src = std::fs::read(examples.join("test_22b1_dup_a.ail.json")) + .expect("examples/test_22b1_dup_a.ail.json present"); + let dup_a_mod: crate::ast::Module = serde_json::from_slice(&dup_a_src).unwrap(); + // test_22b1_dup_a has two defs: the Show class and an instance + // of Show for test_22b1_dup_b.MyInt. Def::name() returns the + // class name for both, so pin each by index. The instance + // carries the migrated cross-module qualifier in its `type`. + assert_eq!(dup_a_mod.defs.len(), 2, "test_22b1_dup_a expected to have exactly 2 defs (class + instance)"); + assert_eq!( + def_hash(&dup_a_mod.defs[0]), + "1c2573661ffd3da3", + "test_22b1_dup_a class Show canonical hash must match captured post-migration value" + ); + assert_eq!( + def_hash(&dup_a_mod.defs[1]), + "cc8685f92f246e40", + "test_22b1_dup_a instance Show for test_22b1_dup_b.MyInt canonical hash must match captured post-migration value" + ); + } + + /// Iter ct.4: re-assert that the canonical-form tightening + /// did NOT change hashes of intra-module-only fixtures. The + /// existing iter-13a / iter-19b / iter-22b.1 pin tests already + /// assert these; this one names the canonical-type-names + /// milestone explicitly so future archaeology finds the + /// connection. + #[test] + fn ct4_unmigrated_fixtures_remain_bit_identical() { + let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let examples = manifest_dir.join("../../examples"); + + let sum_src = std::fs::read(examples.join("sum.ail.json")).unwrap(); + let sum_mod: crate::ast::Module = serde_json::from_slice(&sum_src).unwrap(); + let sum_def = sum_mod.defs.iter().find(|d| d.name() == "sum").unwrap(); + assert_eq!(def_hash(sum_def), "db33f57cb329935e", + "sum.sum hash drifted across canonical-form tightening — unexpected"); + + let list_src = std::fs::read(examples.join("list.ail.json")).unwrap(); + let list_mod: crate::ast::Module = serde_json::from_slice(&list_src).unwrap(); + let int_list_def = list_mod.defs.iter().find(|d| d.name() == "IntList").unwrap(); + assert_eq!(def_hash(int_list_def), "b082192bd0c99202", + "list.IntList hash drifted across canonical-form tightening — unexpected"); + } }