carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package hostclaim
2
3import (
4 "bytes"
5 "crypto/ed25519"
6 "encoding/json"
7 "fmt"
8 "github.com/carlosframework/platform/internal/release"
9 "testing"
10)
11
12func TestPlan10MoveLocalDischargeCompletesDiscovery(t *testing.T) {
13 for _, otherChores := range []bool{false, true} {
14 t.Run(fmt.Sprint(otherChores), func(t *testing.T) {
15 ctx := t.Context()
16 s := release.NewDirStore(t.TempDir())
17 pub, priv, err := ed25519.GenerateKey(nil)
18 if err != nil {
19 t.Fatal(err)
20 }
21 obs, err := Read(ctx, s, testHost)
22 if err != nil {
23 t.Fatal(err)
24 }
25 var regions []string
26 boxes := []string{"box-a"}
27 if otherChores {
28 regions = []string{"uk"}
29 boxes = append(boxes, "other-box")
30 }
31 obs, err = Update(ctx, s, obs, Adopt(newHolder(), "serving-box", regions, boxes, "discovery", counter()))
32 if err != nil {
33 t.Fatal(err)
34 }
35 if obs.Claim.Op.Kind != KindServing || obs.Claim.Op.ID != "" {
36 t.Fatal("fixture is not an adopted serving claim")
37 }
38 obs, err = Update(ctx, s, obs, func(c *Claim) error {
39 c.Op = Op{ID: lostOp, Kind: KindLanding, Phase: PhaseCommitted, Exec: "serving-box", Participants: []string{"box-a", "serving-box"}, Journal: map[string]string{"keep": "unchanged"}}
40 return nil
41 })
42 if err != nil {
43 t.Fatal(err)
44 }
45 var ref ObligationRef
46 for _, o := range obs.Claim.Obligations {
47 if o.Kind == "park-local" && o.Box == "box-a" {
48 ref = ObligationRef{ID: o.ID, Op: o.Op, Retirement: o.Retirement}
49 }
50 }
51 effects := LossConsequences{DischargeLocal: []ObligationRef{ref}, DestroyedStorage: []string{ref.Op + "/" + ref.ID + "/" + ref.Retirement}, RecoveryIntent: []byte(`{"kind":"destroyed-local-only"}`)}
52 r := lossForObservation(t, obs, "local_only", 11, "box-a", effects)
53 if _, err := CreateLossRecord(ctx, s, release.RootSigner(priv), r); err != nil {
54 t.Fatal(err)
55 }
56 got, err := DeclareLost(ctx, s, pub, obs, r.ID, testLossOwner{mutate: true})
57 if err != nil {
58 t.Fatal(err)
59 }
60 want, err := cloneClaim(obs.Claim)
61 if err != nil {
62 t.Fatal(err)
63 }
64 remaining := make([]Obligation, 0, len(want.Obligations)-1)
65 for _, o := range want.Obligations {
66 if o.ID != ref.ID {
67 remaining = append(remaining, o)
68 }
69 }
70 want.Obligations = remaining
71 if !otherChores {
72 want.Touched.Complete, want.Touched.DiscoveryOp = true, ""
73 }
74 want.Gen = got.Claim.Gen
75 want.Lost, want.LostRevisions, want.LossRefs = []string{"box-a"}, map[string]uint64{"box-a": 11}, []string{r.ID}
76 // Compare serialized claims so nil/empty omitted arrays are equivalent.
77 wantBytes, _ := json.Marshal(want)
78 gotBytes, _ := json.Marshal(got.Claim)
79 if !bytes.Equal(wantBytes, gotBytes) {
80 t.Fatalf("local-only changed unrelated state:\nwant %s\ngot %s", wantBytes, gotBytes)
81 }
82 if retry, err := DeclareLost(ctx, s, pub, got, r.ID, testLossOwner{}); err != nil || retry.ETag != got.ETag {
83 t.Fatal("local-only replay", err)
84 }
85 })
86 }
87}
88
89func TestPlan10MoveLocalDischargeRefusesExecutingBoxAndWidening(t *testing.T) {
90 s := release.NewDirStore(t.TempDir())
91 pub, priv, err := ed25519.GenerateKey(nil)
92 if err != nil {
93 t.Fatal(err)
94 }
95 o, err := Read(t.Context(), s, testHost)
96 if err != nil {
97 t.Fatal(err)
98 }
99 o, err = Update(t.Context(), s, o, Adopt(newHolder(), "box-a", nil, []string{"box-a"}, "discovery", counter()))
100 if err != nil {
101 t.Fatal(err)
102 }
103 o, err = Update(t.Context(), s, o, func(c *Claim) error {
104 c.Op = Op{ID: lostOp, Kind: KindLanding, Phase: PhaseCommitted, Exec: "box-a"}
105 return nil
106 })
107 if err != nil {
108 t.Fatal(err)
109 }
110 chore := o.Claim.Obligations[0]
111 e := LossConsequences{DischargeLocal: []ObligationRef{{ID: chore.ID, Op: chore.Op, Retirement: chore.Retirement}}, DestroyedStorage: []string{chore.Op + "/" + chore.ID + "/" + chore.Retirement}, RecoveryIntent: []byte(`{"kind":"destroyed-local-only"}`)}
112 r := lossForObservation(t, o, "local_exec", 11, "box-a", e)
113 if _, err := CreateLossRecord(t.Context(), s, release.RootSigner(priv), r); err != nil {
114 t.Fatal(err)
115 }
116 if _, err := DeclareLost(t.Context(), s, pub, o, r.ID, testLossOwner{}); err == nil {
117 t.Fatal("local-only discharged executing box")
118 }
119 r.ID = "widened"
120 r.ConfirmedConsequences.RevokeExecution = true
121 if err := r.Validate(); err == nil {
122 t.Fatal("local-only intent accepted widened effects")
123 }
124}
125