carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package release
2
3import (
4 "context"
5 "encoding/json"
6 "github.com/carlosframework/platform/internal/hostwrite"
7 "github.com/carlosframework/platform/internal/registry"
8 "os"
9 "path/filepath"
10 "testing"
11 "time"
12)
13
14func TestPlan14RecoveryUnknownHistory(t *testing.T) {
15 for _, state := range []string{"missing", "corrupt", "incomplete", "pending", "unknown-bake", "denied", "denied-incomplete", "denied-unsynced", "denied-corrupt-programs", "outcome-with-corrupt-host", "outcome-with-corrupt-peer"} {
16 t.Run(state, func(t *testing.T) {
17 h, s, b := rootHistoryFixture(t)
18 at := time.Date(2026, 10, 13, 0, 0, 0, 0, time.UTC)
19 p := &ChannelPointer{Version: "new", PromotedAt: at}
20 denied := state == "denied" || state == "denied-incomplete" || state == "denied-unsynced" || state == "denied-corrupt-programs" || state == "outcome-with-corrupt-host" || state == "outcome-with-corrupt-peer"
21 if state == "denied" || state == "denied-incomplete" || state == "denied-unsynced" || state == "denied-corrupt-programs" {
22 if err := h.AddRefusal(b, Refusal{Host: b.Host, App: b.App, Version: p.Version, PointerPromotedAt: at, RefusedAt: at}); err != nil {
23 t.Fatal(err)
24 }
25 }
26 if state == "incomplete" || state == "denied-incomplete" {
27 r, err := h.read(b)
28 if err != nil {
29 t.Fatal(err)
30 }
31 r.Refusals.Complete = false
32 if err := h.write(r); err != nil {
33 t.Fatal(err)
34 }
35 }
36 switch state {
37 case "missing":
38 if err := os.Remove(filepath.Join(s.dir, historyKey(b))); err != nil {
39 t.Fatal(err)
40 }
41 case "corrupt":
42 if err := os.WriteFile(filepath.Join(s.dir, historyKey(b)), []byte("{"), 0600); err != nil {
43 t.Fatal(err)
44 }
45 case "pending":
46 if err := h.Begin(b, HistoryAttempt{ID: "old-attempt", Version: "old", Source: "source", Started: at}); err != nil {
47 t.Fatal(err)
48 }
49 case "denied-corrupt-programs":
50 r, err := h.read(b)
51 if err != nil {
52 t.Fatal(err)
53 }
54 r.Programs.Values = []string{""}
55 raw, err := json.Marshal(r)
56 if err != nil {
57 t.Fatal(err)
58 }
59 if err := os.WriteFile(filepath.Join(s.dir, historyKey(b)), raw, 0600); err != nil {
60 t.Fatal(err)
61 }
62 case "denied-unsynced":
63 h.Store = &historyFailNewStore{healthyRetryStore: s, remaining: 100}
64 case "outcome-with-corrupt-host", "outcome-with-corrupt-peer":
65 outcome := HistoryRecovery{Binding: b, Attempt: HistoryAttempt{ID: "failed", Version: p.Version, Source: "source", Started: at, PromotedAt: at, SHA256: "digest", Platform: AdoptPlatform()}, Failed: true}
66 if err := h.RetainRecovery(outcome); err != nil {
67 t.Fatal(err)
68 }
69 key := historyKey(b)
70 if state == "outcome-with-corrupt-peer" {
71 key = "corrupt.outcome"
72 }
73 if err := os.WriteFile(filepath.Join(s.dir, key), []byte("{"), 0600); err != nil {
74 t.Fatal(err)
75 }
76 }
77 blocked, err := h.Blocks(b, p)
78 if err != nil || blocked != denied {
79 t.Fatalf("%s: blocked=%v error=%v", state, blocked, err)
80 }
81 if state == "missing" || state == "corrupt" || state == "incomplete" || state == "pending" {
82 if _, err := h.Refusals(b); err == nil {
83 t.Fatal("recovery policy granted mint completeness")
84 }
85 }
86 })
87 }
88}
89
90func TestPlan14D1VerifiedRetryClear(t *testing.T) {
91 for _, state := range []string{"retry", "same-promotion", "older-promotion", "other-version", "visible-sync-failure"} {
92 t.Run(state, func(t *testing.T) {
93 h, s, b := rootHistoryFixture(t)
94 at := time.Date(2026, 10, 13, 0, 0, 0, 0, time.UTC)
95 for _, kind := range []string{RefusalNotUp, RefusalWrongProgram, RefusalNotProven} {
96 if err := h.AddRefusal(b, Refusal{Host: b.Host, App: b.App, Version: "new", Kind: kind, PointerPromotedAt: at, RefusedAt: at}); err != nil {
97 t.Fatal(err)
98 }
99 }
100 p := &ChannelPointer{Version: "new", PromotedAt: at.Add(time.Second)}
101 switch state {
102 case "same-promotion":
103 p.PromotedAt = at
104 case "older-promotion":
105 p.PromotedAt = at.Add(-time.Second)
106 case "other-version":
107 p.Version = "other"
108 case "visible-sync-failure":
109 h.Store = &historyFailNewStore{healthyRetryStore: s, remaining: 1}
110 }
111 err := h.RetireD1(b, p)
112 if state == "visible-sync-failure" {
113 if err == nil {
114 t.Fatal("unsynced clear accepted")
115 }
116 if err = h.RetireD1(b, p); err != nil {
117 t.Fatal(err)
118 }
119 } else if err != nil {
120 t.Fatal(err)
121 }
122 refs, err := h.Refusals(b)
123 if err != nil {
124 t.Fatal(err)
125 }
126 count := 3
127 if state == "retry" || state == "visible-sync-failure" {
128 count = 2
129 }
130 if len(refs) != count {
131 t.Fatalf("refusals: %+v", refs)
132 }
133 for _, kind := range []string{RefusalWrongProgram, RefusalNotProven} {
134 found := false
135 for _, r := range refs {
136 found = found || r.Kind == kind
137 }
138 if !found {
139 t.Fatalf("retry cleared %s", kind)
140 }
141 }
142 })
143 }
144}
145
146func TestPlan14RootHistoryVerifiedRetryBeforeAttempt(t *testing.T) {
147 for _, state := range []string{"signed-retry", "unsigned", "same-promotion", "already-live", "record-change"} {
148 t.Run(state, func(t *testing.T) {
149 h, _, b := rootHistoryFixture(t)
150 seedRootBake(t, h, b, time.Now().UTC().Add(-time.Hour))
151 st := NewDirStore(t.TempDir())
152 priv, pub := testKeys(t)
153 opt := t.TempDir()
154 reg := testRegistry(t)
155 addRoute(t, reg, b.Host, b.App, "canary/x", false)
156 shipRelease(t, st, b.Account, b.App, "new", adoptPlatform, []byte("new"))
157 at := time.Now().UTC().Truncate(time.Second).Add(-time.Minute)
158 if err := h.AddRefusal(b, Refusal{Host: b.Host, App: b.App, Version: "new", PointerPromotedAt: at, RefusedAt: at}); err != nil {
159 t.Fatal(err)
160 }
161 promotion := at.Add(time.Second)
162 if state == "same-promotion" {
163 promotion = at
164 }
165 if state == "unsigned" {
166 priv, _ = testKeys(t)
167 }
168 writeBackdatedPointer(t, st, priv, b.Account, b.App, "canary/x", "new", promotion)
169 if state == "already-live" {
170 if err := StageArtifact(opt, b.Account, "new", b.App, []byte("new")); err != nil {
171 t.Fatal(err)
172 }
173 if err := PointLive(opt, b.Host, b.Account, "new", b.App); err != nil {
174 t.Fatal(err)
175 }
176 }
177 prepared, reads := 0, 0
178 deps := &RootHistoryPass{History: h,
179 Resolve: func(context.Context, registry.Route) (HistoryBinding, string, error) {
180 reads++
181 if state == "record-change" && reads > 1 {
182 return b, "changed", nil
183 }
184 return b, "record", nil
185 },
186 Installed: func(context.Context, registry.Route, string) (string, error) { return "", nil },
187 Prepare: func(context.Context, HistoryBinding, HistoryAttempt, hostwrite.Identity) error {
188 prepared++
189 r, err := h.read(b)
190 if err != nil {
191 t.Fatal(err)
192 }
193 if len(r.Refusals.Values) != 0 {
194 t.Fatal("verified retry was not cleared before attempt preparation")
195 }
196 return os.ErrPermission
197 },
198 }
199 effects := 0
200 _ = Pass(t.Context(), reg, st, pub, opt, func(string) error { effects++; return nil }, WithRootHistory(deps))
201 want := 0
202 if state == "signed-retry" {
203 want = 1
204 }
205 if prepared != want || effects != 0 {
206 t.Fatalf("prepared=%d effects=%d", prepared, effects)
207 }
208 r, err := h.read(b)
209 if err != nil {
210 t.Fatal(err)
211 }
212 retained := 1
213 if state == "signed-retry" || state == "already-live" {
214 retained = 0
215 }
216 if len(r.Refusals.Values) != retained {
217 t.Fatalf("wrong refusal clear: %+v", r)
218 }
219 })
220 }
221}
222