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 "os"
5 "path/filepath"
6 "testing"
7 "time"
8)
9
10func TestPlan14HistoryMigrationStatus(t *testing.T) {
11 for _, fault := range []string{"valid", "missing", "changed source", "malformed", "unsynced"} {
12 t.Run(fault, func(t *testing.T) {
13 h, s, b := rootHistoryFixture(t)
14 if fault == "missing" {
15 os.Remove(filepath.Join(s.dir, historyKey(b)))
16 }
17 if fault == "malformed" {
18 s.Write(historyKey(b), []byte("{"))
19 }
20 if fault == "unsynced" {
21 s.fail = 1
22 }
23 calls := 0
24 view, err := h.MigrationStatus(b, func(HistoryBinding) (string, error) {
25 calls++
26 if fault == "changed source" && calls > 1 {
27 return "new", nil
28 }
29 return "canonical", nil
30 })
31 if fault == "valid" || fault == "missing" {
32 if err != nil || view.Identity == "" || view.Revision != 1 || len(view.Hosts) != 1 || view.Hosts[0].Source != "canonical" {
33 t.Fatalf("view=%+v err=%v", view, err)
34 }
35 if fault == "missing" && (view.Hosts[0].Revision != 0 || view.Hosts[0].Programs.Complete || view.Hosts[0].RefusalStatus.Complete) {
36 t.Fatal("absence became completeness")
37 }
38 if fault == "valid" && view.Hosts[0].Revision != 1 {
39 t.Fatal("wrong category revision")
40 }
41 } else if err == nil {
42 t.Fatalf("%s projected usable status: %+v", fault, view)
43 }
44 })
45 }
46}
47
48func TestPlan14HistoryPendingStatus(t *testing.T) {
49 h, s, b, pub, wire, now := reconstructionCrashFixture(t)
50 h.Store = &reconstructionCrashStore{healthyRetryStore: s, stage: "intent", fail: true}
51 resolve := func(HistoryBinding) (string, error) { return "canonical", nil }
52 if err := h.Reconstruct(pub, wire, b.Account, b.App, now, resolve); err == nil {
53 t.Fatal("no crash")
54 }
55 h.Store = s
56 view, err := h.MigrationStatus(b, resolve)
57 if err != nil || !view.Pending || view.PendingDigest == "" || view.PendingGrant == nil || view.PendingGrant.Operation != "crash" {
58 t.Fatalf("pending status: %+v %v", view, err)
59 }
60 if _, err := h.Refusals(b); err == nil {
61 t.Fatal("status removed pending barrier")
62 }
63 projection := &healthyRetryStore{dir: t.TempDir(), fault: os.ErrPermission}
64 if err := h.ProjectMigrationStatus(projection, b, resolve); err != nil {
65 t.Fatal(err)
66 }
67 raw, err := projection.Read(HistoryStatusKey(b.Account, b.App))
68 if err != nil || len(raw) == 0 {
69 t.Fatalf("projection missing: %v", err)
70 }
71 projection.Write(HistoryStatusKey(b.Account, b.App), []byte(`{"complete":true}`))
72 if _, err := h.Refusals(b); err == nil {
73 t.Fatal("projection supplied authority")
74 }
75}
76
77func TestPlan14HistoryStatusReplacementDelivery(t *testing.T) {
78 h, s, b, pub, wire, now := reconstructionCrashFixture(t)
79 // Retain a real interrupted intent, then change its canonical source.
80 h.Store = &reconstructionCrashStore{healthyRetryStore: s, stage: "intent", fail: true}
81 if err := h.Reconstruct(pub, wire, b.Account, b.App, now, func(HistoryBinding) (string, error) { return "canonical", nil }); err == nil {
82 t.Fatal("no interruption")
83 }
84 h.Store = s
85 resolve := func(HistoryBinding) (string, error) { return "new-source", nil }
86 view, err := h.MigrationStatus(b, resolve)
87 if err != nil {
88 t.Fatal(err)
89 }
90 g := *view.PendingGrant
91 g.Operation = "replacement-from-status"
92 g.Replaces = view.PendingDigest
93 g.AppRevision, g.BakeRevision = view.AppRevision, view.BakeRevision
94 g.Hosts = nil
95 for _, host := range view.Hosts {
96 g.Hosts = append(g.Hosts, host.HistoryReconstructionHost)
97 }
98 // The deployment signer signs the exact revisions and sources from status.
99 priv, pub := testKeys(t)
100 st := NewDirStore(t.TempDir())
101 if err := PublishHistoryReconstruction(t.Context(), st, RootSigner(priv), g); err != nil {
102 t.Fatal(err)
103 }
104 if err := h.ReceiveReconstruction(t.Context(), st, pub, b, now, resolve); err != nil {
105 t.Fatal(err)
106 }
107 after, err := h.MigrationStatus(b, resolve)
108 if err != nil || after.Pending || after.Revision != view.Revision+1 || !after.AppComplete || after.BakeComplete == 0 {
109 t.Fatalf("replacement not complete: %+v %v", after, err)
110 }
111 if after.Clock.Holds[channelClockKey(b.Account, b.App, "stable")].Window != time.Hour {
112 t.Fatal("replacement lost intended hold")
113 }
114 g.Operation = "stale-revision"
115 stale, err := SignRootEnvelope(RootSigner(priv), g)
116 if err != nil {
117 t.Fatal(err)
118 }
119 if err := h.Reconstruct(pub, stale, b.Account, b.App, now, resolve); err == nil {
120 t.Fatal("stale projected revision accepted")
121 }
122}
123