github.com/ethersphere/bee/v2@v2.2.0/pkg/api/bytes_test.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package api_test 6 7 import ( 8 "bytes" 9 "context" 10 "errors" 11 "net/http" 12 "strconv" 13 "testing" 14 15 "github.com/ethersphere/bee/v2/pkg/api" 16 "github.com/ethersphere/bee/v2/pkg/jsonhttp" 17 "github.com/ethersphere/bee/v2/pkg/jsonhttp/jsonhttptest" 18 "github.com/ethersphere/bee/v2/pkg/log" 19 mockbatchstore "github.com/ethersphere/bee/v2/pkg/postage/batchstore/mock" 20 mockpost "github.com/ethersphere/bee/v2/pkg/postage/mock" 21 mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock" 22 "github.com/ethersphere/bee/v2/pkg/swarm" 23 "gitlab.com/nolash/go-mockbytes" 24 ) 25 26 // nolint:paralleltest,tparallel 27 // TestBytes tests that the data upload api responds as expected when uploading, 28 // downloading and requesting a resource that cannot be found. 29 func TestBytes(t *testing.T) { 30 t.Parallel() 31 32 const ( 33 resource = "/bytes" 34 expHash = "29a5fb121ce96194ba8b7b823a1f9c6af87e1791f824940a53b5a7efe3f790d9" 35 ) 36 37 var ( 38 storerMock = mockstorer.New() 39 logger = log.Noop 40 client, _, _, _ = newTestServer(t, testServerOptions{ 41 Storer: storerMock, 42 Logger: logger, 43 Post: mockpost.New(mockpost.WithAcceptAll()), 44 }) 45 ) 46 47 g := mockbytes.New(0, mockbytes.MockTypeStandard).WithModulus(255) 48 content, err := g.SequentialBytes(swarm.ChunkSize * 2) 49 if err != nil { 50 t.Fatal(err) 51 } 52 53 t.Run("upload", func(t *testing.T) { 54 chunkAddr := swarm.MustParseHexAddress(expHash) 55 jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusCreated, 56 jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"), 57 jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), 58 jsonhttptest.WithRequestBody(bytes.NewReader(content)), 59 jsonhttptest.WithExpectedJSONResponse(api.BytesPostResponse{ 60 Reference: chunkAddr, 61 }), 62 ) 63 64 has, err := storerMock.ChunkStore().Has(context.Background(), chunkAddr) 65 if err != nil { 66 t.Fatal(err) 67 } 68 if !has { 69 t.Fatal("storer check root chunk address: have none; want one") 70 } 71 72 refs, err := storerMock.Pins() 73 if err != nil { 74 t.Fatal("unable to get pinned references") 75 } 76 if have, want := len(refs), 0; have != want { 77 t.Fatalf("root pin count mismatch: have %d; want %d", have, want) 78 } 79 }) 80 81 t.Run("upload-with-pinning", func(t *testing.T) { 82 var res api.BytesPostResponse 83 jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusCreated, 84 jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"), 85 jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), 86 jsonhttptest.WithRequestBody(bytes.NewReader(content)), 87 jsonhttptest.WithRequestHeader(api.SwarmPinHeader, "true"), 88 jsonhttptest.WithUnmarshalJSONResponse(&res), 89 ) 90 reference := res.Reference 91 92 has, err := storerMock.ChunkStore().Has(context.Background(), reference) 93 if err != nil { 94 t.Fatal(err) 95 } 96 if !has { 97 t.Fatal("storer check root chunk reference: have none; want one") 98 } 99 100 refs, err := storerMock.Pins() 101 if err != nil { 102 t.Fatal(err) 103 } 104 if have, want := len(refs), 1; have != want { 105 t.Fatalf("root pin count mismatch: have %d; want %d", have, want) 106 } 107 if have, want := refs[0], reference; !have.Equal(want) { 108 t.Fatalf("root pin reference mismatch: have %q; want %q", have, want) 109 } 110 }) 111 112 t.Run("download", func(t *testing.T) { 113 jsonhttptest.Request(t, client, http.MethodGet, resource+"/"+expHash, http.StatusOK, 114 jsonhttptest.WithExpectedContentLength(len(content)), 115 jsonhttptest.WithExpectedResponse(content), 116 ) 117 }) 118 119 t.Run("head", func(t *testing.T) { 120 jsonhttptest.Request(t, client, http.MethodHead, resource+"/"+expHash, http.StatusOK, 121 jsonhttptest.WithExpectedContentLength(len(content)), 122 ) 123 }) 124 t.Run("head with compression", func(t *testing.T) { 125 jsonhttptest.Request(t, client, http.MethodHead, resource+"/"+expHash, http.StatusOK, 126 jsonhttptest.WithRequestHeader(api.AcceptEncodingHeader, "gzip"), 127 jsonhttptest.WithExpectedContentLength(len(content)), 128 ) 129 }) 130 131 t.Run("internal error", func(t *testing.T) { 132 jsonhttptest.Request(t, client, http.MethodGet, resource+"/abcd", http.StatusInternalServerError, 133 jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ 134 Message: "joiner failed", 135 Code: http.StatusInternalServerError, 136 }), 137 ) 138 }) 139 140 t.Run("not found", func(t *testing.T) { 141 jsonhttptest.Request(t, client, http.MethodGet, resource+"/"+swarm.EmptyAddress.String(), http.StatusNotFound) 142 }) 143 } 144 145 // nolint:paralleltest,tparallel 146 func TestBytesInvalidStamp(t *testing.T) { 147 t.Parallel() 148 149 const ( 150 resource = "/bytes" 151 expHash = "29a5fb121ce96194ba8b7b823a1f9c6af87e1791f824940a53b5a7efe3f790d9" 152 ) 153 154 var ( 155 storerMock = mockstorer.New() 156 logger = log.Noop 157 retBool = false 158 retErr error = nil 159 invalidTag uint64 = 100 160 existsFn = func(id []byte) (bool, error) { 161 return retBool, retErr 162 } 163 ) 164 165 g := mockbytes.New(0, mockbytes.MockTypeStandard).WithModulus(255) 166 content, err := g.SequentialBytes(swarm.ChunkSize * 2) 167 if err != nil { 168 t.Fatal(err) 169 } 170 171 t.Run("upload batch not found", func(t *testing.T) { 172 clientBatchNotExists, _, _, _ := newTestServer(t, testServerOptions{ 173 Storer: storerMock, 174 Logger: logger, 175 Post: mockpost.New(), 176 BatchStore: mockbatchstore.New(mockbatchstore.WithExistsFunc(existsFn)), 177 }) 178 chunkAddr := swarm.MustParseHexAddress(expHash) 179 180 jsonhttptest.Request(t, clientBatchNotExists, http.MethodPost, resource, http.StatusNotFound, 181 jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"), 182 jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), 183 jsonhttptest.WithRequestBody(bytes.NewReader(content)), 184 ) 185 186 has, err := storerMock.ChunkStore().Has(context.Background(), chunkAddr) 187 if err != nil { 188 t.Fatal(err) 189 } 190 if has { 191 t.Fatal("storer check root chunk address: have ont; want none") 192 } 193 194 refs, err := storerMock.Pins() 195 if err != nil { 196 t.Fatal("unable to get pinned references") 197 } 198 if have, want := len(refs), 0; have != want { 199 t.Fatalf("root pin count mismatch: have %d; want %d", have, want) 200 } 201 }) 202 203 // throw back an error 204 retErr = errors.New("err happened") 205 206 t.Run("upload batch exists error", func(t *testing.T) { 207 client, _, _, _ := newTestServer(t, testServerOptions{ 208 Storer: storerMock, 209 Logger: logger, 210 Post: mockpost.New(mockpost.WithAcceptAll()), 211 BatchStore: mockbatchstore.New(mockbatchstore.WithExistsFunc(existsFn)), 212 }) 213 214 chunkAddr := swarm.MustParseHexAddress(expHash) 215 jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusBadRequest, 216 jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"), 217 jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), 218 jsonhttptest.WithRequestBody(bytes.NewReader(content)), 219 ) 220 221 has, err := storerMock.ChunkStore().Has(context.Background(), chunkAddr) 222 if err != nil { 223 t.Fatal(err) 224 } 225 if has { 226 t.Fatal("storer check root chunk address: have ont; want none") 227 } 228 }) 229 230 t.Run("upload batch unusable", func(t *testing.T) { 231 clientBatchUnusable, _, _, _ := newTestServer(t, testServerOptions{ 232 Storer: storerMock, 233 Logger: logger, 234 Post: mockpost.New(mockpost.WithAcceptAll()), 235 BatchStore: mockbatchstore.New(), 236 }) 237 238 jsonhttptest.Request(t, clientBatchUnusable, http.MethodPost, resource, http.StatusUnprocessableEntity, 239 jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"), 240 jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), 241 jsonhttptest.WithRequestBody(bytes.NewReader(content)), 242 ) 243 }) 244 245 t.Run("upload invalid tag", func(t *testing.T) { 246 clientInvalidTag, _, _, _ := newTestServer(t, testServerOptions{ 247 Storer: storerMock, 248 Logger: logger, 249 Post: mockpost.New(mockpost.WithAcceptAll()), 250 }) 251 252 jsonhttptest.Request(t, clientInvalidTag, http.MethodPost, resource, http.StatusBadRequest, 253 jsonhttptest.WithRequestHeader(api.SwarmTagHeader, "tag"), 254 jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"), 255 jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), 256 jsonhttptest.WithRequestBody(bytes.NewReader(content)), 257 ) 258 }) 259 260 t.Run("upload tag not found", func(t *testing.T) { 261 clientTagExists, _, _, _ := newTestServer(t, testServerOptions{ 262 Storer: storerMock, 263 Logger: logger, 264 Post: mockpost.New(mockpost.WithAcceptAll()), 265 }) 266 267 jsonhttptest.Request(t, clientTagExists, http.MethodPost, resource, http.StatusNotFound, 268 jsonhttptest.WithRequestHeader(api.SwarmTagHeader, strconv.FormatUint(invalidTag, 10)), 269 jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"), 270 jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), 271 jsonhttptest.WithRequestBody(bytes.NewReader(content)), 272 ) 273 }) 274 275 } 276 277 func TestBytesUploadHandlerInvalidInputs(t *testing.T) { 278 t.Parallel() 279 280 client, _, _, _ := newTestServer(t, testServerOptions{ 281 Storer: mockstorer.New(), 282 }) 283 284 tests := []struct { 285 name string 286 hdrKey string 287 hdrVal string 288 want jsonhttp.StatusResponse 289 }{ 290 { 291 name: "no stamp", 292 hdrKey: api.SwarmTagHeader, 293 hdrVal: strconv.FormatUint(1, 10), 294 want: jsonhttp.StatusResponse{ 295 Code: http.StatusBadRequest, 296 Message: "invalid header params", 297 Reasons: []jsonhttp.Reason{ 298 { 299 Field: "swarm-postage-batch-id", 300 Error: "want required:", 301 }, 302 }, 303 }, 304 }, 305 { 306 name: "invalid stamp", 307 hdrKey: api.SwarmPostageBatchIdHeader, 308 hdrVal: batchOkStr, 309 want: jsonhttp.StatusResponse{ 310 Code: http.StatusNotFound, 311 Message: "batch with id not found", 312 }, 313 }, 314 } 315 316 for _, tc := range tests { 317 tc := tc 318 t.Run(tc.name, func(t *testing.T) { 319 t.Parallel() 320 321 jsonhttptest.Request(t, client, http.MethodPost, "/bytes", tc.want.Code, 322 jsonhttptest.WithRequestHeader(tc.hdrKey, tc.hdrVal), 323 jsonhttptest.WithExpectedJSONResponse(tc.want), 324 ) 325 }) 326 } 327 } 328 329 func TestBytesGetHandlerInvalidInputs(t *testing.T) { 330 t.Parallel() 331 332 client, _, _, _ := newTestServer(t, testServerOptions{}) 333 334 tests := []struct { 335 name string 336 address string 337 want jsonhttp.StatusResponse 338 }{{ 339 name: "address - odd hex string", 340 address: "123", 341 want: jsonhttp.StatusResponse{ 342 Code: http.StatusBadRequest, 343 Message: "invalid path params", 344 Reasons: []jsonhttp.Reason{ 345 { 346 Field: "address", 347 Error: api.ErrHexLength.Error(), 348 }, 349 }, 350 }, 351 }, { 352 name: "address - invalid hex character", 353 address: "123G", 354 want: jsonhttp.StatusResponse{ 355 Code: http.StatusBadRequest, 356 Message: "invalid path params", 357 Reasons: []jsonhttp.Reason{ 358 { 359 Field: "address", 360 Error: api.HexInvalidByteError('G').Error(), 361 }, 362 }, 363 }, 364 }} 365 366 for _, tc := range tests { 367 tc := tc 368 t.Run(tc.name, func(t *testing.T) { 369 t.Parallel() 370 371 jsonhttptest.Request(t, client, http.MethodGet, "/bytes/"+tc.address, tc.want.Code, 372 jsonhttptest.WithExpectedJSONResponse(tc.want), 373 ) 374 }) 375 } 376 } 377 378 // TestDirectUploadBytes tests that the direct upload endpoint give correct error message in dev mode 379 func TestBytesDirectUpload(t *testing.T) { 380 t.Parallel() 381 const ( 382 resource = "/bytes" 383 ) 384 385 var ( 386 storerMock = mockstorer.New() 387 logger = log.Noop 388 client, _, _, _ = newTestServer(t, testServerOptions{ 389 Storer: storerMock, 390 Logger: logger, 391 Post: mockpost.New(mockpost.WithAcceptAll()), 392 BeeMode: api.DevMode, 393 }) 394 ) 395 396 g := mockbytes.New(0, mockbytes.MockTypeStandard).WithModulus(255) 397 content, err := g.SequentialBytes(swarm.ChunkSize * 2) 398 if err != nil { 399 t.Fatal(err) 400 } 401 402 jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusBadRequest, 403 jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "false"), 404 jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), 405 jsonhttptest.WithRequestBody(bytes.NewReader(content)), 406 jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ 407 Message: api.ErrUnsupportedDevNodeOperation.Error(), 408 Code: http.StatusBadRequest, 409 }), 410 ) 411 }