276beb8719
When a probe gets a local DNS failure, it will continue and nonetheless query the test helper without any IP address, just an empty list. This diff fixes the behavior of cmd/oohelper to do the same. Work part of https://github.com/ooni/probe/issues/1707.
348 lines
8.9 KiB
Go
348 lines
8.9 KiB
Go
package internal_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"net/url"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/ooni/probe-cli/v3/internal/cmd/oohelper/internal"
|
|
)
|
|
|
|
func TestMakeTCPEndpoints(t *testing.T) {
|
|
type args struct {
|
|
URL *url.URL
|
|
addrs []string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want []string
|
|
err error
|
|
}{{
|
|
name: "with host != hostname",
|
|
args: args{URL: &url.URL{Host: "127.0.0.1:8080"}},
|
|
err: internal.ErrUnsupportedExplicitPort,
|
|
}, {
|
|
name: "with unsupported URL scheme",
|
|
args: args{URL: &url.URL{Host: "127.0.0.1", Scheme: "imap"}},
|
|
err: internal.ErrUnsupportedURLScheme,
|
|
}, {
|
|
name: "with http scheme",
|
|
args: args{
|
|
URL: &url.URL{Host: "www.kernel.org", Scheme: "http"},
|
|
addrs: []string{"1.1.1.1", "2.2.2.2", "::1"},
|
|
},
|
|
want: []string{"1.1.1.1:80", "2.2.2.2:80", "[::1]:80"},
|
|
}, {
|
|
name: "with https scheme",
|
|
args: args{
|
|
URL: &url.URL{Host: "www.kernel.org", Scheme: "https"},
|
|
addrs: []string{"1.1.1.1", "2.2.2.2", "::1"},
|
|
},
|
|
want: []string{"1.1.1.1:443", "2.2.2.2:443", "[::1]:443"},
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := internal.MakeTCPEndpoints(tt.args.URL, tt.args.addrs)
|
|
if !errors.Is(err, tt.err) {
|
|
t.Errorf("MakeTCPEndpoints() error = %v, wantErr %v", err, tt.err)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("MakeTCPEndpoints() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestOOClientDoWithEmptyTargetURL(t *testing.T) {
|
|
ctx := context.Background()
|
|
config := internal.OOConfig{}
|
|
clnt := internal.OOClient{}
|
|
cresp, err := clnt.Do(ctx, config)
|
|
if !errors.Is(err, internal.ErrEmptyURL) {
|
|
t.Fatalf("not the error we expected: %+v", err)
|
|
}
|
|
if cresp != nil {
|
|
t.Fatal("expected nil response")
|
|
}
|
|
}
|
|
|
|
func TestOOClientDoWithEmptyServerURL(t *testing.T) {
|
|
ctx := context.Background()
|
|
config := internal.OOConfig{TargetURL: "http://www.example.com"}
|
|
clnt := internal.OOClient{}
|
|
cresp, err := clnt.Do(ctx, config)
|
|
if !errors.Is(err, internal.ErrEmptyURL) {
|
|
t.Fatalf("not the error we expected: %+v", err)
|
|
}
|
|
if cresp != nil {
|
|
t.Fatal("expected nil response")
|
|
}
|
|
}
|
|
|
|
func TestOOClientDoWithInvalidTargetURL(t *testing.T) {
|
|
ctx := context.Background()
|
|
config := internal.OOConfig{TargetURL: "\t", ServerURL: "https://wcth.ooni.io"}
|
|
clnt := internal.OOClient{}
|
|
cresp, err := clnt.Do(ctx, config)
|
|
if !errors.Is(err, internal.ErrInvalidURL) {
|
|
t.Fatalf("not the error we expected: %+v", err)
|
|
}
|
|
if cresp != nil {
|
|
t.Fatal("expected nil response")
|
|
}
|
|
}
|
|
|
|
func TestOOClientDoWithResolverFailure(t *testing.T) {
|
|
ctx := context.Background()
|
|
config := internal.OOConfig{
|
|
TargetURL: "http://www.example.com",
|
|
ServerURL: "https://wcth.ooni.io",
|
|
}
|
|
clnt := internal.OOClient{
|
|
HTTPClient: http.DefaultClient,
|
|
Resolver: internal.NewFakeResolverThatFails(),
|
|
}
|
|
cresp, err := clnt.Do(ctx, config)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(cresp.TCPConnect) > 0 {
|
|
// The current implementation of the test helper (the legacy codebase)
|
|
// only follows the IP addresses returned by the client.
|
|
t.Fatal("expected empty TCPConnect here")
|
|
}
|
|
if cresp.HTTPRequest.StatusCode != 200 {
|
|
t.Fatal("expected 200 status code here")
|
|
}
|
|
if len(cresp.DNS.Addrs) < 1 {
|
|
t.Fatal("expected at least an IP address here")
|
|
}
|
|
}
|
|
|
|
func TestOOClientDoWithUnsupportedExplicitPort(t *testing.T) {
|
|
ctx := context.Background()
|
|
config := internal.OOConfig{
|
|
TargetURL: "http://www.example.com:8080",
|
|
ServerURL: "https://wcth.ooni.io",
|
|
}
|
|
clnt := internal.OOClient{
|
|
Resolver: internal.NewFakeResolverWithResult([]string{"1.1.1.1"}),
|
|
}
|
|
cresp, err := clnt.Do(ctx, config)
|
|
if !errors.Is(err, internal.ErrUnsupportedExplicitPort) {
|
|
t.Fatalf("not the error we expected: %+v", err)
|
|
}
|
|
if cresp != nil {
|
|
t.Fatal("expected nil response")
|
|
}
|
|
}
|
|
|
|
func TestOOClientDoWithInvalidServerURL(t *testing.T) {
|
|
ctx := context.Background()
|
|
config := internal.OOConfig{
|
|
TargetURL: "http://www.example.com",
|
|
ServerURL: "\t",
|
|
}
|
|
clnt := internal.OOClient{
|
|
Resolver: internal.NewFakeResolverWithResult([]string{"1.1.1.1"}),
|
|
}
|
|
cresp, err := clnt.Do(ctx, config)
|
|
if !errors.Is(err, internal.ErrCannotCreateRequest) {
|
|
t.Fatalf("not the error we expected: %+v", err)
|
|
}
|
|
if cresp != nil {
|
|
t.Fatal("expected nil response")
|
|
}
|
|
}
|
|
|
|
func TestOOClientDoWithRoundTripError(t *testing.T) {
|
|
expected := errors.New("mocked error")
|
|
ctx := context.Background()
|
|
config := internal.OOConfig{
|
|
TargetURL: "http://www.example.com",
|
|
ServerURL: "https://wcth.ooni.io",
|
|
}
|
|
clnt := internal.OOClient{
|
|
Resolver: internal.NewFakeResolverWithResult([]string{"1.1.1.1"}),
|
|
HTTPClient: &http.Client{
|
|
Transport: internal.FakeTransport{Err: expected},
|
|
},
|
|
}
|
|
cresp, err := clnt.Do(ctx, config)
|
|
if !errors.Is(err, expected) {
|
|
t.Fatalf("not the error we expected: %+v", err)
|
|
}
|
|
if cresp != nil {
|
|
t.Fatal("expected nil response")
|
|
}
|
|
}
|
|
|
|
func TestOOClientDoWithInvalidStatusCode(t *testing.T) {
|
|
ctx := context.Background()
|
|
config := internal.OOConfig{
|
|
TargetURL: "http://www.example.com",
|
|
ServerURL: "https://wcth.ooni.io",
|
|
}
|
|
clnt := internal.OOClient{
|
|
Resolver: internal.NewFakeResolverWithResult([]string{"1.1.1.1"}),
|
|
HTTPClient: &http.Client{Transport: internal.FakeTransport{
|
|
Resp: &http.Response{
|
|
StatusCode: 400,
|
|
Body: &internal.FakeBody{},
|
|
},
|
|
}},
|
|
}
|
|
cresp, err := clnt.Do(ctx, config)
|
|
if !errors.Is(err, internal.ErrHTTPStatusCode) {
|
|
t.Fatalf("not the error we expected: %+v", err)
|
|
}
|
|
if cresp != nil {
|
|
t.Fatal("expected nil response")
|
|
}
|
|
}
|
|
|
|
func TestOOClientDoWithBodyReadError(t *testing.T) {
|
|
expected := errors.New("mocked error")
|
|
ctx := context.Background()
|
|
config := internal.OOConfig{
|
|
TargetURL: "http://www.example.com",
|
|
ServerURL: "https://wcth.ooni.io",
|
|
}
|
|
clnt := internal.OOClient{
|
|
Resolver: internal.NewFakeResolverWithResult([]string{"1.1.1.1"}),
|
|
HTTPClient: &http.Client{Transport: internal.FakeTransport{
|
|
Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &internal.FakeBody{
|
|
Err: expected,
|
|
},
|
|
},
|
|
}},
|
|
}
|
|
cresp, err := clnt.Do(ctx, config)
|
|
if !errors.Is(err, expected) {
|
|
t.Fatalf("not the error we expected: %+v", err)
|
|
}
|
|
if cresp != nil {
|
|
t.Fatal("expected nil response")
|
|
}
|
|
}
|
|
|
|
func TestOOClientDoWithInvalidJSON(t *testing.T) {
|
|
ctx := context.Background()
|
|
config := internal.OOConfig{
|
|
TargetURL: "http://www.example.com",
|
|
ServerURL: "https://wcth.ooni.io",
|
|
}
|
|
clnt := internal.OOClient{
|
|
Resolver: internal.NewFakeResolverWithResult([]string{"1.1.1.1"}),
|
|
HTTPClient: &http.Client{Transport: internal.FakeTransport{
|
|
Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &internal.FakeBody{
|
|
Data: []byte("{"),
|
|
},
|
|
},
|
|
}},
|
|
}
|
|
cresp, err := clnt.Do(ctx, config)
|
|
if !errors.Is(err, internal.ErrCannotParseJSONReply) {
|
|
t.Fatalf("not the error we expected: %+v", err)
|
|
}
|
|
if cresp != nil {
|
|
t.Fatal("expected nil response")
|
|
}
|
|
}
|
|
|
|
const goodresponse = `{
|
|
"tcp_connect": {
|
|
"172.217.21.68:80": {
|
|
"status": true,
|
|
"failure": null
|
|
}
|
|
},
|
|
"http_request": {
|
|
"body_length": 207878,
|
|
"failure": null,
|
|
"title": "Google",
|
|
"headers": {
|
|
"Content-Type": "text/html"
|
|
},
|
|
"status_code": 200
|
|
},
|
|
"dns": {
|
|
"failure": null,
|
|
"addrs": [
|
|
"172.217.17.68"
|
|
]
|
|
}
|
|
}`
|
|
|
|
func TestOOClientDoWithParseableJSON(t *testing.T) {
|
|
ctx := context.Background()
|
|
config := internal.OOConfig{
|
|
TargetURL: "http://www.example.com",
|
|
ServerURL: "https://wcth.ooni.io",
|
|
}
|
|
clnt := internal.OOClient{
|
|
Resolver: internal.NewFakeResolverWithResult([]string{"1.1.1.1"}),
|
|
HTTPClient: &http.Client{Transport: internal.FakeTransport{
|
|
Resp: &http.Response{
|
|
StatusCode: 200,
|
|
Body: &internal.FakeBody{
|
|
Data: []byte(goodresponse),
|
|
},
|
|
},
|
|
}},
|
|
}
|
|
cresp, err := clnt.Do(ctx, config)
|
|
if !errors.Is(err, nil) {
|
|
t.Fatalf("not the error we expected: %+v", err)
|
|
}
|
|
if cresp.DNS.Failure != nil {
|
|
t.Fatal("unexpected Failure value")
|
|
}
|
|
if len(cresp.DNS.Addrs) != 1 {
|
|
t.Fatal("unexpected number of DNS entries")
|
|
}
|
|
if cresp.DNS.Addrs[0] != "172.217.17.68" {
|
|
t.Fatal("unexpected DNS addrs [0]")
|
|
}
|
|
if cresp.HTTPRequest.BodyLength != 207878 {
|
|
t.Fatal("invalid http body length")
|
|
}
|
|
if cresp.HTTPRequest.Failure != nil {
|
|
t.Fatal("invalid http failure")
|
|
}
|
|
if cresp.HTTPRequest.Title != "Google" {
|
|
t.Fatal("invalid http title")
|
|
}
|
|
if len(cresp.HTTPRequest.Headers) != 1 {
|
|
t.Fatal("invalid http headers length")
|
|
}
|
|
if cresp.HTTPRequest.Headers["Content-Type"] != "text/html" {
|
|
t.Fatal("invalid http content-type header")
|
|
}
|
|
if cresp.HTTPRequest.StatusCode != 200 {
|
|
t.Fatal("invalid http status code")
|
|
}
|
|
if len(cresp.TCPConnect) != 1 {
|
|
t.Fatal("invalid tcp connect length")
|
|
}
|
|
entry, ok := cresp.TCPConnect["172.217.21.68:80"]
|
|
if !ok {
|
|
t.Fatal("cannot find expected TCP connect entry")
|
|
}
|
|
if entry.Status != true {
|
|
t.Fatal("unexpected TCP connect entry status")
|
|
}
|
|
if entry.Failure != nil {
|
|
t.Fatal("unexpected TCP connect entry failure value")
|
|
}
|
|
}
|