carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package historyreadback
2
3import (
4 "context"
5 "crypto/ed25519"
6 "encoding/json"
7 "errors"
8 "os"
9 "os/exec"
10 "path/filepath"
11 "strings"
12 "testing"
13
14 "github.com/carlosframework/platform/internal/hostactors"
15 "github.com/carlosframework/platform/internal/release"
16)
17
18type crashStore struct {
19 release.Store
20 boundary string
21}
22
23func (s crashStore) Put(ctx context.Context, key string, raw []byte, tag string) (string, error) {
24 if strings.HasSuffix(key, "/receipt.json") && s.boundary == "before" {
25 os.Exit(75)
26 }
27 out, err := s.Store.Put(ctx, key, raw, tag)
28 if strings.HasSuffix(key, "/receipt.json") && s.boundary == "after" && err == nil {
29 os.Exit(75)
30 }
31 return out, err
32}
33
34func TestPlan14HistoryReadbackProcessCrash(t *testing.T) {
35 if boundary := os.Getenv("PLAN14_READBACK_CRASH"); boundary != "" {
36 dir := os.Getenv("PLAN14_READBACK_DIR")
37 raw, err := os.ReadFile(filepath.Join(dir, "fixture.json"))
38 if err != nil {
39 t.Fatal(err)
40 }
41 var saved struct {
42 Request Request
43 Status release.HistoryMigrationStatus
44 Root ed25519.PublicKey
45 Key ed25519.PrivateKey
46 }
47 if err := json.Unmarshal(raw, &saved); err != nil {
48 t.Fatal(err)
49 }
50 store := release.NewDirStore(filepath.Join(dir, "store"))
51 d := hostactors.Directory{Store: crashStore{Store: store, boundary: boundary}, PublicKey: saved.Root, Bucket: saved.Request.Scope.Bucket}
52 if err := Respond(t.Context(), d, saved.Request.Scope, saved.Key, saved.Request.IssuedAt, func(context.Context) (release.HistoryMigrationStatus, error) { return saved.Status, nil }); err != nil {
53 t.Fatal(err)
54 }
55 t.Fatal("missed publication crash")
56 }
57 for _, boundary := range []string{"before", "after"} {
58 t.Run(boundary, func(t *testing.T) {
59 f := newFixture(t)
60 dir := t.TempDir()
61 target := release.NewDirStore(filepath.Join(dir, "store"))
62 roster, _, err := f.directory.Store.Get(t.Context(), hostactors.Key)
63 if err != nil {
64 t.Fatal(err)
65 }
66 if _, err := target.Put(t.Context(), hostactors.Key, roster, ""); err != nil {
67 t.Fatal(err)
68 }
69 f.directory.Store = target
70 q, err := RequestStatus(t.Context(), f.directory, f.signer, f.scope, f.now)
71 if err != nil {
72 t.Fatal(err)
73 }
74 saved := struct {
75 Request Request
76 Status release.HistoryMigrationStatus
77 Root ed25519.PublicKey
78 Key ed25519.PrivateKey
79 }{q, f.status, f.directory.PublicKey, f.key}
80 raw, err := json.Marshal(saved)
81 if err != nil {
82 t.Fatal(err)
83 }
84 if err := os.WriteFile(filepath.Join(dir, "fixture.json"), raw, 0600); err != nil {
85 t.Fatal(err)
86 }
87 cmd := exec.CommandContext(t.Context(), os.Args[0], "-test.run=^TestPlan14HistoryReadbackProcessCrash$", "-test.count=1")
88 cmd.Dir = t.TempDir()
89 cmd.Env = append(os.Environ(), "PLAN14_READBACK_CRASH="+boundary, "PLAN14_READBACK_DIR="+dir)
90 out, err := cmd.CombinedOutput()
91 var exit *exec.ExitError
92 if !errors.As(err, &exit) || exit.ExitCode() != 75 {
93 t.Fatalf("child: %v\n%s", err, out)
94 }
95 _, err = ReadStatus(t.Context(), f.directory, q, f.now)
96 if boundary == "before" && !errors.Is(err, os.ErrNotExist) {
97 t.Fatalf("crash before publication: %v", err)
98 }
99 if boundary == "after" && err != nil {
100 t.Fatal(err)
101 }
102 if err := Respond(t.Context(), f.directory, f.scope, f.key, f.now, f.snapshot); err != nil {
103 t.Fatal(err)
104 }
105 if _, err := ReadStatus(t.Context(), f.directory, q, f.now); err != nil {
106 t.Fatal(err)
107 }
108 next, err := RequestStatus(t.Context(), f.directory, f.signer, f.scope, f.now)
109 if err != nil {
110 t.Fatal(err)
111 }
112 if _, err := ReadStatus(t.Context(), f.directory, next, f.now); err == nil {
113 t.Fatal("precrash receipt completed a new challenge")
114 }
115 })
116 }
117}
118