| 1 | package blob |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "crypto/sha256" |
| 6 | "encoding/hex" |
| 7 | "net/http" |
| 8 | "net/http/httptest" |
| 9 | "strings" |
| 10 | "testing" |
| 11 | ) |
| 12 | |
| 13 | func TestUploadHeadAcceptsEmptyFileAtFullQuota(t *testing.T) { |
| 14 | s := testService(t) |
| 15 | s.config.Limits = func() Limits { return Limits{MaxFileBytes: 2, UserStorageBytes: 2} } |
| 16 | if _, err := s.Put(context.Background(), PutOptions{Reader: strings.NewReader("ab" ), Type: "text/plain" , Uploader: testUploader}); err != nil { |
| 17 | t.Fatal(err) |
| 18 | } |
| 19 | digest := sha256.Sum256(nil) |
| 20 | hash := hex.EncodeToString(digest[:]) |
| 21 | for _, phase := range []string{"before upload" , "after upload" } { |
| 22 | req := httptest.NewRequest(http.MethodHead, "https://relay.test/upload" , nil) |
| 23 | req.Header.Set("X-SHA-256" , hash) |
| 24 | req.Header.Set("X-Content-Length" , "0" ) |
| 25 | req.Header.Set("X-Content-Type" , "application/octet-stream" ) |
| 26 | response := record(s.Handler(), req) |
| 27 | if response.Code != http.StatusOK { |
| 28 | t.Fatalf("%s: empty preflight = %d %s" , phase, response.Code, response.Body.String()) |
| 29 | } |
| 30 | if phase == "before upload" { |
| 31 | upload := record(s.Handler(), httptest.NewRequest(http.MethodPut, "https://relay.test/" +hash, http.NoBody)) |
| 32 | if upload.Code != http.StatusCreated { |
| 33 | t.Fatalf("empty PUT = %d %s" , upload.Code, upload.Body.String()) |
| 34 | } |
| 35 | } |
| 36 | } |
| 37 | stored, file, err := s.Get(context.Background(), hash) |
| 38 | if err != nil { |
| 39 | t.Fatal(err) |
| 40 | } |
| 41 | defer file.Close() |
| 42 | if stored.Size != 0 { |
| 43 | t.Fatalf("empty stored blob = %+v, %v" , stored, err) |
| 44 | } |
| 45 | usage, err := s.quotaUsage(context.Background(), testUploader) |
| 46 | if err != nil || usage != 2 { |
| 47 | t.Fatalf("empty file changed usage: %d, %v" , usage, err) |
| 48 | } |
| 49 | } |
| 50 | |
| 51 | func TestUploadHeadRequiresNonnegativeLength(t *testing.T) { |
| 52 | for _, tc := range []struct { |
| 53 | value string |
| 54 | code int |
| 55 | }{ |
| 56 | {"" , http.StatusLengthRequired}, {"-1" , http.StatusBadRequest}, |
| 57 | {"no" , http.StatusBadRequest}, {"9223372036854775808" , http.StatusBadRequest}, |
| 58 | {"0" , http.StatusOK}, {"1" , http.StatusOK}, |
| 59 | } { |
| 60 | t.Run(tc.value, func(t *testing.T) { |
| 61 | s := testService(t) |
| 62 | req := httptest.NewRequest(http.MethodHead, "https://relay.test/upload" , nil) |
| 63 | req.Header.Set("X-SHA-256" , strings.Repeat("a" , 64)) |
| 64 | req.Header.Set("X-Content-Type" , "text/plain" ) |
| 65 | req.Header.Set("X-Content-Length" , tc.value) |
| 66 | if response := record(s.Handler(), req); response.Code != tc.code { |
| 67 | t.Fatalf("length %q = %d, want %d" , tc.value, response.Code, tc.code) |
| 68 | } |
| 69 | }) |
| 70 | } |
| 71 | } |