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 "errors"
6 "os"
7 "strings"
8 "syscall"
9 "testing"
10
11 "github.com/carlosframework/platform/internal/hostwrite"
12)
13
14type heldClaimWork struct {
15 ids []hostwrite.Identity
16 shut bool
17}
18
19func (w *heldClaimWork) With(_ context.Context, id hostwrite.Identity, fn func() error) error {
20 w.ids = append(w.ids, id)
21 if w.shut {
22 return hostwrite.ErrUnavailable
23 }
24 return fn()
25}
26
27// TestMaterializeHeldUnderPublishesThroughTheClaimSelection (moves Plan 3
28// Task 5, H13): with CARLOS_HOST_CLAIM on, the held release is staged as
29// adoption stages and live/<host> is pointed at it inside the claim
30// selection, under the identity naming exactly the pinned version, with the
31// caller's lock inside it.
32func TestMaterializeHeldUnderPublishesThroughTheClaimSelection(t *testing.T) {
33 ctx, s, opt := heldFixture(t)
34 t.Setenv("CARLOS_HOST_CLAIM", "1")
35 work := &heldClaimWork{}
36 locked := 0
37 m, err := MaterializeHeldUnder(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t), work, func(publish func() error) error {
38 locked++
39 return publish()
40 })
41 if err != nil {
42 t.Fatal(err)
43 }
44 if m == nil || m.Version != "v1" {
45 t.Fatalf("manifest %+v", m)
46 }
47 if got, err := LiveVersion(opt, heldTestHost); err != nil || got != "v1" {
48 t.Fatalf("LiveVersion = %q, %v", got, err)
49 }
50 if len(work.ids) != 1 || work.ids[0] != (hostwrite.Identity{Release: "v1"}) || locked != 1 {
51 t.Fatalf("publication ran under %v with the lock taken %d times", work.ids, locked)
52 }
53}
54
55// A refused selection never points live/<host>; a missing selection is
56// refused before anything is staged.
57func TestMaterializeHeldUnderRefusals(t *testing.T) {
58 ctx, s, opt := heldFixture(t)
59 t.Setenv("CARLOS_HOST_CLAIM", "1")
60 if _, err := MaterializeHeldUnder(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t), nil, nil); !errors.Is(err, errHeldNoGuard) {
61 t.Fatalf("no selection: %v", err)
62 }
63 if _, err := MaterializeHeldUnder(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t), &heldClaimWork{shut: true}, nil); !errors.Is(err, hostwrite.ErrUnavailable) {
64 t.Fatalf("a refused selection: %v", err)
65 }
66 if got, _ := LiveVersion(opt, heldTestHost); got != "" {
67 t.Fatalf("live/<host> names %q", got)
68 }
69 noOp := heldRoute()
70 noOp.HoldOp = ""
71 if _, err := MaterializeHeldUnder(ctx, s, heldPub, nr(t), opt, noOp, "v1", offGate(t), &heldClaimWork{}, nil); err == nil {
72 t.Fatal("a row no move holds was staged")
73 }
74}
75
76// Settings spec rev 11 §4.3a on the claim path: MaterializeHeldUnder stages
77// only a build a promotion record or a ratchet-admitted pointer authorizes,
78// bound to that digest. A build nothing authorizes, or a replayed record over
79// other bytes under the same name, is refused before an artifact byte is read
80// or written: nothing staged, no selection entered, live/<host> untouched.
81func TestMaterializeHeldUnderStagesOnlyAnAuthorizedBuild(t *testing.T) {
82 t.Run("nothing authorizes it", func(t *testing.T) {
83 ctx, s, opt := heldFixture(t)
84 t.Setenv("CARLOS_HOST_CLAIM", "1")
85 shipRelease(t, s, "bab", "console", "v3", adoptPlatform, []byte("shipped, never promoted"))
86 rec := &heldRecordingStore{Store: s}
87 work := &heldClaimWork{}
88 if _, err := MaterializeHeldUnder(ctx, rec, heldPub, nr(t), opt, heldRoute(), "v3", offGate(t), work, nil); !errors.Is(err, ErrHeldNotAuthorized) {
89 t.Fatalf("MaterializeHeldUnder of a never-promoted build = %v; want ErrHeldNotAuthorized", err)
90 }
91 heldClaimNothingStaged(t, rec, work, opt, "v3", false)
92 })
93 t.Run("a replayed record over other bytes", func(t *testing.T) {
94 ctx, s, opt := heldFixture(t)
95 t.Setenv("CARLOS_HOST_CLAIM", "1")
96 saved, _, err := s.Get(ctx, PromotionRecordKey("bab", "console", "v1"))
97 if err != nil {
98 t.Fatal(err)
99 }
100 if err := DeleteVersion(ctx, s, "bab", "console", "v1"); err != nil {
101 t.Fatal(err)
102 }
103 shipRelease(t, s, "bab", "console", "v1", adoptPlatform, []byte("nobody promoted these"))
104 if _, err := s.Put(ctx, PromotionRecordKey("bab", "console", "v1"), saved, ""); err != nil {
105 t.Fatal(err)
106 }
107 rec := &heldRecordingStore{Store: s}
108 work := &heldClaimWork{}
109 if _, err := MaterializeHeldUnder(ctx, rec, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t), work, nil); !errors.Is(err, ErrManifestDigest) {
110 t.Fatalf("MaterializeHeldUnder under a replayed record = %v; want ErrManifestDigest", err)
111 }
112 heldClaimNothingStaged(t, rec, work, opt, "v1", true)
113 })
114}
115
116// heldClaimNothingStaged fails the test if a claim-path held stage of version
117// read its artifact, staged it, entered the selection or pointed live/<host>.
118// manifestRead says whether the stage was allowed to read the manifest (to
119// check a digest) before refusing.
120func heldClaimNothingStaged(t *testing.T, rec *heldRecordingStore, work *heldClaimWork, opt, version string, manifestRead bool) {
121 t.Helper()
122 rel := ReleasesPrefix("bab", "console") + version + "/"
123 manifest := ManifestKey("bab", "console", version)
124 rec.mu.Lock()
125 for _, c := range rec.calls {
126 key, ok := strings.CutPrefix(c, "get ")
127 if !ok || !strings.HasPrefix(key, rel) {
128 continue
129 }
130 if manifestRead && key == manifest {
131 continue
132 }
133 rec.mu.Unlock()
134 t.Fatalf("the refused held stage read %s", key)
135 }
136 rec.mu.Unlock()
137 if _, err := os.Stat(VersionPath(opt, "bab", version, "console")); err == nil {
138 t.Fatalf("%s was staged", version)
139 }
140 if len(work.ids) != 0 {
141 t.Fatalf("the refused held stage entered the selection under %v", work.ids)
142 }
143 if got, _ := LiveVersion(opt, heldTestHost); got != "" {
144 t.Fatalf("live/<host> names %q", got)
145 }
146}
147
148// TestStageHeldVerifiedStagesWithoutPublishing (moves after claim Plan 12,
149// Task 7; Paul's T7 ruling of 2026-10-12): under the claim, T7 stages the
150// pinned release into the shared version store and verifies it, §4.1's
151// authorization and D3's admit before the stage and D3's bind on the staged
152// bytes after it, and never touches live/<host>: the publication is the
153// landing's, at configured, under its held-release selection.
154func TestStageHeldVerifiedStagesWithoutPublishing(t *testing.T) {
155 t.Run("staged, live untouched", func(t *testing.T) {
156 ctx, s, opt := heldFixture(t)
157 t.Setenv("CARLOS_HOST_CLAIM", "1")
158 m, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t))
159 if err != nil {
160 t.Fatal(err)
161 }
162 if m == nil || m.Version != "v1" {
163 t.Fatalf("manifest %+v", m)
164 }
165 if _, err := os.Stat(VersionPath(opt, "bab", "v1", "console")); err != nil {
166 t.Fatalf("v1 was not staged: %v", err)
167 }
168 if _, err := os.Lstat(LiveLink(opt, heldTestHost)); !os.IsNotExist(err) {
169 t.Fatalf("live/<host> was written: %v", err)
170 }
171 // Again: the stage is idempotent, and still publishes nothing.
172 if _, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t)); err != nil {
173 t.Fatal(err)
174 }
175 if _, err := os.Lstat(LiveLink(opt, heldTestHost)); !os.IsNotExist(err) {
176 t.Fatalf("live/<host> was written: %v", err)
177 }
178 })
179 t.Run("proven, live left on the earlier build", func(t *testing.T) {
180 e := newHeldProofEnv(t)
181 t.Setenv("CARLOS_HOST_CLAIM", "1")
182 e.require(t, "console")
183 e.prove(t, e.pinnedSHA)
184 if _, err := StageHeldVerified(e.ctx, e.store, heldPub, nr(t), e.opt, heldRoute(), "v1", e.gate); err != nil {
185 t.Fatal(err)
186 }
187 if _, err := os.Stat(VersionPath(e.opt, "bab", "v1", "console")); err != nil || e.live() != "v0" {
188 t.Fatalf("staged %v, live %q; want v1 staged and v0 live", err, e.live())
189 }
190 })
191 t.Run("not proven", func(t *testing.T) {
192 e := newHeldProofEnv(t)
193 t.Setenv("CARLOS_HOST_CLAIM", "1")
194 e.require(t, "console")
195 if _, err := StageHeldVerified(e.ctx, e.store, heldPub, nr(t), e.opt, heldRoute(), "v1", e.gate); !errors.Is(err, ErrHeldNotProven) {
196 t.Fatalf("an unproven release: %v", err)
197 }
198 if _, err := os.Stat(VersionPath(e.opt, "bab", "v1", "console")); err == nil || e.live() != "v0" {
199 t.Fatalf("an unproven release was staged (%v) or live moved (%q)", err, e.live())
200 }
201 })
202 t.Run("the staged bytes are not the proven ones", func(t *testing.T) {
203 e := newHeldProofEnv(t)
204 t.Setenv("CARLOS_HOST_CLAIM", "1")
205 e.require(t, "console")
206 e.prove(t, e.pinnedSHA)
207 stale := []byte("not the shipped bytes")
208 down := e.staleCopyDown(t, stale)
209 if _, err := StageHeldVerified(e.ctx, down, heldPub, nr(t), e.opt, heldRoute(), "v1", e.gate); !errors.Is(err, ErrHeldNotProven) || e.live() != "v0" {
210 t.Fatalf("other staged bytes: %v, live %q", err, e.live())
211 }
212 r, _ := ReadRefusal(HeldRefusalBase(e.bake), heldTestHost)
213 if r == nil || r.Kind != RefusalNotProven || r.MoveOp != heldRoute().HoldOp || !strings.Contains(r.Reason, etagOf(stale)) {
214 t.Fatalf("refusal = %+v, want the move's not-proven record naming the staged hash", r)
215 }
216 })
217 t.Run("not authorized", func(t *testing.T) {
218 ctx, s, opt := heldFixture(t)
219 t.Setenv("CARLOS_HOST_CLAIM", "1")
220 shipRelease(t, s, "bab", "console", "v3", adoptPlatform, []byte("shipped, never promoted"))
221 if _, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, heldRoute(), "v3", offGate(t)); !errors.Is(err, ErrHeldNotAuthorized) {
222 t.Fatalf("a never-promoted build: %v", err)
223 }
224 if _, err := os.Stat(VersionPath(opt, "bab", "v3", "console")); err == nil {
225 t.Fatal("an unauthorized build was staged")
226 }
227 })
228 t.Run("blocked", func(t *testing.T) {
229 ctx, s, opt := heldFixture(t)
230 t.Setenv("CARLOS_HOST_CLAIM", "1")
231 orig := materializeHeldStage
232 t.Cleanup(func() { materializeHeldStage = orig })
233 materializeHeldStage = func(context.Context, Store, string, string, string, string, string, string) (*Manifest, error) {
234 return nil, &os.PathError{Op: "mkdir", Path: opt, Err: syscall.EROFS}
235 }
236 if _, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t)); !errors.Is(err, ErrStageBlocked) {
237 t.Fatalf("a read-only version store: %v", err)
238 }
239 })
240 t.Run("refusals", func(t *testing.T) {
241 ctx, s, opt := heldFixture(t)
242 if _, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", nil); !errors.Is(err, errHeldNoProofGate) {
243 t.Fatalf("no gate: %v", err)
244 }
245 noOp := heldRoute()
246 noOp.HoldOp = ""
247 if _, err := StageHeldVerified(ctx, s, heldPub, nr(t), opt, noOp, "v1", offGate(t)); err == nil {
248 t.Fatal("a row no move holds was staged")
249 }
250 if _, err := os.Stat(VersionPath(opt, "bab", "v1", "console")); err == nil {
251 t.Fatal("a refused stage staged v1")
252 }
253 })
254}
255
256// TestVerifyHeldLiveReadsUnderTheClaim (moves Task 7): the configured step's
257// reading of a release it published under the held-release selection is
258// VerifyHeldLive's, which writes nothing, so CARLOS_HOST_CLAIM does not
259// refuse it. A link on another version is still ErrHeldNotLive.
260func TestVerifyHeldLiveReadsUnderTheClaim(t *testing.T) {
261 ctx, s, opt := heldFixture(t)
262 t.Setenv("CARLOS_HOST_CLAIM", "1")
263 lock := func(read func() error) error { return read() }
264 if err := VerifyHeldLive(ctx, s, opt, heldRoute(), "v1", offGate(t), lock); !errors.Is(err, ErrHeldNotLive) {
265 t.Fatalf("nothing published: %v, want ErrHeldNotLive", err)
266 }
267 if _, err := MaterializeHeldUnder(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t), &heldClaimWork{}, nil); err != nil {
268 t.Fatal(err)
269 }
270 if err := VerifyHeldLive(ctx, s, opt, heldRoute(), "v1", offGate(t), lock); err != nil {
271 t.Fatalf("published under the selection: %v", err)
272 }
273}
274