// Code generated by go generate; DO NOT EDIT.
// 2021-10-20 14:12:56.092704 +0200 CEST m=+0.000214376

package ooapi

//go:generate go run ./internal/generator -file apis_test.go

import (
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"sync"
	"testing"

	"github.com/google/go-cmp/cmp"
	"github.com/ooni/probe-cli/v3/internal/netxlite"
	"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 := netxlite.ReadAllContext(r.Context(), r.Body)
		if err != nil {
			w.WriteHeader(400)
			return
		}
		h.body = data
	}
	h.method = r.Method
	h.url = r.URL
	h.accept = r.Header.Get("Accept")
	h.contentType = r.Header.Get("Content-Type")
	h.userAgent = r.Header.Get("User-Agent")
	var out *apimodel.CheckReportIDResponse
	ff := fakeFill{}
	ff.fill(&out)
	h.resp = out
	data, err := json.Marshal(out)
	if err != nil {
		w.WriteHeader(400)
		return
	}
	w.Write(data)
}

func TestCheckReportIDRoundTrip(t *testing.T) {
	// setup
	handler := &handleCheckReportID{}
	srvr := httptest.NewServer(handler)
	defer srvr.Close()
	req := &apimodel.CheckReportIDRequest{}
	ff := &fakeFill{}
	ff.fill(&req)
	api := &simpleCheckReportIDAPI{BaseURL: srvr.URL}
	ff.fill(&api.UserAgent)
	// issue request
	ctx := context.Background()
	resp, err := api.Call(ctx, req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response here")
	}
	// compare our response and server's one
	if diff := cmp.Diff(handler.resp, resp); diff != "" {
		t.Fatal(diff)
	}
	// check whether headers are OK
	if handler.accept != "application/json" {
		t.Fatal("invalid accept header")
	}
	if handler.userAgent != api.UserAgent {
		t.Fatal("invalid user-agent header")
	}
	// check whether the method is OK
	if handler.method != "GET" {
		t.Fatal("invalid method")
	}
	// check the query
	httpReq, err := api.newRequest(context.Background(), req)
	if err != nil {
		t.Fatal(err)
	}
	if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
		t.Fatal(diff)
	}
	if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
		t.Fatal(diff)
	}
}

