Commit 1b8135c0 authored by Janos Guljas's avatar Janos Guljas

add jsonhttptest package and debugapi tests

parent 52bbe55a
...@@ -7,11 +7,9 @@ package api ...@@ -7,11 +7,9 @@ package api
import ( import (
"net/http" "net/http"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/logging" "github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/pingpong" "github.com/ethersphere/bee/pkg/pingpong"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"resenje.org/web"
) )
type Service interface { type Service interface {
...@@ -40,9 +38,3 @@ func New(o Options) Service { ...@@ -40,9 +38,3 @@ func New(o Options) Service {
return s return s
} }
type methodHandler map[string]http.Handler
func (h methodHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
web.HandleMethods(h, `{"message":"Method Not Allowed","code":405}`, jsonhttp.DefaultContentTypeHeader, w, r)
}
...@@ -5,13 +5,10 @@ ...@@ -5,13 +5,10 @@
package api package api
import ( import (
"bytes"
"encoding/json"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"strings"
"testing" "testing"
"github.com/ethersphere/bee/pkg/logging" "github.com/ethersphere/bee/pkg/logging"
...@@ -19,8 +16,6 @@ import ( ...@@ -19,8 +16,6 @@ import (
"resenje.org/web" "resenje.org/web"
) )
var _ = testResponseUnmarshal // avoid lint error for unused function
type testServerOptions struct { type testServerOptions struct {
Pingpong pingpong.Interface Pingpong pingpong.Interface
} }
...@@ -45,64 +40,3 @@ func newTestServer(t *testing.T, o testServerOptions) (client *http.Client, clea ...@@ -45,64 +40,3 @@ func newTestServer(t *testing.T, o testServerOptions) (client *http.Client, clea
} }
return client, cleanup return client, cleanup
} }
func testResponseDirect(t *testing.T, client *http.Client, method, url, body string, responseCode int, response interface{}) {
t.Helper()
resp, err := request(client, method, url, body, responseCode)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != responseCode {
t.Errorf("got response status %s, want %v %s", resp.Status, responseCode, http.StatusText(responseCode))
}
gotBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
wantBytes, err := json.Marshal(response)
if err != nil {
t.Error(err)
}
got := string(bytes.TrimSpace(gotBytes))
want := string(wantBytes)
if got != want {
t.Errorf("got response %s, want %s", got, want)
}
}
func testResponseUnmarshal(t *testing.T, client *http.Client, method, url, body string, responseCode int, response interface{}) {
t.Helper()
resp, err := request(client, method, url, body, responseCode)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != responseCode {
t.Errorf("got response status %s, want %v %s", resp.Status, responseCode, http.StatusText(responseCode))
}
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
t.Fatal(err)
}
}
func request(client *http.Client, method, url, body string, responseCode int) (resp *http.Response, err error) {
req, err := http.NewRequest(method, url, strings.NewReader(body))
if err != nil {
return nil, err
}
resp, err = client.Do(req)
if err != nil {
return nil, err
}
return resp, nil
}
...@@ -12,6 +12,7 @@ import ( ...@@ -12,6 +12,7 @@ import (
"time" "time"
"github.com/ethersphere/bee/pkg/jsonhttp" "github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/p2p" "github.com/ethersphere/bee/pkg/p2p"
pingpongmock "github.com/ethersphere/bee/pkg/pingpong/mock" pingpongmock "github.com/ethersphere/bee/pkg/pingpong/mock"
) )
...@@ -39,27 +40,27 @@ func TestPingpong(t *testing.T) { ...@@ -39,27 +40,27 @@ func TestPingpong(t *testing.T) {
defer cleanup() defer cleanup()
t.Run("ok", func(t *testing.T) { t.Run("ok", func(t *testing.T) {
testResponseDirect(t, client, http.MethodPost, "/pingpong/"+peerID, "", http.StatusOK, pingpongResponse{ jsonhttptest.ResponseDirect(t, client, http.MethodPost, "/pingpong/"+peerID, nil, http.StatusOK, pingpongResponse{
RTT: rtt, RTT: rtt,
}) })
}) })
t.Run("peer not found", func(t *testing.T) { t.Run("peer not found", func(t *testing.T) {
testResponseDirect(t, client, http.MethodPost, "/pingpong/"+unknownPeerID, "", http.StatusNotFound, jsonhttp.StatusResponse{ jsonhttptest.ResponseDirect(t, client, http.MethodPost, "/pingpong/"+unknownPeerID, nil, http.StatusNotFound, jsonhttp.StatusResponse{
Code: http.StatusNotFound, Code: http.StatusNotFound,
Message: "peer not found", Message: "peer not found",
}) })
}) })
t.Run("error", func(t *testing.T) { t.Run("error", func(t *testing.T) {
testResponseDirect(t, client, http.MethodPost, "/pingpong/"+errorPeerID, "", http.StatusInternalServerError, jsonhttp.StatusResponse{ jsonhttptest.ResponseDirect(t, client, http.MethodPost, "/pingpong/"+errorPeerID, nil, http.StatusInternalServerError, jsonhttp.StatusResponse{
Code: http.StatusInternalServerError, Code: http.StatusInternalServerError,
Message: testErr.Error(), Message: testErr.Error(),
}) })
}) })
t.Run("get method not allowed", func(t *testing.T) { t.Run("get method not allowed", func(t *testing.T) {
testResponseDirect(t, client, http.MethodGet, "/pingpong/"+peerID, "", http.StatusMethodNotAllowed, jsonhttp.StatusResponse{ jsonhttptest.ResponseDirect(t, client, http.MethodGet, "/pingpong/"+peerID, nil, http.StatusMethodNotAllowed, jsonhttp.StatusResponse{
Code: http.StatusMethodNotAllowed, Code: http.StatusMethodNotAllowed,
Message: http.StatusText(http.StatusMethodNotAllowed), Message: http.StatusText(http.StatusMethodNotAllowed),
}) })
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"resenje.org/web" "resenje.org/web"
...@@ -20,7 +21,7 @@ func (s *server) setupRouting() { ...@@ -20,7 +21,7 @@ func (s *server) setupRouting() {
fmt.Fprintln(w, "User-agent: *\nDisallow: /") fmt.Fprintln(w, "User-agent: *\nDisallow: /")
}) })
baseRouter.Handle("/pingpong/{peer-id}", methodHandler{ baseRouter.Handle("/pingpong/{peer-id}", jsonhttp.MethodHandler{
"POST": http.HandlerFunc(s.pingpongHandler), "POST": http.HandlerFunc(s.pingpongHandler),
}) })
......
...@@ -8,6 +8,7 @@ import ( ...@@ -8,6 +8,7 @@ import (
"net/http" "net/http"
"github.com/ethersphere/bee/pkg/p2p" "github.com/ethersphere/bee/pkg/p2p"
"github.com/ethersphere/bee/pkg/logging"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
...@@ -25,6 +26,7 @@ type server struct { ...@@ -25,6 +26,7 @@ type server struct {
type Options struct { type Options struct {
P2P p2p.Service P2P p2p.Service
Logger logging.Logger
} }
func New(o Options) Service { func New(o Options) Service {
......
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package debugapi
import (
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/p2p"
"resenje.org/web"
)
type testServerOptions struct {
P2P p2p.Service
}
func newTestServer(t *testing.T, o testServerOptions) (client *http.Client, cleanup func()) {
s := New(Options{
P2P: o.P2P,
Logger: logging.New(ioutil.Discard),
})
ts := httptest.NewServer(s)
cleanup = ts.Close
client = &http.Client{
Transport: web.RoundTripperFunc(func(r *http.Request) (*http.Response, error) {
u, err := url.Parse(ts.URL + r.URL.String())
if err != nil {
return nil, err
}
r.URL = u
return ts.Client().Transport.RoundTrip(r)
}),
}
return client, cleanup
}
...@@ -19,12 +19,14 @@ type peerConnectResponse struct { ...@@ -19,12 +19,14 @@ type peerConnectResponse struct {
func (s *server) peerConnectHandler(w http.ResponseWriter, r *http.Request) { func (s *server) peerConnectHandler(w http.ResponseWriter, r *http.Request) {
addr, err := multiaddr.NewMultiaddr("/" + mux.Vars(r)["multi-address"]) addr, err := multiaddr.NewMultiaddr("/" + mux.Vars(r)["multi-address"])
if err != nil { if err != nil {
s.Logger.Debugf("debug api: peer connect: parse multiaddress: %w", err)
jsonhttp.BadRequest(w, err.Error()) jsonhttp.BadRequest(w, err.Error())
return return
} }
address, err := s.P2P.Connect(r.Context(), addr) address, err := s.P2P.Connect(r.Context(), addr)
if err != nil { if err != nil {
s.Logger.Errorf("debug api: peer connect: %w", err)
jsonhttp.InternalServerError(w, err.Error()) jsonhttp.InternalServerError(w, err.Error())
return return
} }
......
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package debugapi
import (
"context"
"errors"
"net/http"
"testing"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/p2p/mock"
ma "github.com/multiformats/go-multiaddr"
)
func TestConnect(t *testing.T) {
underlay := "/ip4/127.0.0.1/tcp/7070/p2p/16Uiu2HAkx8ULY8cTXhdVAcMmLcH9AsTKz6uBQ7DPLKRjMLgBVYkS"
errorUnderlay := "/ip4/127.0.0.1/tcp/7070/p2p/16Uiu2HAkw88cjH2orYrB6fDui4eUNdmgkwnDM8W681UbfsPgM9QY"
overlay := "985732527402"
testErr := errors.New("test error")
client, cleanup := newTestServer(t, testServerOptions{
P2P: mock.NewService(func(ctx context.Context, addr ma.Multiaddr) (string, error) {
if addr.String() == errorUnderlay {
return "", testErr
}
return overlay, nil
}),
})
defer cleanup()
t.Run("ok", func(t *testing.T) {
jsonhttptest.ResponseDirect(t, client, http.MethodPost, "/connect"+underlay, nil, http.StatusOK, peerConnectResponse{
Address: overlay,
})
})
t.Run("error", func(t *testing.T) {
jsonhttptest.ResponseDirect(t, client, http.MethodPost, "/connect"+errorUnderlay, nil, http.StatusInternalServerError, jsonhttp.StatusResponse{
Code: http.StatusInternalServerError,
Message: testErr.Error(),
})
})
t.Run("get method not allowed", func(t *testing.T) {
jsonhttptest.ResponseDirect(t, client, http.MethodGet, "/connect"+underlay, nil, http.StatusMethodNotAllowed, jsonhttp.StatusResponse{
Code: http.StatusMethodNotAllowed,
Message: http.StatusText(http.StatusMethodNotAllowed),
})
})
}
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"net/http/pprof" "net/http/pprof"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/gorilla/handlers" "github.com/gorilla/handlers"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
...@@ -42,7 +43,9 @@ func (s *server) setupRouting() { ...@@ -42,7 +43,9 @@ func (s *server) setupRouting() {
internalRouter.HandleFunc("/health", s.statusHandler) internalRouter.HandleFunc("/health", s.statusHandler)
internalRouter.HandleFunc("/readiness", s.statusHandler) internalRouter.HandleFunc("/readiness", s.statusHandler)
internalRouter.HandleFunc("/connect/{multi-address:.+}", s.peerConnectHandler) internalRouter.Handle("/connect/{multi-address:.+}", jsonhttp.MethodHandler{
"POST": http.HandlerFunc(s.peerConnectHandler),
})
s.Handler = internalBaseRouter s.Handler = internalBaseRouter
} }
...@@ -5,10 +5,17 @@ ...@@ -5,10 +5,17 @@
package debugapi package debugapi
import ( import (
"fmt"
"net/http" "net/http"
"github.com/ethersphere/bee/pkg/jsonhttp"
) )
type statusResponse struct {
Status string `json:"status"`
}
func (s *server) statusHandler(w http.ResponseWriter, r *http.Request) { func (s *server) statusHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, `{"status":"ok"}`) jsonhttp.OK(w, statusResponse{
Status: "ok",
})
} }
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package debugapi
import (
"net/http"
"testing"
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
)
func TestHealth(t *testing.T) {
client, cleanup := newTestServer(t, testServerOptions{})
defer cleanup()
jsonhttptest.ResponseDirect(t, client, http.MethodGet, "/health", nil, http.StatusOK, statusResponse{
Status: "ok",
})
}
func TestReadiness(t *testing.T) {
client, cleanup := newTestServer(t, testServerOptions{})
defer cleanup()
jsonhttptest.ResponseDirect(t, client, http.MethodGet, "/readiness", nil, http.StatusOK, statusResponse{
Status: "ok",
})
}
package jsonhttp
import (
"net/http"
"resenje.org/web"
)
type MethodHandler map[string]http.Handler
func (h MethodHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
web.HandleMethods(h, `{"message":"Method Not Allowed","code":405}`, DefaultContentTypeHeader, w, r)
}
// Copyright 2020 The Swarm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package jsonhttptest
import (
"bytes"
"encoding/json"
"io"
"io/ioutil"
"net/http"
"testing"
)
func ResponseDirect(t *testing.T, client *http.Client, method, url string, body io.Reader, responseCode int, response interface{}) {
t.Helper()
resp, err := request(client, method, url, body, responseCode)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != responseCode {
t.Errorf("got response status %s, want %v %s", resp.Status, responseCode, http.StatusText(responseCode))
}
gotBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatal(err)
}
wantBytes, err := json.Marshal(response)
if err != nil {
t.Error(err)
}
got := string(bytes.TrimSpace(gotBytes))
want := string(wantBytes)
if got != want {
t.Errorf("got response %s, want %s", got, want)
}
}
func ResponseUnmarshal(t *testing.T, client *http.Client, method, url string, body io.Reader, responseCode int, response interface{}) {
t.Helper()
resp, err := request(client, method, url, body, responseCode)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != responseCode {
t.Errorf("got response status %s, want %v %s", resp.Status, responseCode, http.StatusText(responseCode))
}
err = json.NewDecoder(resp.Body).Decode(&response)
if err != nil {
t.Fatal(err)
}
}
func request(client *http.Client, method, url string, body io.Reader, responseCode int) (resp *http.Response, err error) {
req, err := http.NewRequest(method, url, body)
if err != nil {
return nil, err
}
return client.Do(req)
}
...@@ -6,13 +6,31 @@ package mock ...@@ -6,13 +6,31 @@ package mock
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"sync" "sync"
"github.com/ethersphere/bee/pkg/p2p" "github.com/ethersphere/bee/pkg/p2p"
ma "github.com/multiformats/go-multiaddr"
) )
type Service struct {
connectFunc func(ctx context.Context, addr ma.Multiaddr) (overlay string, err error)
}
func NewService(connectFunc func(ctx context.Context, addr ma.Multiaddr) (overlay string, err error)) *Service {
return &Service{connectFunc: connectFunc}
}
func (s *Service) AddProtocol(_ p2p.ProtocolSpec) error {
return errors.New("not implemented")
}
func (s *Service) Connect(ctx context.Context, addr ma.Multiaddr) (overlay string, err error) {
return s.connectFunc(ctx, addr)
}
type Recorder struct { type Recorder struct {
records map[string][]Record records map[string][]Record
recordsMu sync.Mutex recordsMu sync.Mutex
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment