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 "crypto/sha256"
6 "encoding/hex"
7 "errors"
8 "os"
9 "path/filepath"
10 "strings"
11 "testing"
12)
13
14func shaOf(b []byte) string { s := sha256.Sum256(b); return hex.EncodeToString(s[:]) }
15
16func TestFetchToFileStreamsAndVerifies(t *testing.T) {
17 ctx := context.Background()
18 body := []byte(strings.Repeat("carlos", 10000))
19 for name, s := range map[string]Store{
20 "dir": NewDirStore(t.TempDir()),
21 "prefix": NewPrefixStore(NewDirStore(t.TempDir()), "fleets/pi/"),
22 } {
23 t.Run(name, func(t *testing.T) {
24 if _, err := s.Put(ctx, "bootstrap/router/v1/carlos-linux-arm64", body, ""); err != nil {
25 t.Fatal(err)
26 }
27 dst := filepath.Join(t.TempDir(), "carlos")
28 if err := FetchToFile(ctx, s, "bootstrap/router/v1/carlos-linux-arm64", dst, int64(len(body)), shaOf(body)); err != nil {
29 t.Fatal(err)
30 }
31 got, _ := os.ReadFile(dst)
32 if string(got) != string(body) {
33 t.Fatal("content differs")
34 }
35 if _, err := os.Stat(dst + ".tmp"); !errors.Is(err, os.ErrNotExist) {
36 t.Fatal("temp file left behind")
37 }
38 // One byte over the limit is refused, and nothing is left on disk.
39 dst2 := filepath.Join(t.TempDir(), "carlos")
40 if err := FetchToFile(ctx, s, "bootstrap/router/v1/carlos-linux-arm64", dst2, int64(len(body))-1, shaOf(body)); !errors.Is(err, ErrTooLarge) {
41 t.Fatalf("over-limit err = %v", err)
42 }
43 if _, err := os.Stat(dst2); !errors.Is(err, os.ErrNotExist) {
44 t.Fatal("over-limit fetch left a file")
45 }
46 // A sha mismatch is refused and leaves nothing.
47 dst3 := filepath.Join(t.TempDir(), "carlos")
48 err := FetchToFile(ctx, s, "bootstrap/router/v1/carlos-linux-arm64", dst3, int64(len(body)), strings.Repeat("0", 64))
49 if err == nil || !strings.Contains(err.Error(), "sha256") {
50 t.Fatalf("mismatch err = %v", err)
51 }
52 if _, err := os.Stat(dst3); !errors.Is(err, os.ErrNotExist) {
53 t.Fatal("mismatch fetch left a file")
54 }
55 // Missing key is ErrNotExist.
56 if err := FetchToFile(ctx, s, "bootstrap/router/v9/carlos-linux-arm64", dst3, 10, shaOf(nil)); !errors.Is(err, os.ErrNotExist) {
57 t.Fatalf("missing err = %v", err)
58 }
59 })
60 }
61}
62
63func TestPrefixStoreOpenRebasesTheKey(t *testing.T) {
64 // prefixStore embeds the Store INTERFACE, so a promoted Open would forward
65 // the raw key and silently escape the prefix. It must be written out.
66 ctx := context.Background()
67 inner := NewDirStore(t.TempDir())
68 if _, err := inner.Put(ctx, "fleets/pi/x", []byte("in"), ""); err != nil {
69 t.Fatal(err)
70 }
71 if _, err := inner.Put(ctx, "x", []byte("OUT"), ""); err != nil {
72 t.Fatal(err)
73 }
74 rc, _, err := OpenObject(ctx, NewPrefixStore(inner, "fleets/pi/"), "x")
75 if err != nil {
76 t.Fatal(err)
77 }
78 defer rc.Close()
79 b := make([]byte, 8)
80 n, _ := rc.Read(b)
81 if string(b[:n]) != "in" {
82 t.Fatalf("read %q through the prefix, want \"in\"", b[:n])
83 }
84}
85
86func TestOpenObjectFallsBackToGet(t *testing.T) {
87 // A Store that is not an Opener still works: OpenObject buffers via Get.
88 s := getOnly{NewDirStore(t.TempDir())}
89 if _, err := s.Put(context.Background(), "k", []byte("v"), ""); err != nil {
90 t.Fatal(err)
91 }
92 rc, _, err := OpenObject(context.Background(), s, "k")
93 if err != nil {
94 t.Fatal(err)
95 }
96 b := make([]byte, 4)
97 n, _ := rc.Read(b)
98 if string(b[:n]) != "v" {
99 t.Fatalf("got %q", b[:n])
100 }
101}
102
103type getOnly struct{ *DirStore }
104
105func (getOnly) Open() {} // different signature: getOnly is NOT an Opener
106