func TestCheckReportIDMandatoryFields(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 500,
	}}
	api := &simpleCheckReportIDAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.CheckReportIDRequest{} // deliberately empty
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrEmptyField) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestCheckInInvalidURL(t *testing.T) {
	api := &simpleCheckInAPI{
		BaseURL: "\t", // invalid
	}
	ctx := context.Background()
	req := &apimodel.CheckInRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestCheckInWithHTTPErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Err: errMocked}
	api := &simpleCheckInAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.CheckInRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestCheckInMarshalErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleCheckInAPI{
		JSONCodec: &FakeCodec{EncodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.CheckInRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestCheckInWithNewRequestErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleCheckInAPI{
		RequestMaker: &FakeRequestMaker{Err: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.CheckInRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestCheckInWith401(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
	api := &simpleCheckInAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.CheckInRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrUnauthorized) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestCheckInWith400(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
	api := &simpleCheckInAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.CheckInRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrHTTPFailure) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestCheckInWithResponseBodyReadErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Err: errMocked},
	}}
	api := &simpleCheckInAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.CheckInRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestCheckInWithUnmarshalFailure(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`{}`)},
	}}
	api := &simpleCheckInAPI{
		HTTPClient: clnt,
		JSONCodec:  &FakeCodec{DecodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.CheckInRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

type handleCheckIn struct {
	accept      string
	body        []byte
	contentType string
	count       int32
	method      string
	mu          sync.Mutex
	resp        *apimodel.CheckInResponse
	url         *url.URL
	userAgent   string
}

func (h *handleCheckIn) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer h.mu.Unlock()
	h.mu.Lock()
	if h.count > 0 {
		w.WriteHeader(400)
		return
	}
	h.count++
	if r.Body != nil {
		data, err := netxlite.ReadAllContext(r.Context(), r.Body)
		if err != nil {
			w.WriteHeader(400)
			return
		}
		h.body = data
	}
	h.method = r.Method
	h.url = r.URL
	h.accept = r.Header.Get("Accept")
	h.contentType = r.Header.Get("Content-Type")
	h.userAgent = r.Header.Get("User-Agent")
	var out *apimodel.CheckInResponse
	ff := fakeFill{}
	ff.fill(&out)
	h.resp = out
	data, err := json.Marshal(out)
	if err != nil {
		w.WriteHeader(400)
		return
	}
	w.Write(data)
}

func TestCheckInRoundTrip(t *testing.T) {
	// setup
	handler := &handleCheckIn{}
	srvr := httptest.NewServer(handler)
	defer srvr.Close()
	req := &apimodel.CheckInRequest{}
	ff := &fakeFill{}
	ff.fill(&req)
	api := &simpleCheckInAPI{BaseURL: srvr.URL}
	ff.fill(&api.UserAgent)
	// issue request
	ctx := context.Background()
	resp, err := api.Call(ctx, req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response here")
	}
	// compare our response and server's one
	if diff := cmp.Diff(handler.resp, resp); diff != "" {
		t.Fatal(diff)
	}
	// check whether headers are OK
	if handler.accept != "application/json" {
		t.Fatal("invalid accept header")
	}
	if handler.userAgent != api.UserAgent {
		t.Fatal("invalid user-agent header")
	}
	// check whether the method is OK
	if handler.method != "POST" {
		t.Fatal("invalid method")
	}
	// check the body
	if handler.contentType != "application/json" {
		t.Fatal("invalid content-type header")
	}
	got := &apimodel.CheckInRequest{}
	if err := json.Unmarshal(handler.body, &got); err != nil {
		t.Fatal(err)
	}
	if diff := cmp.Diff(req, got); diff != "" {
		t.Fatal(diff)
	}
}

func TestLoginInvalidURL(t *testing.T) {
	api := &simpleLoginAPI{
		BaseURL: "\t", // invalid
	}
	ctx := context.Background()
	req := &apimodel.LoginRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestLoginWithHTTPErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Err: errMocked}
	api := &simpleLoginAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.LoginRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestLoginMarshalErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleLoginAPI{
		JSONCodec: &FakeCodec{EncodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.LoginRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestLoginWithNewRequestErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleLoginAPI{
		RequestMaker: &FakeRequestMaker{Err: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.LoginRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestLoginWith401(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
	api := &simpleLoginAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.LoginRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrUnauthorized) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestLoginWith400(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
	api := &simpleLoginAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.LoginRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrHTTPFailure) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestLoginWithResponseBodyReadErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Err: errMocked},
	}}
	api := &simpleLoginAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.LoginRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestLoginWithUnmarshalFailure(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`{}`)},
	}}
	api := &simpleLoginAPI{
		HTTPClient: clnt,
		JSONCodec:  &FakeCodec{DecodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.LoginRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

type handleLogin struct {
	accept      string
	body        []byte
	contentType string
	count       int32
	method      string
	mu          sync.Mutex
	resp        *apimodel.LoginResponse
	url         *url.URL
	userAgent   string
}

func (h *handleLogin) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer h.mu.Unlock()
	h.mu.Lock()
	if h.count > 0 {
		w.WriteHeader(400)
		return
	}
	h.count++
	if r.Body != nil {
		data, err := netxlite.ReadAllContext(r.Context(), r.Body)
		if err != nil {
			w.WriteHeader(400)
			return
		}
		h.body = data
	}
	h.method = r.Method
	h.url = r.URL
	h.accept = r.Header.Get("Accept")
	h.contentType = r.Header.Get("Content-Type")
	h.userAgent = r.Header.Get("User-Agent")
	var out *apimodel.LoginResponse
	ff := fakeFill{}
	ff.fill(&out)
	h.resp = out
	data, err := json.Marshal(out)
	if err != nil {
		w.WriteHeader(400)
		return
	}
	w.Write(data)
}

func TestLoginRoundTrip(t *testing.T) {
	// setup
	handler := &handleLogin{}
	srvr := httptest.NewServer(handler)
	defer srvr.Close()
	req := &apimodel.LoginRequest{}
	ff := &fakeFill{}
	ff.fill(&req)
	api := &simpleLoginAPI{BaseURL: srvr.URL}
	ff.fill(&api.UserAgent)
	// issue request
	ctx := context.Background()
	resp, err := api.Call(ctx, req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response here")
	}
	// compare our response and server's one
	if diff := cmp.Diff(handler.resp, resp); diff != "" {
		t.Fatal(diff)
	}
	// check whether headers are OK
	if handler.accept != "application/json" {
		t.Fatal("invalid accept header")
	}
	if handler.userAgent != api.UserAgent {
		t.Fatal("invalid user-agent header")
	}
	// check whether the method is OK
	if handler.method != "POST" {
		t.Fatal("invalid method")
	}
	// check the body
	if handler.contentType != "application/json" {
		t.Fatal("invalid content-type header")
	}
	got := &apimodel.LoginRequest{}
	if err := json.Unmarshal(handler.body, &got); err != nil {
		t.Fatal(err)
	}
	if diff := cmp.Diff(req, got); diff != "" {
		t.Fatal(diff)
	}
}

func TestMeasurementMetaInvalidURL(t *testing.T) {
	api := &simpleMeasurementMetaAPI{
		BaseURL: "\t", // invalid
	}
	ctx := context.Background()
	req := &apimodel.MeasurementMetaRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestMeasurementMetaWithHTTPErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Err: errMocked}
	api := &simpleMeasurementMetaAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.MeasurementMetaRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestMeasurementMetaWithNewRequestErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleMeasurementMetaAPI{
		RequestMaker: &FakeRequestMaker{Err: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.MeasurementMetaRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestMeasurementMetaWith401(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
	api := &simpleMeasurementMetaAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.MeasurementMetaRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrUnauthorized) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestMeasurementMetaWith400(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
	api := &simpleMeasurementMetaAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.MeasurementMetaRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrHTTPFailure) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestMeasurementMetaWithResponseBodyReadErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Err: errMocked},
	}}
	api := &simpleMeasurementMetaAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.MeasurementMetaRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestMeasurementMetaWithUnmarshalFailure(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`{}`)},
	}}
	api := &simpleMeasurementMetaAPI{
		HTTPClient: clnt,
		JSONCodec:  &FakeCodec{DecodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.MeasurementMetaRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

type handleMeasurementMeta struct {
	accept      string
	body        []byte
	contentType string
	count       int32
	method      string
	mu          sync.Mutex
	resp        *apimodel.MeasurementMetaResponse
	url         *url.URL
	userAgent   string
}

func (h *handleMeasurementMeta) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer h.mu.Unlock()
	h.mu.Lock()
	if h.count > 0 {
		w.WriteHeader(400)
		return
	}
	h.count++
	if r.Body != nil {
		data, err := netxlite.ReadAllContext(r.Context(), r.Body)
		if err != nil {
			w.WriteHeader(400)
			return
		}
		h.body = data
	}
	h.method = r.Method
	h.url = r.URL
	h.accept = r.Header.Get("Accept")
	h.contentType = r.Header.Get("Content-Type")
	h.userAgent = r.Header.Get("User-Agent")
	var out *apimodel.MeasurementMetaResponse
	ff := fakeFill{}
	ff.fill(&out)
	h.resp = out
	data, err := json.Marshal(out)
	if err != nil {
		w.WriteHeader(400)
		return
	}
	w.Write(data)
}

func TestMeasurementMetaRoundTrip(t *testing.T) {
	// setup
	handler := &handleMeasurementMeta{}
	srvr := httptest.NewServer(handler)
	defer srvr.Close()
	req := &apimodel.MeasurementMetaRequest{}
	ff := &fakeFill{}
	ff.fill(&req)
	api := &simpleMeasurementMetaAPI{BaseURL: srvr.URL}
	ff.fill(&api.UserAgent)
	// issue request
	ctx := context.Background()
	resp, err := api.Call(ctx, req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response here")
	}
	// compare our response and server's one
	if diff := cmp.Diff(handler.resp, resp); diff != "" {
		t.Fatal(diff)
	}
	// check whether headers are OK
	if handler.accept != "application/json" {
		t.Fatal("invalid accept header")
	}
	if handler.userAgent != api.UserAgent {
		t.Fatal("invalid user-agent header")
	}
	// check whether the method is OK
	if handler.method != "GET" {
		t.Fatal("invalid method")
	}
	// check the query
	httpReq, err := api.newRequest(context.Background(), req)
	if err != nil {
		t.Fatal(err)
	}
	if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
		t.Fatal(diff)
	}
	if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
		t.Fatal(diff)
	}
}

