carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "encoding/json"
5 "errors"
6 "os"
7 "path/filepath"
8 "reflect"
9 "testing"
10)
11
12func rootRestartFixture(t *testing.T) (*RootRestarts, *rootRestartSyncStore, RestartBinding, RestartSnapshot) {
13 t.Helper()
14 s := &rootRestartSyncStore{healthyRetryStore: &healthyRetryStore{dir: t.TempDir(), fault: errors.New("injected directory sync failure")}}
15 b := RestartBinding{Box: "box-a", Account: "bab", App: "console"}
16 in := RestartSnapshot{Complete: true, AppID: "initial", AppBody: []byte(`{"schema":1}`), Scopes: ScopedRestartGeneration{Schema: 1}, Targets: []RestartTarget{
17 {Host: "one.example", Source: "record-one", Installation: "installation-one", Unit: "one.service", Environment: "prod"},
18 {Host: "two.example", Source: "record-two", Installation: "installation-two", Unit: "two.service", Environment: "test"},
19 }}
20 return &RootRestarts{Store: s}, s, b, in
21}
22
23func nextRootRestart(in RestartSnapshot) RestartSnapshot {
24 in.Scopes.Requests = []RestartRequest{{ID: "request-1", RequestedAt: "2026-10-13T12:00:00Z", Scope: RestartScope{Kind: RestartScopeEnvironment, Value: "prod"}}}
25 return in
26}
27
28func TestPlan14RootRestartConsumption(t *testing.T) {
29 for _, attack := range []string{"delete", "rename", "forge"} {
30 t.Run(attack, func(t *testing.T) {
31 h, _, b, in := rootRestartFixture(t)
32 if p, err := h.Observe(b, in); err != nil || p != nil {
33 t.Fatalf("baseline: %v %v", p, err)
34 }
35 in = nextRootRestart(in)
36 p, err := h.Observe(b, in)
37 if err != nil || p == nil || len(p.Targets) != 1 || p.Targets[0].Target.Host != "one.example" {
38 t.Fatalf("exact scope: %+v %v", p, err)
39 }
40 legacy := filepath.Join(t.TempDir(), "restart-marks.json")
41 if err := os.WriteFile(legacy, []byte(`{"schema":1,"apps":{},"scopes":{"bab|console":"request-1"}}`), 0600); err != nil {
42 t.Fatal(err)
43 }
44 switch attack {
45 case "delete":
46 err = os.Remove(legacy)
47 case "rename":
48 err = os.Rename(filepath.Dir(legacy), filepath.Dir(legacy)+"-moved")
49 t.Cleanup(func() { os.RemoveAll(filepath.Dir(legacy) + "-moved") })
50 case "forge":
51 err = os.WriteFile(legacy, []byte(`{"schema":1,"apps":{},"scopes":{}}`), 0600)
52 }
53 if err != nil {
54 t.Fatal(err)
55 }
56 target := p.Targets[0].Target
57 if err := h.Begin(b, p.ID, target, "boot-a/invocation-a"); err != nil {
58 t.Fatal(err)
59 }
60 if err := h.Complete(b, p.ID, target, "boot-a/invocation-b"); err != nil {
61 t.Fatal(err)
62 }
63 if err := h.Finish(b, p.ID); err != nil {
64 t.Fatal(err)
65 }
66 if p, err := h.Observe(b, in); err != nil || p != nil {
67 t.Fatalf("replayed after %s: %+v %v", attack, p, err)
68 }
69 })
70 }
71}
72
73func TestPlan14RootRestartPendingSurvivesRollover(t *testing.T) {
74 h, _, b, in := rootRestartFixture(t)
75 if _, err := h.Observe(b, in); err != nil {
76 t.Fatal(err)
77 }
78 in = nextRootRestart(in)
79 p, err := h.Observe(b, in)
80 if err != nil {
81 t.Fatal(err)
82 }
83 // A held target is not begun. Neither a new request nor vanished routes may erase it.
84 in.Targets = nil
85 in.AppID = "later"
86 in.Scopes.Requests = []RestartRequest{{ID: "rolled-over", Scope: RestartScope{Kind: RestartScopeHost, Value: "other.example"}}}
87 again, err := h.Observe(b, in)
88 if err != nil || !reflect.DeepEqual(p, again) {
89 t.Fatalf("pending work changed: %+v %v", again, err)
90 }
91 if err := h.Complete(b, p.ID, p.Targets[0].Target, "unrelated"); err == nil {
92 t.Fatal("unperformed request consumed")
93 }
94}
95
96func TestPlan14RootRestartAmbiguousDoesNotReplay(t *testing.T) {
97 h, _, b, in := rootRestartFixture(t)
98 if _, err := h.Observe(b, in); err != nil {
99 t.Fatal(err)
100 }
101 in = nextRootRestart(in)
102 p, err := h.Observe(b, in)
103 if err != nil {
104 t.Fatal(err)
105 }
106 target := p.Targets[0].Target
107 if err := h.Begin(b, p.ID, target, "boot-a/invocation-a"); err != nil {
108 t.Fatal(err)
109 }
110 if err := h.Begin(b, p.ID, target, "boot-a/invocation-a"); err == nil {
111 t.Fatal("ambiguous attempt granted a second dispatch")
112 }
113 for _, change := range []string{"same-invocation", "empty", "source", "installation", "unit", "request"} {
114 t.Run(change, func(t *testing.T) {
115 target := target
116 id := p.ID
117 after := "boot-a/invocation-b"
118 switch change {
119 case "same-invocation":
120 after = "boot-a/invocation-a"
121 case "empty":
122 after = ""
123 case "source":
124 target.Source = "changed"
125 case "installation":
126 target.Installation = "changed"
127 case "unit":
128 target.Unit = "foreign.service"
129 case "request":
130 id = "other"
131 }
132 if err := h.Complete(b, id, target, after); err == nil {
133 t.Fatal("ambiguous or foreign evidence consumed request")
134 }
135 })
136 }
137 if err := h.Complete(b, p.ID, target, "boot-a/invocation-b"); err != nil {
138 t.Fatal(err)
139 }
140}
141
142func TestPlan14RootRestartVisibleSync(t *testing.T) {
143 for _, boundary := range []string{"baseline", "pending", "begin", "complete"} {
144 t.Run(boundary, func(t *testing.T) {
145 h, s, b, in := rootRestartFixture(t)
146 if boundary == "baseline" {
147 s.failAt = s.writes + 1
148 if _, err := h.Observe(b, in); !errors.Is(err, s.fault) {
149 t.Fatalf("baseline: %v", err)
150 }
151 }
152 if _, err := h.Observe(b, in); err != nil {
153 t.Fatal(err)
154 }
155 in = nextRootRestart(in)
156 if boundary == "pending" {
157 s.failAt = s.writes + 2
158 if _, err := h.Observe(b, in); !errors.Is(err, s.fault) {
159 t.Fatalf("pending: %v", err)
160 }
161 }
162 p, err := h.Observe(b, in)
163 if err != nil {
164 t.Fatal(err)
165 }
166 target := p.Targets[0].Target
167 if boundary == "begin" {
168 s.failAt = s.writes + 2
169 if err := h.Begin(b, p.ID, target, "boot-a/invocation-a"); !errors.Is(err, s.fault) {
170 t.Fatalf("begin: %v", err)
171 }
172 if err := h.Begin(b, p.ID, target, "boot-a/invocation-a"); err == nil {
173 t.Fatal("visible intent dispatched twice")
174 }
175 return
176 }
177 if err := h.Begin(b, p.ID, target, "boot-a/invocation-a"); err != nil {
178 t.Fatal(err)
179 }
180 if boundary == "complete" {
181 s.failAt = s.writes + 2
182 if err := h.Complete(b, p.ID, target, "boot-a/invocation-b"); !errors.Is(err, s.fault) {
183 t.Fatalf("complete: %v", err)
184 }
185 }
186 if err := h.Complete(b, p.ID, target, "boot-a/invocation-b"); err != nil {
187 t.Fatal(err)
188 }
189 if err := h.Finish(b, p.ID); err != nil {
190 t.Fatal(err)
191 }
192 if p, err := h.Observe(b, in); err != nil || p != nil {
193 t.Fatalf("completion not durable: %+v %v", p, err)
194 }
195 })
196 }
197}
198
199func TestPlan14RootRestartStrictState(t *testing.T) {
200 for _, change := range []string{"schema", "box", "complete", "revision", "extra", "duplicate", "trailing", "null"} {
201 t.Run(change, func(t *testing.T) {
202 h, s, b, in := rootRestartFixture(t)
203 h.Observe(b, in)
204 raw, err := s.Read(rootRestartKey(b))
205 if err != nil {
206 t.Fatal(err)
207 }
208 var r rootRestartRecord
209 if err := json.Unmarshal(raw, &r); err != nil {
210 t.Fatal(err)
211 }
212 switch change {
213 case "schema":
214 r.Schema++
215 case "box":
216 r.Binding.Box = "other"
217 case "complete":
218 r.Complete = 0
219 case "revision":
220 r.Revision = 0
221 }
222 raw, _ = json.Marshal(r)
223 switch change {
224 case "extra":
225 raw = append(raw[:len(raw)-1], []byte(`,"extra":true}`)...)
226 case "duplicate":
227 raw = append(raw[:len(raw)-1], []byte(`,"schema":1}`)...)
228 case "trailing":
229 raw = append(raw, []byte(` {}`)...)
230 case "null":
231 raw = []byte(`null`)
232 }
233 if err := s.Write(rootRestartKey(b), raw); err != nil {
234 t.Fatal(err)
235 }
236 if _, err := h.Observe(b, nextRootRestart(in)); err == nil {
237 t.Fatal("invalid state granted consumption")
238 }
239 })
240 }
241}
242
243type rootRestartSyncStore struct {
244 *healthyRetryStore
245 writes int
246 failAt int
247}
248
249func (s *rootRestartSyncStore) Write(name string, raw []byte) error {
250 s.writes++
251 if s.writes == s.failAt {
252 s.fail = 1
253 }
254 return s.healthyRetryStore.Write(name, raw)
255}
256
257func TestPlan14RootRestartSelection(t *testing.T) {
258 for _, kind := range []string{"app", "host", "environment", "unknown", "retry", "hibernate", "rollover", "incomplete", "absent", "duplicate-target", "duplicate-request"} {
259 t.Run(kind, func(t *testing.T) {
260 h, _, b, in := rootRestartFixture(t)
261 if kind == "absent" {
262 in.AppID = RootRestartAbsent
263 in.AppBody = nil
264 }
265 if _, err := h.Observe(b, in); err != nil {
266 t.Fatal(err)
267 }
268 in = nextRootRestart(in)
269 want := 1
270 switch kind {
271 case "app", "absent":
272 in.AppID = "new-app-request"
273 in.AppBody = []byte(`{"requested_at":"2026-10-13T12:00:00Z"}`)
274 want = 2
275 case "host":
276 in.Scopes.Requests[0].Scope = RestartScope{Kind: RestartScopeHost, Value: "two.example"}
277 case "unknown":
278 in.Scopes.Requests[0].Scope = RestartScope{Kind: "future-selector", Value: "prod"}
279 want = 0
280 case "retry":
281 in.Scopes.Requests[0].Scope = RestartScope{Kind: RestartScopeRetry, Value: "one.example"}
282 want = 0
283 case "hibernate":
284 in.Targets[0].Hibernate = true
285 want = 0
286 case "rollover":
287 if p, err := h.Observe(b, RestartSnapshot{Complete: true, AppID: in.AppID, AppBody: in.AppBody, Scopes: in.Scopes}); err != nil || p != nil {
288 t.Fatalf("first empty target request: %+v %v", p, err)
289 }
290 in.Scopes.Requests[0].ID = "new-position"
291 want = 0
292 case "incomplete":
293 in.Complete = false
294 case "duplicate-target":
295 in.Targets = append(in.Targets, in.Targets[0])
296 case "duplicate-request":
297 in.Scopes.Requests = append(in.Scopes.Requests, in.Scopes.Requests[0])
298 }
299 p, err := h.Observe(b, in)
300 if kind == "incomplete" || kind == "duplicate-target" || kind == "duplicate-request" {
301 if err == nil {
302 t.Fatal("invalid census/request accepted")
303 }
304 return
305 }
306 if err != nil {
307 t.Fatal(err)
308 }
309 if want == 0 {
310 if p != nil {
311 t.Fatalf("unexpected restart: %+v", p)
312 }
313 return
314 }
315 if p == nil || len(p.Targets) != want {
316 t.Fatalf("targets: %+v", p)
317 }
318 if kind == "host" && p.Targets[0].Target.Host != "two.example" {
319 t.Fatal("host selection widened")
320 }
321 if kind == "app" && string(p.AppBody) != string(in.AppBody) {
322 t.Fatal("lost exact app request")
323 }
324 if err := h.Finish(b, p.ID); err == nil {
325 t.Fatal("unperformed targets consumed")
326 }
327 if err := h.Begin(b, p.ID, p.Targets[0].Target, "old"); err != nil {
328 t.Fatal(err)
329 }
330 if err := h.Complete(b, p.ID, p.Targets[0].Target, "new"); err != nil {
331 t.Fatal(err)
332 }
333 if want == 2 {
334 if err := h.Finish(b, p.ID); err == nil {
335 t.Fatal("partial target set consumed")
336 }
337 }
338 })
339 }
340}
341
342func TestPlan14RootRestartFailureIsTerminal(t *testing.T) {
343 h, _, b, in := rootRestartFixture(t)
344 if _, err := h.Observe(b, in); err != nil {
345 t.Fatal(err)
346 }
347 in = nextRootRestart(in)
348 p, err := h.Observe(b, in)
349 if err != nil {
350 t.Fatal(err)
351 }
352 target := p.Targets[0].Target
353 if err := h.Fail(b, p.ID, target); err == nil {
354 t.Fatal("undispatched request consumed as failure")
355 }
356 if err := h.Begin(b, p.ID, target, "before"); err != nil {
357 t.Fatal(err)
358 }
359 if err := h.Fail(b, p.ID, target); err != nil {
360 t.Fatal(err)
361 }
362 if err := h.Begin(b, p.ID, target, "before"); err == nil {
363 t.Fatal("terminal failure retried")
364 }
365 if err := h.Complete(b, p.ID, target, "after"); err == nil {
366 t.Fatal("terminal failure changed to success")
367 }
368 if err := h.Finish(b, p.ID); err != nil {
369 t.Fatal(err)
370 }
371 if p, err := h.Observe(b, in); err != nil || p != nil {
372 t.Fatalf("failure replayed: %+v %v", p, err)
373 }
374 in.Scopes.Requests = append(in.Scopes.Requests, RestartRequest{ID: "retry-as-new-request", Scope: RestartScope{Kind: RestartScopeHost, Value: target.Host}})
375 if p, err := h.Observe(b, in); err != nil || p == nil {
376 t.Fatalf("fresh explicit request refused: %+v %v", p, err)
377 }
378}
379
380func TestPlan14RootRestartReadMustSync(t *testing.T) {
381 for _, step := range []string{"observe", "begin", "complete", "finish"} {
382 t.Run(step, func(t *testing.T) {
383 h, s, b, in := rootRestartFixture(t)
384 if _, err := h.Observe(b, in); err != nil {
385 t.Fatal(err)
386 }
387 in = nextRootRestart(in)
388 p, err := h.Observe(b, in)
389 if err != nil {
390 t.Fatal(err)
391 }
392 target := p.Targets[0].Target
393 if step == "complete" || step == "finish" {
394 if err := h.Begin(b, p.ID, target, "before"); err != nil {
395 t.Fatal(err)
396 }
397 }
398 if step == "finish" {
399 if err := h.Complete(b, p.ID, target, "after"); err != nil {
400 t.Fatal(err)
401 }
402 }
403 s.failAt = s.writes + 1
404 switch step {
405 case "observe":
406 _, err = h.Observe(b, in)
407 case "begin":
408 err = h.Begin(b, p.ID, target, "before")
409 case "complete":
410 err = h.Complete(b, p.ID, target, "after")
411 case "finish":
412 err = h.Finish(b, p.ID)
413 }
414 if !errors.Is(err, s.fault) {
415 t.Fatalf("%s granted without durable read: %v", step, err)
416 }
417 })
418 }
419}
420
421func TestPlan14RootRestartPendingStateTampering(t *testing.T) {
422 for _, change := range []string{"remove-target", "append-target", "source", "selector", "body", "id", "completed-without-begin", "failed-without-begin"} {
423 t.Run(change, func(t *testing.T) {
424 h, s, b, in := rootRestartFixture(t)
425 if _, err := h.Observe(b, in); err != nil {
426 t.Fatal(err)
427 }
428 if _, err := h.Observe(b, nextRootRestart(in)); err != nil {
429 t.Fatal(err)
430 }
431 raw, err := s.Read(rootRestartKey(b))
432 if err != nil {
433 t.Fatal(err)
434 }
435 var r rootRestartRecord
436 if err := json.Unmarshal(raw, &r); err != nil {
437 t.Fatal(err)
438 }
439 switch change {
440 case "remove-target":
441 r.Pending.Targets = nil
442 case "append-target":
443 r.Pending.Targets = append(r.Pending.Targets, RestartProgress{Target: in.Targets[1]})
444 case "source":
445 r.Pending.Targets[0].Target.Source = "other"
446 case "selector":
447 r.Pending.Requests[0].Scope.Value = "test"
448 case "body":
449 r.Pending.AppBody = []byte("other")
450 case "id":
451 r.Pending.ID = "other"
452 case "completed-without-begin":
453 r.Pending.Targets[0].After = "after"
454 case "failed-without-begin":
455 r.Pending.Targets[0].Failed = true
456 }
457 raw, _ = json.Marshal(r)
458 if err := s.Write(rootRestartKey(b), raw); err != nil {
459 t.Fatal(err)
460 }
461 if _, err := h.Observe(b, nextRootRestart(in)); err == nil {
462 t.Fatal("changed pending authority accepted")
463 }
464 })
465 }
466}
467