ooni-probe-cli/internal/ooapi/apis_test.go
Simone Basso 0fdc9cafb5
fix(all): introduce and use iox.ReadAllContext (#379)
* 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
2021-06-15 11:57:40 +02:00

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")
}
}