func TestMeasurementMetaMandatoryFields(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 500,
	}}
	api := &simpleMeasurementMetaAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.MeasurementMetaRequest{} // deliberately empty
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrEmptyField) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestRegisterInvalidURL(t *testing.T) {
	api := &simpleRegisterAPI{
		BaseURL: "\t", // invalid
	}
	ctx := context.Background()
	req := &apimodel.RegisterRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestRegisterWithHTTPErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Err: errMocked}
	api := &simpleRegisterAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.RegisterRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestRegisterMarshalErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleRegisterAPI{
		JSONCodec: &FakeCodec{EncodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.RegisterRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestRegisterWithNewRequestErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleRegisterAPI{
		RequestMaker: &FakeRequestMaker{Err: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.RegisterRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestRegisterWith401(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
	api := &simpleRegisterAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.RegisterRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrUnauthorized) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestRegisterWith400(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
	api := &simpleRegisterAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.RegisterRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrHTTPFailure) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestRegisterWithResponseBodyReadErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Err: errMocked},
	}}
	api := &simpleRegisterAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.RegisterRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestRegisterWithUnmarshalFailure(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`{}`)},
	}}
	api := &simpleRegisterAPI{
		HTTPClient: clnt,
		JSONCodec:  &FakeCodec{DecodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.RegisterRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

type handleRegister struct {
	accept      string
	body        []byte
	contentType string
	count       int32
	method      string
	mu          sync.Mutex
	resp        *apimodel.RegisterResponse
	url         *url.URL
	userAgent   string
}

func (h *handleRegister) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer h.mu.Unlock()
	h.mu.Lock()
	if h.count > 0 {
		w.WriteHeader(400)
		return
	}
	h.count++
	if r.Body != nil {
		data, err := netxlite.ReadAllContext(r.Context(), r.Body)
		if err != nil {
			w.WriteHeader(400)
			return
		}
		h.body = data
	}
	h.method = r.Method
	h.url = r.URL
	h.accept = r.Header.Get("Accept")
	h.contentType = r.Header.Get("Content-Type")
	h.userAgent = r.Header.Get("User-Agent")
	var out *apimodel.RegisterResponse
	ff := fakeFill{}
	ff.fill(&out)
	h.resp = out
	data, err := json.Marshal(out)
	if err != nil {
		w.WriteHeader(400)
		return
	}
	w.Write(data)
}

func TestRegisterRoundTrip(t *testing.T) {
	// setup
	handler := &handleRegister{}
	srvr := httptest.NewServer(handler)
	defer srvr.Close()
	req := &apimodel.RegisterRequest{}
	ff := &fakeFill{}
	ff.fill(&req)
	api := &simpleRegisterAPI{BaseURL: srvr.URL}
	ff.fill(&api.UserAgent)
	// issue request
	ctx := context.Background()
	resp, err := api.Call(ctx, req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response here")
	}
	// compare our response and server's one
	if diff := cmp.Diff(handler.resp, resp); diff != "" {
		t.Fatal(diff)
	}
	// check whether headers are OK
	if handler.accept != "application/json" {
		t.Fatal("invalid accept header")
	}
	if handler.userAgent != api.UserAgent {
		t.Fatal("invalid user-agent header")
	}
	// check whether the method is OK
	if handler.method != "POST" {
		t.Fatal("invalid method")
	}
	// check the body
	if handler.contentType != "application/json" {
		t.Fatal("invalid content-type header")
	}
	got := &apimodel.RegisterRequest{}
	if err := json.Unmarshal(handler.body, &got); err != nil {
		t.Fatal(err)
	}
	if diff := cmp.Diff(req, got); diff != "" {
		t.Fatal(diff)
	}
}

func TestTestHelpersInvalidURL(t *testing.T) {
	api := &simpleTestHelpersAPI{
		BaseURL: "\t", // invalid
	}
	ctx := context.Background()
	req := &apimodel.TestHelpersRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTestHelpersWithHTTPErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Err: errMocked}
	api := &simpleTestHelpersAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.TestHelpersRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTestHelpersWithNewRequestErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleTestHelpersAPI{
		RequestMaker: &FakeRequestMaker{Err: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.TestHelpersRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTestHelpersWith401(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
	api := &simpleTestHelpersAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.TestHelpersRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrUnauthorized) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTestHelpersWith400(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
	api := &simpleTestHelpersAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.TestHelpersRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrHTTPFailure) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTestHelpersWithResponseBodyReadErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Err: errMocked},
	}}
	api := &simpleTestHelpersAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.TestHelpersRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTestHelpersWithUnmarshalFailure(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`{}`)},
	}}
	api := &simpleTestHelpersAPI{
		HTTPClient: clnt,
		JSONCodec:  &FakeCodec{DecodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.TestHelpersRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

type handleTestHelpers struct {
	accept      string
	body        []byte
	contentType string
	count       int32
	method      string
	mu          sync.Mutex
	resp        apimodel.TestHelpersResponse
	url         *url.URL
	userAgent   string
}

func (h *handleTestHelpers) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer h.mu.Unlock()
	h.mu.Lock()
	if h.count > 0 {
		w.WriteHeader(400)
		return
	}
	h.count++
	if r.Body != nil {
		data, err := netxlite.ReadAllContext(r.Context(), r.Body)
		if err != nil {
			w.WriteHeader(400)
			return
		}
		h.body = data
	}
	h.method = r.Method
	h.url = r.URL
	h.accept = r.Header.Get("Accept")
	h.contentType = r.Header.Get("Content-Type")
	h.userAgent = r.Header.Get("User-Agent")
	var out apimodel.TestHelpersResponse
	ff := fakeFill{}
	ff.fill(&out)
	h.resp = out
	data, err := json.Marshal(out)
	if err != nil {
		w.WriteHeader(400)
		return
	}
	w.Write(data)
}

func TestTestHelpersRoundTrip(t *testing.T) {
	// setup
	handler := &handleTestHelpers{}
	srvr := httptest.NewServer(handler)
	defer srvr.Close()
	req := &apimodel.TestHelpersRequest{}
	ff := &fakeFill{}
	ff.fill(&req)
	api := &simpleTestHelpersAPI{BaseURL: srvr.URL}
	ff.fill(&api.UserAgent)
	// issue request
	ctx := context.Background()
	resp, err := api.Call(ctx, req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response here")
	}
	// compare our response and server's one
	if diff := cmp.Diff(handler.resp, resp); diff != "" {
		t.Fatal(diff)
	}
	// check whether headers are OK
	if handler.accept != "application/json" {
		t.Fatal("invalid accept header")
	}
	if handler.userAgent != api.UserAgent {
		t.Fatal("invalid user-agent header")
	}
	// check whether the method is OK
	if handler.method != "GET" {
		t.Fatal("invalid method")
	}
	// check the query
	httpReq, err := api.newRequest(context.Background(), req)
	if err != nil {
		t.Fatal(err)
	}
	if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
		t.Fatal(diff)
	}
	if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
		t.Fatal(diff)
	}
}

