carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package appconfig
2
3import (
4 "testing"
5
6 "github.com/carlosframework/platform/internal/registry"
7)
8
9// fpExecRoute is the TCP-addr exec shape (issue #18): carlos proxies to it
10// but neither spawns nor owns the process.
11func fpExecRoute(host, env string) registry.Route {
12 return registry.Route{Host: host, Kind: "instance", App: "titogo",
13 Account: "bab", Status: "ready", Addr: "127.0.0.1:9001", Environment: env}
14}
15
16// fpUnitRoute is the unit-backed shape the ROOT pass delivers config for.
17func fpUnitRoute(host, env string) registry.Route {
18 return registry.Route{Host: host, Kind: "instance", App: "keymail",
19 Account: "bae", Status: "ready", Addr: "127.0.0.1:9100",
20 Backing: registry.BackingUnit, Environment: env}
21}
22
23func TestConfigRouteFingerprintsMoveOnRebinding(t *testing.T) {
24 before := []registry.Route{fpExecRoute("connect.go.tito.io", "")}
25 after := []registry.Route{fpExecRoute("connect.go.tito.io", "relay")}
26 be, bu := ConfigRouteFingerprints(before)
27 ae, au := ConfigRouteFingerprints(after)
28 if be == ae {
29 t.Fatal("binding an exec route to an environment must move the exec digest — this is the hour issue #196 cost")
30 }
31 if bu != au {
32 t.Fatal("an exec route's rebinding must not move the UNIT digest — that would ask for a root config pass for nothing")
33 }
34}
35
36func TestConfigRouteFingerprintsUnitHalfIsIndependent(t *testing.T) {
37 before := []registry.Route{fpUnitRoute("keymail.dev", "")}
38 after := []registry.Route{fpUnitRoute("keymail.dev", "staging")}
39 be, bu := ConfigRouteFingerprints(before)
40 ae, au := ConfigRouteFingerprints(after)
41 if bu == au {
42 t.Fatal("binding a unit-backed route to an environment must move the unit digest — the root pass resolves per environment too")
43 }
44 if be != ae {
45 t.Fatal("a unit route's rebinding must not move the exec digest")
46 }
47}
48
49func TestConfigRouteFingerprintsIgnoreOrderAndIrrelevantColumns(t *testing.T) {
50 a := fpExecRoute("a.example", "")
51 b := fpExecRoute("b.example", "relay")
52 e1, _ := ConfigRouteFingerprints([]registry.Route{a, b})
53 e2, _ := ConfigRouteFingerprints([]registry.Route{b, a})
54 if e1 != e2 {
55 t.Fatal("registry order must not move the digest — it is not stable across passes")
56 }
57 // A column that cannot change which bytes belong in a route's config
58 // file must not move the digest, or every adopt would fire a pass.
59 c := fpExecRoute("a.example", "")
60 c.Version = "deadbeef"
61 c.PrevVersion = "cafebabe"
62 c.LastError = "whatever"
63 c.Hibernate = true
64 e3, _ := ConfigRouteFingerprints([]registry.Route{c, b})
65 if e1 != e3 {
66 t.Fatalf("an irrelevant column moved the digest (%s -> %s)", e1, e3)
67 }
68}
69
70func TestConfigRouteFingerprintsMoveWhenARouteLeavesScope(t *testing.T) {
71 both := []registry.Route{fpExecRoute("a.example", ""), fpExecRoute("b.example", "")}
72 one := []registry.Route{fpExecRoute("a.example", "")}
73 e1, _ := ConfigRouteFingerprints(both)
74 e2, _ := ConfigRouteFingerprints(one)
75 if e1 == e2 {
76 t.Fatal("a route leaving the registry must move the digest — its file needs reaping")
77 }
78 // Leaving scope by STATUS, not by deletion, counts exactly the same:
79 // the eligibility predicate is what set membership is drawn from.
80 failed := []registry.Route{fpExecRoute("a.example", ""), fpExecRoute("b.example", "")}
81 failed[1].Status = "failed"
82 e3, _ := ConfigRouteFingerprints(failed)
83 if e1 == e3 {
84 t.Fatal("a route falling out of the eligibility predicate must move the digest")
85 }
86}
87
88func TestConfigRouteFingerprintsIgnoreOutOfScopeRoutes(t *testing.T) {
89 base := []registry.Route{fpExecRoute("a.example", "")}
90 withOthers := append([]registry.Route{}, base...)
91 withOthers = append(withOthers,
92 // A static route: no config is delivered to it by either half.
93 registry.Route{Host: "site.example", Kind: "static", App: "site", Account: "bab"},
94 // A platform-minted row: excluded by execRouteBase and by
95 // release.Adoptable alike.
96 registry.Route{Host: "edge.example", Kind: "instance", App: "titogo", Account: "bab",
97 Status: "ready", Addr: "127.0.0.1:9500", Platform: true},
98 )
99 e1, u1 := ConfigRouteFingerprints(base)
100 e2, u2 := ConfigRouteFingerprints(withOthers)
101 if e1 != e2 || u1 != u2 {
102 t.Fatal("a route neither half can deliver config to must not move either digest")
103 }
104}
105
106func TestConfigRouteFingerprintsEmptySetIsStable(t *testing.T) {
107 e1, u1 := ConfigRouteFingerprints(nil)
108 e2, u2 := ConfigRouteFingerprints([]registry.Route{})
109 if e1 != e2 || u1 != u2 {
110 t.Fatal("nil and empty must digest identically — a box with no routes must not look like it changed every tick")
111 }
112 if e1 == "" || u1 == "" {
113 t.Fatal("the empty set must have a real digest, not the empty string: the fast loop tells 'never computed' from 'computed, and empty' by its own bool, not by this value")
114 }
115}
116