github.com/ethersphere/bee/v2@v2.2.0/pkg/api/tag_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 "fmt" 9 "net/http" 10 "sort" 11 "strconv" 12 "testing" 13 14 mockpost "github.com/ethersphere/bee/v2/pkg/postage/mock" 15 mockstorer "github.com/ethersphere/bee/v2/pkg/storer/mock" 16 "github.com/ethersphere/bee/v2/pkg/swarm" 17 "github.com/google/go-cmp/cmp" 18 19 "github.com/ethersphere/bee/v2/pkg/api" 20 "github.com/ethersphere/bee/v2/pkg/jsonhttp" 21 "github.com/ethersphere/bee/v2/pkg/jsonhttp/jsonhttptest" 22 ) 23 24 func tagsWithIdResource(id uint64) string { return fmt.Sprintf("/tags/%d", id) } 25 26 // nolint:paralleltest 27 func TestTags(t *testing.T) { 28 29 var ( 30 tagsResource = "/tags" 31 storerMock = mockstorer.New() 32 client, _, _, _ = newTestServer(t, testServerOptions{ 33 Storer: storerMock, 34 Post: mockpost.New(mockpost.WithAcceptAll()), 35 }) 36 ) 37 38 // list tags without anything pinned 39 t.Run("list tags zero", func(t *testing.T) { 40 jsonhttptest.Request(t, client, http.MethodGet, tagsResource, http.StatusOK, 41 jsonhttptest.WithExpectedJSONResponse(api.ListTagsResponse{ 42 Tags: []api.TagResponse{}, 43 }), 44 ) 45 }) 46 47 t.Run("create tag", func(t *testing.T) { 48 tr := api.TagResponse{} 49 jsonhttptest.Request(t, client, http.MethodPost, tagsResource, http.StatusCreated, 50 jsonhttptest.WithJSONRequestBody(api.TagRequest{}), 51 jsonhttptest.WithUnmarshalJSONResponse(&tr), 52 ) 53 }) 54 55 t.Run("get non-existent tag", func(t *testing.T) { 56 jsonhttptest.Request(t, client, http.MethodGet, tagsWithIdResource(uint64(333)), http.StatusNotFound, 57 jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ 58 Message: "tag not present", 59 Code: http.StatusNotFound, 60 }), 61 ) 62 }) 63 64 t.Run("list tags", func(t *testing.T) { 65 // list all current tags 66 var resp api.ListTagsResponse 67 jsonhttptest.Request(t, client, http.MethodGet, tagsResource, http.StatusOK, 68 jsonhttptest.WithUnmarshalJSONResponse(&resp), 69 ) 70 71 // create 2 new tags 72 tRes1 := api.TagResponse{} 73 jsonhttptest.Request(t, client, http.MethodPost, tagsResource, http.StatusCreated, 74 jsonhttptest.WithJSONRequestBody(api.TagRequest{}), 75 jsonhttptest.WithUnmarshalJSONResponse(&tRes1), 76 ) 77 78 tRes2 := api.TagResponse{} 79 jsonhttptest.Request(t, client, http.MethodPost, tagsResource, http.StatusCreated, 80 jsonhttptest.WithJSONRequestBody(api.TagRequest{}), 81 jsonhttptest.WithUnmarshalJSONResponse(&tRes2), 82 ) 83 84 resp.Tags = append(resp.Tags, tRes1, tRes2) 85 86 // check if listing returns expected tags 87 var got api.ListTagsResponse 88 jsonhttptest.Request(t, client, http.MethodGet, tagsResource, http.StatusOK, 89 jsonhttptest.WithUnmarshalJSONResponse(&got), 90 ) 91 92 sort.Slice(resp.Tags, func(i, j int) bool { return resp.Tags[i].Uid < resp.Tags[j].Uid }) 93 sort.Slice(got.Tags, func(i, j int) bool { return got.Tags[i].Uid < got.Tags[j].Uid }) 94 95 if diff := cmp.Diff(resp.Tags, got.Tags); diff != "" { 96 t.Fatalf("unexpected tags (-want +have):\n%s", diff) 97 } 98 }) 99 100 t.Run("delete non-existent tag", func(t *testing.T) { 101 // try to delete non-existent tag 102 jsonhttptest.Request(t, client, http.MethodDelete, tagsWithIdResource(uint64(333)), http.StatusNotFound, 103 jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ 104 Message: "tag not present", 105 Code: http.StatusNotFound, 106 }), 107 ) 108 }) 109 110 t.Run("delete tag", func(t *testing.T) { 111 // create a tag through API 112 tRes := api.TagResponse{} 113 jsonhttptest.Request(t, client, http.MethodPost, tagsResource, http.StatusCreated, 114 jsonhttptest.WithJSONRequestBody(api.TagRequest{}), 115 jsonhttptest.WithUnmarshalJSONResponse(&tRes), 116 ) 117 118 // check tag existence 119 jsonhttptest.Request(t, client, http.MethodGet, tagsWithIdResource(tRes.Uid), http.StatusOK) 120 121 // delete tag through API 122 jsonhttptest.Request(t, client, http.MethodDelete, tagsWithIdResource(tRes.Uid), http.StatusNoContent, 123 jsonhttptest.WithNoResponseBody(), 124 ) 125 126 // try to get tag 127 jsonhttptest.Request(t, client, http.MethodGet, tagsWithIdResource(tRes.Uid), http.StatusNotFound, 128 jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ 129 Message: "tag not present", 130 Code: http.StatusNotFound, 131 }), 132 ) 133 }) 134 135 t.Run("done split non-existent tag", func(t *testing.T) { 136 // non-existent tag 137 jsonhttptest.Request(t, client, http.MethodPatch, tagsWithIdResource(uint64(333)), http.StatusNotFound, 138 jsonhttptest.WithJSONRequestBody(api.TagResponse{}), 139 jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ 140 Message: "tag not present", 141 Code: http.StatusNotFound, 142 }), 143 ) 144 }) 145 146 t.Run("done split", func(t *testing.T) { 147 // create a tag through API 148 tRes := api.TagResponse{} 149 jsonhttptest.Request(t, client, http.MethodPost, tagsResource, http.StatusCreated, 150 jsonhttptest.WithUnmarshalJSONResponse(&tRes), 151 ) 152 tagId := tRes.Uid 153 154 // generate address to be supplied to the done split 155 addr := swarm.RandAddress(t) 156 157 // call done split 158 jsonhttptest.Request(t, client, http.MethodPatch, tagsWithIdResource(tagId), http.StatusOK, 159 jsonhttptest.WithJSONRequestBody(api.TagRequest{ 160 Address: addr, 161 }), 162 jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ 163 Message: "ok", 164 Code: http.StatusOK, 165 }), 166 ) 167 168 jsonhttptest.Request(t, client, http.MethodGet, tagsWithIdResource(tagId), http.StatusOK, 169 jsonhttptest.WithUnmarshalJSONResponse(&tRes), 170 ) 171 172 if !tRes.Address.Equal(addr) { 173 t.Fatalf("invalid session reference got %s want %s", tRes.Address, addr) 174 } 175 176 // try different address value 177 addr = swarm.RandAddress(t) 178 179 // call done split 180 jsonhttptest.Request(t, client, http.MethodPatch, tagsWithIdResource(tagId), http.StatusOK, 181 jsonhttptest.WithJSONRequestBody(api.TagRequest{ 182 Address: addr, 183 }), 184 jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ 185 Message: "ok", 186 Code: http.StatusOK, 187 }), 188 ) 189 190 jsonhttptest.Request(t, client, http.MethodGet, tagsWithIdResource(tagId), http.StatusOK, 191 jsonhttptest.WithUnmarshalJSONResponse(&tRes), 192 ) 193 194 if !tRes.Address.Equal(addr) { 195 t.Fatalf("invalid session reference got %s want %s", tRes.Address, addr) 196 } 197 }) 198 } 199 200 func TestTagsHandlersInvalidInputs(t *testing.T) { 201 t.Parallel() 202 203 client, _, _, _ := newTestServer(t, testServerOptions{}) 204 205 tests := []struct { 206 name string 207 tagID string 208 want jsonhttp.StatusResponse 209 }{{ 210 name: "id - invalid value", 211 tagID: "a", 212 want: jsonhttp.StatusResponse{ 213 Code: http.StatusBadRequest, 214 Message: "invalid path params", 215 Reasons: []jsonhttp.Reason{ 216 { 217 Field: "id", 218 Error: strconv.ErrSyntax.Error(), 219 }, 220 }, 221 }, 222 }} 223 224 for _, method := range []string{http.MethodGet, http.MethodDelete, http.MethodPatch} { 225 method := method 226 for _, tc := range tests { 227 tc := tc 228 t.Run(method+" "+tc.name, func(t *testing.T) { 229 t.Parallel() 230 231 jsonhttptest.Request(t, client, method, "/tags/"+tc.tagID, tc.want.Code, 232 jsonhttptest.WithExpectedJSONResponse(tc.want), 233 ) 234 }) 235 } 236 } 237 } 238 239 // isTagFoundInResponse verifies that the tag id is found in the supplied HTTP headers 240 // if an API tag response is supplied, it also verifies that it contains an id which matches the headers 241 func isTagFoundInResponse(t *testing.T, headers http.Header, tr *api.TagResponse) uint64 { 242 t.Helper() 243 244 idStr := headers.Get(api.SwarmTagHeader) 245 if idStr == "" { 246 t.Fatalf("could not find tag id header in chunk upload response") 247 } 248 nId, err := strconv.Atoi(idStr) 249 id := uint64(nId) 250 if err != nil { 251 t.Fatal(err) 252 } 253 if tr != nil { 254 if id != tr.Uid { 255 t.Fatalf("expected created tag id to be %d, but got %d when uploading chunk", tr.Uid, id) 256 } 257 } 258 return id 259 }