func TestTestHelpersResponseLiteralNull(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`null`)},
	}}
	api := &simpleTestHelpersAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.TestHelpersRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrJSONLiteralNull) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestPsiphonConfigInvalidURL(t *testing.T) {
	api := &simplePsiphonConfigAPI{
		BaseURL: "\t", // invalid
	}
	ctx := context.Background()
	req := &apimodel.PsiphonConfigRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestPsiphonConfigWithMissingToken(t *testing.T) {
	api := &simplePsiphonConfigAPI{} // no token
	ctx := context.Background()
	req := &apimodel.PsiphonConfigRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrMissingToken) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestPsiphonConfigWithHTTPErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Err: errMocked}
	api := &simplePsiphonConfigAPI{
		HTTPClient: clnt,
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.PsiphonConfigRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestPsiphonConfigWithNewRequestErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simplePsiphonConfigAPI{
		RequestMaker: &FakeRequestMaker{Err: errMocked},
		Token:        "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.PsiphonConfigRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestPsiphonConfigWith401(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
	api := &simplePsiphonConfigAPI{
		HTTPClient: clnt,
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.PsiphonConfigRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrUnauthorized) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestPsiphonConfigWith400(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
	api := &simplePsiphonConfigAPI{
		HTTPClient: clnt,
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.PsiphonConfigRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrHTTPFailure) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestPsiphonConfigWithResponseBodyReadErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Err: errMocked},
	}}
	api := &simplePsiphonConfigAPI{
		HTTPClient: clnt,
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.PsiphonConfigRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestPsiphonConfigWithUnmarshalFailure(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`{}`)},
	}}
	api := &simplePsiphonConfigAPI{
		HTTPClient: clnt,
		JSONCodec:  &FakeCodec{DecodeErr: errMocked},
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.PsiphonConfigRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

type handlePsiphonConfig struct {
	accept      string
	body        []byte
	contentType string
	count       int32
	method      string
	mu          sync.Mutex
	resp        apimodel.PsiphonConfigResponse
	url         *url.URL
	userAgent   string
}

func (h *handlePsiphonConfig) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer h.mu.Unlock()
	h.mu.Lock()
	if h.count > 0 {
		w.WriteHeader(400)
		return
	}
	h.count++
	if r.Body != nil {
		data, err := netxlite.ReadAllContext(r.Context(), r.Body)
		if err != nil {
			w.WriteHeader(400)
			return
		}
		h.body = data
	}
	h.method = r.Method
	h.url = r.URL
	h.accept = r.Header.Get("Accept")
	h.contentType = r.Header.Get("Content-Type")
	h.userAgent = r.Header.Get("User-Agent")
	var out apimodel.PsiphonConfigResponse
	ff := fakeFill{}
	ff.fill(&out)
	h.resp = out
	data, err := json.Marshal(out)
	if err != nil {
		w.WriteHeader(400)
		return
	}
	w.Write(data)
}

