Skip to content

Commit

Permalink
test: add tests for http inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
mrekucci committed Oct 18, 2022
1 parent 07470b8 commit 25f9f1c
Show file tree
Hide file tree
Showing 38 changed files with 1,427 additions and 278 deletions.
4 changes: 2 additions & 2 deletions openapi/Swarm.yaml
Expand Up @@ -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

Expand Down
33 changes: 29 additions & 4 deletions pkg/api/api.go
Expand Up @@ -9,13 +9,15 @@ package api
import (
"context"
"crypto/ecdsa"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"math"
"math/big"
"mime"
"net/http"
"reflect"
"strconv"
Expand All @@ -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"
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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(),
})
}
Expand All @@ -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)
}

Expand Down
98 changes: 98 additions & 0 deletions pkg/api/balances_test.go
Expand Up @@ -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),
)
})
}
}
27 changes: 5 additions & 22 deletions pkg/api/bytes.go
Expand Up @@ -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
Expand Down
106 changes: 88 additions & 18 deletions pkg/api/bytes_test.go
Expand Up @@ -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{
Expand All @@ -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
Expand Down Expand Up @@ -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),
)
})
}
}

0 comments on commit 25f9f1c

Please sign in to comment.