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  }