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 "path/filepath"
5 "testing"
6)
7
8func TestUpdateSettingsColumn(t *testing.T) {
9 reg, err := Open(filepath.Join(t.TempDir(), "r.db"))
10 if err != nil {
11 t.Fatal(err)
12 }
13 if err := reg.Upsert(Route{Host: "a.example.com", Kind: "instance", App: "km"}); err != nil {
14 t.Fatal(err)
15 }
16 rt, _ := reg.Get("a.example.com")
17 if rt.UpdateSettings != "" {
18 t.Fatalf("a new row has settings %q", rt.UpdateSettings)
19 }
20 before, err := reg.Fingerprint()
21 if err != nil {
22 t.Fatal(err)
23 }
24 col := `{"updates":"manual","channel":"stable"}`
25 if err := reg.SetUpdateSettings("a.example.com", col); err != nil {
26 t.Fatal(err)
27 }
28 if rt, _ = reg.Get("a.example.com"); rt.UpdateSettings != col {
29 t.Fatalf("column = %q", rt.UpdateSettings)
30 }
31 after, err := reg.Fingerprint()
32 if err != nil {
33 t.Fatal(err)
34 }
35 if after != before {
36 t.Fatal("update settings changed the edge's fingerprint")
37 }
38 // A full-row Upsert that does not carry the column keeps it.
39 if err := reg.Upsert(Route{Host: "a.example.com", Kind: "instance", App: "km"}); err != nil {
40 t.Fatal(err)
41 }
42 if rt, _ = reg.Get("a.example.com"); rt.UpdateSettings != col {
43 t.Fatalf("Upsert cleared the column: %q", rt.UpdateSettings)
44 }
45 if err := reg.SetUpdateSettings("a.example.com", ""); err != nil {
46 t.Fatal(err)
47 }
48 if rt, _ = reg.Get("a.example.com"); rt.UpdateSettings != "" {
49 t.Fatal("the narrow setter could not clear the column")
50 }
51 if err := reg.SetUpdateSettings("nobody.example.com", col); err == nil {
52 t.Fatal("an unknown host was not an error")
53 }
54}
55
56// A hostname that changes tenant on an Upsert takes the new tenant's update
57// settings and build state, empty included: A's manual hold or major hold,
58// and A's build state, never carry into B's row (task 8, ruling R25). A
59// same-tenant Upsert that does not carry the columns keeps them.
60func TestATenantChangeUpsertTakesTheNewTenantsSettings(t *testing.T) {
61 reg, err := Open(filepath.Join(t.TempDir(), "r.db"))
62 if err != nil {
63 t.Fatal(err)
64 }
65 const host = "a.example.com"
66 settings := `{"updates":"manual","channel":"stable"}`
67 state := `{"known":true,"replica":true}`
68 tenantA := Route{Host: host, Kind: "instance", Account: "bab", App: "km", UpdateSettings: settings, BuildState: state}
69 if err := reg.Upsert(tenantA); err != nil {
70 t.Fatal(err)
71 }
72 rt, _ := reg.Get(host)
73 if rt.UpdateSettings != settings || rt.BuildState != state {
74 t.Fatalf("A's row: settings %q, build state %q", rt.UpdateSettings, rt.BuildState)
75 }
76
77 // The same tenant, columns not carried: kept (today's behaviour).
78 if err := reg.Upsert(Route{Host: host, Kind: "instance", Account: "bab", App: "km"}); err != nil {
79 t.Fatal(err)
80 }
81 if rt, _ = reg.Get(host); rt.UpdateSettings != settings || rt.BuildState != state {
82 t.Fatalf("a same-tenant Upsert lost the columns: settings %q, build state %q", rt.UpdateSettings, rt.BuildState)
83 }
84
85 // Another app on the same account: B's empty columns win.
86 if err := reg.Upsert(Route{Host: host, Kind: "instance", Account: "bab", App: "other"}); err != nil {
87 t.Fatal(err)
88 }
89 if rt, _ = reg.Get(host); rt.UpdateSettings != "" || rt.BuildState != "" {
90 t.Fatalf("another app kept A's columns: settings %q, build state %q", rt.UpdateSettings, rt.BuildState)
91 }
92
93 // Another account with the same app name, and B's own values: B's win.
94 if err := reg.Upsert(tenantA); err != nil {
95 t.Fatal(err)
96 }
97 bSettings := `{"updates":"auto","major":2}`
98 if err := reg.Upsert(Route{Host: host, Kind: "instance", Account: "bes", App: "km", UpdateSettings: bSettings}); err != nil {
99 t.Fatal(err)
100 }
101 if rt, _ = reg.Get(host); rt.UpdateSettings != bSettings || rt.BuildState != "" {
102 t.Fatalf("another account: settings %q, build state %q", rt.UpdateSettings, rt.BuildState)
103 }
104}
105
106// Astra r4 finding 2: the tenant is the EFFECTIVE account, as
107// release.RouteAccount reads it (an empty or invalid account is the ops
108// account, "bab"). A legacy row with account "" re-added as "bab" (or the
109// reverse, or an invalid spelling) is the same tenant, so its columns are
110// kept as today. A genuine account change with empty incoming columns
111// clears both.
112func TestAnEquivalentAccountSpellingIsTheSameTenant(t *testing.T) {
113 reg, err := Open(filepath.Join(t.TempDir(), "r.db"))
114 if err != nil {
115 t.Fatal(err)
116 }
117 const host = "a.example.com"
118 settings := `{"updates":"manual","channel":"stable"}`
119 state := `{"known":true,"replica":true}`
120 for _, tc := range []struct{ from, to string }{
121 {"", "bab"}, {"bab", ""}, {"", ""}, {"NOT-VALID", "bab"}, {"bab", "x"},
122 // Astra r5 N1: an embedded NUL is invalid (SQLite's length and
123 // GLOB stop at it), so these are the ops account too.
124 {"abc\x00x", "bab"}, {"bab", "abc\x00x"}, {"abc\x00x", ""}, {"abc\x00", "bab"}, {"\x00", "bab"}, {"abc\x00x", "abc\x00y"},
125 } {
126 if err := reg.Upsert(Route{Host: host, Kind: "instance", Account: tc.from, App: "km", UpdateSettings: settings, BuildState: state}); err != nil {
127 t.Fatal(err)
128 }
129 if err := reg.Upsert(Route{Host: host, Kind: "instance", Account: tc.to, App: "km"}); err != nil {
130 t.Fatal(err)
131 }
132 if rt, _ := reg.Get(host); rt.Account != tc.to || rt.UpdateSettings != settings || rt.BuildState != state {
133 t.Fatalf("%q -> %q (one effective account): account %q, settings %q, build state %q; want both columns kept",
134 tc.from, tc.to, rt.Account, rt.UpdateSettings, rt.BuildState)
135 }
136 }
137 for _, tc := range []struct{ from, to string }{{"", "bes"}, {"bes", ""}, {"bes", "cdf"}} {
138 if err := reg.Upsert(Route{Host: host, Kind: "instance", Account: tc.from, App: "km", UpdateSettings: settings, BuildState: state}); err != nil {
139 t.Fatal(err)
140 }
141 if err := reg.Upsert(Route{Host: host, Kind: "instance", Account: tc.to, App: "km"}); err != nil {
142 t.Fatal(err)
143 }
144 if rt, _ := reg.Get(host); rt.UpdateSettings != "" || rt.BuildState != "" {
145 t.Fatalf("%q -> %q (another account): settings %q, build state %q; want both cleared",
146 tc.from, tc.to, rt.UpdateSettings, rt.BuildState)
147 }
148 }
149}
150
151// Upsert's tenant compare runs in SQLite (effectiveAccountSQL); it must
152// say exactly what EffectiveAccount (controlname.ValidAccount) says.
153func TestEffectiveAccountSQLIsEffectiveAccount(t *testing.T) {
154 reg, err := Open(filepath.Join(t.TempDir(), "r.db"))
155 if err != nil {
156 t.Fatal(err)
157 }
158 for _, a := range []string{"", "bab", "bes", "abc", "ab", "a", "abcdefghijklmnop", "abcdefghijklmnopq",
159 "Abc", "ABC", "ab1", "a-b", "a_b", "a b", " abc", "abc ", "abc\n", "\u00e1bc", "ab\u00e1", "ab]", "ab^", "ab*", "ab?", "ab[",
160 "abc\x00x", "abc\x00", "\x00abc", "\x00", "abc\x00\x00", "ab\x00", "abcdefghijklmnop\x00x"} {
161 var got string
162 if err := reg.sql.QueryRow(`SELECT `+effectiveAccountSQL("?1"), a).Scan(&got); err != nil {
163 t.Fatal(err)
164 }
165 if want := EffectiveAccount(a); got != want {
166 t.Fatalf("account %q: SQL says %q, EffectiveAccount says %q", a, got, want)
167 }
168 }
169}
170
171func TestUpdateSettingsMovesSnapshotFingerprint(t *testing.T) {
172 reg, err := Open(filepath.Join(t.TempDir(), "r.db"))
173 if err != nil {
174 t.Fatal(err)
175 }
176 if err := reg.Upsert(Route{Host: "s.example.com", Kind: "instance", App: "km"}); err != nil {
177 t.Fatal(err)
178 }
179 // SnapshotFingerprint with unset update_settings
180 before, err := reg.SnapshotFingerprint()
181 if err != nil {
182 t.Fatal(err)
183 }
184 col := `{"updates":"manual","channel":"stable"}`
185 if err := reg.SetUpdateSettings("s.example.com", col); err != nil {
186 t.Fatal(err)
187 }
188 // SnapshotFingerprint with set update_settings must be different
189 // (restores must reproduce the column value)
190 after, err := reg.SnapshotFingerprint()
191 if err != nil {
192 t.Fatal(err)
193 }
194 if after == before {
195 t.Fatal("setting update_settings did not move SnapshotFingerprint, so a restore would miss the column")
196 }
197}
198