ooni-probe-cli/internal/ooapi/apis_test.go
Simone Basso 33de701263
refactor: flatten and separate (#353)
* refactor(atomicx): move outside the engine package

After merging probe-engine into probe-cli, my impression is that we have
too much unnecessary nesting of packages in this repository.

The idea of this commit and of a bunch of following commits will instead
be to reduce the nesting and simplify the structure.

While there, improve the documentation.

* fix: always use the atomicx package

For consistency, never use sync/atomic and always use ./internal/atomicx
so we can just grep and make sure we're not risking to crash if we make
a subtle mistake on a 32 bit platform.

While there, mention in the contributing guidelines that we want to
always prefer the ./internal/atomicx package over sync/atomic.

* fix(atomicx): remove unnecessary constructor

We don't need a constructor here. The default constructed `&Int64{}`
instance is already usable and the constructor does not add anything to
what we are doing, rather it just creates extra confusion.

* cleanup(atomicx): we are not using Float64

Because atomicx.Float64 is unused, we can safely zap it.

* cleanup(atomicx): simplify impl and improve tests

We can simplify the implementation by using defer and by letting
the Load() method call Add(0).

We can improve tests by making many goroutines updated the
atomic int64 value concurrently.

* refactor(fsx): can live in the ./internal pkg

Let us reduce the amount of nesting. While there, ensure that the
package only exports the bare minimum, and improve the documentation
of the tests, to ease reading the code.

* refactor: move runtimex to ./internal

* refactor: move shellx into the ./internal package

While there, remove unnecessary dependency between packages.

While there, specify in the contributing guidelines that
one should use x/sys/execabs instead of os/exec.

* refactor: move ooapi into the ./internal pkg

* refactor(humanize): move to ./internal and better docs

* refactor: move platform to ./internal

* refactor(randx): move to ./internal

* refactor(multierror): move into the ./internal pkg

* refactor(kvstore): all kvstores in ./internal

Rather than having part of the kvstore inside ./internal/engine/kvstore
and part in ./internal/engine/kvstore.go, let us put every piece of code
that is kvstore related into the ./internal/kvstore package.

* fix(kvstore): always return ErrNoSuchKey on Get() error

It should help to use the kvstore everywhere removing all the
copies that are lingering around the tree.

* sessionresolver: make KVStore mandatory

Simplifies implementation. While there, use the ./internal/kvstore
package rather than having our private implementation.

* fix(ooapi): use the ./internal/kvstore package

* fix(platform): better documentation
2021-06-04 10:34:18 +02:00

2777 lines
64 KiB
Go

// Code generated by go generate; DO NOT EDIT.
// 2021-05-12 09:15:01.254917327 +0200 CEST m=+0.000217025
package ooapi
//go:generate go run ./internal/generator -file apis_test.go
import (
"context"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"sync"
"testing"
"github.com/google/go-cmp/cmp"
"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 := ioutil.ReadAll(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 := ioutil.ReadAll(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 := ioutil.ReadAll(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 := ioutil.ReadAll(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 := ioutil.ReadAll(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 := ioutil.ReadAll(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 := ioutil.ReadAll(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 := ioutil.ReadAll(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 := ioutil.ReadAll(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 := ioutil.ReadAll(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 := ioutil.ReadAll(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")
}
}