carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package registry
2
3import (
4 "context"
5 "errors"
6 "path/filepath"
7 "testing"
8)
9
10func TestClaimRowsArchiveBeforeDeleteAndReplayPreservesSuccessor(t *testing.T) {
11 t.Setenv("CARLOS_HOST_CLAIM", "1")
12 ctx := context.Background()
13 path := filepath.Join(t.TempDir(), "registry.db")
14 r, err := Open(path)
15 if err != nil {
16 t.Fatal(err)
17 }
18 owner := ClaimOwnership{"inc", 7, "installation-7", "route-7"}
19 route := Route{Host: "shop.example", App: "app", ClaimOwnership: owner}
20 if err = r.Upsert(route); err != nil {
21 t.Fatal(err)
22 }
23 if err = r.UpsertTimer(Timer{Host: route.Host, Name: "daily", Kind: "cron", Spec: "0 0 * * *", NextAt: 1}); err != nil {
24 t.Fatal(err)
25 }
26 timer, err := r.GetTimer(route.Host, "daily")
27 if err != nil {
28 t.Fatal(err)
29 }
30 if timer.ClaimOwner != owner.ClaimOwner || timer.ClaimRowID != timer.Registration {
31 t.Fatalf("timer ownership = %+v", timer)
32 }
33 targets := []ClaimRow{{Kind: "route", Host: route.Host, Ownership: owner}, {Kind: "timer", Host: route.Host, Name: timer.Name, Registration: timer.Registration, Ownership: timer.ClaimOwnership}}
34 crash := errors.New("archive fsync failed")
35 if err = r.RetireClaimRows(ctx, "retire-7", targets, func([]ClaimRowArchive) error { return crash }); !errors.Is(err, crash) {
36 t.Fatal(err)
37 }
38 if rt, e := r.Get(route.Host); e != nil || rt == nil {
39 t.Fatalf("deleted before archive: %v", e)
40 }
41 var archived []ClaimRowArchive
42 if err = r.RetireClaimRows(ctx, "retire-7", targets, func(a []ClaimRowArchive) error { archived = a; return nil }); err != nil {
43 t.Fatal(err)
44 }
45 if len(archived) != 2 || len(archived[0].Contents) == 0 || len(archived[1].Contents) == 0 {
46 t.Fatal("incomplete archive")
47 }
48 if err = r.Close(); err != nil {
49 t.Fatal(err)
50 }
51 r, err = Open(path)
52 if err != nil {
53 t.Fatal(err)
54 }
55 defer r.Close()
56 successor := route
57 successor.ClaimOwnership = ClaimOwnership{"inc", 8, "installation-8", "route-8"}
58 if err = r.Upsert(successor); err != nil {
59 t.Fatal(err)
60 }
61 if err = r.UpsertTimer(Timer{Host: route.Host, Name: "daily", Kind: "cron", Spec: "0 0 * * *", NextAt: 2}); err != nil {
62 t.Fatal(err)
63 }
64 if err = r.RetireClaimRows(ctx, "retire-7", targets, func(a []ClaimRowArchive) error {
65 if len(a) != 2 {
66 t.Fatal("missing replay archive")
67 }
68 return nil
69 }); err != nil {
70 t.Fatal(err)
71 }
72 got, err := r.Get(route.Host)
73 if err != nil || got.ClaimOwnership != successor.ClaimOwnership {
74 t.Fatalf("successor removed: %v", err)
75 }
76 gotTimer, err := r.GetTimer(route.Host, "daily")
77 if err != nil || gotTimer.ClaimOwner != successor.ClaimOwner {
78 t.Fatalf("successor timer removed: %v", err)
79 }
80 if err = r.RetireClaimRows(ctx, "another-retirement", targets, func([]ClaimRowArchive) error { return nil }); !errors.Is(err, ErrClaimOwnership) {
81 t.Fatalf("unknown predecessor disposition accepted: %v", err)
82 }
83}
84func TestClaimRowsRefusesChangedRegistrationAtomically(t *testing.T) {
85 t.Setenv("CARLOS_HOST_CLAIM", "1")
86 r, err := Open(filepath.Join(t.TempDir(), "registry.db"))
87 if err != nil {
88 t.Fatal(err)
89 }
90 defer r.Close()
91 owner := ClaimOwnership{"inc", 1, "owner", "route"}
92 route := Route{Host: "shop.example", ClaimOwnership: owner}
93 if err = r.Upsert(route); err != nil {
94 t.Fatal(err)
95 }
96 if err = r.UpsertTimer(Timer{Host: route.Host, Name: "once", Kind: "once", NextAt: 1}); err != nil {
97 t.Fatal(err)
98 }
99 old, err := r.GetTimer(route.Host, "once")
100 if err != nil {
101 t.Fatal(err)
102 }
103 if err = r.FireTimerNow(route.Host, "once", 2); err != nil {
104 t.Fatal(err)
105 }
106 targets := []ClaimRow{{Kind: "route", Host: route.Host, Ownership: owner}, {Kind: "timer", Host: route.Host, Name: "once", Registration: old.Registration, Ownership: old.ClaimOwnership}}
107 called := false
108 err = r.RetireClaimRows(context.Background(), "retire", targets, func([]ClaimRowArchive) error { called = true; return nil })
109 if !errors.Is(err, ErrClaimOwnership) || called {
110 t.Fatalf("stale timer accepted: %v", err)
111 }
112 if got, e := r.Get(route.Host); e != nil || got == nil {
113 t.Fatal("partial route deletion")
114 }
115 if err = r.Upsert(Route{Host: route.Host}); !errors.Is(err, ErrClaimOwnership) {
116 t.Fatalf("unowned writer replaced claim row: %v", err)
117 }
118}
119