From 25f9f1c335e8d8fef72085fe451d5f27775090c3 Mon Sep 17 00:00:00 2001 From: mrekucci Date: Mon, 17 Oct 2022 19:46:54 -0500 Subject: [PATCH] test: add tests for http inputs --- openapi/Swarm.yaml | 4 +- pkg/api/api.go | 33 ++- pkg/api/balances_test.go | 98 +++++++ pkg/api/bytes.go | 27 +- pkg/api/bytes_test.go | 106 +++++-- pkg/api/bzz.go | 40 +-- pkg/api/bzz_test.go | 50 ++++ pkg/api/chequebook_test.go | 49 ++++ pkg/api/chunk.go | 17 +- pkg/api/chunk_test.go | 50 ++++ pkg/api/export_test.go | 4 +- pkg/api/feed.go | 12 +- pkg/api/logger.go | 25 +- pkg/api/logger_test.go | 115 ++++++++ pkg/api/node.go | 2 +- pkg/api/peer.go | 2 +- pkg/api/peer_test.go | 85 ++++++ pkg/api/pin_test.go | 52 ++++ pkg/api/pingpong_test.go | 49 ++++ pkg/api/postage_test.go | 370 +++++++++++++++++++++++++ pkg/api/pss_test.go | 52 ++++ pkg/api/rchash.go | 29 +- pkg/api/settlements_test.go | 49 ++++ pkg/api/soc.go | 1 - pkg/api/staking.go | 1 - pkg/api/staking_test.go | 36 +++ pkg/api/stewardship.go | 47 +--- pkg/api/stewardship_test.go | 97 +++---- pkg/api/subdomain.go | 16 +- pkg/api/tag_debug_test.go | 38 +++ pkg/api/tag_test.go | 39 +++ pkg/api/util.go | 71 +++-- pkg/api/util_test.go | 18 +- pkg/api/welcome_message.go | 2 +- pkg/jsonhttp/jsonhttp.go | 2 +- pkg/resolver/client/ens/ens.go | 8 +- pkg/resolver/client/ens/export_test.go | 2 - pkg/resolver/mock/resolver.go | 7 +- 38 files changed, 1427 insertions(+), 278 deletions(-) diff --git a/openapi/Swarm.yaml b/openapi/Swarm.yaml index 5262507d242..76d6bf95c88 100644 --- a/openapi/Swarm.yaml +++ b/openapi/Swarm.yaml @@ -166,8 +166,8 @@ paths: schema: type: string format: binary - "404": - $ref: "SwarmCommon.yaml#/components/responses/404" + "400": + $ref: "SwarmCommon.yaml#/components/responses/400" default: description: Default response diff --git a/pkg/api/api.go b/pkg/api/api.go index b5bb3e4e52c..70ae4524de0 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -9,6 +9,7 @@ package api import ( "context" "crypto/ecdsa" + "encoding/base64" "encoding/hex" "encoding/json" "errors" @@ -16,6 +17,7 @@ import ( "io" "math" "math/big" + "mime" "net/http" "reflect" "strconv" @@ -41,6 +43,7 @@ import ( "github.com/ethersphere/bee/pkg/pss" "github.com/ethersphere/bee/pkg/pusher" "github.com/ethersphere/bee/pkg/resolver" + "github.com/ethersphere/bee/pkg/resolver/client/ens" "github.com/ethersphere/bee/pkg/sctx" "github.com/ethersphere/bee/pkg/settlement" "github.com/ethersphere/bee/pkg/settlement/swap" @@ -174,7 +177,8 @@ type Service struct { erc20Service erc20.Service chainID int64 - validate *validator.Validate + preMapHooks map[string]func(v string) (string, error) + validate *validator.Validate } func (s *Service) SetP2P(p2p p2p.DebugService) { @@ -234,6 +238,16 @@ func New(publicKey, pssPublicKey ecdsa.PublicKey, ethereumAddress common.Address s.batchStore = batchStore s.chainBackend = chainBackend s.metricsRegistry = newDebugMetrics() + s.preMapHooks = map[string]func(v string) (string, error){ + "mimeMediaType": func(v string) (string, error) { + typ, _, err := mime.ParseMediaType(v) + return typ, err + }, + "decBase64url": func(v string) (string, error) { + buf, err := base64.URLEncoding.DecodeString(v) + return string(buf), err + }, + } s.validate = validator.New() s.validate.RegisterTagNameFunc(func(fld reflect.StructField) string { name := strings.SplitN(fld.Tag.Get(mapStructureTagName), ",", 2)[0] @@ -286,6 +300,17 @@ func (s *Service) Configure(signer crypto.Signer, auth auth.Authenticator, trace s.erc20Service = erc20 s.syncStatus = e.SyncStatus + s.preMapHooks["resolve"] = func(v string) (string, error) { + switch addr, err := s.resolveNameOrAddress(v); { + case err == nil: + return addr.String(), nil + case errors.Is(err, ens.ErrNotImplemented): + return v, nil + default: + return "", err + } + } + return s.chunkPushC } @@ -670,14 +695,14 @@ func (s *Service) mapStructure(input, output interface{}) func(string, log.Logge var perr *parseError if errors.As(err, &perr) { resp.Reasons = append(resp.Reasons, jsonhttp.Reason{ - Entry: perr.Entry, + Field: perr.Entry, Error: perr.Cause.Error(), }) } var verr *validationError if errors.As(err, &verr) { resp.Reasons = append(resp.Reasons, jsonhttp.Reason{ - Entry: verr.Entry, + Field: verr.Entry, Error: verr.Cause.Error(), }) } @@ -686,7 +711,7 @@ func (s *Service) mapStructure(input, output interface{}) func(string, log.Logge } } - if err := mapStructure(input, output); err != nil { + if err := mapStructure(input, output, s.preMapHooks); err != nil { return response(err) } diff --git a/pkg/api/balances_test.go b/pkg/api/balances_test.go index 193dc9c5cee..4cb985fefc0 100644 --- a/pkg/api/balances_test.go +++ b/pkg/api/balances_test.go @@ -299,3 +299,101 @@ func TestConsumedPeersNoBalance(t *testing.T) { }), ) } + +func Test_peerBalanceHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + peer string + want jsonhttp.StatusResponse + }{{ + name: "peer - odd hex string", + peer: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "peer", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "peer - invalid hex character", + peer: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "peer", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodGet, "/consumed/"+tc.peer, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + +func Test_compensatedPeerBalanceHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + peer string + want jsonhttp.StatusResponse + }{{ + name: "peer - odd hex string", + peer: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "peer", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "peer - invalid hex character", + peer: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "peer", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodGet, "/balances/"+tc.peer, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} diff --git a/pkg/api/bytes.go b/pkg/api/bytes.go index c165eb54347..9b5144db4c7 100644 --- a/pkg/api/bytes.go +++ b/pkg/api/bytes.go @@ -131,51 +131,34 @@ func (s *Service) bytesGetHandler(w http.ResponseWriter, r *http.Request) { logger := tracing.NewLoggerWithTraceID(r.Context(), s.logger.WithName("get_bytes_by_address").Build()) paths := struct { - Address string `map:"address" validate:"required"` + Address swarm.Address `map:"address,resolve" validate:"required"` }{} if response := s.mapStructure(mux.Vars(r), &paths); response != nil { response("invalid path params", logger, w) return } - // TODO: move this to the parsing phase, consider using a `resolve` tag value to indicate this. - address, err := s.resolveNameOrAddress(paths.Address) - if err != nil { - logger.Debug("mapStructure address string failed", paths.Address, err) - logger.Error(nil, "mapStructure address string failed") - jsonhttp.NotFound(w, nil) - return - } - additionalHeaders := http.Header{ "Content-Type": {"application/octet-stream"}, } - s.downloadHandler(logger, w, r, address, additionalHeaders, true) + s.downloadHandler(logger, w, r, paths.Address, additionalHeaders, true) } func (s *Service) bytesHeadHandler(w http.ResponseWriter, r *http.Request) { logger := tracing.NewLoggerWithTraceID(r.Context(), s.logger.WithName("head_bytes_by_address").Build()) paths := struct { - Address string `map:"address" validate:"required"` + Address swarm.Address `map:"address,resolve" validate:"required"` }{} if response := s.mapStructure(mux.Vars(r), &paths); response != nil { response("invalid path params", logger, w) return } - // TODO: move this to the parsing phase, consider using a `resolve` tag value to indicate this. - address, err := s.resolveNameOrAddress(paths.Address) - if err != nil { - logger.Debug("mapStructure address string failed", "string", paths.Address, "error", err) - logger.Error(nil, "mapStructure address string failed") - w.WriteHeader(http.StatusBadRequest) // HEAD requests do not write a body - return - } - ch, err := s.storer.Get(r.Context(), storage.ModeGetRequest, address) + ch, err := s.storer.Get(r.Context(), storage.ModeGetRequest, paths.Address) if err != nil { - logger.Debug("get root chunk failed", "chunk_address", address, "error", err) + logger.Debug("get root chunk failed", "chunk_address", paths.Address, "error", err) logger.Error(nil, "get rook chunk failed") w.WriteHeader(http.StatusNotFound) return diff --git a/pkg/api/bytes_test.go b/pkg/api/bytes_test.go index cbc1b872faf..ad25b8c3e3b 100644 --- a/pkg/api/bytes_test.go +++ b/pkg/api/bytes_test.go @@ -145,15 +145,6 @@ func TestBytes(t *testing.T) { } }) - t.Run("not found", func(t *testing.T) { - jsonhttptest.Request(t, client, http.MethodGet, resource+"/0xabcd", http.StatusNotFound, - jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: "Not Found", - Code: http.StatusNotFound, - }), - ) - }) - t.Run("internal error", func(t *testing.T) { jsonhttptest.Request(t, client, http.MethodGet, resource+"/abcd", http.StatusInternalServerError, jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ @@ -162,15 +153,6 @@ func TestBytes(t *testing.T) { }), ) }) - - t.Run("upload multipart error", func(t *testing.T) { - jsonhttptest.Request(t, client, http.MethodPost, resource, http.StatusBadRequest, - jsonhttptest.WithRequestHeader(api.SwarmDeferredUploadHeader, "true"), - jsonhttptest.WithRequestHeader(api.SwarmPostageBatchIdHeader, batchOkStr), - jsonhttptest.WithRequestHeader(api.ContentTypeHeader, "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"), - jsonhttptest.WithRequestBody(bytes.NewReader(content)), - ) - }) } // nolint:paralleltest @@ -250,3 +232,91 @@ func TestBytesInvalidStamp(t *testing.T) { } }) } + +func Test_bytesUploadHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{}) + + tests := []struct { + name string + hdrKey string + hdrVal string + want jsonhttp.StatusResponse + }{{ + name: "Content-Type - invalid", + hdrKey: "Content-Type", + hdrVal: "multipart/form-data", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid header params", + Reasons: []jsonhttp.Reason{ + { + Field: "content-type", + Error: "want excludes:multipart/form-data", + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodPost, "/bytes", tc.want.Code, + jsonhttptest.WithRequestHeader(tc.hdrKey, tc.hdrVal), + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + +func Test_bytesGetHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{}) + + tests := []struct { + name string + address string + want jsonhttp.StatusResponse + }{{ + name: "address - odd hex string", + address: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "address - invalid hex character", + address: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodGet, "/bytes/"+tc.address, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} diff --git a/pkg/api/bzz.go b/pkg/api/bzz.go index 097abaa8de4..690f9522c17 100644 --- a/pkg/api/bzz.go +++ b/pkg/api/bzz.go @@ -126,13 +126,24 @@ func (s *Service) fileUploadHandler(logger log.Logger, w http.ResponseWriter, r // If filename is still empty, use the file hash as the filename if queries.FileName == "" { queries.FileName = fr.String() - } - if err := s.validate.Var(queries.FileName, "startsnotwith=/"); err != nil { - // TODO: return standard validation error. - logger.Debug("bzz upload file: / in prefix not allowed", "file_name", queries.FileName, "error", err) - logger.Error(nil, "bzz upload file: / in prefix not allowed", "file_name", queries.FileName) - jsonhttp.BadRequest(w, "/ in prefix not allowed") - return + if err := s.validate.Struct(queries); err != nil { + verr := &validationError{ + Entry: "file hash", + Value: queries.FileName, + Cause: err, + } + logger.Debug("invalid body filename", "error", verr) + logger.Error(nil, "invalid body filename") + jsonhttp.BadRequest(w, jsonhttp.StatusResponse{ + Message: "invalid body params", + Code: http.StatusBadRequest, + Reasons: []jsonhttp.Reason{{ + Field: "file hash", + Error: verr.Error(), + }}, + }) + return + } } encrypt := requestEncrypt(r) @@ -240,8 +251,8 @@ func (s *Service) bzzDownloadHandler(w http.ResponseWriter, r *http.Request) { logger := tracing.NewLoggerWithTraceID(r.Context(), s.logger.WithName("get_bzz_by_path").Build()) paths := struct { - Address string `map:"address" validate:"required"` - Path string `map:"path"` + Address swarm.Address `map:"address,resolve" validate:"required"` + Path string `map:"path"` }{} if response := s.mapStructure(mux.Vars(r), &paths); response != nil { response("invalid path params", logger, w) @@ -252,16 +263,7 @@ func (s *Service) bzzDownloadHandler(w http.ResponseWriter, r *http.Request) { paths.Path = strings.TrimRight(paths.Path, "/") + "/" // NOTE: leave one slash if there was some. } - // TODO: move this to the parsing phase, consider using a `resolve` tag value to indicate this. - address, err := s.resolveNameOrAddress(paths.Address) - if err != nil { - logger.Debug("bzz download: mapStructure address string failed", "string", paths.Address, "error", err) - logger.Error(nil, "bzz download: mapStructure address string failed") - jsonhttp.NotFound(w, nil) - return - } - - s.serveReference(logger, address, paths.Path, w, r) + s.serveReference(logger, paths.Address, paths.Path, w, r) } func (s *Service) serveReference(logger log.Logger, address swarm.Address, pathVar string, w http.ResponseWriter, r *http.Request) { diff --git a/pkg/api/bzz_test.go b/pkg/api/bzz_test.go index 9a0696d13cb..cf748649056 100644 --- a/pkg/api/bzz_test.go +++ b/pkg/api/bzz_test.go @@ -18,6 +18,7 @@ import ( "github.com/ethersphere/bee/pkg/api" "github.com/ethersphere/bee/pkg/file/loadsave" + "github.com/ethersphere/bee/pkg/jsonhttp" "github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest" "github.com/ethersphere/bee/pkg/log" "github.com/ethersphere/bee/pkg/manifest" @@ -638,3 +639,52 @@ func TestFeedIndirection(t *testing.T) { jsonhttptest.WithExpectedResponse(updateData), ) } + +func Test_bzzDownloadHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{}) + + tests := []struct { + name string + address string + want jsonhttp.StatusResponse + }{{ + name: "address - odd hex string", + address: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "address - invalid hex character", + address: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodGet, fmt.Sprintf("/bzz/%s/abc", tc.address), tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} diff --git a/pkg/api/chequebook_test.go b/pkg/api/chequebook_test.go index f5ef4d7577e..16de3299a23 100644 --- a/pkg/api/chequebook_test.go +++ b/pkg/api/chequebook_test.go @@ -730,6 +730,55 @@ func TestChequebookCashoutStatus(t *testing.T) { }) } +func Test_chequebookLastPeerHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + peer string + want jsonhttp.StatusResponse + }{{ + name: "peer - odd hex string", + peer: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "peer", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "peer - invalid hex character", + peer: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "peer", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodGet, "/chequebook/cheque/"+tc.peer, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + func LastChequesEqual(a, b *api.ChequebookLastChequesResponse) bool { var state bool diff --git a/pkg/api/chunk.go b/pkg/api/chunk.go index edf594abc39..19dc28c03b9 100644 --- a/pkg/api/chunk.go +++ b/pkg/api/chunk.go @@ -170,31 +170,22 @@ func (s *Service) chunkGetHandler(w http.ResponseWriter, r *http.Request) { loggerV1 := logger.V(1).Build() paths := struct { - Address string `map:"address" validate:"required"` + Address swarm.Address `map:"address,resolve" validate:"required"` }{} if response := s.mapStructure(mux.Vars(r), &paths); response != nil { response("invalid path params", logger, w) return } - // TODO: move this to the parsing phase, consider using a `resolve` tag value to indicate this. - address, err := s.resolveNameOrAddress(paths.Address) - if err != nil { - logger.Debug("mapStructure chunk address string failed", "string", paths.Address, "error", err) - logger.Error(nil, "mapStructure chunk address string failed") - jsonhttp.NotFound(w, nil) - return - } - - chunk, err := s.storer.Get(r.Context(), storage.ModeGetRequest, address) + chunk, err := s.storer.Get(r.Context(), storage.ModeGetRequest, paths.Address) if err != nil { if errors.Is(err, storage.ErrNotFound) { - loggerV1.Debug("chunk not found", "address", address) + loggerV1.Debug("chunk not found", "address", paths.Address) jsonhttp.NotFound(w, "chunk not found") return } - logger.Debug("read chunk failed", "chunk_address", address, "error", err) + logger.Debug("read chunk failed", "chunk_address", paths.Address, "error", err) logger.Error(nil, "read chunk failed") jsonhttp.InternalServerError(w, "read chunk failed") return diff --git a/pkg/api/chunk_test.go b/pkg/api/chunk_test.go index ed3139c7a68..8b301b77278 100644 --- a/pkg/api/chunk_test.go +++ b/pkg/api/chunk_test.go @@ -208,3 +208,53 @@ func TestHasChunkHandler(t *testing.T) { } }) } + +//nolint:tparallel +func Test_chunkHandlers_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{}) + + tests := []struct { + name string + address string + want jsonhttp.StatusResponse + }{{ + name: "address - odd hex string", + address: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "address - invalid hex character", + address: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }} + + //nolint:paralleltest + for _, method := range []string{http.MethodGet, http.MethodHead, http.MethodDelete} { + for _, tc := range tests { + t.Run(method+" "+tc.name, func(t *testing.T) { + jsonhttptest.Request(t, client, method, "/chunks/"+tc.address, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } + } +} diff --git a/pkg/api/export_test.go b/pkg/api/export_test.go index 03f11434cc5..e7f9461aa3d 100644 --- a/pkg/api/export_test.go +++ b/pkg/api/export_test.go @@ -133,7 +133,9 @@ var ErrHexLength = errHexLength type HexInvalidByteError = hexInvalidByteError -func MapStructure(input, output interface{}) error { return mapStructure(input, output) } +func MapStructure(input, output interface{}, hooks map[string]func(v string) (string, error)) error { + return mapStructure(input, output, hooks) +} func NewParseError(entry, value string, cause error) error { return newParseError(entry, value, cause) } diff --git a/pkg/api/feed.go b/pkg/api/feed.go index 18ec7dfd77f..66d365d175a 100644 --- a/pkg/api/feed.go +++ b/pkg/api/feed.go @@ -39,8 +39,8 @@ func (s *Service) feedGetHandler(w http.ResponseWriter, r *http.Request) { logger := s.logger.WithName("get_feed").Build() paths := struct { - Owner []byte `map:"owner" validate:"required,len=20"` - Topic []byte `map:"topic" validate:"required"` + Owner common.Address `map:"owner" validate:"required"` + Topic []byte `map:"topic" validate:"required"` }{} if response := s.mapStructure(mux.Vars(r), &paths); response != nil { response("invalid path params", logger, w) @@ -58,7 +58,7 @@ func (s *Service) feedGetHandler(w http.ResponseWriter, r *http.Request) { queries.At = time.Now().Unix() } - f := feeds.New(paths.Topic, common.BytesToAddress(paths.Owner)) + f := feeds.New(paths.Topic, paths.Owner) lookup, err := s.feedFactory.NewLookup(feeds.Sequence, f) if err != nil { logger.Debug("new lookup failed", "owner", paths.Owner, "error", err) @@ -118,8 +118,8 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { logger := s.logger.WithName("post_feed").Build() paths := struct { - Owner []byte `map:"owner" validate:"required,len=20"` - Topic []byte `map:"topic" validate:"required"` + Owner common.Address `map:"owner" validate:"required"` + Topic []byte `map:"topic" validate:"required"` }{} if response := s.mapStructure(mux.Vars(r), &paths); response != nil { response("invalid path params", logger, w) @@ -153,7 +153,7 @@ func (s *Service) feedPostHandler(w http.ResponseWriter, r *http.Request) { } meta := map[string]string{ - feedMetadataEntryOwner: hex.EncodeToString(paths.Owner), + feedMetadataEntryOwner: hex.EncodeToString(paths.Owner.Bytes()), feedMetadataEntryTopic: hex.EncodeToString(paths.Topic), feedMetadataEntryType: feeds.Sequence.String(), // only sequence allowed for now } diff --git a/pkg/api/logger.go b/pkg/api/logger.go index 49cfc681d6f..cb371d6a851 100644 --- a/pkg/api/logger.go +++ b/pkg/api/logger.go @@ -90,11 +90,17 @@ func (s *Service) loggerGetHandler(w http.ResponseWriter, r *http.Request) { return true }) - // TODO: return custom validation error. if len(result.Loggers) == 0 && err != nil { - logger.Debug("regexp compilation failed", "error", err) - logger.Error(nil, "regexp compilation failed") - jsonhttp.BadRequest(w, err) + logger.Debug("invalid path params", "error", err) + logger.Error(nil, "invalid path params") + jsonhttp.BadRequest(w, jsonhttp.StatusResponse{ + Message: "invalid path params", + Code: http.StatusBadRequest, + Reasons: []jsonhttp.Reason{{ + Field: "exp", + Error: err.Error(), + }}, + }) } else { jsonhttp.OK(w, result) } @@ -115,7 +121,16 @@ func (s *Service) loggerSetVerbosityHandler(w http.ResponseWriter, r *http.Reque } if err := logSetVerbosityByExp(paths.Exp, log.MustParseVerbosityLevel(paths.Verbosity)); err != nil { - jsonhttp.BadRequest(w, err) + logger.Debug("invalid path params", "error", err) + logger.Error(nil, "invalid path params") + jsonhttp.BadRequest(w, jsonhttp.StatusResponse{ + Message: "invalid path params", + Code: http.StatusBadRequest, + Reasons: []jsonhttp.Reason{{ + Field: "exp", + Error: err.Error(), + }}, + }) } else { jsonhttp.OK(w, nil) } diff --git a/pkg/api/logger_test.go b/pkg/api/logger_test.go index afab51469bf..e29341355a1 100644 --- a/pkg/api/logger_test.go +++ b/pkg/api/logger_test.go @@ -139,3 +139,118 @@ func TestSetLoggerVerbosity(t *testing.T) { }) } } + +func Test_loggerGetHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + exp string + want jsonhttp.StatusResponse + }{{ + name: "exp - illegal base64", + exp: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "exp", + Error: "illegal base64 data at input byte 0", + }, + }, + }, + }, { + name: "exp - invalid regex", + exp: base64.URLEncoding.EncodeToString([]byte("[")), + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "exp", + Error: "error parsing regexp: missing closing ]: `[`", + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodGet, "/loggers/"+tc.exp, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + +func Test_loggerSetVerbosityHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + exp string + verbosity string + want jsonhttp.StatusResponse + }{{ + name: "exp - illegal base64", + exp: "123", + verbosity: "info", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "exp", + Error: "illegal base64 data at input byte 0", + }, + }, + }, + }, { + name: "exp - invalid regex", + exp: base64.URLEncoding.EncodeToString([]byte("[")), + verbosity: "info", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "exp", + Error: "error parsing regexp: missing closing ]: `[`", + }, + }, + }, + }, { + name: "verbosity - invalid value", + exp: base64.URLEncoding.EncodeToString([]byte("123")), + verbosity: "invalid", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "verbosity", + Error: "want oneof:none error warning info debug all", + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodPut, "/loggers/"+tc.exp+"/"+tc.verbosity, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} diff --git a/pkg/api/node.go b/pkg/api/node.go index 51fb623ce99..2ae41d72fea 100644 --- a/pkg/api/node.go +++ b/pkg/api/node.go @@ -40,7 +40,7 @@ func (b BeeNodeMode) String() string { } // nodeGetHandler gives back information about the Bee node configuration. -func (s *Service) nodeGetHandler(w http.ResponseWriter, r *http.Request) { +func (s *Service) nodeGetHandler(w http.ResponseWriter, _ *http.Request) { jsonhttp.OK(w, nodeResponse{ BeeMode: s.beeMode.String(), ChequebookEnabled: s.chequebookEnabled, diff --git a/pkg/api/peer.go b/pkg/api/peer.go index 53beec99997..3d747421a85 100644 --- a/pkg/api/peer.go +++ b/pkg/api/peer.go @@ -87,7 +87,7 @@ type peersResponse struct { Peers []Peer `json:"peers"` } -func (s *Service) peersHandler(w http.ResponseWriter, r *http.Request) { +func (s *Service) peersHandler(w http.ResponseWriter, _ *http.Request) { jsonhttp.OK(w, peersResponse{ Peers: mapPeers(s.p2p.Peers()), }) diff --git a/pkg/api/peer_test.go b/pkg/api/peer_test.go index c7a0d7f089f..0c5f6ebd8a7 100644 --- a/pkg/api/peer_test.go +++ b/pkg/api/peer_test.go @@ -220,3 +220,88 @@ func TestBlocklistedPeersErr(t *testing.T) { }), ) } + +func Test_peerConnectHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + multiAddress string + want jsonhttp.StatusResponse + }{{ + name: "multi-address - invalid value", + multiAddress: "ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59a", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "multi-address", + Error: "failed to parse multiaddr \"/ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59a\": unknown protocol ca1e9f3938cc1425c6061b96ad9eb93e134dfe8734ad490164ef20af9d1cf59a", + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodPost, "/connect/"+tc.multiAddress, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + +func Test_peerDisconnectHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + address string + want jsonhttp.StatusResponse + }{{ + name: "address - odd hex string", + address: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "address - invalid hex character", + address: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodDelete, "/peers/"+tc.address, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} diff --git a/pkg/api/pin_test.go b/pkg/api/pin_test.go index cbe1d44d62b..56a0283889a 100644 --- a/pkg/api/pin_test.go +++ b/pkg/api/pin_test.go @@ -156,3 +156,55 @@ func TestPinHandlers(t *testing.T) { checkPinHandlers(t, client, rootHash, true) }) } + +func Test_pinHandlers_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{}) + + tests := []struct { + name string + reference string + want jsonhttp.StatusResponse + }{{ + name: "reference - odd hex string", + reference: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "reference", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "reference - invalid hex character", + reference: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "reference", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }} + + for _, method := range []string{http.MethodGet, http.MethodPost, http.MethodDelete} { + method := method + for _, tc := range tests { + tc := tc + t.Run(method+" "+tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, method, "/pins/"+tc.reference, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } + } +} diff --git a/pkg/api/pingpong_test.go b/pkg/api/pingpong_test.go index 9bf9e3e7458..b97bdd77413 100644 --- a/pkg/api/pingpong_test.go +++ b/pkg/api/pingpong_test.go @@ -75,3 +75,52 @@ func TestPingpong(t *testing.T) { ) }) } + +func Test_pingpongHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + address string + want jsonhttp.StatusResponse + }{{ + name: "address - odd hex string", + address: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "address - invalid hex character", + address: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodPost, "/pingpong/"+tc.address, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} diff --git a/pkg/api/postage_test.go b/pkg/api/postage_test.go index 67399664f24..29bf2ea5ea5 100644 --- a/pkg/api/postage_test.go +++ b/pkg/api/postage_test.go @@ -12,6 +12,7 @@ import ( "fmt" "math/big" "net/http" + "strconv" "testing" "time" @@ -921,3 +922,372 @@ func TestPostageAccessHandler(t *testing.T) { } } } + +//nolint:tparallel +func Test_postageCreateHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + amount string + depth string + want jsonhttp.StatusResponse + }{{ + name: "amount - invalid value", + amount: "a", + depth: "1", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "amount", + Error: "invalid value", + }, + }, + }, + }, { + name: "depth - invalid value", + amount: "1", + depth: "a", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "depth", + Error: strconv.ErrSyntax.Error(), + }, + }, + }, + }} + + //nolint:paralleltest + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + jsonhttptest.Request(t, client, http.MethodPost, "/stamps/"+tc.amount+"/"+tc.depth, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + +func Test_postageGetStampsHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + all string + want jsonhttp.StatusResponse + }{{ + name: "all - invalid value", + all: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid query params", + Reasons: []jsonhttp.Reason{ + { + Field: "all", + Error: strconv.ErrSyntax.Error(), + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodGet, "/stamps?all="+tc.all, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + +func Test_postageGetStampBucketsHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + batchID string + want jsonhttp.StatusResponse + }{{ + name: "batch_id - odd hex string", + batchID: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "batch_id - invalid hex character", + batchID: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }, { + name: "batch_id - invalid length", + batchID: "1234", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: "want len:32", + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodGet, "/stamps/"+tc.batchID+"/buckets", tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + +func Test_postageGetStampHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + batchID string + want jsonhttp.StatusResponse + }{{ + name: "batch_id - odd hex string", + batchID: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "batch_id - invalid hex character", + batchID: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }, { + name: "batch_id - invalid length", + batchID: "1234", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: "want len:32", + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodGet, "/stamps/"+tc.batchID, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + +//nolint:tparallel +func Test_postageTopUpHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + batchID string + amount string + want jsonhttp.StatusResponse + }{{ + name: "batch_id - odd hex string", + batchID: "123", + amount: "1", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "batch_id - invalid hex character", + batchID: "123G", + amount: "1", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }, { + name: "batch_id - invalid length", + batchID: "1234", + amount: "1", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: "want len:32", + }, + }, + }, + }, { + name: "amount - invalid value", + batchID: hex.EncodeToString([]byte{31: 0}), + amount: "a", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "amount", + Error: "invalid value", + }, + }, + }, + }} + + //nolint:paralleltest + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + jsonhttptest.Request(t, client, http.MethodPatch, "/stamps/topup/"+tc.batchID+"/"+tc.amount, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + +//nolint:tparallel +func Test_postageDiluteHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + batchID string + depth string + want jsonhttp.StatusResponse + }{{ + name: "batch_id - odd hex string", + batchID: "123", + depth: "1", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "batch_id - invalid hex character", + batchID: "123G", + depth: "1", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }, { + name: "batch_id - invalid length", + batchID: "1234", + depth: "1", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "batch_id", + Error: "want len:32", + }, + }, + }, + }, { + name: "depth - invalid syntax", + batchID: hex.EncodeToString([]byte{31: 0}), + depth: "a", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "depth", + Error: strconv.ErrSyntax.Error(), + }, + }, + }, + }} + + //nolint:paralleltest + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + jsonhttptest.Request(t, client, http.MethodPatch, "/stamps/dilute/"+tc.batchID+"/"+tc.depth, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} diff --git a/pkg/api/pss_test.go b/pkg/api/pss_test.go index ff5820f876e..97eff37866f 100644 --- a/pkg/api/pss_test.go +++ b/pkg/api/pss_test.go @@ -390,6 +390,58 @@ func newPssTest(t *testing.T, o opts) (pss.Interface, *ecdsa.PublicKey, *websock return pss, &privkey.PublicKey, cl, listener } +func Test_pssPostHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{}) + + tests := []struct { + name string + topic string + targets string + want jsonhttp.StatusResponse + }{{ + name: "targets - odd length hex string", + topic: "test_topic", + targets: "1", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "target", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "targets - odd length hex string", + topic: "test_topic", + targets: "1G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "target", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodPost, "/pss/send/"+tc.topic+"/"+tc.targets, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + type pssSendFn func(context.Context, pss.Targets, swarm.Chunk) error type mpss struct { f pssSendFn diff --git a/pkg/api/rchash.go b/pkg/api/rchash.go index 61604729ff1..04865aac2b3 100644 --- a/pkg/api/rchash.go +++ b/pkg/api/rchash.go @@ -6,7 +6,6 @@ package api import ( "net/http" - "strconv" "time" "github.com/ethersphere/bee/pkg/jsonhttp" @@ -24,27 +23,21 @@ type rchash struct { // no documentation or tests are added here. This should be removed before next // breaking release. func (s *Service) rchasher(w http.ResponseWriter, r *http.Request) { - - start := time.Now() - depthStr := mux.Vars(r)["depth"] - - depth, err := strconv.ParseUint(depthStr, 10, 8) - if err != nil { - s.logger.Error(err, "reserve commitment hasher: invalid depth") - jsonhttp.BadRequest(w, "invalid depth") + logger := s.logger.WithName("get_rchash").Build() + + paths := struct { + Depth uint8 `map:"depth" validate:"required"` + Anchor string `map:"anchor" validate:"required"` + }{} + if response := s.mapStructure(mux.Vars(r), &paths); response != nil { + response("invalid path params", logger, w) return } - if depth > 255 { - depth = 255 - } - - anchorStr := mux.Vars(r)["anchor"] - anchor := []byte(anchorStr) - - sample, err := s.storer.ReserveSample(r.Context(), anchor, uint8(depth), uint64(time.Now().Nanosecond())) + start := time.Now() + sample, err := s.storer.ReserveSample(r.Context(), []byte(paths.Anchor), paths.Depth, uint64(start.Nanosecond())) if err != nil { - s.logger.Error(err, "reserve commitment hasher: failed generating sample") + logger.Error(err, "reserve commitment hasher: failed generating sample") jsonhttp.InternalServerError(w, "failed generating sample") return } diff --git a/pkg/api/settlements_test.go b/pkg/api/settlements_test.go index 88d9ed32c08..bc06f540452 100644 --- a/pkg/api/settlements_test.go +++ b/pkg/api/settlements_test.go @@ -175,6 +175,55 @@ func TestSettlementsPeersNoSettlements(t *testing.T) { }) } +func Test_peerSettlementsHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + peer string + want jsonhttp.StatusResponse + }{{ + name: "peer - odd hex string", + peer: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "peer", + Error: api.ErrHexLength.Error(), + }, + }, + }, + }, { + name: "peer - invalid hex character", + peer: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "peer", + Error: api.HexInvalidByteError('G').Error(), + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodGet, "/settlements/"+tc.peer, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + func TestSettlementsPeersError(t *testing.T) { t.Parallel() diff --git a/pkg/api/soc.go b/pkg/api/soc.go index e4b206b1cd5..c33ef939ead 100644 --- a/pkg/api/soc.go +++ b/pkg/api/soc.go @@ -42,7 +42,6 @@ func (s *Service) socUploadHandler(w http.ResponseWriter, r *http.Request) { return } - // TODO: return structured error response as in validation case. data, err := io.ReadAll(r.Body) if err != nil { if jsonhttp.HandleBodyReadError(err, w) { diff --git a/pkg/api/staking.go b/pkg/api/staking.go index 96d45ba3ac1..33f54827bae 100644 --- a/pkg/api/staking.go +++ b/pkg/api/staking.go @@ -43,7 +43,6 @@ func (s *Service) stakingDepositHandler(w http.ResponseWriter, r *http.Request) return } - // TODO: provide the reason in the response. err := s.stakingContract.DepositStake(r.Context(), paths.Amount) if err != nil { if errors.Is(err, staking.ErrInsufficientStakeAmount) { diff --git a/pkg/api/staking_test.go b/pkg/api/staking_test.go index 351504868c2..a3a76ea13e7 100644 --- a/pkg/api/staking_test.go +++ b/pkg/api/staking_test.go @@ -131,3 +131,39 @@ func TestGetStake(t *testing.T) { jsonhttptest.WithExpectedJSONResponse(&jsonhttp.StatusResponse{Code: http.StatusInternalServerError, Message: "get staked amount failed"})) }) } + +func Test_stakingDepositHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + amount string + want jsonhttp.StatusResponse + }{{ + name: "amount - invalid value", + amount: "a", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "amount", + Error: "invalid value", + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodPost, "/stake/deposit/"+tc.amount, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} diff --git a/pkg/api/stewardship.go b/pkg/api/stewardship.go index dd9e4b03e6d..96207f750ce 100644 --- a/pkg/api/stewardship.go +++ b/pkg/api/stewardship.go @@ -5,10 +5,9 @@ package api import ( - "errors" "net/http" - "github.com/ethersphere/bee/pkg/resolver" + "github.com/ethersphere/bee/pkg/swarm" "github.com/ethersphere/bee/pkg/jsonhttp" "github.com/gorilla/mux" @@ -19,40 +18,16 @@ func (s *Service) stewardshipPutHandler(w http.ResponseWriter, r *http.Request) logger := s.logger.WithName("put_stewardship").Build() paths := struct { - Address string `map:"address" validate:"required"` + Address swarm.Address `map:"address,resolve" validate:"required"` }{} if response := s.mapStructure(mux.Vars(r), &paths); response != nil { response("invalid path params", logger, w) return } - // TODO: normalize the response errors as in validation case. - address, err := s.resolveNameOrAddress(paths.Address) - switch { - case errors.Is(err, resolver.ErrParse), errors.Is(err, resolver.ErrInvalidContentHash): - logger.Debug("mapStructure address string failed", "string", paths.Address, "error", err) - logger.Error(nil, "invalid address") - jsonhttp.BadRequest(w, "invalid address") - return - case errors.Is(err, resolver.ErrNotFound): - logger.Debug("address not found", "string", paths.Address, "error", err) - logger.Error(nil, "address not found") - jsonhttp.NotFound(w, "address not found") - return - case errors.Is(err, resolver.ErrServiceNotAvailable): - logger.Debug("service unavailable", "string", paths.Address, "error", err) - logger.Error(nil, "service unavailable") - jsonhttp.InternalServerError(w, "resolver service unavailable") - return - case err != nil: - logger.Debug("resolve address or name string failed", "string", paths.Address, "error", err) - logger.Error(nil, "resolve address or name string failed") - jsonhttp.InternalServerError(w, "resolve name or address") - return - } - err = s.steward.Reupload(r.Context(), address) + err := s.steward.Reupload(r.Context(), paths.Address) if err != nil { - logger.Debug("re-upload failed", "chunk_address", address, "error", err) + logger.Debug("re-upload failed", "chunk_address", paths.Address, "error", err) logger.Error(nil, "re-upload failed") jsonhttp.InternalServerError(w, "re-upload failed") return @@ -69,24 +44,16 @@ func (s *Service) stewardshipGetHandler(w http.ResponseWriter, r *http.Request) logger := s.logger.WithName("get_stewardship").Build() paths := struct { - Address string `map:"address" validate:"required"` + Address swarm.Address `map:"address,resolve" validate:"required"` }{} if response := s.mapStructure(mux.Vars(r), &paths); response != nil { response("invalid path params", logger, w) return } - // TODO: normalize the response errors as in validation case. - address, err := s.resolveNameOrAddress(paths.Address) - if err != nil { - logger.Debug("mapStructure address string failed", "string", paths.Address, "error", err) - logger.Error(nil, "mapStructure address string failed") - jsonhttp.NotFound(w, nil) - return - } - res, err := s.steward.IsRetrievable(r.Context(), address) + res, err := s.steward.IsRetrievable(r.Context(), paths.Address) if err != nil { - logger.Debug("is retrievable check failed", "chunk_address", address, "error", err) + logger.Debug("is retrievable check failed", "chunk_address", paths.Address, "error", err) logger.Error(nil, "is retrievable") jsonhttp.InternalServerError(w, "is retrievable check failed") return diff --git a/pkg/api/stewardship_test.go b/pkg/api/stewardship_test.go index 5a9eeb3c75b..67bf09bc508 100644 --- a/pkg/api/stewardship_test.go +++ b/pkg/api/stewardship_test.go @@ -13,8 +13,6 @@ import ( "github.com/ethersphere/bee/pkg/jsonhttp" "github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest" "github.com/ethersphere/bee/pkg/log" - "github.com/ethersphere/bee/pkg/resolver" - resolverMock "github.com/ethersphere/bee/pkg/resolver/mock" statestore "github.com/ethersphere/bee/pkg/statestore/mock" "github.com/ethersphere/bee/pkg/steward/mock" smock "github.com/ethersphere/bee/pkg/storage/mock" @@ -63,63 +61,54 @@ func TestStewardship(t *testing.T) { }) } -func TestStewardshipInputValidations(t *testing.T) { +func Test_stewardshipHandlers_invalidInputs(t *testing.T) { t.Parallel() - var ( - logger = log.Noop - statestoreMock = statestore.NewStateStore() - stewardMock = &mock.Steward{} - storer = smock.NewStorer() - ) - client, _, _, _ := newTestServer(t, testServerOptions{ - Storer: storer, - Tags: tags.NewTags(statestoreMock, logger), - Logger: logger, - Steward: stewardMock, - Resolver: resolverMock.NewResolver( - resolverMock.WithResolveFunc( - func(string) (swarm.Address, error) { - return swarm.Address{}, resolver.ErrParse + client, _, _, _ := newTestServer(t, testServerOptions{}) + + tests := []struct { + name string + address string + want jsonhttp.StatusResponse + }{{ + name: "address - odd hex string", + address: "123", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.ErrHexLength.Error(), }, - ), - ), - }) - for _, tt := range []struct { - name string - reference string - expectedStatus int - expectedMessage string - }{ - { - name: "correct reference", - reference: "1e477b015af480e387fbf5edd90f1685a30c0e3ba88eeb3871b326b816a542da", - expectedStatus: http.StatusOK, - expectedMessage: http.StatusText(http.StatusOK), + }, }, - { - name: "reference not found", - reference: "1e477b015af480e387fbf5edd90f1685a30c0e3ba88eeb3871b326b816a542d/", - expectedStatus: http.StatusNotFound, - expectedMessage: http.StatusText(http.StatusNotFound), - }, - { - name: "incorrect reference", - reference: "xc0f6", - expectedStatus: http.StatusBadRequest, - expectedMessage: "invalid address", + }, { + name: "address - invalid hex character", + address: "123G", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "address", + Error: api.HexInvalidByteError('G').Error(), + }, + }, }, - } { - tt := tt - t.Run("input validation -"+tt.name, func(t *testing.T) { - t.Parallel() + }} - jsonhttptest.Request(t, client, http.MethodPut, "/v1/stewardship/"+tt.reference, tt.expectedStatus, - jsonhttptest.WithExpectedJSONResponse(jsonhttp.StatusResponse{ - Message: tt.expectedMessage, - Code: tt.expectedStatus, - }), - ) - }) + for _, method := range []string{http.MethodGet, http.MethodPut} { + method := method + for _, tc := range tests { + tc := tc + t.Run(method+" "+tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, method, "/stewardship/"+tc.address, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } } } diff --git a/pkg/api/subdomain.go b/pkg/api/subdomain.go index 653ec2d51fd..2e90c4ac1ce 100644 --- a/pkg/api/subdomain.go +++ b/pkg/api/subdomain.go @@ -8,7 +8,7 @@ import ( "net/http" "strings" - "github.com/ethersphere/bee/pkg/jsonhttp" + "github.com/ethersphere/bee/pkg/swarm" "github.com/ethersphere/bee/pkg/tracing" "github.com/gorilla/mux" ) @@ -17,8 +17,8 @@ func (s *Service) subdomainHandler(w http.ResponseWriter, r *http.Request) { logger := tracing.NewLoggerWithTraceID(r.Context(), s.logger.WithName("get_subdomain").Build()) paths := struct { - Subdomain string `map:"subdomain" validate:"required"` - Path string `map:"path"` + Subdomain swarm.Address `map:"subdomain,resolve" validate:"required"` + Path string `map:"path"` }{} if response := s.mapStructure(mux.Vars(r), &paths); response != nil { response("invalid path params", logger, w) @@ -28,13 +28,5 @@ func (s *Service) subdomainHandler(w http.ResponseWriter, r *http.Request) { paths.Path = strings.TrimRight(paths.Path, "/") + "/" // NOTE: leave one slash if there was some. } - address, err := s.resolveNameOrAddress(paths.Subdomain) - if err != nil { - logger.Debug("subdomain get: mapStructure address string failed", "string", paths.Subdomain, "error", err) - logger.Error(nil, "subdomain get: mapStructure address string failed") - jsonhttp.NotFound(w, nil) - return - } - - s.serveReference(logger, address, paths.Path, w, r) + s.serveReference(logger, paths.Subdomain, paths.Path, w, r) } diff --git a/pkg/api/tag_debug_test.go b/pkg/api/tag_debug_test.go index 06d6762222a..b16a7a1ba12 100644 --- a/pkg/api/tag_debug_test.go +++ b/pkg/api/tag_debug_test.go @@ -8,9 +8,11 @@ import ( "context" "fmt" "net/http" + "strconv" "testing" "github.com/ethersphere/bee/pkg/api" + "github.com/ethersphere/bee/pkg/jsonhttp" "github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest" "github.com/ethersphere/bee/pkg/log" mockpost "github.com/ethersphere/bee/pkg/postage/mock" @@ -68,6 +70,42 @@ func TestDebugTags(t *testing.T) { }) } +func Test_getDebugTagHandler_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{DebugAPI: true}) + + tests := []struct { + name string + tagID string + want jsonhttp.StatusResponse + }{{ + name: "id - invalid value", + tagID: "a", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "id", + Error: strconv.ErrSyntax.Error(), + }, + }, + }, + }} + + for _, tc := range tests { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, http.MethodGet, "/tags/"+tc.tagID, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } +} + func debugTagsWithIdResource(id uint32) string { return fmt.Sprintf("/tags/%d", id) } func debugTagValueTest(t *testing.T, id uint32, split, stored, seen, sent, synced, total int64, address swarm.Address, client *http.Client) { diff --git a/pkg/api/tag_test.go b/pkg/api/tag_test.go index a2f5b3ea6fd..21de8be910f 100644 --- a/pkg/api/tag_test.go +++ b/pkg/api/tag_test.go @@ -391,6 +391,45 @@ func TestTags(t *testing.T) { }) } +func Test_tagHandlers_invalidInputs(t *testing.T) { + t.Parallel() + + client, _, _, _ := newTestServer(t, testServerOptions{}) + + tests := []struct { + name string + tagID string + want jsonhttp.StatusResponse + }{{ + name: "id - invalid value", + tagID: "a", + want: jsonhttp.StatusResponse{ + Code: http.StatusBadRequest, + Message: "invalid path params", + Reasons: []jsonhttp.Reason{ + { + Field: "id", + Error: strconv.ErrSyntax.Error(), + }, + }, + }, + }} + + for _, method := range []string{http.MethodGet, http.MethodDelete, http.MethodPatch} { + method := method + for _, tc := range tests { + tc := tc + t.Run(method+" "+tc.name, func(t *testing.T) { + t.Parallel() + + jsonhttptest.Request(t, client, method, "/tags/"+tc.tagID, tc.want.Code, + jsonhttptest.WithExpectedJSONResponse(tc.want), + ) + }) + } + } +} + // isTagFoundInResponse verifies that the tag id is found in the supplied HTTP headers // if an API tag response is supplied, it also verifies that it contains an id which matches the headers func isTagFoundInResponse(t *testing.T, headers http.Header, tr *api.TagResponse) uint32 { diff --git a/pkg/api/util.go b/pkg/api/util.go index c2b41ab48f1..950be4ab068 100644 --- a/pkg/api/util.go +++ b/pkg/api/util.go @@ -6,12 +6,10 @@ package api import ( "crypto/ecdsa" - "encoding/base64" "encoding/hex" "errors" "fmt" "math/big" - "mime" "reflect" "strconv" "strings" @@ -110,18 +108,6 @@ var flattenErrorsFormat = func(es []error) string { ) } -// preMapHooks is a set of hooks that are called before the value is parsed. -var preMapHooks = map[string]func(v string) (string, error){ - "mimeMediaType": func(v string) (string, error) { - typ, _, err := mime.ParseMediaType(v) - return typ, err - }, - "decBase64url": func(v string) (string, error) { - buf, err := base64.URLEncoding.DecodeString(v) - return string(buf), err - }, -} - // mapStructure maps the input to the output values. // The input is one of the following: // - map[string]string @@ -146,7 +132,7 @@ var preMapHooks = map[string]func(v string) (string, error){ // // In case of parsing error, a new parseError is returned to the caller. // The caller can use the Unwrap method to get the original error. -func mapStructure(input, output interface{}) (err error) { +func mapStructure(input, output interface{}, hooks map[string]func(v string) (string, error)) (err error) { if input == nil || output == nil { return nil } @@ -237,6 +223,9 @@ func mapStructure(input, output interface{}) (err error) { case common.Hash: val := common.HexToHash(value) field.Set(reflect.ValueOf(val)) + case common.Address: + val := common.HexToAddress(value) + field.Set(reflect.ValueOf(val)) } case reflect.Struct: switch field.Interface().(type) { @@ -277,6 +266,35 @@ func mapStructure(input, output interface{}) (err error) { return nil } + // parseFieldTags parses the given field tags into name, hook, and omitempty. + parseFieldTags := func(field reflect.StructField) (name string, hook func(v string) (string, error), omitempty bool) { + hook = func(v string) (string, error) { return v, nil } + + val, ok := field.Tag.Lookup(mapStructureTagName) + if !ok { + return field.Name, hook, false + } + + tags := strings.SplitN(val, ",", 3) + name = tags[0] + for _, tag := range tags[1:] { + switch tag { + case "omitempty": + omitempty = true + default: + if len(hooks) == 0 { + panic(errors.New("zero registered hooks")) + } + hook, ok = hooks[tag] + if !ok { + panic(fmt.Errorf("unknown hook %q for field: %s", tag, field.Name)) + } + } + } + + return name, hook, omitempty + } + // Map input into output. pErrs := &multierror.Error{ErrorFormat: flattenErrorsFormat} for i := 0; i < outputVal.NumField(); i++ { @@ -306,29 +324,6 @@ func mapStructure(input, output interface{}) (err error) { return pErrs.ErrorOrNil() } -// parseFieldTags parses the given field tags into name, hook, and omitempty. -func parseFieldTags(field reflect.StructField) (name string, hook func(v string) (string, error), omitempty bool) { - hook = func(v string) (string, error) { return v, nil } - - val, ok := field.Tag.Lookup(mapStructureTagName) - if !ok { - return field.Name, hook, false - } - - tags := strings.SplitN(val, ",", 3) - name = tags[0] - for _, tag := range tags[1:] { - switch tag { - case "omitempty": - omitempty = true - default: - hook = preMapHooks[tag] - } - } - - return name, hook, omitempty -} - // numberSize returns the size of the number in bits. func numberSize(k reflect.Kind) int { switch k { diff --git a/pkg/api/util_test.go b/pkg/api/util_test.go index 4a857881021..5f8dd3564b4 100644 --- a/pkg/api/util_test.go +++ b/pkg/api/util_test.go @@ -505,12 +505,12 @@ func TestMapStructure(t *testing.T) { t.Parallel() have := reflect.New(reflect.TypeOf(tc.want).Elem()).Interface() - haveErr := errors.Unwrap(api.MapStructure(tc.src, have)) + haveErr := errors.Unwrap(api.MapStructure(tc.src, have, nil)) if diff := cmp.Diff(tc.wantErr, haveErr); diff != "" { - t.Fatalf("api.MapStructure(...): error mismatch (-want +have):\n%s", diff) + t.Fatalf("api.mapStructure(...): error mismatch (-want +have):\n%s", diff) } if diff := cmp.Diff(tc.want, have, cmp.AllowUnexported(big.Int{})); diff != "" { - t.Errorf("api.MapStructure(...): result mismatch (-want +have):\n%s", diff) + t.Errorf("api.mapStructure(...): result mismatch (-want +have):\n%s", diff) } }) } @@ -523,7 +523,7 @@ func TestMapStructure_InputOutputSanityCheck(t *testing.T) { t.Parallel() var input interface{} - err := api.MapStructure(input, struct{}{}) + err := api.MapStructure(input, struct{}{}, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -533,7 +533,7 @@ func TestMapStructure_InputOutputSanityCheck(t *testing.T) { t.Parallel() input := "foo" - err := api.MapStructure(&input, struct{}{}) + err := api.MapStructure(&input, struct{}{}, nil) if err == nil { t.Fatalf("expected error; have none") } @@ -548,7 +548,7 @@ func TestMapStructure_InputOutputSanityCheck(t *testing.T) { SomeVal string `map:"someVal"` } ) - err := api.MapStructure(&input, output) + err := api.MapStructure(&input, output, nil) if err == nil { t.Fatalf("expected error; have none") } @@ -561,7 +561,7 @@ func TestMapStructure_InputOutputSanityCheck(t *testing.T) { input = map[string]interface{}{"someVal": "123"} output interface{} ) - err := api.MapStructure(&input, output) + err := api.MapStructure(&input, output, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -576,7 +576,7 @@ func TestMapStructure_InputOutputSanityCheck(t *testing.T) { SomeVal string `map:"someVal"` }{} ) - err := api.MapStructure(&input, &output) + err := api.MapStructure(&input, &output, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } @@ -589,7 +589,7 @@ func TestMapStructure_InputOutputSanityCheck(t *testing.T) { input = map[string]interface{}{"someVal": "123"} output = "foo" ) - err := api.MapStructure(&input, &output) + err := api.MapStructure(&input, &output, nil) if err == nil { t.Fatalf("expected error; have none") } diff --git a/pkg/api/welcome_message.go b/pkg/api/welcome_message.go index 4ded83d5ca5..0d51d4977f7 100644 --- a/pkg/api/welcome_message.go +++ b/pkg/api/welcome_message.go @@ -21,7 +21,7 @@ type welcomeMessageResponse struct { WelcomeMesssage string `json:"welcomeMessage"` } -func (s *Service) getWelcomeMessageHandler(w http.ResponseWriter, r *http.Request) { +func (s *Service) getWelcomeMessageHandler(w http.ResponseWriter, _ *http.Request) { val := s.p2p.GetWelcomeMessage() jsonhttp.OK(w, welcomeMessageResponse{ WelcomeMesssage: val, diff --git a/pkg/jsonhttp/jsonhttp.go b/pkg/jsonhttp/jsonhttp.go index 03895e83b98..defc88b147f 100644 --- a/pkg/jsonhttp/jsonhttp.go +++ b/pkg/jsonhttp/jsonhttp.go @@ -26,7 +26,7 @@ var ( // Reason represents a reason for an invalid request entry. type Reason struct { - Entry string `json:"field"` + Field string `json:"field"` Error string `json:"error"` } diff --git a/pkg/resolver/client/ens/ens.go b/pkg/resolver/client/ens/ens.go index 781925c2ef9..8e5429daf6b 100644 --- a/pkg/resolver/client/ens/ens.go +++ b/pkg/resolver/client/ens/ens.go @@ -36,8 +36,8 @@ var ( ErrFailedToConnect = errors.New("failed to connect") // ErrResolveFailed denotes that a name could not be resolved. ErrResolveFailed = errors.New("resolve failed") - // errNotImplemented denotes that the function has not been implemented. - errNotImplemented = errors.New("function not implemented") + // ErrNotImplemented denotes that the function has not been implemented. + ErrNotImplemented = errors.New("function not implemented") // errNameNotRegistered denotes that the name is not registered. errNameNotRegistered = errors.New("name is not registered") ) @@ -76,7 +76,7 @@ func NewClient(endpoint string, opts ...Option) (client.Interface, error) { // Establish a connection to the ENS. if c.connectFn == nil { - return nil, fmt.Errorf("connectFn: %w", errNotImplemented) + return nil, fmt.Errorf("connectFn: %w", ErrNotImplemented) } ethCl, registry, err := c.connectFn(c.endpoint, c.contractAddr) if err != nil { @@ -109,7 +109,7 @@ func (c *Client) Endpoint() string { // Resolve implements the resolver.Client interface. func (c *Client) Resolve(name string) (Address, error) { if c.resolveFn == nil { - return swarm.ZeroAddress, fmt.Errorf("resolveFn: %w", errNotImplemented) + return swarm.ZeroAddress, fmt.Errorf("resolveFn: %w", ErrNotImplemented) } hash, err := c.resolveFn(c.registry, common.HexToAddress(c.contractAddr), name) diff --git a/pkg/resolver/client/ens/export_test.go b/pkg/resolver/client/ens/export_test.go index 203389d830d..2749e5484aa 100644 --- a/pkg/resolver/client/ens/export_test.go +++ b/pkg/resolver/client/ens/export_test.go @@ -12,8 +12,6 @@ import ( const SwarmContentHashPrefix = swarmContentHashPrefix -var ErrNotImplemented = errNotImplemented - // WithConnectFunc will set the Dial function implementation. func WithConnectFunc(fn func(endpoint string, contractAddr string) (*ethclient.Client, *goens.Registry, error)) Option { return func(c *Client) { diff --git a/pkg/resolver/mock/resolver.go b/pkg/resolver/mock/resolver.go index 692c69e35d7..547abd6270b 100644 --- a/pkg/resolver/mock/resolver.go +++ b/pkg/resolver/mock/resolver.go @@ -5,18 +5,15 @@ package mock import ( - "errors" "fmt" "github.com/ethersphere/bee/pkg/resolver" + "github.com/ethersphere/bee/pkg/resolver/client/ens" ) // Assure mock Resolver implements the Resolver interface. var _ resolver.Interface = (*Resolver)(nil) -// ErrNotImplemented denotes a function has not been implemented. -var ErrNotImplemented = errors.New("not implemented") - // Resolver is the mock Resolver implementation. type Resolver struct { IsClosed bool @@ -50,7 +47,7 @@ func (r *Resolver) Resolve(name string) (resolver.Address, error) { if r.resolveFunc != nil { return r.resolveFunc(name) } - return resolver.Address{}, fmt.Errorf("resolveFunc: %w", ErrNotImplemented) + return resolver.Address{}, fmt.Errorf("resolveFunc: %w", ens.ErrNotImplemented) } // Close implements the Resolver interface.