github.com/ethersphere/bee/v2@v2.2.0/pkg/api/pin_test.go (about)

     1  // Copyright 2021 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  	"context"
     9  	"net/http"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/ethersphere/bee/v2/pkg/api"
    14  	"github.com/ethersphere/bee/v2/pkg/jsonhttp"
    15  	"github.com/ethersphere/bee/v2/pkg/jsonhttp/jsonhttptest"
    16  	"github.com/ethersphere/bee/v2/pkg/log"
    17  	mockpost "github.com/ethersphere/bee/v2/pkg/postage/mock"
    18  	storage "github.com/ethersphere/bee/v2/pkg/storage"
    19  	"github.com/ethersphere/bee/v2/pkg/storage/inmemstore"
    20  	storer "github.com/ethersphere/bee/v2/pkg/storer"
    21  	mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock"
    22  	"github.com/ethersphere/bee/v2/pkg/swarm"
    23  )
    24  
    25  func checkPinHandlers(t *testing.T, client *http.Client, rootHash string, createPin bool) {
    26  	t.Helper()
    27  
    28  	const pinsBasePath = "/pins"
    29  
    30  	var (
    31  		pinsReferencePath        = pinsBasePath + "/" + rootHash
    32  		pinsInvalidReferencePath = pinsBasePath + "/" + "838d0a193ecd1152d1bb1432d5ecc02398533b2494889e23b8bd5ace30ac2zzz"
    33  		pinsUnknownReferencePath = pinsBasePath + "/" + "838d0a193ecd1152d1bb1432d5ecc02398533b2494889e23b8bd5ace30ac2ccc"
    34  	)
    35  
    36  	jsonhttptest.Request(t, client, http.MethodGet, pinsInvalidReferencePath, http.StatusBadRequest)
    37  
    38  	jsonhttptest.Request(t, client, http.MethodGet, pinsUnknownReferencePath, http.StatusNotFound,
    39  		jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
    40  			Message: http.StatusText(http.StatusNotFound),
    41  			Code:    http.StatusNotFound,
    42  		}),
    43  	)
    44  
    45  	if createPin {
    46  		jsonhttptest.Request(t, client, http.MethodPost, pinsReferencePath, http.StatusCreated,
    47  			jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
    48  				Message: http.StatusText(http.StatusCreated),
    49  				Code:    http.StatusCreated,
    50  			}),
    51  		)
    52  	}
    53  
    54  	jsonhttptest.Request(t, client, http.MethodGet, pinsReferencePath, http.StatusOK,
    55  		jsonhttptest.WithExpectedJSONResponse(struct {
    56  			Reference swarm.Address `json:"reference"`
    57  		}{
    58  			Reference: swarm.MustParseHexAddress(rootHash),
    59  		}),
    60  	)
    61  
    62  	jsonhttptest.Request(t, client, http.MethodGet, pinsBasePath, http.StatusOK,
    63  		jsonhttptest.WithExpectedJSONResponse(struct {
    64  			References []swarm.Address `json:"references"`
    65  		}{
    66  			References: []swarm.Address{swarm.MustParseHexAddress(rootHash)},
    67  		}),
    68  	)
    69  
    70  	jsonhttptest.Request(t, client, http.MethodDelete, pinsReferencePath, http.StatusOK)
    71  
    72  	jsonhttptest.Request(t, client, http.MethodGet, pinsReferencePath, http.StatusNotFound,
    73  		jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{
    74  			Message: http.StatusText(http.StatusNotFound),
    75  			Code:    http.StatusNotFound,
    76  		}),
    77  	)
    78  }
    79  
    80  // nolint:paralleltest
    81  func TestPinHandlers(t *testing.T) {
    82  	var (
    83  		storerMock      = mockstorer.New()
    84  		client, _, _, _ = newTestServer(t, testServerOptions{
    85  			Storer: storerMock,
    86  			Post:   mockpost.New(mockpost.WithAcceptAll()),
    87  		})
    88  	)
    89  
    90  	t.Run("bytes", func(t *testing.T) {
    91  		const rootHash = "838d0a193ecd1152d1bb1432d5ecc02398533b2494889e23b8bd5ace30ac2aeb"
    92  		jsonhttptest.Request(t, client, http.MethodPost, "/bytes", http.StatusCreated,
    93  			jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"),
    94  			jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr),
    95  			jsonhttptest.WithRequestBody(strings.NewReader("this is a simple text")),
    96  			jsonhttptest.WithExpectedJSONResponse(api.BzzUploadResponse{
    97  				Reference: swarm.MustParseHexAddress(rootHash),
    98  			}),
    99  		)
   100  		checkPinHandlers(t, client, rootHash, true)
   101  	})
   102  
   103  	t.Run("bytes missing", func(t *testing.T) {
   104  		jsonhttptest.Request(t, client, http.MethodPost, "/pins/"+swarm.RandAddress(t).String(), http.StatusNotFound)
   105  	})
   106  
   107  	t.Run("bzz", func(t *testing.T) {
   108  		tarReader := tarFiles(t, []f{{
   109  			data: []byte("<h1>Swarm"),
   110  			name: "index.html",
   111  			dir:  "",
   112  		}})
   113  		rootHash := "9e178dbd1ed4b748379e25144e28dfb29c07a4b5114896ef454480115a56b237"
   114  		jsonhttptest.Request(t, client, http.MethodPost, "/bzz", http.StatusCreated,
   115  			jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"),
   116  			jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr),
   117  			jsonhttptest.WithRequestBody(tarReader),
   118  			jsonhttptest.WithRequestHeader(api.ContentTypeHeader, api.ContentTypeTar),
   119  			jsonhttptest.WithRequestHeader(api.SwarmCollectionHeader, "true"),
   120  			jsonhttptest.WithRequestHeader(api.SwarmPinHeader, "true"),
   121  			jsonhttptest.WithExpectedJSONResponse(api.BzzUploadResponse{
   122  				Reference: swarm.MustParseHexAddress(rootHash),
   123  			}),
   124  		)
   125  		checkPinHandlers(t, client, rootHash, false)
   126  
   127  		header := jsonhttptest.Request(t, client, http.MethodPost, "/bzz?name=somefile.txt", http.StatusCreated,
   128  			jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"),
   129  			jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr),
   130  			jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "text/plain"),
   131  			jsonhttptest.WithRequestHeader(api.SwarmEncryptHeader, "true"),
   132  			jsonhttptest.WithRequestHeader(api.SwarmPinHeader, "true"),
   133  			jsonhttptest.WithRequestBody(strings.NewReader("this is a simple text")),
   134  		)
   135  
   136  		rootHash = strings.Trim(header.Get(api.ETagHeader), "\"")
   137  		checkPinHandlers(t, client, rootHash, false)
   138  	})
   139  
   140  }
   141  
   142  func TestPinHandlersInvalidInputs(t *testing.T) {
   143  	t.Parallel()
   144  
   145  	client, _, _, _ := newTestServer(t, testServerOptions{})
   146  
   147  	tests := []struct {
   148  		name      string
   149  		reference string
   150  		want      jsonhttp.StatusResponse
   151  	}{{
   152  		name:      "reference - odd hex string",
   153  		reference: "123",
   154  		want: jsonhttp.StatusResponse{
   155  			Code:    http.StatusBadRequest,
   156  			Message: "invalid path params",
   157  			Reasons: []jsonhttp.Reason{
   158  				{
   159  					Field: "reference",
   160  					Error: api.ErrHexLength.Error(),
   161  				},
   162  			},
   163  		},
   164  	}, {
   165  		name:      "reference - invalid hex character",
   166  		reference: "123G",
   167  		want: jsonhttp.StatusResponse{
   168  			Code:    http.StatusBadRequest,
   169  			Message: "invalid path params",
   170  			Reasons: []jsonhttp.Reason{
   171  				{
   172  					Field: "reference",
   173  					Error: api.HexInvalidByteError('G').Error(),
   174  				},
   175  			},
   176  		},
   177  	}}
   178  
   179  	for _, method := range []string{http.MethodGet, http.MethodPost, http.MethodDelete} {
   180  		method := method
   181  		for _, tc := range tests {
   182  			tc := tc
   183  			t.Run(method+" "+tc.name, func(t *testing.T) {
   184  				t.Parallel()
   185  
   186  				jsonhttptest.Request(t, client, method, "/pins/"+tc.reference, tc.want.Code,
   187  					jsonhttptest.WithExpectedJSONResponse(tc.want),
   188  				)
   189  			})
   190  		}
   191  	}
   192  }
   193  
   194  const pinRef = "620fcd78c7ce54da2d1b7cc2274a02e190cbe8fecbc3bd244690ab6517ce8f39"
   195  
   196  func TestIntegrityHandler(t *testing.T) {
   197  
   198  	t.Parallel()
   199  
   200  	t.Run("ok", func(t *testing.T) {
   201  		t.Parallel()
   202  		testServer, _, _, _ := newTestServer(t, testServerOptions{
   203  			PinIntegrity: &mockPinIntegrity{
   204  				Store:  inmemstore.New(),
   205  				tester: t,
   206  			},
   207  		})
   208  
   209  		endp := "/pins/check?ref=" + pinRef
   210  
   211  		// When probe is not set health endpoint should indicate that node is not healthy
   212  		jsonhttptest.Request(t, testServer, http.MethodGet, endp, http.StatusOK, jsonhttptest.WithExpectedResponse(nil))
   213  	})
   214  
   215  	t.Run("wrong hash format", func(t *testing.T) {
   216  		t.Parallel()
   217  		testServer, _, _, _ := newTestServer(t, testServerOptions{
   218  			PinIntegrity: &mockPinIntegrity{
   219  				Store:  inmemstore.New(),
   220  				tester: t,
   221  			},
   222  		})
   223  
   224  		endp := "/pins/check?ref=0xbadhash"
   225  
   226  		// When probe is not set health endpoint should indicate that node is not healthy
   227  		jsonhttptest.Request(t, testServer, http.MethodGet, endp, http.StatusBadRequest, jsonhttptest.WithExpectedResponse(nil))
   228  	})
   229  }
   230  
   231  type mockPinIntegrity struct {
   232  	tester *testing.T
   233  	Store  storage.Store
   234  }
   235  
   236  func (p *mockPinIntegrity) Check(ctx context.Context, logger log.Logger, pin string, out chan storer.PinStat) {
   237  	if pin != pinRef {
   238  		p.tester.Fatal("bad pin", pin)
   239  	}
   240  	close(out)
   241  }