1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
//! Test-helpers for typed-primitive authors.
//!
//! Codifies the **Determinism law** pattern named in
//! `theory/PATTERN-EXTRACTION.md` Pattern 10 — every pure primitive
//! in the substrate ships a test asserting that the same input
//! produces the same output across repeated invocations.
//!
//! Authoring shape:
//!
//! ```rust,no_run
//! use shigoto_types::testing::assert_deterministic;
//!
//! fn add(a: u32, b: u32) -> u32 { a + b }
//!
//! #[test]
//! fn add_is_deterministic() {
//! assert_deterministic(|| add(2, 3));
//! }
//! ```
//!
//! ## Why centralize this
//!
//! 11+ primitives in `magma-converge` + `shigoto-types` carry a
//! determinism-named test today (`Classifier::classifier_law_determinism`,
//! `TimeoutWatcher::determinism_law`, `Decision::determinism_law`,
//! `LabelSelector::matches_is_deterministic`,
//! `CascadePolicy::determinism_law`,
//! `OutcomeLattice::worst_determinism_across_impls`,
//! `RefSpec::display_round_trips_through_parse`, ...). Each spells
//! out the same `let a = f(x); let b = f(x); assert_eq!(a, b);` shape.
//! This helper collapses that into one line.
//!
//! ## What it doesn't cover
//!
//! The helper is for **pure functions** — functions whose only
//! input is their argument and whose only output is their return.
//! Side-effecting work (filesystem, network, time, randomness)
//! requires the Environment-trait pattern from
//! `pleme-io/CLAUDE.md` ★★ TYPED-SPEC + INTERPRETER TRIPLET §3.
//! Don't reach for `assert_deterministic` to test those — mock
//! the side effects and test the pure decision logic instead.
/// Assert that the closure produces an identical result when invoked
/// **once vs twice vs three times** with no intervening state change.
///
/// Used in tests that codify the determinism law for a pure function.
/// The closure must:
///
/// - Have no captured mutable state
/// - Make no I/O calls (filesystem, network, time)
/// - Return a `T: PartialEq + std::fmt::Debug`
///
/// Failure is panic via `assert_eq!`, surfacing the first divergence
/// with both values printed.
///
/// # Examples
///
/// ```
/// use shigoto_types::testing::assert_deterministic;
///
/// assert_deterministic(|| 1 + 1);
/// assert_deterministic(|| String::from("hello"));
/// ```
///
/// # When to reach for the proptest variant instead
///
/// For functions over multiple typed inputs where you want the
/// determinism law to hold over a **distribution** of inputs (not
/// just one fixed input), use `assert_deterministic_over` and
/// drive it from a proptest strategy.
/// Variant for closures taking one typed input — exercises
/// determinism *given a fixed input value*.
///
/// Use when the function signature carries one input you want to
/// pin while asserting the law. For multi-arg functions, capture
/// extra args in the closure.
///
/// # Examples
///
/// ```
/// use shigoto_types::testing::assert_deterministic_with;
///
/// assert_deterministic_with(42_u32, |x| x.wrapping_mul(7));
/// ```
/// Variant exercising determinism **across a slice of fixed
/// inputs** — useful for table-driven tests where every input in
/// a list should individually witness the law.
///
/// # Examples
///
/// ```
/// use shigoto_types::testing::assert_deterministic_over;
///
/// assert_deterministic_over(&[0_u32, 1, 2, 100], |&x| x.wrapping_mul(7));
/// ```