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 "bytes"
5 "context"
6 "errors"
7 "io"
8 "os"
9 "runtime"
10 "strings"
11 "testing"
12)
13
14func TestDirStorePutFromMatchesPutSemantics(t *testing.T) {
15 ctx := context.Background()
16 d := NewDirStore(t.TempDir())
17 var _ PutStreamer = d
18 etag, err := d.PutFrom(ctx, "a/b.bin", strings.NewReader("hello"), 5, "")
19 if err != nil {
20 t.Fatal(err)
21 }
22 got, getEtag, err := d.Get(ctx, "a/b.bin")
23 if err != nil || string(got) != "hello" || getEtag != etag {
24 t.Fatalf("Get = (%q, %q, %v), want hello with etag %q", got, getEtag, err, etag)
25 }
26 if _, err := d.PutFrom(ctx, "a/b.bin", strings.NewReader("again"), 5, ""); !errors.Is(err, ErrCASConflict) {
27 t.Fatalf("create-only over an existing key = %v, want ErrCASConflict", err)
28 }
29 if _, err := d.PutFrom(ctx, "a/b.bin", strings.NewReader("next!"), 5, etag); err != nil {
30 t.Fatalf("replace with the right etag: %v", err)
31 }
32 if _, err := d.PutFrom(ctx, "a/b.bin", strings.NewReader("short"), 9, ""); err == nil {
33 t.Fatal("a body shorter than size must fail")
34 }
35}
36
37func TestPrefixStorePutFromRebases(t *testing.T) {
38 ctx := context.Background()
39 d := NewDirStore(t.TempDir())
40 p := NewPrefixStore(d, "fleets/x/")
41 ps, ok := p.(PutStreamer)
42 if !ok {
43 t.Fatal("prefixStore must forward PutFrom (codex R1-8)")
44 }
45 if _, err := ps.PutFrom(ctx, "k", strings.NewReader("v"), 1, ""); err != nil {
46 t.Fatal(err)
47 }
48 if b, _, err := d.Get(ctx, "fleets/x/k"); err != nil || string(b) != "v" {
49 t.Fatalf("rebased object = (%q, %v)", b, err)
50 }
51}
52
53type putOnlyStore struct{ Store }
54
55func TestPutObjectFromFallsBackOnlyForSmallBodies(t *testing.T) {
56 ctx := context.Background()
57 s := putOnlyStore{NewDirStore(t.TempDir())}
58 if _, err := PutObjectFrom(ctx, s, "small", bytes.NewReader([]byte("abc")), 3, ""); err != nil {
59 t.Fatalf("small fallback: %v", err)
60 }
61 big := io.NewSectionReader(zeroReaderAt{}, 0, PutFromFallbackMax+1)
62 if _, err := PutObjectFrom(ctx, s, "big", big, PutFromFallbackMax+1, ""); !errors.Is(err, ErrTooLargeForPut) {
63 t.Fatalf("big fallback = %v, want ErrTooLargeForPut", err)
64 }
65}
66
67type zeroReaderAt struct{}
68
69func (zeroReaderAt) ReadAt(p []byte, _ int64) (int, error) {
70 for i := range p {
71 p[i] = 0
72 }
73 return len(p), nil
74}
75
76// Put's CAS gate must not read the existing object into memory: the feed
77// status write runs Put over whatever sits at the key, and an oversized
78// object there would otherwise be loaded whole (R2-1). A sparse 256 MiB
79// file stands in for it; ReadFile would allocate all of it.
80func TestDirStorePutGateDoesNotLoadExistingObject(t *testing.T) {
81 ctx := context.Background()
82 root := t.TempDir()
83 d := NewDirStore(root)
84 if _, err := d.Put(ctx, "big.json", []byte("x"), ""); err != nil {
85 t.Fatal(err)
86 }
87 p, err := d.path("big.json")
88 if err != nil {
89 t.Fatal(err)
90 }
91 const size = 256 << 20
92 if err := os.Truncate(p, size); err != nil {
93 t.Fatal(err)
94 }
95 var before, after runtime.MemStats
96 runtime.ReadMemStats(&before)
97 if _, err := d.Put(ctx, "big.json", []byte("y"), ""); !errors.Is(err, ErrCASConflict) {
98 t.Fatalf("create-only over an existing key = %v, want ErrCASConflict", err)
99 }
100 if _, err := d.Put(ctx, "big.json", []byte("y"), "not-the-etag"); !errors.Is(err, ErrCASConflict) {
101 t.Fatalf("replace with a stale etag = %v, want ErrCASConflict", err)
102 }
103 runtime.ReadMemStats(&after)
104 if grew := after.TotalAlloc - before.TotalAlloc; grew > 4<<20 {
105 t.Fatalf("Put's gate allocated %d bytes over a %d-byte object; it must stream", grew, size)
106 }
107 cur, err := hashFile(p)
108 if err != nil {
109 t.Fatal(err)
110 }
111 if _, err := d.Put(ctx, "big.json", []byte("small"), cur); err != nil {
112 t.Fatalf("replace with the right etag: %v", err)
113 }
114 if got, _, err := d.Get(ctx, "big.json"); err != nil || string(got) != "small" {
115 t.Fatalf("Get = (%q, %v), want small", got, err)
116 }
117}
118