carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostclaim
2
3import (
4 "context"
5 "encoding/json"
6 "errors"
7 "reflect"
8 "strings"
9 "testing"
10 "time"
11
12 "github.com/carlosframework/platform/internal/release"
13)
14
15const testHost = "shop.bab.oncarlos.com"
16
17func TestKeyRefusesBadHosts(t *testing.T) {
18 if k, err := Key(testHost); err != nil || k != "control/hosts/"+testHost+".json" {
19 t.Fatalf("Key = %q, %v", k, err)
20 }
21 for _, bad := range []string{"", "../x", "a/b", "UPPER.example.com"} {
22 if _, err := Key(bad); err == nil {
23 t.Errorf("Key(%q) accepted", bad)
24 }
25 }
26}
27
28func TestIncarnationStableAndLegacyDistinct(t *testing.T) {
29 at := time.Date(2026, 10, 4, 1, 2, 3, 4, time.UTC)
30 a1, err := Incarnation("bab", "jam", testHost, at)
31 if err != nil {
32 t.Fatal(err)
33 }
34 a2, _ := Incarnation("bab", "jam", testHost, at)
35 if a1 != a2 || len(a1) != 64 {
36 t.Fatalf("not stable 64-hex: %q %q", a1, a2)
37 }
38 l1, err := Incarnation("bab", "jam", testHost, time.Time{})
39 if err != nil {
40 t.Fatal(err)
41 }
42 l2, _ := Incarnation("bab", "other", testHost, time.Time{})
43 if l1 == a1 || l1 == l2 || len(l1) != 64 {
44 t.Fatalf("legacy identity not distinct: %q %q %q", l1, l2, a1)
45 }
46}
47
48func TestValidate(t *testing.T) {
49 created := time.Date(2026, 10, 4, 1, 0, 0, 0, time.UTC)
50 inc, _ := Incarnation("bab", "jam", testHost, created)
51 ok := &Claim{V: 2, Host: testHost, Gen: 1,
52 Holder: &Holder{Family: "instance", Key: "apps/bab/jam/instances/" + testHost + ".json", Account: "bab", App: "jam",
53 Incarnation: inc, CreatedAt: created, Location: "instances/" + testHost + "/" + inc},
54 Op: Op{Kind: KindServing}}
55 if err := ok.Validate(); err != nil {
56 t.Fatalf("valid claim: %v", err)
57 }
58 bad := map[string]func(c *Claim){
59 "holderless serving": func(c *Claim) { c.Holder = nil },
60 "unknown kind": func(c *Claim) { c.Op = Op{Kind: "teleporting", ID: "x"} },
61 "foreign location": func(c *Claim) { c.Holder.Location = "instances/other.example.com" },
62 "short incarnation": func(c *Claim) { c.Holder.Incarnation = "abc" },
63 "wrong incarnation": func(c *Claim) { c.Holder.Incarnation = strings.Repeat("a", 64) },
64 "foreign key": func(c *Claim) { c.Holder.Key = "apps/zzz/jam/instances/" + testHost + ".json" },
65 "free with chores": func(c *Claim) {
66 c.Holder, c.Op = nil, Op{}
67 c.Obligations = []Obligation{{ID: "o", Op: "x", Kind: "park-local", Box: "b"}}
68 },
69 "unknown chore kind": func(c *Claim) {
70 c.Obligations = []Obligation{{ID: "o", Op: "x", Kind: "teleport", Region: "ie", Retirement: "r", Owner: "region:ie"}}
71 },
72 "locationless chore": func(c *Claim) {
73 c.Obligations = []Obligation{{ID: "o", Op: "x", Kind: "park-replica", Retirement: "r", Owner: "region:"}}
74 },
75 "owner mismatch": func(c *Claim) {
76 c.Obligations = []Obligation{{ID: "o", Op: "x", Kind: "park-local", Box: "b", Retirement: "r", Owner: "box:other"}}
77 },
78 "duplicate chore id": func(c *Claim) {
79 ob := Obligation{ID: "o", Op: "x", Kind: "park-local", Box: "b", Retirement: "r", Owner: "box:b"}
80 c.Obligations = []Obligation{ob, ob}
81 },
82 "old schema": func(c *Claim) { c.V = 1 },
83 }
84 for name, mut := range bad {
85 raw, _ := json.Marshal(ok)
86 var c Claim
87 _ = json.Unmarshal(raw, &c)
88 mut(&c)
89 if err := c.Validate(); err == nil {
90 t.Errorf("%s validated", name)
91 }
92 }
93}
94
95func TestParkGenerationValidation(t *testing.T) {
96 for _, name := range []string{"valid", "no region", "no generation", "box", "wrong owner", "no owner", "park-replica", "park-local"} {
97 t.Run(name, func(t *testing.T) {
98 ctx := context.Background()
99 s := release.NewDirStore(t.TempDir())
100 before := servingClaim(t, ctx, s)
101 ob := Obligation{ID: "o1", Op: "m1", Kind: "park-generation", Region: "ie", Generation: "dead_gen", Retirement: "r1", Owner: "region:ie"}
102 switch name {
103 case "no region":
104 ob.Region, ob.Owner = "", "region:"
105 case "no generation":
106 ob.Generation = ""
107 case "box":
108 ob.Box = "boxS"
109 case "wrong owner":
110 ob.Owner = "region:uk"
111 case "no owner":
112 ob.Owner = ""
113 case "park-replica":
114 ob.Kind = name
115 case "park-local":
116 ob.Kind, ob.Region, ob.Box, ob.Owner = name, "", "boxS", "box:boxS"
117 }
118 next, err := Update(ctx, s, before, func(c *Claim) error { c.Obligations = []Obligation{ob}; return nil })
119 if name != "valid" {
120 if !errors.Is(err, ErrMalformed) {
121 t.Fatalf("invalid obligation = %v, want ErrMalformed", err)
122 }
123 assertAdmissionStored(t, ctx, s, before)
124 return
125 }
126 if err != nil {
127 t.Fatal(err)
128 }
129 stored := assertAdmissionStored(t, ctx, s, next)
130 if !reflect.DeepEqual(stored.Claim.Obligations, []Obligation{ob}) {
131 t.Fatalf("stored obligations = %+v", stored.Claim.Obligations)
132 }
133 raw, err := json.Marshal(ob)
134 if err != nil {
135 t.Fatal(err)
136 }
137 if !strings.Contains(string(raw), `"generation":"dead_gen"`) {
138 t.Fatalf("generation wire form = %s", raw)
139 }
140 ob.Generation = ""
141 raw, err = json.Marshal(ob)
142 if err != nil {
143 t.Fatal(err)
144 }
145 if strings.Contains(string(raw), `"generation"`) {
146 t.Fatalf("empty generation not omitted: %s", raw)
147 }
148 })
149 }
150}
151