func TestPsiphonConfigRoundTrip(t *testing.T) {
	// setup
	handler := &handlePsiphonConfig{}
	srvr := httptest.NewServer(handler)
	defer srvr.Close()
	req := &apimodel.PsiphonConfigRequest{}
	ff := &fakeFill{}
	ff.fill(&req)
	api := &simplePsiphonConfigAPI{BaseURL: srvr.URL}
	ff.fill(&api.UserAgent)
	ff.fill(&api.Token)
	// issue request
	ctx := context.Background()
	resp, err := api.Call(ctx, req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response here")
	}
	// compare our response and server's one
	if diff := cmp.Diff(handler.resp, resp); diff != "" {
		t.Fatal(diff)
	}
	// check whether headers are OK
	if handler.accept != "application/json" {
		t.Fatal("invalid accept header")
	}
	if handler.userAgent != api.UserAgent {
		t.Fatal("invalid user-agent header")
	}
	// check whether the method is OK
	if handler.method != "GET" {
		t.Fatal("invalid method")
	}
	// check the query
	httpReq, err := api.newRequest(context.Background(), req)
	if err != nil {
		t.Fatal(err)
	}
	if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
		t.Fatal(diff)
	}
	if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
		t.Fatal(diff)
	}
}

func TestPsiphonConfigResponseLiteralNull(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`null`)},
	}}
	api := &simplePsiphonConfigAPI{
		HTTPClient: clnt,
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.PsiphonConfigRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrJSONLiteralNull) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTorTargetsInvalidURL(t *testing.T) {
	api := &simpleTorTargetsAPI{
		BaseURL: "\t", // invalid
	}
	ctx := context.Background()
	req := &apimodel.TorTargetsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTorTargetsWithMissingToken(t *testing.T) {
	api := &simpleTorTargetsAPI{} // no token
	ctx := context.Background()
	req := &apimodel.TorTargetsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrMissingToken) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTorTargetsWithHTTPErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Err: errMocked}
	api := &simpleTorTargetsAPI{
		HTTPClient: clnt,
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.TorTargetsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTorTargetsWithNewRequestErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleTorTargetsAPI{
		RequestMaker: &FakeRequestMaker{Err: errMocked},
		Token:        "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.TorTargetsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTorTargetsWith401(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
	api := &simpleTorTargetsAPI{
		HTTPClient: clnt,
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.TorTargetsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrUnauthorized) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTorTargetsWith400(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
	api := &simpleTorTargetsAPI{
		HTTPClient: clnt,
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.TorTargetsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrHTTPFailure) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTorTargetsWithResponseBodyReadErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Err: errMocked},
	}}
	api := &simpleTorTargetsAPI{
		HTTPClient: clnt,
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.TorTargetsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestTorTargetsWithUnmarshalFailure(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`{}`)},
	}}
	api := &simpleTorTargetsAPI{
		HTTPClient: clnt,
		JSONCodec:  &FakeCodec{DecodeErr: errMocked},
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.TorTargetsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

type handleTorTargets struct {
	accept      string
	body        []byte
	contentType string
	count       int32
	method      string
	mu          sync.Mutex
	resp        apimodel.TorTargetsResponse
	url         *url.URL
	userAgent   string
}

func (h *handleTorTargets) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer h.mu.Unlock()
	h.mu.Lock()
	if h.count > 0 {
		w.WriteHeader(400)
		return
	}
	h.count++
	if r.Body != nil {
		data, err := netxlite.ReadAllContext(r.Context(), r.Body)
		if err != nil {
			w.WriteHeader(400)
			return
		}
		h.body = data
	}
	h.method = r.Method
	h.url = r.URL
	h.accept = r.Header.Get("Accept")
	h.contentType = r.Header.Get("Content-Type")
	h.userAgent = r.Header.Get("User-Agent")
	var out apimodel.TorTargetsResponse
	ff := fakeFill{}
	ff.fill(&out)
	h.resp = out
	data, err := json.Marshal(out)
	if err != nil {
		w.WriteHeader(400)
		return
	}
	w.Write(data)
}

