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 }