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 "errors"
6 "net/http"
7 "net/http/httptest"
8 "os"
9 "testing"
10
11 "github.com/aws/aws-sdk-go-v2/aws"
12 "github.com/aws/aws-sdk-go-v2/credentials"
13 awss3 "github.com/aws/aws-sdk-go-v2/service/s3"
14)
15
16func TestGetIfChangedDirStore(t *testing.T) {
17 t.Parallel()
18 ctx := context.Background()
19 d := NewDirStore(t.TempDir())
20 const key = "apps/bab/shop/instances/h.json"
21 if _, err := d.Put(ctx, key, []byte("one"), ""); err != nil {
22 t.Fatal(err)
23 }
24 var cg ConditionalGetter = d
25 body, e, changed, err := cg.GetIfChanged(ctx, key, "")
26 if err != nil || !changed || string(body) != "one" || e == "" {
27 t.Fatalf("empty etag: %q %q %v %v", body, e, changed, err)
28 }
29 _, ge, err := d.Get(ctx, key)
30 if err != nil || ge != e {
31 t.Fatalf("GetIfChanged etag %q, Get etag %q (%v)", e, ge, err)
32 }
33 body, e2, changed, err := cg.GetIfChanged(ctx, key, e)
34 if err != nil || changed || body != nil || e2 != e {
35 t.Fatalf("unchanged: %q %q %v %v", body, e2, changed, err)
36 }
37 if _, err := d.Put(ctx, key, []byte("two"), e); err != nil {
38 t.Fatal(err)
39 }
40 body, e3, changed, err := cg.GetIfChanged(ctx, key, e)
41 if err != nil || !changed || string(body) != "two" || e3 == e {
42 t.Fatalf("rewritten: %q %q %v %v", body, e3, changed, err)
43 }
44}
45
46func TestGetIfChangedAbsent(t *testing.T) {
47 t.Parallel()
48 ctx := context.Background()
49 d := NewDirStore(t.TempDir())
50 for _, etag := range []string{"", "some-etag"} {
51 _, _, changed, err := d.GetIfChanged(ctx, "apps/bab/shop/absent.json", etag)
52 if !errors.Is(err, os.ErrNotExist) || changed {
53 t.Fatalf("etag %q: changed=%v err=%v", etag, changed, err)
54 }
55 }
56}
57
58func TestListETagsMatchesGet(t *testing.T) {
59 t.Parallel()
60 ctx := context.Background()
61 d := NewDirStore(t.TempDir())
62 keys := []string{"apps/bab/shop/b.json", "apps/bab/shop/a.json", "apps/bab/other/c.json", "accounts/bab/x.json"}
63 for i, k := range keys {
64 if _, err := d.Put(ctx, k, []byte{byte('a' + i)}, ""); err != nil {
65 t.Fatal(err)
66 }
67 }
68 var el ETagLister = d
69 got, err := el.ListETags(ctx, "apps/bab/")
70 if err != nil {
71 t.Fatal(err)
72 }
73 want := []string{"apps/bab/other/c.json", "apps/bab/shop/a.json", "apps/bab/shop/b.json"}
74 if len(got) != len(want) {
75 t.Fatalf("listed %v, want keys %v", got, want)
76 }
77 for i, ke := range got {
78 if ke.Key != want[i] {
79 t.Fatalf("entry %d: %q, want %q (sorted by key)", i, ke.Key, want[i])
80 }
81 _, e, err := d.Get(ctx, ke.Key)
82 if err != nil || e != ke.ETag {
83 t.Fatalf("%s: listed etag %q, Get etag %q (%v)", ke.Key, ke.ETag, e, err)
84 }
85 }
86 none, err := d.ListETags(ctx, "nothing/")
87 if err != nil || len(none) != 0 {
88 t.Fatalf("empty prefix: %v %v", none, err)
89 }
90}
91
92func TestPrefixStoreForwardsReadExtensions(t *testing.T) {
93 t.Parallel()
94 ctx := context.Background()
95 inner := NewDirStore(t.TempDir())
96 if _, err := inner.Put(ctx, "fleets/f/apps/bab/shop/x.json", []byte("x"), ""); err != nil {
97 t.Fatal(err)
98 }
99 if _, err := inner.Put(ctx, "fleets/g/apps/bab/shop/y.json", []byte("y"), ""); err != nil {
100 t.Fatal(err)
101 }
102 s := NewPrefixStore(inner, "fleets/f/")
103 cg, ok := s.(ConditionalGetter)
104 if !ok {
105 t.Fatal("prefix store does not forward ConditionalGetter")
106 }
107 el, ok := s.(ETagLister)
108 if !ok {
109 t.Fatal("prefix store does not forward ETagLister")
110 }
111 body, e, changed, err := cg.GetIfChanged(ctx, "apps/bab/shop/x.json", "")
112 if err != nil || !changed || string(body) != "x" {
113 t.Fatalf("GetIfChanged: %q %v %v", body, changed, err)
114 }
115 if _, _, changed, err = cg.GetIfChanged(ctx, "apps/bab/shop/x.json", e); err != nil || changed {
116 t.Fatalf("unchanged through prefix: %v %v", changed, err)
117 }
118 if _, _, _, err = cg.GetIfChanged(ctx, "apps/bab/shop/y.json", ""); !errors.Is(err, os.ErrNotExist) {
119 t.Fatalf("other fleet's key must be absent: %v", err)
120 }
121 got, err := el.ListETags(ctx, "apps/")
122 if err != nil || len(got) != 1 || got[0].Key != "apps/bab/shop/x.json" || got[0].ETag != e {
123 t.Fatalf("ListETags: %v %v", got, err)
124 }
125}
126
127// bareStore has none of the optional extensions.
128type bareStore struct{ Store }
129
130func TestPrefixStoreWithoutExtensionsRefuses(t *testing.T) {
131 t.Parallel()
132 ctx := context.Background()
133 s := NewPrefixStore(bareStore{NewDirStore(t.TempDir())}, "fleets/f/")
134 if _, _, _, err := s.(ConditionalGetter).GetIfChanged(ctx, "k", ""); !errors.Is(err, errors.ErrUnsupported) {
135 t.Fatalf("GetIfChanged: %v", err)
136 }
137 if _, err := s.(ETagLister).ListETags(ctx, "k"); !errors.Is(err, errors.ErrUnsupported) {
138 t.Fatalf("ListETags: %v", err)
139 }
140}
141
142// The SDK reports a 304 from GetObject as an error; GetIfChanged must read it
143// as "unchanged". A loopback server stands in for the 304 so the mapping runs
144// here, not only against MinIO in CI.
145func TestS3GetIfChangedMaps304(t *testing.T) {
146 t.Parallel()
147 const etag = `"abc123"`
148 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
149 if r.Header.Get("If-None-Match") == etag {
150 w.WriteHeader(http.StatusNotModified)
151 return
152 }
153 w.Header().Set("ETag", etag)
154 _, _ = w.Write([]byte("body"))
155 }))
156 defer srv.Close()
157 client := awss3.New(awss3.Options{
158 Region: "us-east-1",
159 Credentials: credentials.NewStaticCredentialsProvider("k", "s", ""),
160 BaseEndpoint: aws.String(srv.URL),
161 UsePathStyle: true,
162 Retryer: aws.NopRetryer{},
163 })
164 s := &S3Store{Client: client, Bucket: "b"}
165 ctx := context.Background()
166 body, e, changed, err := s.GetIfChanged(ctx, "k", "")
167 if err != nil || !changed || string(body) != "body" || e != etag {
168 t.Fatalf("empty etag: %q %q %v %v", body, e, changed, err)
169 }
170 body, e, changed, err = s.GetIfChanged(ctx, "k", etag)
171 if err != nil || changed || body != nil || e != etag {
172 t.Fatalf("304: %q %q %v %v", body, e, changed, err)
173 }
174 body, e, changed, err = s.GetIfChanged(ctx, "k", `"other"`)
175 if err != nil || !changed || string(body) != "body" {
176 t.Fatalf("stale etag: %q %q %v %v", body, e, changed, err)
177 }
178}
179