ooni-probe-cli/internal/engine/ooapi/apis_test.go
Simone Basso 55bdebe8b2
engine/ooapi: autogenerated API with login and caching (#234)
* internal/engine/ooapi: auto-generated API client

* feat: introduce the callers abstraction

* feat: implement API caching on disk

* feat: implement cloneWithToken when we require login

* feat: implement login

* fix: do not cache all APIs

* feat: start making space for more tests

* feat: implement caching policy

* feat: write tests for caching layer

* feat: add integration tests and fix some minor issues

* feat: write much more unit tests

* feat: add some more easy unit tests

* feat: add tests that use a local server

While there, make sure many fields we care about are OK.

* doc: write basic documentation

* fix: tweak sentence

* doc: improve ooapi documentation

* doc(ooapi): other documentation improvements

* fix(ooapi): remove caching for most APIs

We discussed this topic yesterday with @FedericoCeratto. The only
place where we want LRU caching is MeasurementMeta.

* feat(ooapi): improve handling of errors during login

This was also discussed yesterday with @FedericoCeratto

* fix(swaggerdiff_test.go): temporarily disable

Before I work on this, I need to tend onto other tasks.

* fix(ootest): add one more test case

We're going towards 100% coverage of this package, as it ought to be.

* feat(ooapi): test cases for when the probe clock is off

* fix(ooapi): change test to have 100% unittest coverage

* feat: sync server and client APIs definition

Companion PR: https://github.com/ooni/api/pull/218

* fix(ooapi): start testing again against API

* fix(ooapi): only generate each file once

* chore: set version to 3.7.0-alpha

While there, make sure we don't always skip a currently failing
riseupvpn test, and slightly clarify the readme.

* fix(kvstore): less scoped error message
2021-03-04 11:51:07 +01:00

2777 lines
64 KiB
Go

// Code generated by go generate; DO NOT EDIT.
// 2021-02-26 15:45:50.81792142 +0100 CET m=+0.000095792
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/engine/ooapi/apimodel"
)
func TestCheckReportIDInvalidURL(t *testing.T) {
api := &CheckReportIDAPI{
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 := &CheckReportIDAPI{
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 := &CheckReportIDAPI{
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 := &CheckReportIDAPI{
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 := &CheckReportIDAPI{
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 := &CheckReportIDAPI{
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 := &CheckReportIDAPI{
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 := &CheckReportIDAPI{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 := &CheckReportIDAPI{
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 := &CheckInAPI{
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 := &CheckInAPI{
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 := &CheckInAPI{
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 := &CheckInAPI{
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 := &CheckInAPI{
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 := &CheckInAPI{
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 := &CheckInAPI{
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 := &CheckInAPI{
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 := &CheckInAPI{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 := &LoginAPI{
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 := &LoginAPI{
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 := &LoginAPI{
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 := &LoginAPI{
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 := &LoginAPI{
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 := &LoginAPI{
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 := &LoginAPI{
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 := &LoginAPI{
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 := &LoginAPI{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 := &MeasurementMetaAPI{
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 := &MeasurementMetaAPI{
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 := &MeasurementMetaAPI{
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 := &MeasurementMetaAPI{
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 := &MeasurementMetaAPI{
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 := &MeasurementMetaAPI{
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 := &MeasurementMetaAPI{
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 := &MeasurementMetaAPI{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 := &MeasurementMetaAPI{
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 := &RegisterAPI{
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 := &RegisterAPI{
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 := &RegisterAPI{
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 := &RegisterAPI{
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 := &RegisterAPI{
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 := &RegisterAPI{
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 := &RegisterAPI{
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 := &RegisterAPI{
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 := &RegisterAPI{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 := &TestHelpersAPI{
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 := &TestHelpersAPI{
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 := &TestHelpersAPI{
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 := &TestHelpersAPI{
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 := &TestHelpersAPI{
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 := &TestHelpersAPI{
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 := &TestHelpersAPI{
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 := &TestHelpersAPI{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 := &TestHelpersAPI{
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 := &PsiphonConfigAPI{
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 := &PsiphonConfigAPI{} // 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 := &PsiphonConfigAPI{
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 := &PsiphonConfigAPI{
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 := &PsiphonConfigAPI{
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 := &PsiphonConfigAPI{
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 := &PsiphonConfigAPI{
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 := &PsiphonConfigAPI{
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 := &PsiphonConfigAPI{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 := &PsiphonConfigAPI{
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 := &TorTargetsAPI{
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 := &TorTargetsAPI{} // 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 := &TorTargetsAPI{
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 := &TorTargetsAPI{
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 := &TorTargetsAPI{
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 := &TorTargetsAPI{
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 := &TorTargetsAPI{
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 := &TorTargetsAPI{
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 := &TorTargetsAPI{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 := &TorTargetsAPI{
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 := &URLsAPI{
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 := &URLsAPI{
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 := &URLsAPI{
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 := &URLsAPI{
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 := &URLsAPI{
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 := &URLsAPI{
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 := &URLsAPI{
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 := &URLsAPI{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 := &OpenReportAPI{
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 := &OpenReportAPI{
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 := &OpenReportAPI{
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 := &OpenReportAPI{
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 := &OpenReportAPI{
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 := &OpenReportAPI{
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 := &OpenReportAPI{
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 := &OpenReportAPI{
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 := &OpenReportAPI{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 := &SubmitMeasurementAPI{
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 := &SubmitMeasurementAPI{
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 := &SubmitMeasurementAPI{
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 := &SubmitMeasurementAPI{
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 := &SubmitMeasurementAPI{
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 := &SubmitMeasurementAPI{
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 := &SubmitMeasurementAPI{
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 := &SubmitMeasurementAPI{
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 := &SubmitMeasurementAPI{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 := &SubmitMeasurementAPI{
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")
}
}