carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package console
2
3// The bake controls (2026-08-04 console bake controls spec §7). Properties
4// in the order they'd hurt if lost: OFF cannot happen without owner +
5// passkey (and a member probing it gets the byte-identical 404); ON is any
6// member's, instantly; fast-track is owner + passkey and lands on the
7// pointer; the stable-promote checkbox flips the flag in the same confirmed
8// action.
9
10import (
11 "context"
12 "net/http"
13 "net/url"
14 "strings"
15 "testing"
16 "time"
17
18 "github.com/carlosframework/platform/internal/release"
19)
20
21// productionFlag reads the app's flag straight from the bucket, through the
22// same verification the adopt path uses. The fixture carries the release
23// public key, so there is nothing to derive.
24func productionFlag(t *testing.T, fx *consoleFixture, app string) *release.ProductionFlag {
25 t.Helper()
26 f, err := release.GetProduction(context.Background(), fx.rel, fx.pub, release.OpsAccount, app)
27 if err != nil {
28 t.Fatal(err)
29 }
30 return f
31}
32
33// strangerRefusal is what someone with no membership at all gets from one of
34// this account's app routes: a signed-in browser belonging to nobody here.
35// It is the yardstick the member's refusal must match byte for byte.
36func strangerRefusal(t *testing.T, fx *consoleFixture, code, u string, form url.Values) string {
37 t.Helper()
38 fx.fk.mu.Lock()
39 prev := fx.fk.address
40 fx.fk.mu.Unlock()
41 fx.setUser("zoe@keymail.dev") // signs up into an account of her own
42 defer fx.setUser(prev)
43 c := newBrowser(t)
44 signUp(t, fx, c, code)
45 form.Set("csrf", sessionToken(t, fx, c))
46 status, _, body := postForm(t, c, u, form)
47 if status != http.StatusNotFound {
48 t.Fatalf("non-member posting %s = %d, want 404", u, status)
49 }
50 return body
51}
52
53// auditedBy asserts the console's OWN activity trail (store events, which is
54// what the app page renders) recorded this action for this actor. The
55// pointer's audit trail is a different record with a different purpose; a
56// console action that leaves no console event is an invisible one.
57func auditedBy(t *testing.T, fx *consoleFixture, app, action, actor, detailPart string) {
58 t.Helper()
59 events, err := fx.st.Events(OpsAccountID, app, 20)
60 if err != nil {
61 t.Fatal(err)
62 }
63 for _, e := range events {
64 if e.Action == action && e.Address == actor && strings.Contains(e.Detail, detailPart) {
65 return
66 }
67 }
68 t.Fatalf("events = %+v, want a %q row by %s mentioning %q", events, action, actor, detailPart)
69}
70
71func TestProductionOnIsAnyMembers(t *testing.T) {
72 fx := newConsoleFixture(t)
73 if err := fx.st.AddMember(OpsAccountID, "sam@keymail.dev", RoleMember, time.Now()); err != nil {
74 t.Fatal(err)
75 }
76 fx.setUser("sam@keymail.dev")
77 c := newBrowser(t)
78 signUp(t, fx, c, "code-sam-bake")
79 csrf := sessionToken(t, fx, c)
80
81 status, _, _ := postForm(t, c, fx.u("/apps/kass/production"),
82 url.Values{"csrf": {csrf}, "production": {"on"}})
83 if status != http.StatusSeeOther {
84 t.Fatalf("member turning bake ON = %d, want a plain redirect (no step-up)", status)
85 }
86 if f := productionFlag(t, fx, "kass"); f == nil || !f.Production {
87 t.Fatalf("flag = %+v, want production on", f)
88 }
89 auditedBy(t, fx, "kass", "production", "sam@keymail.dev", "production: on")
90}
91
92func TestProductionOffIsOwnerPasskeyGated(t *testing.T) {
93 fx := newConsoleFixture(t)
94 c := signInPaul(t, fx)
95 csrf := sessionToken(t, fx, c)
96 // Seed on, as any member could.
97 if err := release.SetProduction(context.Background(), fx.rel, fx.priv,
98 release.OpsAccount, "kass", true, "test"); err != nil {
99 t.Fatal(err)
100 }
101
102 // Without the passkey the flag does not move.
103 status, loc, _ := postForm(t, c, fx.u("/apps/kass/production"),
104 url.Values{"csrf": {csrf}, "production": {"off"}})
105 if status != http.StatusSeeOther || !strings.HasPrefix(loc, fx.fk.srv.URL) {
106 t.Fatalf("un-stepped-up off = %d -> %q, want a redirect into Keymail", status, loc)
107 }
108 if f := productionFlag(t, fx, "kass"); f == nil || !f.Production {
109 t.Fatal("the flag flipped off with no passkey confirmation")
110 }
111 // With it, the held action resumes.
112 if status, _, body := completeStepUp(t, fx, c, loc); status != http.StatusSeeOther {
113 t.Fatalf("resumed off = %d:\n%s", status, body)
114 }
115 if f := productionFlag(t, fx, "kass"); f == nil || f.Production {
116 t.Fatal("the flag did not flip off after confirmation")
117 }
118 auditedBy(t, fx, "kass", "production", "paul@keymail.dev", "production: off")
119}
120
121// A MEMBER probing the off direction meets the byte-identical 404 — before
122// any step-up redirect could confirm the account exists.
123func TestProductionOffMemberGets404(t *testing.T) {
124 fx := newConsoleFixture(t)
125 // Seeded ON, so "the flag stayed put" is an assertion about a value that
126 // could have moved rather than about an absent object.
127 if err := release.SetProduction(context.Background(), fx.rel, fx.priv,
128 release.OpsAccount, "kass", true, "test"); err != nil {
129 t.Fatal(err)
130 }
131 if err := fx.st.AddMember(OpsAccountID, "sam@keymail.dev", RoleMember, time.Now()); err != nil {
132 t.Fatal(err)
133 }
134 fx.setUser("sam@keymail.dev")
135 c := newBrowser(t)
136 signUp(t, fx, c, "code-sam-bake2")
137 csrf := sessionToken(t, fx, c)
138 form := url.Values{"csrf": {csrf}, "production": {"off"}}
139 status, _, memberBody := postForm(t, c, fx.u("/apps/kass/production"), form)
140 if status != http.StatusNotFound {
141 t.Fatalf("member turning bake OFF = %d, want 404", status)
142 }
143 if f := productionFlag(t, fx, "kass"); f == nil || !f.Production {
144 t.Fatalf("flag = %+v after a member's refused post, want it untouched", f)
145 }
146 // Byte-identical, not merely same-status: the difference between "not
147 // allowed" and "no such account" is not information this hands out.
148 strangerBody := strangerRefusal(t, fx, "code-zoe-bake2", fx.u("/apps/kass/production"),
149 url.Values{"production": {"off"}})
150 if memberBody != strangerBody {
151 t.Fatalf("the two refusals are not byte-identical:\n(member) %q\n(stranger) %q", memberBody, strangerBody)
152 }
153 // http.NotFound's own, well-known output — pinned so a future change to
154 // either path is caught even if it stays self-consistent.
155 if memberBody != "404 page not found\n" {
156 t.Fatalf("refusal body = %q, want the stdlib http.NotFound output", memberBody)
157 }
158}
159
160// Fast-track is owner + passkey, ALWAYS. The un-stepped-up post is what
161// proves the second half: postGated alone would stay green with the step-up
162// call deleted, because it only walks the round trip when one is demanded.
163func TestFastTrackIsOwnerPasskeyGatedAndLands(t *testing.T) {
164 fx := newConsoleFixture(t)
165 c := signInPaul(t, fx)
166 csrf := sessionToken(t, fx, c)
167
168 // The fixture's stable already points at v1. Without the passkey the
169 // waiver does not happen.
170 status, loc, _ := postForm(t, c, fx.u("/apps/kass/fasttrack"),
171 url.Values{"csrf": {csrf}, "channel": {"stable"}, "confirmed": {"1"}})
172 if status != http.StatusSeeOther || !strings.HasPrefix(loc, fx.fk.srv.URL) {
173 t.Fatalf("un-stepped-up fast-track = %d -> %q, want a redirect into Keymail", status, loc)
174 }
175 if p := fx.channelVersion(t, "kass", "stable"); p.FastTrack {
176 t.Fatal("the bake window was waived with no passkey confirmation")
177 }
178
179 // With it, the held action resumes and lands on the pointer.
180 if status, _, body := completeStepUp(t, fx, c, loc); status != http.StatusSeeOther {
181 t.Fatalf("resumed fast-track = %d:\n%s", status, body)
182 }
183 p := fx.channelVersion(t, "kass", "stable")
184 if !p.FastTrack {
185 t.Fatalf("pointer = %+v, want fast_track set", p)
186 }
187 if len(p.Audit) == 0 || !p.Audit[0].FastTrack || p.Audit[0].Actor != "paul@keymail.dev" {
188 t.Fatalf("audit[0] = %+v, want the waiver recorded with its actor", p.Audit)
189 }
190 auditedBy(t, fx, "kass", "fast-track", "paul@keymail.dev", "skipped the safety delay for v1 on stable")
191}
192
193// Fast-tracking a channel that never bakes is refused. It looks inert —
194// adopt ignores the waiver on a zero-hold channel — but the re-sign stamps a
195// fresh PromotedAt, which is exactly what the canary TTL lease is measured
196// from: the post would quietly renew the lease and leave a permanent
197// "fast-tracked past the bake window" badge on a channel that has no window.
198func TestFastTrackRefusesAChannelWithNoBakeWindow(t *testing.T) {
199 fx := newConsoleFixture(t)
200 shipVersion(t, fx, "kass", "v3")
201 seedChannel(t, fx, "kass", "canary/x", "v3")
202 before := fx.channelVersion(t, "kass", "canary/x")
203 c := signInPaul(t, fx)
204 csrf := sessionToken(t, fx, c)
205
206 status, _, _ := postGated(t, fx, c, fx.u("/apps/kass/fasttrack"),
207 url.Values{"csrf": {csrf}, "channel": {"canary/x"}, "confirmed": {"1"}})
208 if status != http.StatusSeeOther {
209 t.Fatalf("fast-track of a bake-free channel = %d, want the flash redirect", status)
210 }
211 if _, _, body := get(t, c, fx.u("/apps/kass")); !strings.Contains(body,
212 "Channel canary/x has no safety delay running — there is nothing to skip.") {
213 t.Fatalf("no refusal flash:\n%s", body)
214 }
215 after := fx.channelVersion(t, "kass", "canary/x")
216 if after.FastTrack {
217 t.Fatal("a bake-free channel wears the waiver badge")
218 }
219 if !after.PromotedAt.Equal(before.PromotedAt) {
220 t.Fatalf("PromotedAt moved %s -> %s — the refused post renewed the canary's TTL lease",
221 before.PromotedAt, after.PromotedAt)
222 }
223 if len(after.Audit) != len(before.Audit) {
224 t.Fatalf("audit trail grew from %d to %d on a refused fast-track", len(before.Audit), len(after.Audit))
225 }
226}
227
228// A MEMBER probing fast-track meets the byte-identical 404 — the loud
229// instant path past the window is the owner's alone.
230func TestFastTrackMemberGets404(t *testing.T) {
231 fx := newConsoleFixture(t)
232 if err := fx.st.AddMember(OpsAccountID, "sam@keymail.dev", RoleMember, time.Now()); err != nil {
233 t.Fatal(err)
234 }
235 fx.setUser("sam@keymail.dev")
236 c := newBrowser(t)
237 signUp(t, fx, c, "code-sam-bake3")
238 csrf := sessionToken(t, fx, c)
239 status, _, memberBody := postForm(t, c, fx.u("/apps/kass/fasttrack"),
240 url.Values{"csrf": {csrf}, "channel": {"stable"}})
241 if status != http.StatusNotFound {
242 t.Fatalf("member fast-tracking = %d, want 404", status)
243 }
244 if p := fx.channelVersion(t, "kass", "stable"); p.FastTrack {
245 t.Fatal("a member's post waived the bake window")
246 }
247 // Byte-identical, not merely same-status: the difference between "not
248 // allowed" and "no such account" is not information this hands out.
249 strangerBody := strangerRefusal(t, fx, "code-zoe-bake3", fx.u("/apps/kass/fasttrack"),
250 url.Values{"channel": {"stable"}})
251 if memberBody != strangerBody {
252 t.Fatalf("the two refusals are not byte-identical:\n(member) %q\n(stranger) %q", memberBody, strangerBody)
253 }
254 // http.NotFound's own, well-known output — pinned so a future change to
255 // either path is caught even if it stays self-consistent.
256 if memberBody != "404 page not found\n" {
257 t.Fatalf("refusal body = %q, want the stdlib http.NotFound output", memberBody)
258 }
259}
260
261// The stable-promote prompt (spec §7): enable_bake rides the same
262// passkey-confirmed promote, and flips the flag on only when it is off.
263func TestPromoteEnableBakeFlipsTheFlag(t *testing.T) {
264 fx := newConsoleFixture(t)
265 shipVersion(t, fx, "kass", "v3")
266 seedChannel(t, fx, "kass", "beta", "v3")
267 c := signInPaul(t, fx)
268 csrf := sessionToken(t, fx, c)
269
270 postGated(t, fx, c, fx.u("/apps/kass/promote"),
271 url.Values{"csrf": {csrf}, "channel": {"stable"}, "version": {"v3"}, "enable_bake": {"1"}, "confirmed": {"1"}})
272 if p := fx.channelVersion(t, "kass", "stable"); p.Version != "v3" {
273 t.Fatalf("stable = %s, want v3", p.Version)
274 }
275 if f := productionFlag(t, fx, "kass"); f == nil || !f.Production {
276 t.Fatalf("flag = %+v after enable_bake promote, want production on", f)
277 }
278 // The flash tells the truth about both halves of what just happened: the
279 // flag went on in this action, so the safety-delay note is now accurate.
280 if _, _, body := get(t, c, fx.u("/apps/kass")); !strings.Contains(body, "Safety delay enabled.") ||
281 !strings.Contains(body, "It goes live everywhere after the safety delay") {
282 t.Fatalf("enable_bake flash missing its two halves:\n%s", body)
283 }
284}
285
286// A checkbox that could not be honored SAYS so. When the flag will not read,
287// the flip is never attempted — and the promote flash used to carry no trace
288// of that, leaving the human who ticked the box believing the app now bakes.
289func TestPromoteEnableBakeSaysWhenItCouldNotRead(t *testing.T) {
290 fx := newConsoleFixture(t)
291 shipVersion(t, fx, "kass", "v3")
292 seedChannel(t, fx, "kass", "beta", "v3")
293 c := signInPaul(t, fx)
294 csrf := sessionToken(t, fx, c)
295 if _, err := fx.rel.Put(context.Background(),
296 release.ProductionKey(release.OpsAccount, "kass"), []byte("{not a wrapper"), ""); err != nil {
297 t.Fatal(err)
298 }
299
300 postGated(t, fx, c, fx.u("/apps/kass/promote"),
301 url.Values{"csrf": {csrf}, "channel": {"stable"}, "version": {"v3"}, "enable_bake": {"1"}, "confirmed": {"1"}})
302 if p := fx.channelVersion(t, "kass", "stable"); p.Version != "v3" {
303 t.Fatalf("stable = %s, want v3 — the promote itself must not depend on the flag", p.Version)
304 }
305 _, _, body := get(t, c, fx.u("/apps/kass"))
306 if !strings.Contains(body, "could not be read, so the safety delay was not enabled") {
307 t.Fatalf("the flash is silent about the unhonored checkbox:\n%s", body)
308 }
309 if strings.Contains(body, "Safety delay enabled.") {
310 t.Fatal("the flash claims the safety delay was enabled over an unreadable flag")
311 }
312}
313
314// The bake note is a claim about this app, not about the channel: a
315// non-production app adopts a held channel immediately (spec §2), so
316// promising a window would be a lie.
317func TestPromoteBakeNoteFollowsTheFlag(t *testing.T) {
318 fx := newConsoleFixture(t)
319 shipVersion(t, fx, "kass", "v3")
320 c := signInPaul(t, fx)
321 csrf := sessionToken(t, fx, c)
322
323 // No flag at all: beta holds, but this app does not bake. (edge first —
324 // the ladder promotes a build one rung at a time.)
325 seedChannel(t, fx, "kass", "edge", "v3")
326 // Checked, not fired and forgotten: a refused promote would make the
327 // absence assertion below true for the wrong reason.
328 if status, loc, body := postForm(t, c, fx.u("/apps/kass/promote"),
329 url.Values{"csrf": {csrf}, "version": {"v3"}, "channel": {"beta"}, "confirmed": {"1"}}); status != http.StatusSeeOther ||
330 loc != fx.path("/apps/kass") {
331 t.Fatalf("beta promote = %d -> %q:\n%s", status, loc, body)
332 }
333 if p := fx.channelVersion(t, "kass", "beta"); p.Version != "v3" {
334 t.Fatalf("beta = %+v, want v3", p)
335 }
336 if _, _, body := get(t, c, fx.u("/apps/kass")); strings.Contains(body, "It goes live everywhere after the safety delay") {
337 t.Fatalf("a flagless app was promised a safety delay:\n%s", body)
338 }
339
340 // Flag on: the same rung now says so.
341 if err := release.SetProduction(context.Background(), fx.rel, fx.priv,
342 release.OpsAccount, "kass", true, "test"); err != nil {
343 t.Fatal(err)
344 }
345 shipVersion(t, fx, "kass", "v4")
346 seedChannel(t, fx, "kass", "edge", "v4")
347 if status, _, body := postForm(t, c, fx.u("/apps/kass/promote"),
348 url.Values{"csrf": {csrf}, "version": {"v4"}, "channel": {"beta"}, "confirmed": {"1"}}); status != http.StatusSeeOther {
349 t.Fatalf("beta promote of v4 = %d:\n%s", status, body)
350 }
351 if _, _, body := get(t, c, fx.u("/apps/kass")); !strings.Contains(body, "It goes live everywhere after the safety delay") {
352 t.Fatalf("a production app's held promotion did not mention the window:\n%s", body)
353 }
354}
355