carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package moves
2
3import (
4 "context"
5 "testing"
6 "time"
7
8 "github.com/carlosframework/platform/internal/release"
9)
10
11const tLogPrefix = "moves/bab/app/a.go.example.com/mv_abcdefghijklmnopqrstuvwxyz/"
12
13func logEvent(identity, event string, at time.Time, actor string) LogEvent {
14 return LogEvent{V: 1, Op: "mv_abcdefghijklmnopqrstuvwxyz", Host: "a.go.example.com", Event: event, Identity: identity, Actor: actor, At: at}
15}
16
17// Plan 6 Task 3 (preflight L10, L27): a log object's key is the transition's
18// identity, never a counter. A key is written create-only: the first writer
19// wins and creates it; a second writer of the same transition, or a replay,
20// meets the existing object, which is success with nothing created.
21func TestLogKeysAreTransitionIdentities(t *testing.T) {
22 ctx := context.Background()
23 s := release.NewDirStore(t.TempDir())
24 t0 := time.Date(2026, 10, 11, 9, 0, 0, 0, time.UTC)
25
26 // Two writers of landing-probing (two boxes, or a crash and a replay),
27 // with different bodies: one object, the first writer's.
28 created, err := WriteLog(ctx, s, tLogPrefix, logEvent("landing-probing", "probing", t0, "box-uk"))
29 if err != nil || !created {
30 t.Fatalf("first writer: created %v, %v", created, err)
31 }
32 created, err = WriteLog(ctx, s, tLogPrefix, logEvent("landing-probing", "probing", t0.Add(time.Second), "box-ie"))
33 if err != nil || created {
34 t.Fatalf("second writer: created %v, %v; want no object and no error", created, err)
35 }
36 // Three probe attempts: three objects.
37 for n, id := range []string{"probe_attempt-1", "probe_attempt-2", "probe_attempt-3"} {
38 if created, err := WriteLog(ctx, s, tLogPrefix, logEvent(id, "probe_attempt", t0.Add(time.Duration(n+2)*time.Second), "box-uk")); err != nil || !created {
39 t.Fatalf("%s: created %v, %v", id, created, err)
40 }
41 }
42 // A replayed cutting-stopped writes nothing new.
43 if created, err := WriteLog(ctx, s, tLogPrefix, logEvent("cutting-stopped", "stopped", t0.Add(-time.Minute), "box-ie")); err != nil || !created {
44 t.Fatalf("cutting-stopped: %v %v", created, err)
45 }
46 if created, err := WriteLog(ctx, s, tLogPrefix, logEvent("cutting-stopped", "stopped", t0.Add(-time.Minute), "box-ie")); err != nil || created {
47 t.Fatalf("replayed cutting-stopped: created %v, %v", created, err)
48 }
49
50 keys, err := s.List(ctx, tLogPrefix+"log/")
51 if err != nil {
52 t.Fatal(err)
53 }
54 if len(keys) != 5 {
55 t.Fatalf("keys %v, want five objects", keys)
56 }
57 for _, id := range []string{"landing-probing", "probe_attempt-1", "probe_attempt-2", "probe_attempt-3", "cutting-stopped"} {
58 if _, _, err := s.Get(ctx, LogKeyFor(tLogPrefix, id)); err != nil {
59 t.Fatalf("%s: %v", id, err)
60 }
61 }
62
63 // ReadLog orders by At, never by key.
64 got, err := ReadLog(ctx, s, tLogPrefix)
65 if err != nil {
66 t.Fatal(err)
67 }
68 var order []string
69 for _, ev := range got {
70 order = append(order, ev.Identity)
71 }
72 want := "cutting-stopped landing-probing probe_attempt-1 probe_attempt-2 probe_attempt-3"
73 if j := joinSpace(order); j != want {
74 t.Fatalf("ReadLog order %q, want %q", j, want)
75 }
76 if got[1].Actor != "box-uk" {
77 t.Fatalf("landing-probing is %+v, want the first writer's", got[1])
78 }
79
80 // An identity is one key segment.
81 for _, bad := range []string{"", "a/b", "..", "."} {
82 if _, err := WriteLog(ctx, s, tLogPrefix, logEvent(bad, "x", t0, "box")); err == nil {
83 t.Errorf("identity %q accepted", bad)
84 }
85 }
86}
87
88func joinSpace(xs []string) string {
89 out := ""
90 for i, x := range xs {
91 if i > 0 {
92 out += " "
93 }
94 out += x
95 }
96 return out
97}
98