func TestTorTargetsRoundTrip(t *testing.T) {
	// setup
	handler := &handleTorTargets{}
	srvr := httptest.NewServer(handler)
	defer srvr.Close()
	req := &apimodel.TorTargetsRequest{}
	ff := &fakeFill{}
	ff.fill(&req)
	api := &simpleTorTargetsAPI{BaseURL: srvr.URL}
	ff.fill(&api.UserAgent)
	ff.fill(&api.Token)
	// issue request
	ctx := context.Background()
	resp, err := api.Call(ctx, req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response here")
	}
	// compare our response and server's one
	if diff := cmp.Diff(handler.resp, resp); diff != "" {
		t.Fatal(diff)
	}
	// check whether headers are OK
	if handler.accept != "application/json" {
		t.Fatal("invalid accept header")
	}
	if handler.userAgent != api.UserAgent {
		t.Fatal("invalid user-agent header")
	}
	// check whether the method is OK
	if handler.method != "GET" {
		t.Fatal("invalid method")
	}
	// check the query
	httpReq, err := api.newRequest(context.Background(), req)
	if err != nil {
		t.Fatal(err)
	}
	if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
		t.Fatal(diff)
	}
	if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
		t.Fatal(diff)
	}
}

func TestTorTargetsResponseLiteralNull(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`null`)},
	}}
	api := &simpleTorTargetsAPI{
		HTTPClient: clnt,
		Token:      "fakeToken",
	}
	ctx := context.Background()
	req := &apimodel.TorTargetsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrJSONLiteralNull) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestURLsInvalidURL(t *testing.T) {
	api := &simpleURLsAPI{
		BaseURL: "\t", // invalid
	}
	ctx := context.Background()
	req := &apimodel.URLsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestURLsWithHTTPErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Err: errMocked}
	api := &simpleURLsAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.URLsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestURLsWithNewRequestErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleURLsAPI{
		RequestMaker: &FakeRequestMaker{Err: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.URLsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestURLsWith401(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
	api := &simpleURLsAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.URLsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrUnauthorized) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestURLsWith400(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
	api := &simpleURLsAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.URLsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrHTTPFailure) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestURLsWithResponseBodyReadErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Err: errMocked},
	}}
	api := &simpleURLsAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.URLsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestURLsWithUnmarshalFailure(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`{}`)},
	}}
	api := &simpleURLsAPI{
		HTTPClient: clnt,
		JSONCodec:  &FakeCodec{DecodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.URLsRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

type handleURLs struct {
	accept      string
	body        []byte
	contentType string
	count       int32
	method      string
	mu          sync.Mutex
	resp        *apimodel.URLsResponse
	url         *url.URL
	userAgent   string
}

func (h *handleURLs) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer h.mu.Unlock()
	h.mu.Lock()
	if h.count > 0 {
		w.WriteHeader(400)
		return
	}
	h.count++
	if r.Body != nil {
		data, err := netxlite.ReadAllContext(r.Context(), r.Body)
		if err != nil {
			w.WriteHeader(400)
			return
		}
		h.body = data
	}
	h.method = r.Method
	h.url = r.URL
	h.accept = r.Header.Get("Accept")
	h.contentType = r.Header.Get("Content-Type")
	h.userAgent = r.Header.Get("User-Agent")
	var out *apimodel.URLsResponse
	ff := fakeFill{}
	ff.fill(&out)
	h.resp = out
	data, err := json.Marshal(out)
	if err != nil {
		w.WriteHeader(400)
		return
	}
	w.Write(data)
}

func TestURLsRoundTrip(t *testing.T) {
	// setup
	handler := &handleURLs{}
	srvr := httptest.NewServer(handler)
	defer srvr.Close()
	req := &apimodel.URLsRequest{}
	ff := &fakeFill{}
	ff.fill(&req)
	api := &simpleURLsAPI{BaseURL: srvr.URL}
	ff.fill(&api.UserAgent)
	// issue request
	ctx := context.Background()
	resp, err := api.Call(ctx, req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response here")
	}
	// compare our response and server's one
	if diff := cmp.Diff(handler.resp, resp); diff != "" {
		t.Fatal(diff)
	}
	// check whether headers are OK
	if handler.accept != "application/json" {
		t.Fatal("invalid accept header")
	}
	if handler.userAgent != api.UserAgent {
		t.Fatal("invalid user-agent header")
	}
	// check whether the method is OK
	if handler.method != "GET" {
		t.Fatal("invalid method")
	}
	// check the query
	httpReq, err := api.newRequest(context.Background(), req)
	if err != nil {
		t.Fatal(err)
	}
	if diff := cmp.Diff(handler.url.Path, httpReq.URL.Path); diff != "" {
		t.Fatal(diff)
	}
	if diff := cmp.Diff(handler.url.RawQuery, httpReq.URL.RawQuery); diff != "" {
		t.Fatal(diff)
	}
}

func TestOpenReportInvalidURL(t *testing.T) {
	api := &simpleOpenReportAPI{
		BaseURL: "\t", // invalid
	}
	ctx := context.Background()
	req := &apimodel.OpenReportRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestOpenReportWithHTTPErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Err: errMocked}
	api := &simpleOpenReportAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.OpenReportRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestOpenReportMarshalErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleOpenReportAPI{
		JSONCodec: &FakeCodec{EncodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.OpenReportRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestOpenReportWithNewRequestErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleOpenReportAPI{
		RequestMaker: &FakeRequestMaker{Err: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.OpenReportRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestOpenReportWith401(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
	api := &simpleOpenReportAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.OpenReportRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrUnauthorized) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestOpenReportWith400(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
	api := &simpleOpenReportAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.OpenReportRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrHTTPFailure) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestOpenReportWithResponseBodyReadErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Err: errMocked},
	}}
	api := &simpleOpenReportAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.OpenReportRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestOpenReportWithUnmarshalFailure(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`{}`)},
	}}
	api := &simpleOpenReportAPI{
		HTTPClient: clnt,
		JSONCodec:  &FakeCodec{DecodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.OpenReportRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

type handleOpenReport struct {
	accept      string
	body        []byte
	contentType string
	count       int32
	method      string
	mu          sync.Mutex
	resp        *apimodel.OpenReportResponse
	url         *url.URL
	userAgent   string
}

func (h *handleOpenReport) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer h.mu.Unlock()
	h.mu.Lock()
	if h.count > 0 {
		w.WriteHeader(400)
		return
	}
	h.count++
	if r.Body != nil {
		data, err := netxlite.ReadAllContext(r.Context(), r.Body)
		if err != nil {
			w.WriteHeader(400)
			return
		}
		h.body = data
	}
	h.method = r.Method
	h.url = r.URL
	h.accept = r.Header.Get("Accept")
	h.contentType = r.Header.Get("Content-Type")
	h.userAgent = r.Header.Get("User-Agent")
	var out *apimodel.OpenReportResponse
	ff := fakeFill{}
	ff.fill(&out)
	h.resp = out
	data, err := json.Marshal(out)
	if err != nil {
		w.WriteHeader(400)
		return
	}
	w.Write(data)
}

func TestOpenReportRoundTrip(t *testing.T) {
	// setup
	handler := &handleOpenReport{}
	srvr := httptest.NewServer(handler)
	defer srvr.Close()
	req := &apimodel.OpenReportRequest{}
	ff := &fakeFill{}
	ff.fill(&req)
	api := &simpleOpenReportAPI{BaseURL: srvr.URL}
	ff.fill(&api.UserAgent)
	// issue request
	ctx := context.Background()
	resp, err := api.Call(ctx, req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response here")
	}
	// compare our response and server's one
	if diff := cmp.Diff(handler.resp, resp); diff != "" {
		t.Fatal(diff)
	}
	// check whether headers are OK
	if handler.accept != "application/json" {
		t.Fatal("invalid accept header")
	}
	if handler.userAgent != api.UserAgent {
		t.Fatal("invalid user-agent header")
	}
	// check whether the method is OK
	if handler.method != "POST" {
		t.Fatal("invalid method")
	}
	// check the body
	if handler.contentType != "application/json" {
		t.Fatal("invalid content-type header")
	}
	got := &apimodel.OpenReportRequest{}
	if err := json.Unmarshal(handler.body, &got); err != nil {
		t.Fatal(err)
	}
	if diff := cmp.Diff(req, got); diff != "" {
		t.Fatal(diff)
	}
}

func TestSubmitMeasurementInvalidURL(t *testing.T) {
	api := &simpleSubmitMeasurementAPI{
		BaseURL: "\t", // invalid
	}
	ctx := context.Background()
	req := &apimodel.SubmitMeasurementRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if err == nil || !strings.HasSuffix(err.Error(), "invalid control character in URL") {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestSubmitMeasurementWithHTTPErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Err: errMocked}
	api := &simpleSubmitMeasurementAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.SubmitMeasurementRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestSubmitMeasurementMarshalErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleSubmitMeasurementAPI{
		JSONCodec: &FakeCodec{EncodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.SubmitMeasurementRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestSubmitMeasurementWithNewRequestErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	api := &simpleSubmitMeasurementAPI{
		RequestMaker: &FakeRequestMaker{Err: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.SubmitMeasurementRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestSubmitMeasurementWith401(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 401}}
	api := &simpleSubmitMeasurementAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.SubmitMeasurementRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrUnauthorized) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestSubmitMeasurementWith400(t *testing.T) {
	clnt := &FakeHTTPClient{Resp: &http.Response{StatusCode: 400}}
	api := &simpleSubmitMeasurementAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.SubmitMeasurementRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, ErrHTTPFailure) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestSubmitMeasurementWithResponseBodyReadErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Err: errMocked},
	}}
	api := &simpleSubmitMeasurementAPI{
		HTTPClient: clnt,
	}
	ctx := context.Background()
	req := &apimodel.SubmitMeasurementRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

func TestSubmitMeasurementWithUnmarshalFailure(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 200,
		Body:       &FakeBody{Data: []byte(`{}`)},
	}}
	api := &simpleSubmitMeasurementAPI{
		HTTPClient: clnt,
		JSONCodec:  &FakeCodec{DecodeErr: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.SubmitMeasurementRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}

type handleSubmitMeasurement struct {
	accept      string
	body        []byte
	contentType string
	count       int32
	method      string
	mu          sync.Mutex
	resp        *apimodel.SubmitMeasurementResponse
	url         *url.URL
	userAgent   string
}

func (h *handleSubmitMeasurement) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	defer h.mu.Unlock()
	h.mu.Lock()
	if h.count > 0 {
		w.WriteHeader(400)
		return
	}
	h.count++
	if r.Body != nil {
		data, err := netxlite.ReadAllContext(r.Context(), r.Body)
		if err != nil {
			w.WriteHeader(400)
			return
		}
		h.body = data
	}
	h.method = r.Method
	h.url = r.URL
	h.accept = r.Header.Get("Accept")
	h.contentType = r.Header.Get("Content-Type")
	h.userAgent = r.Header.Get("User-Agent")
	var out *apimodel.SubmitMeasurementResponse
	ff := fakeFill{}
	ff.fill(&out)
	h.resp = out
	data, err := json.Marshal(out)
	if err != nil {
		w.WriteHeader(400)
		return
	}
	w.Write(data)
}

