0fdc9cafb5
* fix(all): introduce and use iox.ReadAllContext This improvement over the ioutil.ReadAll utility returns early if the context expires. This enables us to unblock stuck code in case there's censorship confounding the TCP stack. See https://github.com/ooni/probe/issues/1417. Compared to the functionality postulated in the above mentioned issue, I choose to be more generic and separate limiting the maximum body size (not implemented here) from using the context to return early when reading a body (or any other reader). After implementing iox.ReadAllContext, I made sure we always use it everywhere in the tree instead of ioutil.ReadAll. This includes many parts of the codebase where in theory we don't need iox.ReadAllContext. Though, changing all the places makes checking whether we're not using ioutil.ReadAll where we should not be using it easy: `git grep` should return no lines. * Update internal/iox/iox_test.go * fix(ndt7): treat context errors as non-errors The rationale is explained by the comment documenting reduceErr. * Update internal/engine/experiment/ndt7/download.go
2777 lines
65 KiB
Go
2777 lines
65 KiB
Go
// Code generated by go generate; DO NOT EDIT.
|
|
// 2021-06-15 10:55:56.308361 +0200 CEST m=+0.000215751
|
|
|
|
package ooapi
|
|
|
|
//go:generate go run ./internal/generator -file apis_test.go
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/ooni/probe-cli/v3/internal/iox"
|
|
"github.com/ooni/probe-cli/v3/internal/ooapi/apimodel"
|
|
)
|
|
|
|
func TestCheckReportIDInvalidURL(t *testing.T) {
|
|
api := &simpleCheckReportIDAPI{
|
|
BaseURL: "\t", // invalid
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckReportIDRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckReportIDWithHTTPErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Err: errMocked}
|
|
api := &simpleCheckReportIDAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckReportIDRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckReportIDWithNewRequestErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleCheckReportIDAPI{
|
|
RequestMaker: &FakeRequestMaker{Err: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckReportIDRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckReportIDWith401(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
|
|
api := &simpleCheckReportIDAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckReportIDRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckReportIDWith400(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
|
|
api := &simpleCheckReportIDAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckReportIDRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrHTTPFailure) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckReportIDWithResponseBodyReadErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Err: errMocked},
|
|
}}
|
|
api := &simpleCheckReportIDAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckReportIDRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckReportIDWithUnmarshalFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`{}`)},
|
|
}}
|
|
api := &simpleCheckReportIDAPI{
|
|
HTTPClient: clnt,
|
|
JSONCodec: &FakeCodec{DecodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckReportIDRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
type handleCheckReportID struct {
|
|
accept string
|
|
body []byte
|
|
contentType string
|
|
count int32
|
|
method string
|
|
mu sync.Mutex
|
|
resp *apimodel.CheckReportIDResponse
|
|
url *url.URL
|
|
userAgent string
|
|
}
|
|
|
|
func (h *handleCheckReportID) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer h.mu.Unlock()
|
|
h.mu.Lock()
|
|
if h.count > 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.count++
|
|
if r.Body != nil {
|
|
data, err := iox.ReadAllContext(r.Context(), r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.body = data
|
|
}
|
|
h.method = r.Method
|
|
h.url = r.URL
|
|
h.accept = r.Header.Get("Accept")
|
|
h.contentType = r.Header.Get("Content-Type")
|
|
h.userAgent = r.Header.Get("User-Agent")
|
|
var out *apimodel.CheckReportIDResponse
|
|
ff := fakeFill{}
|
|
ff.fill(&out)
|
|
h.resp = out
|
|
data, err := json.Marshal(out)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
w.Write(data)
|
|
}
|
|
|
|
func TestCheckReportIDRoundTrip(t *testing.T) {
|
|
// setup
|
|
handler := &handleCheckReportID{}
|
|
srvr := httptest.NewServer(handler)
|
|
defer srvr.Close()
|
|
req := &apimodel.CheckReportIDRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(&req)
|
|
api := &simpleCheckReportIDAPI{BaseURL: srvr.URL}
|
|
ff.fill(&api.UserAgent)
|
|
// issue request
|
|
ctx := context.Background()
|
|
resp, err := api.Call(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected non-nil response here")
|
|
}
|
|
// compare our response and server's one
|
|
if diff := cmp.Diff(handler.resp, resp); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
// check whether headers are OK
|
|
if handler.accept != "application/json" {
|
|
t.Fatal("invalid accept header")
|
|
}
|
|
if handler.userAgent != api.UserAgent {
|
|
t.Fatal("invalid user-agent header")
|
|
}
|
|
// check whether the method is OK
|
|
if handler.method != "GET" {
|
|
t.Fatal("invalid method")
|
|
}
|
|
// check the query
|
|
httpReq, err := api.newRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestCheckReportIDMandatoryFields(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 500,
|
|
}}
|
|
api := &simpleCheckReportIDAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckReportIDRequest{} // deliberately empty
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrEmptyField) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckInInvalidURL(t *testing.T) {
|
|
api := &simpleCheckInAPI{
|
|
BaseURL: "\t", // invalid
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckInRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckInWithHTTPErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Err: errMocked}
|
|
api := &simpleCheckInAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckInRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckInMarshalErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleCheckInAPI{
|
|
JSONCodec: &FakeCodec{EncodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckInRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckInWithNewRequestErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleCheckInAPI{
|
|
RequestMaker: &FakeRequestMaker{Err: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckInRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckInWith401(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
|
|
api := &simpleCheckInAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckInRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckInWith400(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
|
|
api := &simpleCheckInAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckInRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrHTTPFailure) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckInWithResponseBodyReadErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Err: errMocked},
|
|
}}
|
|
api := &simpleCheckInAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckInRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestCheckInWithUnmarshalFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`{}`)},
|
|
}}
|
|
api := &simpleCheckInAPI{
|
|
HTTPClient: clnt,
|
|
JSONCodec: &FakeCodec{DecodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.CheckInRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
type handleCheckIn struct {
|
|
accept string
|
|
body []byte
|
|
contentType string
|
|
count int32
|
|
method string
|
|
mu sync.Mutex
|
|
resp *apimodel.CheckInResponse
|
|
url *url.URL
|
|
userAgent string
|
|
}
|
|
|
|
func (h *handleCheckIn) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer h.mu.Unlock()
|
|
h.mu.Lock()
|
|
if h.count > 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.count++
|
|
if r.Body != nil {
|
|
data, err := iox.ReadAllContext(r.Context(), r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.body = data
|
|
}
|
|
h.method = r.Method
|
|
h.url = r.URL
|
|
h.accept = r.Header.Get("Accept")
|
|
h.contentType = r.Header.Get("Content-Type")
|
|
h.userAgent = r.Header.Get("User-Agent")
|
|
var out *apimodel.CheckInResponse
|
|
ff := fakeFill{}
|
|
ff.fill(&out)
|
|
h.resp = out
|
|
data, err := json.Marshal(out)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
w.Write(data)
|
|
}
|
|
|
|
func TestCheckInRoundTrip(t *testing.T) {
|
|
// setup
|
|
handler := &handleCheckIn{}
|
|
srvr := httptest.NewServer(handler)
|
|
defer srvr.Close()
|
|
req := &apimodel.CheckInRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(&req)
|
|
api := &simpleCheckInAPI{BaseURL: srvr.URL}
|
|
ff.fill(&api.UserAgent)
|
|
// issue request
|
|
ctx := context.Background()
|
|
resp, err := api.Call(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected non-nil response here")
|
|
}
|
|
// compare our response and server's one
|
|
if diff := cmp.Diff(handler.resp, resp); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
// check whether headers are OK
|
|
if handler.accept != "application/json" {
|
|
t.Fatal("invalid accept header")
|
|
}
|
|
if handler.userAgent != api.UserAgent {
|
|
t.Fatal("invalid user-agent header")
|
|
}
|
|
// check whether the method is OK
|
|
if handler.method != "POST" {
|
|
t.Fatal("invalid method")
|
|
}
|
|
// check the body
|
|
if handler.contentType != "application/json" {
|
|
t.Fatal("invalid content-type header")
|
|
}
|
|
got := &apimodel.CheckInRequest{}
|
|
if err := json.Unmarshal(handler.body, &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(req, got); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestLoginInvalidURL(t *testing.T) {
|
|
api := &simpleLoginAPI{
|
|
BaseURL: "\t", // invalid
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.LoginRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestLoginWithHTTPErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Err: errMocked}
|
|
api := &simpleLoginAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.LoginRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestLoginMarshalErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleLoginAPI{
|
|
JSONCodec: &FakeCodec{EncodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.LoginRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestLoginWithNewRequestErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleLoginAPI{
|
|
RequestMaker: &FakeRequestMaker{Err: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.LoginRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestLoginWith401(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
|
|
api := &simpleLoginAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.LoginRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestLoginWith400(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
|
|
api := &simpleLoginAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.LoginRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrHTTPFailure) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestLoginWithResponseBodyReadErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Err: errMocked},
|
|
}}
|
|
api := &simpleLoginAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.LoginRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestLoginWithUnmarshalFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`{}`)},
|
|
}}
|
|
api := &simpleLoginAPI{
|
|
HTTPClient: clnt,
|
|
JSONCodec: &FakeCodec{DecodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.LoginRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
type handleLogin struct {
|
|
accept string
|
|
body []byte
|
|
contentType string
|
|
count int32
|
|
method string
|
|
mu sync.Mutex
|
|
resp *apimodel.LoginResponse
|
|
url *url.URL
|
|
userAgent string
|
|
}
|
|
|
|
func (h *handleLogin) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer h.mu.Unlock()
|
|
h.mu.Lock()
|
|
if h.count > 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.count++
|
|
if r.Body != nil {
|
|
data, err := iox.ReadAllContext(r.Context(), r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.body = data
|
|
}
|
|
h.method = r.Method
|
|
h.url = r.URL
|
|
h.accept = r.Header.Get("Accept")
|
|
h.contentType = r.Header.Get("Content-Type")
|
|
h.userAgent = r.Header.Get("User-Agent")
|
|
var out *apimodel.LoginResponse
|
|
ff := fakeFill{}
|
|
ff.fill(&out)
|
|
h.resp = out
|
|
data, err := json.Marshal(out)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
w.Write(data)
|
|
}
|
|
|
|
func TestLoginRoundTrip(t *testing.T) {
|
|
// setup
|
|
handler := &handleLogin{}
|
|
srvr := httptest.NewServer(handler)
|
|
defer srvr.Close()
|
|
req := &apimodel.LoginRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(&req)
|
|
api := &simpleLoginAPI{BaseURL: srvr.URL}
|
|
ff.fill(&api.UserAgent)
|
|
// issue request
|
|
ctx := context.Background()
|
|
resp, err := api.Call(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected non-nil response here")
|
|
}
|
|
// compare our response and server's one
|
|
if diff := cmp.Diff(handler.resp, resp); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
// check whether headers are OK
|
|
if handler.accept != "application/json" {
|
|
t.Fatal("invalid accept header")
|
|
}
|
|
if handler.userAgent != api.UserAgent {
|
|
t.Fatal("invalid user-agent header")
|
|
}
|
|
// check whether the method is OK
|
|
if handler.method != "POST" {
|
|
t.Fatal("invalid method")
|
|
}
|
|
// check the body
|
|
if handler.contentType != "application/json" {
|
|
t.Fatal("invalid content-type header")
|
|
}
|
|
got := &apimodel.LoginRequest{}
|
|
if err := json.Unmarshal(handler.body, &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(req, got); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestMeasurementMetaInvalidURL(t *testing.T) {
|
|
api := &simpleMeasurementMetaAPI{
|
|
BaseURL: "\t", // invalid
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.MeasurementMetaRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestMeasurementMetaWithHTTPErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Err: errMocked}
|
|
api := &simpleMeasurementMetaAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.MeasurementMetaRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestMeasurementMetaWithNewRequestErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleMeasurementMetaAPI{
|
|
RequestMaker: &FakeRequestMaker{Err: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.MeasurementMetaRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestMeasurementMetaWith401(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
|
|
api := &simpleMeasurementMetaAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.MeasurementMetaRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestMeasurementMetaWith400(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
|
|
api := &simpleMeasurementMetaAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.MeasurementMetaRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrHTTPFailure) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestMeasurementMetaWithResponseBodyReadErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Err: errMocked},
|
|
}}
|
|
api := &simpleMeasurementMetaAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.MeasurementMetaRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestMeasurementMetaWithUnmarshalFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`{}`)},
|
|
}}
|
|
api := &simpleMeasurementMetaAPI{
|
|
HTTPClient: clnt,
|
|
JSONCodec: &FakeCodec{DecodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.MeasurementMetaRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
type handleMeasurementMeta struct {
|
|
accept string
|
|
body []byte
|
|
contentType string
|
|
count int32
|
|
method string
|
|
mu sync.Mutex
|
|
resp *apimodel.MeasurementMetaResponse
|
|
url *url.URL
|
|
userAgent string
|
|
}
|
|
|
|
func (h *handleMeasurementMeta) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer h.mu.Unlock()
|
|
h.mu.Lock()
|
|
if h.count > 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.count++
|
|
if r.Body != nil {
|
|
data, err := iox.ReadAllContext(r.Context(), r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.body = data
|
|
}
|
|
h.method = r.Method
|
|
h.url = r.URL
|
|
h.accept = r.Header.Get("Accept")
|
|
h.contentType = r.Header.Get("Content-Type")
|
|
h.userAgent = r.Header.Get("User-Agent")
|
|
var out *apimodel.MeasurementMetaResponse
|
|
ff := fakeFill{}
|
|
ff.fill(&out)
|
|
h.resp = out
|
|
data, err := json.Marshal(out)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
w.Write(data)
|
|
}
|
|
|
|
func TestMeasurementMetaRoundTrip(t *testing.T) {
|
|
// setup
|
|
handler := &handleMeasurementMeta{}
|
|
srvr := httptest.NewServer(handler)
|
|
defer srvr.Close()
|
|
req := &apimodel.MeasurementMetaRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(&req)
|
|
api := &simpleMeasurementMetaAPI{BaseURL: srvr.URL}
|
|
ff.fill(&api.UserAgent)
|
|
// issue request
|
|
ctx := context.Background()
|
|
resp, err := api.Call(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected non-nil response here")
|
|
}
|
|
// compare our response and server's one
|
|
if diff := cmp.Diff(handler.resp, resp); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
// check whether headers are OK
|
|
if handler.accept != "application/json" {
|
|
t.Fatal("invalid accept header")
|
|
}
|
|
if handler.userAgent != api.UserAgent {
|
|
t.Fatal("invalid user-agent header")
|
|
}
|
|
// check whether the method is OK
|
|
if handler.method != "GET" {
|
|
t.Fatal("invalid method")
|
|
}
|
|
// check the query
|
|
httpReq, err := api.newRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestMeasurementMetaMandatoryFields(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 500,
|
|
}}
|
|
api := &simpleMeasurementMetaAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.MeasurementMetaRequest{} // deliberately empty
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrEmptyField) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestRegisterInvalidURL(t *testing.T) {
|
|
api := &simpleRegisterAPI{
|
|
BaseURL: "\t", // invalid
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.RegisterRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestRegisterWithHTTPErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Err: errMocked}
|
|
api := &simpleRegisterAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.RegisterRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestRegisterMarshalErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleRegisterAPI{
|
|
JSONCodec: &FakeCodec{EncodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.RegisterRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestRegisterWithNewRequestErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleRegisterAPI{
|
|
RequestMaker: &FakeRequestMaker{Err: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.RegisterRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestRegisterWith401(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
|
|
api := &simpleRegisterAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.RegisterRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestRegisterWith400(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
|
|
api := &simpleRegisterAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.RegisterRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrHTTPFailure) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestRegisterWithResponseBodyReadErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Err: errMocked},
|
|
}}
|
|
api := &simpleRegisterAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.RegisterRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestRegisterWithUnmarshalFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`{}`)},
|
|
}}
|
|
api := &simpleRegisterAPI{
|
|
HTTPClient: clnt,
|
|
JSONCodec: &FakeCodec{DecodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.RegisterRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
type handleRegister struct {
|
|
accept string
|
|
body []byte
|
|
contentType string
|
|
count int32
|
|
method string
|
|
mu sync.Mutex
|
|
resp *apimodel.RegisterResponse
|
|
url *url.URL
|
|
userAgent string
|
|
}
|
|
|
|
func (h *handleRegister) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer h.mu.Unlock()
|
|
h.mu.Lock()
|
|
if h.count > 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.count++
|
|
if r.Body != nil {
|
|
data, err := iox.ReadAllContext(r.Context(), r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.body = data
|
|
}
|
|
h.method = r.Method
|
|
h.url = r.URL
|
|
h.accept = r.Header.Get("Accept")
|
|
h.contentType = r.Header.Get("Content-Type")
|
|
h.userAgent = r.Header.Get("User-Agent")
|
|
var out *apimodel.RegisterResponse
|
|
ff := fakeFill{}
|
|
ff.fill(&out)
|
|
h.resp = out
|
|
data, err := json.Marshal(out)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
w.Write(data)
|
|
}
|
|
|
|
func TestRegisterRoundTrip(t *testing.T) {
|
|
// setup
|
|
handler := &handleRegister{}
|
|
srvr := httptest.NewServer(handler)
|
|
defer srvr.Close()
|
|
req := &apimodel.RegisterRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(&req)
|
|
api := &simpleRegisterAPI{BaseURL: srvr.URL}
|
|
ff.fill(&api.UserAgent)
|
|
// issue request
|
|
ctx := context.Background()
|
|
resp, err := api.Call(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected non-nil response here")
|
|
}
|
|
// compare our response and server's one
|
|
if diff := cmp.Diff(handler.resp, resp); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
// check whether headers are OK
|
|
if handler.accept != "application/json" {
|
|
t.Fatal("invalid accept header")
|
|
}
|
|
if handler.userAgent != api.UserAgent {
|
|
t.Fatal("invalid user-agent header")
|
|
}
|
|
// check whether the method is OK
|
|
if handler.method != "POST" {
|
|
t.Fatal("invalid method")
|
|
}
|
|
// check the body
|
|
if handler.contentType != "application/json" {
|
|
t.Fatal("invalid content-type header")
|
|
}
|
|
got := &apimodel.RegisterRequest{}
|
|
if err := json.Unmarshal(handler.body, &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(req, got); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestTestHelpersInvalidURL(t *testing.T) {
|
|
api := &simpleTestHelpersAPI{
|
|
BaseURL: "\t", // invalid
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TestHelpersRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTestHelpersWithHTTPErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Err: errMocked}
|
|
api := &simpleTestHelpersAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TestHelpersRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTestHelpersWithNewRequestErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleTestHelpersAPI{
|
|
RequestMaker: &FakeRequestMaker{Err: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TestHelpersRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTestHelpersWith401(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
|
|
api := &simpleTestHelpersAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TestHelpersRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTestHelpersWith400(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
|
|
api := &simpleTestHelpersAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TestHelpersRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrHTTPFailure) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTestHelpersWithResponseBodyReadErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Err: errMocked},
|
|
}}
|
|
api := &simpleTestHelpersAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TestHelpersRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTestHelpersWithUnmarshalFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`{}`)},
|
|
}}
|
|
api := &simpleTestHelpersAPI{
|
|
HTTPClient: clnt,
|
|
JSONCodec: &FakeCodec{DecodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TestHelpersRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
type handleTestHelpers struct {
|
|
accept string
|
|
body []byte
|
|
contentType string
|
|
count int32
|
|
method string
|
|
mu sync.Mutex
|
|
resp apimodel.TestHelpersResponse
|
|
url *url.URL
|
|
userAgent string
|
|
}
|
|
|
|
func (h *handleTestHelpers) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer h.mu.Unlock()
|
|
h.mu.Lock()
|
|
if h.count > 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.count++
|
|
if r.Body != nil {
|
|
data, err := iox.ReadAllContext(r.Context(), r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.body = data
|
|
}
|
|
h.method = r.Method
|
|
h.url = r.URL
|
|
h.accept = r.Header.Get("Accept")
|
|
h.contentType = r.Header.Get("Content-Type")
|
|
h.userAgent = r.Header.Get("User-Agent")
|
|
var out apimodel.TestHelpersResponse
|
|
ff := fakeFill{}
|
|
ff.fill(&out)
|
|
h.resp = out
|
|
data, err := json.Marshal(out)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
w.Write(data)
|
|
}
|
|
|
|
func TestTestHelpersRoundTrip(t *testing.T) {
|
|
// setup
|
|
handler := &handleTestHelpers{}
|
|
srvr := httptest.NewServer(handler)
|
|
defer srvr.Close()
|
|
req := &apimodel.TestHelpersRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(&req)
|
|
api := &simpleTestHelpersAPI{BaseURL: srvr.URL}
|
|
ff.fill(&api.UserAgent)
|
|
// issue request
|
|
ctx := context.Background()
|
|
resp, err := api.Call(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected non-nil response here")
|
|
}
|
|
// compare our response and server's one
|
|
if diff := cmp.Diff(handler.resp, resp); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
// check whether headers are OK
|
|
if handler.accept != "application/json" {
|
|
t.Fatal("invalid accept header")
|
|
}
|
|
if handler.userAgent != api.UserAgent {
|
|
t.Fatal("invalid user-agent header")
|
|
}
|
|
// check whether the method is OK
|
|
if handler.method != "GET" {
|
|
t.Fatal("invalid method")
|
|
}
|
|
// check the query
|
|
httpReq, err := api.newRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestTestHelpersResponseLiteralNull(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`null`)},
|
|
}}
|
|
api := &simpleTestHelpersAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TestHelpersRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrJSONLiteralNull) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestPsiphonConfigInvalidURL(t *testing.T) {
|
|
api := &simplePsiphonConfigAPI{
|
|
BaseURL: "\t", // invalid
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.PsiphonConfigRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestPsiphonConfigWithMissingToken(t *testing.T) {
|
|
api := &simplePsiphonConfigAPI{} // no token
|
|
ctx := context.Background()
|
|
req := &apimodel.PsiphonConfigRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrMissingToken) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestPsiphonConfigWithHTTPErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Err: errMocked}
|
|
api := &simplePsiphonConfigAPI{
|
|
HTTPClient: clnt,
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.PsiphonConfigRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestPsiphonConfigWithNewRequestErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simplePsiphonConfigAPI{
|
|
RequestMaker: &FakeRequestMaker{Err: errMocked},
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.PsiphonConfigRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestPsiphonConfigWith401(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
|
|
api := &simplePsiphonConfigAPI{
|
|
HTTPClient: clnt,
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.PsiphonConfigRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestPsiphonConfigWith400(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
|
|
api := &simplePsiphonConfigAPI{
|
|
HTTPClient: clnt,
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.PsiphonConfigRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrHTTPFailure) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestPsiphonConfigWithResponseBodyReadErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Err: errMocked},
|
|
}}
|
|
api := &simplePsiphonConfigAPI{
|
|
HTTPClient: clnt,
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.PsiphonConfigRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestPsiphonConfigWithUnmarshalFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`{}`)},
|
|
}}
|
|
api := &simplePsiphonConfigAPI{
|
|
HTTPClient: clnt,
|
|
JSONCodec: &FakeCodec{DecodeErr: errMocked},
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.PsiphonConfigRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
type handlePsiphonConfig struct {
|
|
accept string
|
|
body []byte
|
|
contentType string
|
|
count int32
|
|
method string
|
|
mu sync.Mutex
|
|
resp apimodel.PsiphonConfigResponse
|
|
url *url.URL
|
|
userAgent string
|
|
}
|
|
|
|
func (h *handlePsiphonConfig) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer h.mu.Unlock()
|
|
h.mu.Lock()
|
|
if h.count > 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.count++
|
|
if r.Body != nil {
|
|
data, err := iox.ReadAllContext(r.Context(), r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.body = data
|
|
}
|
|
h.method = r.Method
|
|
h.url = r.URL
|
|
h.accept = r.Header.Get("Accept")
|
|
h.contentType = r.Header.Get("Content-Type")
|
|
h.userAgent = r.Header.Get("User-Agent")
|
|
var out apimodel.PsiphonConfigResponse
|
|
ff := fakeFill{}
|
|
ff.fill(&out)
|
|
h.resp = out
|
|
data, err := json.Marshal(out)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
w.Write(data)
|
|
}
|
|
|
|
func TestPsiphonConfigRoundTrip(t *testing.T) {
|
|
// setup
|
|
handler := &handlePsiphonConfig{}
|
|
srvr := httptest.NewServer(handler)
|
|
defer srvr.Close()
|
|
req := &apimodel.PsiphonConfigRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(&req)
|
|
api := &simplePsiphonConfigAPI{BaseURL: srvr.URL}
|
|
ff.fill(&api.UserAgent)
|
|
ff.fill(&api.Token)
|
|
// issue request
|
|
ctx := context.Background()
|
|
resp, err := api.Call(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected non-nil response here")
|
|
}
|
|
// compare our response and server's one
|
|
if diff := cmp.Diff(handler.resp, resp); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
// check whether headers are OK
|
|
if handler.accept != "application/json" {
|
|
t.Fatal("invalid accept header")
|
|
}
|
|
if handler.userAgent != api.UserAgent {
|
|
t.Fatal("invalid user-agent header")
|
|
}
|
|
// check whether the method is OK
|
|
if handler.method != "GET" {
|
|
t.Fatal("invalid method")
|
|
}
|
|
// check the query
|
|
httpReq, err := api.newRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestPsiphonConfigResponseLiteralNull(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`null`)},
|
|
}}
|
|
api := &simplePsiphonConfigAPI{
|
|
HTTPClient: clnt,
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.PsiphonConfigRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrJSONLiteralNull) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTorTargetsInvalidURL(t *testing.T) {
|
|
api := &simpleTorTargetsAPI{
|
|
BaseURL: "\t", // invalid
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TorTargetsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTorTargetsWithMissingToken(t *testing.T) {
|
|
api := &simpleTorTargetsAPI{} // no token
|
|
ctx := context.Background()
|
|
req := &apimodel.TorTargetsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrMissingToken) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTorTargetsWithHTTPErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Err: errMocked}
|
|
api := &simpleTorTargetsAPI{
|
|
HTTPClient: clnt,
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TorTargetsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTorTargetsWithNewRequestErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleTorTargetsAPI{
|
|
RequestMaker: &FakeRequestMaker{Err: errMocked},
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TorTargetsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTorTargetsWith401(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
|
|
api := &simpleTorTargetsAPI{
|
|
HTTPClient: clnt,
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TorTargetsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTorTargetsWith400(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
|
|
api := &simpleTorTargetsAPI{
|
|
HTTPClient: clnt,
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TorTargetsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrHTTPFailure) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTorTargetsWithResponseBodyReadErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Err: errMocked},
|
|
}}
|
|
api := &simpleTorTargetsAPI{
|
|
HTTPClient: clnt,
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TorTargetsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestTorTargetsWithUnmarshalFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`{}`)},
|
|
}}
|
|
api := &simpleTorTargetsAPI{
|
|
HTTPClient: clnt,
|
|
JSONCodec: &FakeCodec{DecodeErr: errMocked},
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TorTargetsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
type handleTorTargets struct {
|
|
accept string
|
|
body []byte
|
|
contentType string
|
|
count int32
|
|
method string
|
|
mu sync.Mutex
|
|
resp apimodel.TorTargetsResponse
|
|
url *url.URL
|
|
userAgent string
|
|
}
|
|
|
|
func (h *handleTorTargets) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer h.mu.Unlock()
|
|
h.mu.Lock()
|
|
if h.count > 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.count++
|
|
if r.Body != nil {
|
|
data, err := iox.ReadAllContext(r.Context(), r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.body = data
|
|
}
|
|
h.method = r.Method
|
|
h.url = r.URL
|
|
h.accept = r.Header.Get("Accept")
|
|
h.contentType = r.Header.Get("Content-Type")
|
|
h.userAgent = r.Header.Get("User-Agent")
|
|
var out apimodel.TorTargetsResponse
|
|
ff := fakeFill{}
|
|
ff.fill(&out)
|
|
h.resp = out
|
|
data, err := json.Marshal(out)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
w.Write(data)
|
|
}
|
|
|
|
func TestTorTargetsRoundTrip(t *testing.T) {
|
|
// setup
|
|
handler := &handleTorTargets{}
|
|
srvr := httptest.NewServer(handler)
|
|
defer srvr.Close()
|
|
req := &apimodel.TorTargetsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(&req)
|
|
api := &simpleTorTargetsAPI{BaseURL: srvr.URL}
|
|
ff.fill(&api.UserAgent)
|
|
ff.fill(&api.Token)
|
|
// issue request
|
|
ctx := context.Background()
|
|
resp, err := api.Call(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected non-nil response here")
|
|
}
|
|
// compare our response and server's one
|
|
if diff := cmp.Diff(handler.resp, resp); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
// check whether headers are OK
|
|
if handler.accept != "application/json" {
|
|
t.Fatal("invalid accept header")
|
|
}
|
|
if handler.userAgent != api.UserAgent {
|
|
t.Fatal("invalid user-agent header")
|
|
}
|
|
// check whether the method is OK
|
|
if handler.method != "GET" {
|
|
t.Fatal("invalid method")
|
|
}
|
|
// check the query
|
|
httpReq, err := api.newRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestTorTargetsResponseLiteralNull(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`null`)},
|
|
}}
|
|
api := &simpleTorTargetsAPI{
|
|
HTTPClient: clnt,
|
|
Token: "fakeToken",
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.TorTargetsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrJSONLiteralNull) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestURLsInvalidURL(t *testing.T) {
|
|
api := &simpleURLsAPI{
|
|
BaseURL: "\t", // invalid
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.URLsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestURLsWithHTTPErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Err: errMocked}
|
|
api := &simpleURLsAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.URLsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestURLsWithNewRequestErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleURLsAPI{
|
|
RequestMaker: &FakeRequestMaker{Err: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.URLsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestURLsWith401(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
|
|
api := &simpleURLsAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.URLsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestURLsWith400(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
|
|
api := &simpleURLsAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.URLsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrHTTPFailure) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestURLsWithResponseBodyReadErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Err: errMocked},
|
|
}}
|
|
api := &simpleURLsAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.URLsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestURLsWithUnmarshalFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`{}`)},
|
|
}}
|
|
api := &simpleURLsAPI{
|
|
HTTPClient: clnt,
|
|
JSONCodec: &FakeCodec{DecodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.URLsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
type handleURLs struct {
|
|
accept string
|
|
body []byte
|
|
contentType string
|
|
count int32
|
|
method string
|
|
mu sync.Mutex
|
|
resp *apimodel.URLsResponse
|
|
url *url.URL
|
|
userAgent string
|
|
}
|
|
|
|
func (h *handleURLs) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer h.mu.Unlock()
|
|
h.mu.Lock()
|
|
if h.count > 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.count++
|
|
if r.Body != nil {
|
|
data, err := iox.ReadAllContext(r.Context(), r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.body = data
|
|
}
|
|
h.method = r.Method
|
|
h.url = r.URL
|
|
h.accept = r.Header.Get("Accept")
|
|
h.contentType = r.Header.Get("Content-Type")
|
|
h.userAgent = r.Header.Get("User-Agent")
|
|
var out *apimodel.URLsResponse
|
|
ff := fakeFill{}
|
|
ff.fill(&out)
|
|
h.resp = out
|
|
data, err := json.Marshal(out)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
w.Write(data)
|
|
}
|
|
|
|
func TestURLsRoundTrip(t *testing.T) {
|
|
// setup
|
|
handler := &handleURLs{}
|
|
srvr := httptest.NewServer(handler)
|
|
defer srvr.Close()
|
|
req := &apimodel.URLsRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(&req)
|
|
api := &simpleURLsAPI{BaseURL: srvr.URL}
|
|
ff.fill(&api.UserAgent)
|
|
// issue request
|
|
ctx := context.Background()
|
|
resp, err := api.Call(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected non-nil response here")
|
|
}
|
|
// compare our response and server's one
|
|
if diff := cmp.Diff(handler.resp, resp); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
// check whether headers are OK
|
|
if handler.accept != "application/json" {
|
|
t.Fatal("invalid accept header")
|
|
}
|
|
if handler.userAgent != api.UserAgent {
|
|
t.Fatal("invalid user-agent header")
|
|
}
|
|
// check whether the method is OK
|
|
if handler.method != "GET" {
|
|
t.Fatal("invalid method")
|
|
}
|
|
// check the query
|
|
httpReq, err := api.newRequest(context.Background(), req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestOpenReportInvalidURL(t *testing.T) {
|
|
api := &simpleOpenReportAPI{
|
|
BaseURL: "\t", // invalid
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.OpenReportRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestOpenReportWithHTTPErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Err: errMocked}
|
|
api := &simpleOpenReportAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.OpenReportRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestOpenReportMarshalErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleOpenReportAPI{
|
|
JSONCodec: &FakeCodec{EncodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.OpenReportRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestOpenReportWithNewRequestErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleOpenReportAPI{
|
|
RequestMaker: &FakeRequestMaker{Err: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.OpenReportRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestOpenReportWith401(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
|
|
api := &simpleOpenReportAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.OpenReportRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestOpenReportWith400(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
|
|
api := &simpleOpenReportAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.OpenReportRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrHTTPFailure) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestOpenReportWithResponseBodyReadErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Err: errMocked},
|
|
}}
|
|
api := &simpleOpenReportAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.OpenReportRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestOpenReportWithUnmarshalFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`{}`)},
|
|
}}
|
|
api := &simpleOpenReportAPI{
|
|
HTTPClient: clnt,
|
|
JSONCodec: &FakeCodec{DecodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.OpenReportRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
type handleOpenReport struct {
|
|
accept string
|
|
body []byte
|
|
contentType string
|
|
count int32
|
|
method string
|
|
mu sync.Mutex
|
|
resp *apimodel.OpenReportResponse
|
|
url *url.URL
|
|
userAgent string
|
|
}
|
|
|
|
func (h *handleOpenReport) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer h.mu.Unlock()
|
|
h.mu.Lock()
|
|
if h.count > 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.count++
|
|
if r.Body != nil {
|
|
data, err := iox.ReadAllContext(r.Context(), r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.body = data
|
|
}
|
|
h.method = r.Method
|
|
h.url = r.URL
|
|
h.accept = r.Header.Get("Accept")
|
|
h.contentType = r.Header.Get("Content-Type")
|
|
h.userAgent = r.Header.Get("User-Agent")
|
|
var out *apimodel.OpenReportResponse
|
|
ff := fakeFill{}
|
|
ff.fill(&out)
|
|
h.resp = out
|
|
data, err := json.Marshal(out)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
w.Write(data)
|
|
}
|
|
|
|
func TestOpenReportRoundTrip(t *testing.T) {
|
|
// setup
|
|
handler := &handleOpenReport{}
|
|
srvr := httptest.NewServer(handler)
|
|
defer srvr.Close()
|
|
req := &apimodel.OpenReportRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(&req)
|
|
api := &simpleOpenReportAPI{BaseURL: srvr.URL}
|
|
ff.fill(&api.UserAgent)
|
|
// issue request
|
|
ctx := context.Background()
|
|
resp, err := api.Call(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected non-nil response here")
|
|
}
|
|
// compare our response and server's one
|
|
if diff := cmp.Diff(handler.resp, resp); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
// check whether headers are OK
|
|
if handler.accept != "application/json" {
|
|
t.Fatal("invalid accept header")
|
|
}
|
|
if handler.userAgent != api.UserAgent {
|
|
t.Fatal("invalid user-agent header")
|
|
}
|
|
// check whether the method is OK
|
|
if handler.method != "POST" {
|
|
t.Fatal("invalid method")
|
|
}
|
|
// check the body
|
|
if handler.contentType != "application/json" {
|
|
t.Fatal("invalid content-type header")
|
|
}
|
|
got := &apimodel.OpenReportRequest{}
|
|
if err := json.Unmarshal(handler.body, &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(req, got); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestSubmitMeasurementInvalidURL(t *testing.T) {
|
|
api := &simpleSubmitMeasurementAPI{
|
|
BaseURL: "\t", // invalid
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.SubmitMeasurementRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestSubmitMeasurementWithHTTPErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Err: errMocked}
|
|
api := &simpleSubmitMeasurementAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.SubmitMeasurementRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestSubmitMeasurementMarshalErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleSubmitMeasurementAPI{
|
|
JSONCodec: &FakeCodec{EncodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.SubmitMeasurementRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestSubmitMeasurementWithNewRequestErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
api := &simpleSubmitMeasurementAPI{
|
|
RequestMaker: &FakeRequestMaker{Err: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.SubmitMeasurementRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestSubmitMeasurementWith401(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
|
|
api := &simpleSubmitMeasurementAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.SubmitMeasurementRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestSubmitMeasurementWith400(t *testing.T) {
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
|
|
api := &simpleSubmitMeasurementAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.SubmitMeasurementRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, ErrHTTPFailure) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestSubmitMeasurementWithResponseBodyReadErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Err: errMocked},
|
|
}}
|
|
api := &simpleSubmitMeasurementAPI{
|
|
HTTPClient: clnt,
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.SubmitMeasurementRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
func TestSubmitMeasurementWithUnmarshalFailure(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &FakeBody{Data: []byte(`{}`)},
|
|
}}
|
|
api := &simpleSubmitMeasurementAPI{
|
|
HTTPClient: clnt,
|
|
JSONCodec: &FakeCodec{DecodeErr: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.SubmitMeasurementRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|
|
|
|
type handleSubmitMeasurement struct {
|
|
accept string
|
|
body []byte
|
|
contentType string
|
|
count int32
|
|
method string
|
|
mu sync.Mutex
|
|
resp *apimodel.SubmitMeasurementResponse
|
|
url *url.URL
|
|
userAgent string
|
|
}
|
|
|
|
func (h *handleSubmitMeasurement) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
defer h.mu.Unlock()
|
|
h.mu.Lock()
|
|
if h.count > 0 {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.count++
|
|
if r.Body != nil {
|
|
data, err := iox.ReadAllContext(r.Context(), r.Body)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
h.body = data
|
|
}
|
|
h.method = r.Method
|
|
h.url = r.URL
|
|
h.accept = r.Header.Get("Accept")
|
|
h.contentType = r.Header.Get("Content-Type")
|
|
h.userAgent = r.Header.Get("User-Agent")
|
|
var out *apimodel.SubmitMeasurementResponse
|
|
ff := fakeFill{}
|
|
ff.fill(&out)
|
|
h.resp = out
|
|
data, err := json.Marshal(out)
|
|
if err != nil {
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
w.Write(data)
|
|
}
|
|
|
|
func TestSubmitMeasurementRoundTrip(t *testing.T) {
|
|
// setup
|
|
handler := &handleSubmitMeasurement{}
|
|
srvr := httptest.NewServer(handler)
|
|
defer srvr.Close()
|
|
req := &apimodel.SubmitMeasurementRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(&req)
|
|
api := &simpleSubmitMeasurementAPI{BaseURL: srvr.URL}
|
|
ff.fill(&api.UserAgent)
|
|
// issue request
|
|
ctx := context.Background()
|
|
resp, err := api.Call(ctx, req)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if resp == nil {
|
|
t.Fatal("expected non-nil response here")
|
|
}
|
|
// compare our response and server's one
|
|
if diff := cmp.Diff(handler.resp, resp); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
// check whether headers are OK
|
|
if handler.accept != "application/json" {
|
|
t.Fatal("invalid accept header")
|
|
}
|
|
if handler.userAgent != api.UserAgent {
|
|
t.Fatal("invalid user-agent header")
|
|
}
|
|
// check whether the method is OK
|
|
if handler.method != "POST" {
|
|
t.Fatal("invalid method")
|
|
}
|
|
// check the body
|
|
if handler.contentType != "application/json" {
|
|
t.Fatal("invalid content-type header")
|
|
}
|
|
got := &apimodel.SubmitMeasurementRequest{}
|
|
if err := json.Unmarshal(handler.body, &got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if diff := cmp.Diff(req, got); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
}
|
|
|
|
func TestSubmitMeasurementTemplateErr(t *testing.T) {
|
|
errMocked := errors.New("mocked error")
|
|
clnt := &FakeHTTPClient{Resp: &http.Response{
|
|
StatusCode: 500,
|
|
}}
|
|
api := &simpleSubmitMeasurementAPI{
|
|
HTTPClient: clnt,
|
|
TemplateExecutor: &FakeTemplateExecutor{Err: errMocked},
|
|
}
|
|
ctx := context.Background()
|
|
req := &apimodel.SubmitMeasurementRequest{}
|
|
ff := &fakeFill{}
|
|
ff.fill(req)
|
|
resp, err := api.Call(ctx, req)
|
|
if !errors.Is(err, errMocked) {
|
|
t.Fatal("not the error we expected", err)
|
|
}
|
|
if resp != nil {
|
|
t.Fatal("expected nil resp")
|
|
}
|
|
}
|