nx_iot_provision_softap_test.nx source
↩ module page · 240 lines · 12846 B
1// nx_iot_provision_softap_test.nx -- gate for the auto-pair state machine.
2//
3// Proves the operator's "no more manual re-pair" outcome as PURE LOGIC,
4// off the LAN, plus the never-brick guarantee (CLAUDE.md #26) mechanically:
5//
6// - sealed-enum validity (state / action / event / fail)
7// - NEVER-BRICK: every action in the alphabet is brick-safe and writes
8// no firmware; the firmware classifier is FAIL-SAFE (an out-of-range
9// / future action defaults to "writes firmware" = RED)
10// - init writes a coherent session; default + override + null guard
11// - pairing-SSID filter routes known vendors, rejects unknown APs
12// - HAPPY PATH end-to-end: SCAN -> JOIN -> PUSH_CREDS -> REJOIN -> VERIFY
13// -> ANCHOR -> DONE, asserting action + state + brick-safety each step,
14// landing a stable logical id with NO manual step
15// - terminal states are sticky + idempotent (CLAUDE.md #10)
16// - every FAILURE path: no-AP, unknown-vendor, join/push/rejoin/verify
17// retries-exhausted, anchor-fail -- each with the right sealed reason
18// - bounded retry RECOVERS (transient join failure then success)
19// - defensive: malformed event is a no-op; null session does not crash
20//
21// Grounded in the live fleet: the pairing SSID is the operator's REAL
22// device, TP-LINK_HS210_C209 (the Kasa HS210 found blinking 2026-06-18).
23//
24// expect_exit: 0
25//
26// license_tier: ORIGINAL
27
28import "nx_syscalls_x86_64.nx"
29import "nx_iot_provision_softap.nx"
30
31func main() -> i64 {
32 // ---- sealed-enum validity --------------------------------------
33 if nx_iot_prov_state_is_valid(PROV_ST_IDLE) != 1 { return 1 }
34 if nx_iot_prov_state_is_valid(PROV_ST_DONE) != 1 { return 2 }
35 if nx_iot_prov_state_is_valid(PROV_ST_N) != 0 { return 3 }
36 if nx_iot_prov_state_is_valid(-1) != 0 { return 4 }
37 if nx_iot_prov_action_is_valid(PROV_ACT_PUSH_CREDS) != 1 { return 5 }
38 if nx_iot_prov_action_is_valid(PROV_ACT_N) != 0 { return 6 }
39 if nx_iot_prov_event_is_valid(PROV_EV_ANCHOR_OK) != 1 { return 7 }
40 if nx_iot_prov_event_is_valid(PROV_EV_N) != 0 { return 8 }
41 if nx_iot_prov_event_is_valid(-1) != 0 { return 9 }
42 if nx_iot_prov_fail_is_valid(PROV_FAIL_PUSH) != 1 { return 10 }
43 if nx_iot_prov_fail_is_valid(PROV_FAIL_N) != 0 { return 11 }
44 if nx_iot_prov_fail_is_valid(-1) != 0 { return 12 }
45
46 // ---- NEVER-BRICK: the whole action alphabet is brick-safe -------
47 // (CLAUDE.md #26 -- mechanical, not asserted-as-promise)
48 var a: i64 = 0
49 while a < PROV_ACT_N {
50 if nx_iot_prov_action_is_brick_safe(a) != 1 { return 20 }
51 if nx_iot_prov_action_writes_firmware(a) != 0 { return 21 }
52 a = a + 1
53 }
54 // FAIL-SAFE: an out-of-range / future action defaults to firmware-write
55 // (= RED) and is NOT brick-safe. This is what makes the guarantee
56 // hold by construction for actions not yet invented.
57 if nx_iot_prov_action_writes_firmware(PROV_ACT_N) != 1 { return 22 }
58 if nx_iot_prov_action_is_brick_safe(PROV_ACT_N) != 0 { return 23 }
59 if nx_iot_prov_action_is_brick_safe(-1) != 0 { return 24 }
60 if nx_iot_prov_action_writes_firmware(9999) != 1 { return 25 }
61
62 // ---- init ------------------------------------------------------
63 let s: *IotProvSession = sys_mmap(64) as *IotProvSession
64 if nx_iot_prov_init(s, 0) != 0 { return 40 }
65 if nx_iot_prov_state(s) != PROV_ST_IDLE { return 41 }
66 if nx_iot_prov_vendor(s) != NX_IOT_VENDOR_UNKNOWN { return 42 }
67 if nx_iot_prov_fail_reason(s) != PROV_FAIL_NONE { return 43 }
68 if nx_iot_prov_attempts(s) != 0 { return 44 }
69 if nx_iot_prov_logical_id(s) != 0 { return 45 }
70 if nx_iot_prov_action(s) != PROV_ACT_NONE { return 46 }
71 if nx_iot_prov_ev_count(s) != 0 { return 47 }
72 if s.max_attempts != NX_IOT_PROV_MAX_ATTEMPTS { return 48 }
73 // override + null guard
74 if nx_iot_prov_init(s, 5) != 0 { return 49 }
75 if s.max_attempts != 5 { return 50 }
76 if nx_iot_prov_init(s, -3) != 0 { return 51 }
77 if s.max_attempts != NX_IOT_PROV_MAX_ATTEMPTS { return 52 }
78 if nx_iot_prov_init(0 as *IotProvSession, 0) != PROV_FAIL_BAD_ARG { return 53 }
79
80 // ---- pairing-SSID filter ---------------------------------------
81 if nx_iot_prov_is_pairing_ssid("TP-LINK_HS210_C209", 18) != 1 { return 54 }
82 if nx_iot_prov_is_pairing_ssid("SmartLife-1234", 14) != 1 { return 55 }
83 if nx_iot_prov_is_pairing_ssid("wiz_a1b2c3", 10) != 1 { return 56 }
84 if nx_iot_prov_is_pairing_ssid("MyHomeWiFi", 10) != 0 { return 57 }
85 if nx_iot_prov_is_pairing_ssid("-", 0) != 0 { return 58 }
86
87 // ---- HAPPY PATH: no manual step, end to end --------------------
88 if nx_iot_prov_init(s, 0) != 0 { return 60 }
89
90 let r1: i64 = nx_iot_prov_step(s, PROV_EV_START, "-", 0, 0)
91 if r1 != PROV_ACT_SCAN { return 61 }
92 if nx_iot_prov_state(s) != PROV_ST_SCANNING { return 62 }
93 if nx_iot_prov_action_is_brick_safe(r1) != 1 { return 63 }
94 if nx_iot_prov_action(s) != PROV_ACT_SCAN { return 64 }
95
96 let r2: i64 = nx_iot_prov_step(s, PROV_EV_AP_FOUND, "TP-LINK_HS210_C209", 18, 0)
97 if r2 != PROV_ACT_JOIN_AP { return 65 }
98 if nx_iot_prov_state(s) != PROV_ST_JOINING { return 66 }
99 if nx_iot_prov_vendor(s) != NX_IOT_VENDOR_KASA { return 67 }
100 if nx_iot_prov_action_is_brick_safe(r2) != 1 { return 68 }
101
102 let r3: i64 = nx_iot_prov_step(s, PROV_EV_JOIN_OK, "-", 0, 0)
103 if r3 != PROV_ACT_PUSH_CREDS { return 69 }
104 if nx_iot_prov_state(s) != PROV_ST_PUSHING { return 70 }
105 if nx_iot_prov_action_is_brick_safe(r3) != 1 { return 71 }
106
107 let r4: i64 = nx_iot_prov_step(s, PROV_EV_PUSH_OK, "-", 0, 0)
108 if r4 != PROV_ACT_REJOIN_HOME { return 72 }
109 if nx_iot_prov_state(s) != PROV_ST_AWAIT_REJOIN { return 73 }
110 if nx_iot_prov_action_is_brick_safe(r4) != 1 { return 74 }
111
112 let r5: i64 = nx_iot_prov_step(s, PROV_EV_REJOIN_OK, "-", 0, 0)
113 if r5 != PROV_ACT_VERIFY { return 75 }
114 if nx_iot_prov_state(s) != PROV_ST_VERIFYING { return 76 }
115 if nx_iot_prov_action_is_brick_safe(r5) != 1 { return 77 }
116
117 let r6: i64 = nx_iot_prov_step(s, PROV_EV_VERIFY_OK, "-", 0, 0)
118 if r6 != PROV_ACT_ANCHOR { return 78 }
119 if nx_iot_prov_state(s) != PROV_ST_ANCHORING { return 79 }
120 if nx_iot_prov_action_is_brick_safe(r6) != 1 { return 80 }
121
122 // anchor adopt happened in the runner; it feeds back logical_id 7
123 let r7: i64 = nx_iot_prov_step(s, PROV_EV_ANCHOR_OK, "-", 0, 7)
124 if r7 != PROV_ACT_REPORT_DONE { return 81 }
125 if nx_iot_prov_state(s) != PROV_ST_DONE { return 82 }
126 if nx_iot_prov_logical_id(s) != 7 { return 83 }
127 if nx_iot_prov_fail_reason(s) != PROV_FAIL_NONE { return 84 }
128 if nx_iot_prov_is_success(s) != 1 { return 85 }
129 if nx_iot_prov_is_terminal(s) != 1 { return 86 }
130 if nx_iot_prov_action_is_brick_safe(r7) != 1 { return 87 }
131 if nx_iot_prov_ev_count(s) != 7 { return 88 }
132
133 // ---- terminal stickiness + idempotency (CLAUDE.md #10) ---------
134 let rt: i64 = nx_iot_prov_step(s, PROV_EV_START, "-", 0, 0)
135 if rt != PROV_ACT_NONE { return 100 }
136 if nx_iot_prov_state(s) != PROV_ST_DONE { return 101 }
137 if nx_iot_prov_is_success(s) != 1 { return 102 }
138 if nx_iot_prov_logical_id(s) != 7 { return 103 }
139
140 // ---- FAIL: scan found no pairing AP ----------------------------
141 if nx_iot_prov_init(s, 0) != 0 { return 110 }
142 nx_iot_prov_step(s, PROV_EV_START, "-", 0, 0)
143 let f1: i64 = nx_iot_prov_step(s, PROV_EV_AP_NONE, "-", 0, 0)
144 if f1 != PROV_ACT_REPORT_FAIL { return 111 }
145 if nx_iot_prov_state(s) != PROV_ST_FAILED { return 112 }
146 if nx_iot_prov_fail_reason(s) != PROV_FAIL_NO_AP { return 113 }
147 if nx_iot_prov_is_terminal(s) != 1 { return 114 }
148 if nx_iot_prov_is_success(s) != 0 { return 115 }
149
150 // ---- FAIL: pairing AP we have no driver for (honest dead-end) ---
151 if nx_iot_prov_init(s, 0) != 0 { return 120 }
152 nx_iot_prov_step(s, PROV_EV_START, "-", 0, 0)
153 let f2: i64 = nx_iot_prov_step(s, PROV_EV_AP_FOUND, "MyHomeWiFi", 10, 0)
154 if f2 != PROV_ACT_REPORT_FAIL { return 121 }
155 if nx_iot_prov_state(s) != PROV_ST_FAILED { return 122 }
156 if nx_iot_prov_fail_reason(s) != PROV_FAIL_UNKNOWN_VENDOR { return 123 }
157 if nx_iot_prov_vendor(s) != NX_IOT_VENDOR_UNKNOWN { return 124 }
158
159 // ---- bounded retry RECOVERS: transient join failures then OK ----
160 if nx_iot_prov_init(s, 0) != 0 { return 130 } // default max = 3
161 nx_iot_prov_step(s, PROV_EV_START, "-", 0, 0)
162 nx_iot_prov_step(s, PROV_EV_AP_FOUND, "TP-LINK_HS210_C209", 18, 0)
163 let j1: i64 = nx_iot_prov_step(s, PROV_EV_JOIN_FAIL, "-", 0, 0)
164 if j1 != PROV_ACT_JOIN_AP { return 131 } // retry, not fail
165 if nx_iot_prov_state(s) != PROV_ST_JOINING { return 132 }
166 if nx_iot_prov_attempts(s) != 1 { return 133 }
167 let j2: i64 = nx_iot_prov_step(s, PROV_EV_JOIN_FAIL, "-", 0, 0)
168 if j2 != PROV_ACT_JOIN_AP { return 134 }
169 if nx_iot_prov_attempts(s) != 2 { return 135 }
170 let j3: i64 = nx_iot_prov_step(s, PROV_EV_JOIN_OK, "-", 0, 0)
171 if j3 != PROV_ACT_PUSH_CREDS { return 136 } // recovered
172 if nx_iot_prov_state(s) != PROV_ST_PUSHING { return 137 }
173 if nx_iot_prov_attempts(s) != 0 { return 138 } // counter reset on advance
174
175 // ---- retry EXHAUSTED at the cap -> FAIL JOIN -------------------
176 if nx_iot_prov_init(s, 2) != 0 { return 140 } // max = 2
177 nx_iot_prov_step(s, PROV_EV_START, "-", 0, 0)
178 nx_iot_prov_step(s, PROV_EV_AP_FOUND, "TP-LINK_HS210_C209", 18, 0)
179 let k1: i64 = nx_iot_prov_step(s, PROV_EV_JOIN_FAIL, "-", 0, 0)
180 if k1 != PROV_ACT_JOIN_AP { return 141 } // attempt 1 < 2, retry
181 let k2: i64 = nx_iot_prov_step(s, PROV_EV_JOIN_FAIL, "-", 0, 0)
182 if k2 != PROV_ACT_REPORT_FAIL { return 142 } // attempt 2, give up
183 if nx_iot_prov_state(s) != PROV_ST_FAILED { return 143 }
184 if nx_iot_prov_fail_reason(s) != PROV_FAIL_JOIN { return 144 }
185
186 // ---- PUSH fail exhausted (device left SAFE in pairing) ---------
187 if nx_iot_prov_init(s, 1) != 0 { return 150 } // max = 1: one fail exhausts
188 nx_iot_prov_step(s, PROV_EV_START, "-", 0, 0)
189 nx_iot_prov_step(s, PROV_EV_AP_FOUND, "TP-LINK_HS210_C209", 18, 0)
190 nx_iot_prov_step(s, PROV_EV_JOIN_OK, "-", 0, 0)
191 let p1: i64 = nx_iot_prov_step(s, PROV_EV_PUSH_FAIL, "-", 0, 0)
192 if p1 != PROV_ACT_REPORT_FAIL { return 151 }
193 if nx_iot_prov_state(s) != PROV_ST_FAILED { return 152 }
194 if nx_iot_prov_fail_reason(s) != PROV_FAIL_PUSH { return 153 }
195
196 // ---- REJOIN timeout --------------------------------------------
197 if nx_iot_prov_init(s, 1) != 0 { return 160 }
198 nx_iot_prov_step(s, PROV_EV_START, "-", 0, 0)
199 nx_iot_prov_step(s, PROV_EV_AP_FOUND, "TP-LINK_HS210_C209", 18, 0)
200 nx_iot_prov_step(s, PROV_EV_JOIN_OK, "-", 0, 0)
201 nx_iot_prov_step(s, PROV_EV_PUSH_OK, "-", 0, 0)
202 let q1: i64 = nx_iot_prov_step(s, PROV_EV_REJOIN_FAIL, "-", 0, 0)
203 if q1 != PROV_ACT_REPORT_FAIL { return 161 }
204 if nx_iot_prov_fail_reason(s) != PROV_FAIL_REJOIN_TIMEOUT { return 162 }
205
206 // ---- VERIFY fail -----------------------------------------------
207 if nx_iot_prov_init(s, 1) != 0 { return 170 }
208 nx_iot_prov_step(s, PROV_EV_START, "-", 0, 0)
209 nx_iot_prov_step(s, PROV_EV_AP_FOUND, "TP-LINK_HS210_C209", 18, 0)
210 nx_iot_prov_step(s, PROV_EV_JOIN_OK, "-", 0, 0)
211 nx_iot_prov_step(s, PROV_EV_PUSH_OK, "-", 0, 0)
212 nx_iot_prov_step(s, PROV_EV_REJOIN_OK, "-", 0, 0)
213 let v1: i64 = nx_iot_prov_step(s, PROV_EV_VERIFY_FAIL, "-", 0, 0)
214 if v1 != PROV_ACT_REPORT_FAIL { return 171 }
215 if nx_iot_prov_fail_reason(s) != PROV_FAIL_VERIFY { return 172 }
216
217 // ---- ANCHOR fail (e.g. registry full) --------------------------
218 if nx_iot_prov_init(s, 1) != 0 { return 180 }
219 nx_iot_prov_step(s, PROV_EV_START, "-", 0, 0)
220 nx_iot_prov_step(s, PROV_EV_AP_FOUND, "TP-LINK_HS210_C209", 18, 0)
221 nx_iot_prov_step(s, PROV_EV_JOIN_OK, "-", 0, 0)
222 nx_iot_prov_step(s, PROV_EV_PUSH_OK, "-", 0, 0)
223 nx_iot_prov_step(s, PROV_EV_REJOIN_OK, "-", 0, 0)
224 nx_iot_prov_step(s, PROV_EV_VERIFY_OK, "-", 0, 0)
225 let w1: i64 = nx_iot_prov_step(s, PROV_EV_ANCHOR_FAIL, "-", 0, 0)
226 if w1 != PROV_ACT_REPORT_FAIL { return 181 }
227 if nx_iot_prov_state(s) != PROV_ST_FAILED { return 182 }
228 if nx_iot_prov_fail_reason(s) != PROV_FAIL_ANCHOR { return 183 }
229
230 // ---- defensive: malformed event is a no-op; null does not crash -
231 if nx_iot_prov_init(s, 0) != 0 { return 190 }
232 let d1: i64 = nx_iot_prov_step(s, 999, "-", 0, 0) // invalid event
233 if d1 != PROV_ACT_NONE { return 191 }
234 if nx_iot_prov_state(s) != PROV_ST_IDLE { return 192 } // unchanged
235 if nx_iot_prov_ev_count(s) != 0 { return 193 } // invalid events do not count
236 let d2: i64 = nx_iot_prov_step(0 as *IotProvSession, PROV_EV_START, "-", 0, 0)
237 if d2 != PROV_ACT_NONE { return 194 } // null guard, no crash
238
239 return 0
240}