carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "context"
5 "fmt"
6 "net/http"
7 "net/http/httptest"
8 "strings"
9 "testing"
10
11 "github.com/carlosframework/platform/internal/rolloutwords"
12)
13
14func TestInstalledAndServingCells(t *testing.T) {
15 old := apiInstanceRecord{StagedVersion: "v1"}
16 if installedCell(old) != "v1" || servingCell(old) != "—" {
17 t.Errorf("no rollout: %q, %q", installedCell(old), servingCell(old))
18 }
19 r := apiInstanceRecord{StagedVersion: "v1", Rollout: &apiRollout{Installed: "v2", Serving: "v1", Awake: true}}
20 if installedCell(r) != "v2" || servingCell(r) != "v1" {
21 t.Errorf("rollout cells = %q, %q", installedCell(r), servingCell(r))
22 }
23 asleep := apiInstanceRecord{StagedVersion: "v1", Rollout: &apiRollout{Installed: "v2", Good: "v1"}}
24 if servingCell(asleep) != "—" {
25 t.Errorf("an asleep instance serves nothing: %q", servingCell(asleep))
26 }
27 // A box that reports itself unavailable reports no serving build, even
28 // if a stale one rides along.
29 down := apiInstanceRecord{StagedVersion: "v1", Rollout: &apiRollout{Installed: "v2", Serving: "v1", Unavailable: "no-release-key"}}
30 if installedCell(down) != "v2" || servingCell(down) != "—" {
31 t.Errorf("unavailable cells = %q, %q", installedCell(down), servingCell(down))
32 }
33}
34
35func TestRolloutReason(t *testing.T) {
36 for _, tc := range []struct {
37 r *apiRollout
38 want string
39 }{
40 {nil, ""},
41 {&apiRollout{Installed: "v2", Serving: "v2", Awake: true}, ""},
42 {&apiRollout{Installed: "v2", Serving: "v1", Awake: true, HeldBy: "v2"}, fmt.Sprintf(rolloutwords.StateHeld, "v2")},
43 // Plan 5 reports no serving build for an asleep instance: its last
44 // good build is what it ran.
45 {&apiRollout{Installed: "v2", Good: "v1", HeldBy: "v2"}, fmt.Sprintf(rolloutwords.StateHeld, "v2")},
46 {&apiRollout{Installed: "v2", Good: "v1"}, fmt.Sprintf(rolloutwords.StateAsleep, "v2")},
47 {&apiRollout{Installed: "v2", Good: "v2"}, ""},
48 {&apiRollout{Installed: "v2", Serving: "v1", Awake: true}, rolloutwords.StateRollingOut},
49 {&apiRollout{Installed: "v2", Serving: "v2", Awake: true, ConfigPending: true}, rolloutwords.StateConfigPending},
50 // A box that cannot arm says why, and nothing else: awake and the
51 // other flags are meaningless false, and a hold does not outrank it.
52 {&apiRollout{Installed: "v2", Unavailable: "no-release-key"}, rolloutwords.Unavailable("no-release-key")},
53 {&apiRollout{Installed: "v2", Unavailable: "generations-off"}, rolloutwords.Unavailable("generations-off")},
54 {&apiRollout{Installed: "v2", Unavailable: "generations-off", HeldBy: "v2"}, rolloutwords.Unavailable("generations-off")},
55 } {
56 if got := rolloutReason(tc.r); got != tc.want {
57 t.Errorf("rolloutReason(%+v) = %q, want %q", tc.r, got, tc.want)
58 }
59 }
60 if rolloutReason(&apiRollout{Installed: "v2", Unavailable: "no-release-key"}) == rolloutReason(&apiRollout{Installed: "v2", Unavailable: "generations-off"}) {
61 t.Error("a refusing and a declined code read the same")
62 }
63 if anyHeld([]apiInstanceRecord{{}, {Rollout: &apiRollout{}}}) || !anyHeld([]apiInstanceRecord{{Rollout: &apiRollout{HeldBy: "v2"}}}) {
64 t.Error("anyHeld is wrong")
65 }
66}
67
68// TestRolloutWireShape pins the JSON the CLI reads from Task 8's console: the
69// rollout join on a list record (including the unavailable code) and held_until
70// on promote's 200.
71func TestRolloutWireShape(t *testing.T) {
72 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
73 if strings.HasSuffix(r.URL.Path, "/promote") {
74 fmt.Fprint(w, `{"result":"promoted","held":true,"held_until":"2026-10-08T10:00:00Z"}`)
75 return
76 }
77 fmt.Fprint(w, `{"instances":[
78 {"host":"a.example","staged_version":"v1","rollout":{"box":"b1","installed":"v2","serving":"v1","good":"v1","awake":true,"held_by":"v2","reason":"health","config_pending":true,"scheduler_idle":true}},
79 {"host":"b.example","staged_version":"v1","rollout":{"box":"b2","installed":"v2","unavailable":"no-release-key"}},
80 {"host":"c.example","staged_version":"v1"}],"truncated":false,"total":3}`)
81 }))
82 defer ts.Close()
83 s := newTestSession(ts.URL)
84
85 recs, _, _, err := s.instanceList(context.Background(), apiAcct{Seg: "acct"}, "app", "", "")
86 if err != nil {
87 t.Fatalf("instanceList: %v", err)
88 }
89 want := &apiRollout{Box: "b1", Installed: "v2", Serving: "v1", Good: "v1", Awake: true, HeldBy: "v2", Reason: "health", ConfigPending: true, SchedulerIdle: true}
90 if len(recs) != 3 || recs[0].Rollout == nil || *recs[0].Rollout != *want {
91 t.Fatalf("rollout decoded as %+v", recs)
92 }
93 if recs[1].Rollout == nil || recs[1].Rollout.Unavailable != "no-release-key" {
94 t.Errorf("unavailable not decoded: %+v", recs[1].Rollout)
95 }
96 if recs[2].Rollout != nil {
97 t.Errorf("a record with no rollout decoded one: %+v", recs[2].Rollout)
98 }
99 // Flag off: installed prints the staged version, serving and reason a dash and nothing.
100 if installedCell(recs[2]) != "v1" || servingCell(recs[2]) != "—" || rolloutReason(recs[2].Rollout) != "" {
101 t.Errorf("no-rollout row: %q %q %q", installedCell(recs[2]), servingCell(recs[2]), rolloutReason(recs[2].Rollout))
102 }
103
104 res, err := s.promote(context.Background(), apiAcct{Seg: "acct"}, "app", "v2", "stable", false, false)
105 if err != nil {
106 t.Fatalf("promote: %v", err)
107 }
108 if !res.Held || res.HeldUntil != "2026-10-08T10:00:00Z" {
109 t.Errorf("promote result = %+v", res)
110 }
111}
112