func TestSubmitMeasurementRoundTrip(t *testing.T) {
	// setup
	handler := &handleSubmitMeasurement{}
	srvr := httptest.NewServer(handler)
	defer srvr.Close()
	req := &apimodel.SubmitMeasurementRequest{}
	ff := &fakeFill{}
	ff.fill(&req)
	api := &simpleSubmitMeasurementAPI{BaseURL: srvr.URL}
	ff.fill(&api.UserAgent)
	// issue request
	ctx := context.Background()
	resp, err := api.Call(ctx, req)
	if err != nil {
		t.Fatal(err)
	}
	if resp == nil {
		t.Fatal("expected non-nil response here")
	}
	// compare our response and server's one
	if diff := cmp.Diff(handler.resp, resp); diff != "" {
		t.Fatal(diff)
	}
	// check whether headers are OK
	if handler.accept != "application/json" {
		t.Fatal("invalid accept header")
	}
	if handler.userAgent != api.UserAgent {
		t.Fatal("invalid user-agent header")
	}
	// check whether the method is OK
	if handler.method != "POST" {
		t.Fatal("invalid method")
	}
	// check the body
	if handler.contentType != "application/json" {
		t.Fatal("invalid content-type header")
	}
	got := &apimodel.SubmitMeasurementRequest{}
	if err := json.Unmarshal(handler.body, &got); err != nil {
		t.Fatal(err)
	}
	if diff := cmp.Diff(req, got); diff != "" {
		t.Fatal(diff)
	}
}

func TestSubmitMeasurementTemplateErr(t *testing.T) {
	errMocked := errors.New("mocked error")
	clnt := &FakeHTTPClient{Resp: &http.Response{
		StatusCode: 500,
	}}
	api := &simpleSubmitMeasurementAPI{
		HTTPClient:       clnt,
		TemplateExecutor: &FakeTemplateExecutor{Err: errMocked},
	}
	ctx := context.Background()
	req := &apimodel.SubmitMeasurementRequest{}
	ff := &fakeFill{}
	ff.fill(req)
	resp, err := api.Call(ctx, req)
	if !errors.Is(err, errMocked) {
		t.Fatal("not the error we expected", err)
	}
	if resp != nil {
		t.Fatal("expected nil resp")
	}
}
