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

add jsonhttptest package and debugapi tests

parent 52bbe55a
......@@ -7,11 +7,9 @@ package api
import (
"net/http"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/logging"
"github.com/ethersphere/bee/pkg/pingpong"
"github.com/prometheus/client_golang/prometheus"
"resenje.org/web"
)
type Service interface {
......@@ -40,9 +38,3 @@ func New(o Options) Service {
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 @@
package api
import (
"bytes"
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"github.com/ethersphere/bee/pkg/logging"
......@@ -19,8 +16,6 @@ import (
"resenje.org/web"
)
var _ = testResponseUnmarshal // avoid lint error for unused function
type testServerOptions struct {
Pingpong pingpong.Interface
}
......@@ -45,64 +40,3 @@ func newTestServer(t *testing.T, o testServerOptions) (client *http.Client, clea
}
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 (
"time"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/ethersphere/bee/pkg/jsonhttp/jsonhttptest"
"github.com/ethersphere/bee/pkg/p2p"
pingpongmock "github.com/ethersphere/bee/pkg/pingpong/mock"
)
......@@ -39,27 +40,27 @@ func TestPingpong(t *testing.T) {
defer cleanup()
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,
})
})
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,
Message: "peer not found",
})
})
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,
Message: testErr.Error(),
})
})
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,
Message: http.StatusText(http.StatusMethodNotAllowed),
})
......
......@@ -8,6 +8,7 @@ import (
"fmt"
"net/http"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"resenje.org/web"
......@@ -20,7 +21,7 @@ func (s *server) setupRouting() {
fmt.Fprintln(w, "User-agent: *\nDisallow: /")
})
baseRouter.Handle("/pingpong/{peer-id}", methodHandler{
baseRouter.Handle("/pingpong/{peer-id}", jsonhttp.MethodHandler{
"POST": http.HandlerFunc(s.pingpongHandler),
})
......
......@@ -8,6 +8,7 @@ import (
"net/http"
"github.com/ethersphere/bee/pkg/p2p"
"github.com/ethersphere/bee/pkg/logging"
"github.com/prometheus/client_golang/prometheus"
)
......@@ -25,6 +26,7 @@ type server struct {
type Options struct {
P2P p2p.Service
Logger logging.Logger
}
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 {
func (s *server) peerConnectHandler(w http.ResponseWriter, r *http.Request) {
addr, err := multiaddr.NewMultiaddr("/" + mux.Vars(r)["multi-address"])
if err != nil {
s.Logger.Debugf("debug api: peer connect: parse multiaddress: %w", err)
jsonhttp.BadRequest(w, err.Error())
return
}
address, err := s.P2P.Connect(r.Context(), addr)
if err != nil {
s.Logger.Errorf("debug api: peer connect: %w", err)
jsonhttp.InternalServerError(w, err.Error())
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 (
"net/http"
"net/http/pprof"
"github.com/ethersphere/bee/pkg/jsonhttp"
"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/prometheus/client_golang/prometheus/promhttp"
......@@ -42,7 +43,9 @@ func (s *server) setupRouting() {
internalRouter.HandleFunc("/health", 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
}
......@@ -5,10 +5,17 @@
package debugapi
import (
"fmt"
"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) {
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
import (
"context"
"errors"
"fmt"
"io"
"sync"
"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 {
records map[string][]Record
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