ooni-probe-cli/internal/engine/legacy/oonidatamodel/oonidatamodel_test.go
Simone Basso 83440cf110
refactor: split errorsx in good and legacy (#477)
The legacy part for now is internal/errorsx. It will stay there until
I figure out whether it also needs some extra bug fixing.

The good part is now in internal/netxlite/errorsx and contains all the
logic for mapping errors. We need to further improve upon this logic
by writing more thorough integration tests for QUIC.

We also need to copy the various dialer, conn, etc adapters that set
errors. We will put them inside netxlite and we will generate errors in
a way that is less crazy with respect to the major operation. (The
idea is to always wrap, given that now we measure in an incremental way
and we don't measure every operation together.)

Part of https://github.com/ooni/probe/issues/1591
2021-09-07 17:09:30 +02:00

1086 lines
26 KiB
Go

package oonidatamodel
import (
"bytes"
"crypto/tls"
"encoding/json"
"errors"
"net/http"
"reflect"
"testing"
"time"
"github.com/ooni/probe-cli/v3/internal/engine/legacy/netx/modelx"
"github.com/ooni/probe-cli/v3/internal/engine/legacy/oonitemplates"
"github.com/ooni/probe-cli/v3/internal/netxlite/errorsx"
)
func TestNewTCPConnectListEmpty(t *testing.T) {
out := NewTCPConnectList(oonitemplates.Results{})
if len(out) != 0 {
t.Fatal("unexpected output length")
}
}
func TestNewTCPConnectListSuccess(t *testing.T) {
out := NewTCPConnectList(oonitemplates.Results{
Connects: []*modelx.ConnectEvent{
{
RemoteAddress: "8.8.8.8:53",
},
{
RemoteAddress: "8.8.4.4:853",
},
},
})
if len(out) != 2 {
t.Fatal("unexpected output length")
}
if out[0].IP != "8.8.8.8" {
t.Fatal("unexpected out[0].IP")
}
if out[0].Port != 53 {
t.Fatal("unexpected out[0].Port")
}
if out[0].Status.Failure != nil {
t.Fatal("unexpected out[0].Failure")
}
if out[0].Status.Success != true {
t.Fatal("unexpected out[0].Success")
}
if out[1].IP != "8.8.4.4" {
t.Fatal("unexpected out[1].IP")
}
if out[1].Port != 853 {
t.Fatal("unexpected out[1].Port")
}
if out[1].Status.Failure != nil {
t.Fatal("unexpected out[0].Failure")
}
if out[1].Status.Success != true {
t.Fatal("unexpected out[0].Success")
}
}
func TestNewTCPConnectListFailure(t *testing.T) {
out := NewTCPConnectList(oonitemplates.Results{
Connects: []*modelx.ConnectEvent{
{
RemoteAddress: "8.8.8.8:53",
Error: errors.New(errorsx.FailureConnectionReset),
},
},
})
if len(out) != 1 {
t.Fatal("unexpected output length")
}
if out[0].IP != "8.8.8.8" {
t.Fatal("unexpected out[0].IP")
}
if out[0].Port != 53 {
t.Fatal("unexpected out[0].Port")
}
if *out[0].Status.Failure != errorsx.FailureConnectionReset {
t.Fatal("unexpected out[0].Failure")
}
if out[0].Status.Success != false {
t.Fatal("unexpected out[0].Success")
}
}
func TestNewTCPConnectListInvalidInput(t *testing.T) {
out := NewTCPConnectList(oonitemplates.Results{
Connects: []*modelx.ConnectEvent{
{
RemoteAddress: "8.8.8.8",
Error: errors.New(errorsx.FailureConnectionReset),
},
},
})
if len(out) != 1 {
t.Fatal("unexpected output length")
}
if out[0].IP != "" {
t.Fatal("unexpected out[0].IP")
}
if out[0].Port != 0 {
t.Fatal("unexpected out[0].Port")
}
if *out[0].Status.Failure != errorsx.FailureConnectionReset {
t.Fatal("unexpected out[0].Failure")
}
if out[0].Status.Success != false {
t.Fatal("unexpected out[0].Success")
}
}
func TestNewRequestsListEmptyList(t *testing.T) {
out := NewRequestList(oonitemplates.Results{})
if len(out) != 0 {
t.Fatal("unexpected output length")
}
}
func TestNewRequestsListGood(t *testing.T) {
out := NewRequestList(oonitemplates.Results{
HTTPRequests: []*modelx.HTTPRoundTripDoneEvent{
// need two requests to test that order is inverted
{
RequestBodySnap: []byte("abcdefx"),
RequestHeaders: http.Header{
"Content-Type": []string{
"text/plain",
"foobar",
},
"Content-Length": []string{
"17",
},
},
RequestMethod: "GET",
RequestURL: "http://x.org/",
ResponseBodySnap: []byte("abcdef"),
ResponseHeaders: http.Header{
"Content-Type": []string{
"application/json",
"foobaz",
},
"Server": []string{
"antani",
},
"Content-Length": []string{
"14",
},
},
ResponseStatusCode: 451,
MaxBodySnapSize: 10,
},
{
Error: errors.New("antani"),
},
},
})
if len(out) != 2 {
t.Fatal("unexpected output length")
}
if *out[0].Failure != "antani" {
t.Fatal("unexpected out[0].Failure")
}
if out[0].Request.Body.Value != "" {
t.Fatal("unexpected out[0].Request.Body.Value")
}
if len(out[0].Request.Headers) != 0 {
t.Fatal("unexpected out[0].Request.Headers")
}
if out[0].Request.Method != "" {
t.Fatal("unexpected out[0].Request.Method")
}
if out[0].Request.URL != "" {
t.Fatal("unexpected out[0].Request.URL")
}
if out[0].Request.BodyIsTruncated != false {
t.Fatal("unexpected out[0].Request.BodyIsTruncated")
}
if out[0].Response.Body.Value != "" {
t.Fatal("unexpected out[0].Response.Body.Value")
}
if out[0].Response.Code != 0 {
t.Fatal("unexpected out[0].Response.Code")
}
if len(out[0].Response.Headers) != 0 {
t.Fatal("unexpected out[0].Response.Headers")
}
if out[0].Response.BodyIsTruncated != false {
t.Fatal("unexpected out[0].Response.BodyIsTruncated")
}
if out[1].Failure != nil {
t.Fatal("unexpected out[1].Failure")
}
if out[1].Request.Body.Value != "abcdefx" {
t.Fatal("unexpected out[1].Request.Body.Value")
}
if len(out[1].Request.Headers) != 2 {
t.Fatal("unexpected out[1].Request.Headers")
}
if out[1].Request.Headers["Content-Type"].Value != "text/plain" {
t.Fatal("unexpected out[1].Request.Headers Content-Type value")
}
if out[1].Request.Headers["Content-Length"].Value != "17" {
t.Fatal("unexpected out[1].Request.Headers Content-Length value")
}
var (
requestHasTextPlain bool
requestHasFoobar bool
requestHasContentLength bool
requestHasOther int64
)
for _, header := range out[1].Request.HeadersList {
if header.Key == "Content-Type" {
if header.Value.Value == "text/plain" {
requestHasTextPlain = true
} else if header.Value.Value == "foobar" {
requestHasFoobar = true
} else {
requestHasOther++
}
} else if header.Key == "Content-Length" {
if header.Value.Value == "17" {
requestHasContentLength = true
} else {
requestHasOther++
}
} else {
requestHasOther++
}
}
if !requestHasTextPlain {
t.Fatal("missing text/plain for request")
}
if !requestHasFoobar {
t.Fatal("missing foobar for request")
}
if !requestHasContentLength {
t.Fatal("missing content_length for request")
}
if requestHasOther != 0 {
t.Fatal("seen something unexpected")
}
if out[1].Request.Method != "GET" {
t.Fatal("unexpected out[1].Request.Method")
}
if out[1].Request.URL != "http://x.org/" {
t.Fatal("unexpected out[1].Request.URL")
}
if out[1].Request.BodyIsTruncated != false {
t.Fatal("unexpected out[1].Request.BodyIsTruncated")
}
if out[1].Response.Body.Value != "abcdef" {
t.Fatal("unexpected out[1].Response.Body.Value")
}
if out[1].Response.Code != 451 {
t.Fatal("unexpected out[1].Response.Code")
}
if len(out[1].Response.Headers) != 3 {
t.Fatal("unexpected out[1].Response.Headers")
}
if out[1].Response.Headers["Content-Type"].Value != "application/json" {
t.Fatal("unexpected out[1].Response.Headers Content-Type value")
}
if out[1].Response.Headers["Server"].Value != "antani" {
t.Fatal("unexpected out[1].Response.Headers Server value")
}
if out[1].Response.Headers["Content-Length"].Value != "14" {
t.Fatal("unexpected out[1].Response.Headers Content-Length value")
}
var (
responseHasApplicationJSON bool
responseHasFoobaz bool
responseHasServer bool
responseHasContentLength bool
responseHasOther int64
)
for _, header := range out[1].Response.HeadersList {
if header.Key == "Content-Type" {
if header.Value.Value == "application/json" {
responseHasApplicationJSON = true
} else if header.Value.Value == "foobaz" {
responseHasFoobaz = true
} else {
responseHasOther++
}
} else if header.Key == "Content-Length" {
if header.Value.Value == "14" {
responseHasContentLength = true
} else {
responseHasOther++
}
} else if header.Key == "Server" {
if header.Value.Value == "antani" {
responseHasServer = true
} else {
responseHasOther++
}
} else {
responseHasOther++
}
}
if !responseHasApplicationJSON {
t.Fatal("missing application/json for response")
}
if !responseHasFoobaz {
t.Fatal("missing foobaz for response")
}
if !responseHasContentLength {
t.Fatal("missing content_length for response")
}
if !responseHasServer {
t.Fatal("missing server for response")
}
if responseHasOther != 0 {
t.Fatal("seen something unexpected")
}
if out[1].Response.BodyIsTruncated != false {
t.Fatal("unexpected out[1].Response.BodyIsTruncated")
}
}
func TestNewRequestsSnaps(t *testing.T) {
out := NewRequestList(oonitemplates.Results{
HTTPRequests: []*modelx.HTTPRoundTripDoneEvent{
{
RequestBodySnap: []byte("abcd"),
MaxBodySnapSize: 4,
ResponseBodySnap: []byte("defg"),
},
},
})
if len(out) != 1 {
t.Fatal("unexpected output length")
}
if out[0].Request.BodyIsTruncated != true {
t.Fatal("wrong out[0].Request.BodyIsTruncated")
}
if out[0].Response.BodyIsTruncated != true {
t.Fatal("wrong out[0].Response.BodyIsTruncated")
}
}
func TestMarshalUnmarshalHTTPBodyString(t *testing.T) {
mbv := HTTPBody{
Value: "1234",
}
data, err := json.Marshal(mbv)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(data, []byte(`"1234"`)) {
t.Fatal("result is unexpected")
}
var newbody HTTPBody
if err := json.Unmarshal(data, &newbody); err != nil {
t.Fatal(err)
}
if newbody.Value != mbv.Value {
t.Fatal("string value mistmatch")
}
}
var binaryInput = []uint8{
0x57, 0xe5, 0x79, 0xfb, 0xa6, 0xbb, 0x0d, 0xbc, 0xce, 0xbd, 0xa7, 0xa0,
0xba, 0xa4, 0x78, 0x78, 0x12, 0x59, 0xee, 0x68, 0x39, 0xa4, 0x07, 0x98,
0xc5, 0x3e, 0xbc, 0x55, 0xcb, 0xfe, 0x34, 0x3c, 0x7e, 0x1b, 0x5a, 0xb3,
0x22, 0x9d, 0xc1, 0x2d, 0x6e, 0xca, 0x5b, 0xf1, 0x10, 0x25, 0x47, 0x1e,
0x44, 0xe2, 0x2d, 0x60, 0x08, 0xea, 0xb0, 0x0a, 0xcc, 0x05, 0x48, 0xa0,
0xf5, 0x78, 0x38, 0xf0, 0xdb, 0x3f, 0x9d, 0x9f, 0x25, 0x6f, 0x89, 0x00,
0x96, 0x93, 0xaf, 0x43, 0xac, 0x4d, 0xc9, 0xac, 0x13, 0xdb, 0x22, 0xbe,
0x7a, 0x7d, 0xd9, 0x24, 0xa2, 0x52, 0x69, 0xd8, 0x89, 0xc1, 0xd1, 0x57,
0xaa, 0x04, 0x2b, 0xa2, 0xd8, 0xb1, 0x19, 0xf6, 0xd5, 0x11, 0x39, 0xbb,
0x80, 0xcf, 0x86, 0xf9, 0x5f, 0x9d, 0x8c, 0xab, 0xf5, 0xc5, 0x74, 0x24,
0x3a, 0xa2, 0xd4, 0x40, 0x4e, 0xd7, 0x10, 0x1f,
}
func TestMarshalUnmarshalHTTPBodyBinary(t *testing.T) {
mbv := HTTPBody{
Value: string(binaryInput),
}
data, err := json.Marshal(mbv)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(data, []byte(`{"data":"V+V5+6a7DbzOvaeguqR4eBJZ7mg5pAeYxT68Vcv+NDx+G1qzIp3BLW7KW/EQJUceROItYAjqsArMBUig9Xg48Ns/nZ8lb4kAlpOvQ6xNyawT2yK+en3ZJKJSadiJwdFXqgQrotixGfbVETm7gM+G+V+djKv1xXQkOqLUQE7XEB8=","format":"base64"}`)) {
t.Fatal("result is unexpected")
}
var newbody HTTPBody
if err := json.Unmarshal(data, &newbody); err != nil {
t.Fatal(err)
}
if newbody.Value != mbv.Value {
t.Fatal("string value mistmatch")
}
}
func TestMaybeBinaryValueUnmarshalJSON(t *testing.T) {
t.Run("when the code is not a map or string", func(t *testing.T) {
var (
mbv MaybeBinaryValue
input = []byte("[1, 2, 3, 4]")
)
if err := json.Unmarshal(input, &mbv); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the format field is missing", func(t *testing.T) {
var (
mbv MaybeBinaryValue
input = []byte("{}")
)
if err := json.Unmarshal(input, &mbv); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the format field is invalid", func(t *testing.T) {
var (
mbv MaybeBinaryValue
input = []byte(`{"format":"antani"}`)
)
if err := json.Unmarshal(input, &mbv); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the data field is missing", func(t *testing.T) {
var (
mbv MaybeBinaryValue
input = []byte(`{"format":"base64"}`)
)
if err := json.Unmarshal(input, &mbv); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the data field is not base64", func(t *testing.T) {
var (
mbv MaybeBinaryValue
input = []byte(`{"format":"base64","data":"antani"}`)
)
if err := json.Unmarshal(input, &mbv); err == nil {
t.Fatal("expected an error here")
}
})
}
func TestMarshalUnmarshalHTTPHeaderString(t *testing.T) {
mbh := HTTPHeadersList{
HTTPHeader{
Key: "Content-Type",
Value: MaybeBinaryValue{
Value: "application/json",
},
},
HTTPHeader{
Key: "Content-Type",
Value: MaybeBinaryValue{
Value: "antani",
},
},
HTTPHeader{
Key: "Content-Length",
Value: MaybeBinaryValue{
Value: "17",
},
},
}
data, err := json.Marshal(mbh)
if err != nil {
t.Fatal(err)
}
expected := []byte(
`[["Content-Type","application/json"],["Content-Type","antani"],["Content-Length","17"]]`,
)
if !bytes.Equal(data, expected) {
t.Fatal("result is unexpected")
}
var newlist HTTPHeadersList
if err := json.Unmarshal(data, &newlist); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(mbh, newlist) {
t.Fatal("result mismatch")
}
}
func TestMarshalUnmarshalHTTPHeaderBinary(t *testing.T) {
mbh := HTTPHeadersList{
HTTPHeader{
Key: "Content-Type",
Value: MaybeBinaryValue{
Value: "application/json",
},
},
HTTPHeader{
Key: "Content-Type",
Value: MaybeBinaryValue{
Value: string(binaryInput),
},
},
HTTPHeader{
Key: "Content-Length",
Value: MaybeBinaryValue{
Value: "17",
},
},
}
data, err := json.Marshal(mbh)
if err != nil {
t.Fatal(err)
}
expected := []byte(
`[["Content-Type","application/json"],["Content-Type",{"data":"V+V5+6a7DbzOvaeguqR4eBJZ7mg5pAeYxT68Vcv+NDx+G1qzIp3BLW7KW/EQJUceROItYAjqsArMBUig9Xg48Ns/nZ8lb4kAlpOvQ6xNyawT2yK+en3ZJKJSadiJwdFXqgQrotixGfbVETm7gM+G+V+djKv1xXQkOqLUQE7XEB8=","format":"base64"}],["Content-Length","17"]]`,
)
if !bytes.Equal(data, expected) {
t.Fatal("result is unexpected")
}
var newlist HTTPHeadersList
if err := json.Unmarshal(data, &newlist); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(mbh, newlist) {
t.Fatal("result mismatch")
}
}
func TestHTTPHeaderUnmarshalJSON(t *testing.T) {
t.Run("when the code is not a list", func(t *testing.T) {
var (
hh HTTPHeader
input = []byte(`{"foo":1}`)
)
if err := json.Unmarshal(input, &hh); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the pair length is not two", func(t *testing.T) {
var (
hh HTTPHeader
input = []byte("[1,2,3]")
)
if err := json.Unmarshal(input, &hh); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the first element is not a string", func(t *testing.T) {
var (
hh HTTPHeader
input = []byte(`[1, "antani"]`)
)
if err := json.Unmarshal(input, &hh); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the second element is not map[string]interface{}", func(t *testing.T) {
var (
hh HTTPHeader
input = []byte(`["antani", ["base64", "foo"]]`)
)
if err := json.Unmarshal(input, &hh); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the format field is missing", func(t *testing.T) {
var (
hh HTTPHeader
input = []byte(`["antani", {}]`)
)
if err := json.Unmarshal(input, &hh); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the format field is not a string", func(t *testing.T) {
var (
hh HTTPHeader
input = []byte(`["antani", {"format":1}]`)
)
if err := json.Unmarshal(input, &hh); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the format field is invalid", func(t *testing.T) {
var (
hh HTTPHeader
input = []byte(`["antani", {"format":"antani"}]`)
)
if err := json.Unmarshal(input, &hh); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the data field is missing", func(t *testing.T) {
var (
hh HTTPHeader
input = []byte(`["antani", {"format":"base64"}]`)
)
if err := json.Unmarshal(input, &hh); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the data field is not a string", func(t *testing.T) {
var (
hh HTTPHeader
input = []byte(`["antani", {"format":"base64","data":10}]`)
)
if err := json.Unmarshal(input, &hh); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the data field is not base64", func(t *testing.T) {
var (
hh HTTPHeader
input = []byte(`["antani", {"format":"base64","data":"antani"}]`)
)
if err := json.Unmarshal(input, &hh); err == nil {
t.Fatal("expected an error here")
}
})
t.Run("when the data field is not base64", func(t *testing.T) {
var (
hh HTTPHeader
input = []byte(`["antani", {"format":"base64","data":"antani"}]`)
)
if err := json.Unmarshal(input, &hh); err == nil {
t.Fatal("expected an error here")
}
})
}
func TestNewDNSQueriesListEmpty(t *testing.T) {
out := NewDNSQueriesList(oonitemplates.Results{})
if len(out) != 0 {
t.Fatal("unexpected output length")
}
}
func TestNewDNSQueriesListSuccess(t *testing.T) {
out := NewDNSQueriesList(oonitemplates.Results{
Resolves: []*modelx.ResolveDoneEvent{
{
Addresses: []string{
"8.8.4.4", "2001:4860:4860::8888",
},
Hostname: "dns.google",
TransportNetwork: "system",
},
{
Error: errors.New(errorsx.FailureDNSNXDOMAINError),
Hostname: "dns.googlex",
TransportNetwork: "system",
},
},
})
if len(out) != 4 {
t.Fatal("unexpected output length")
}
var (
foundDNSGoogleA bool
foundDNSGoogleAAAA bool
foundErrorA bool
foundErrorAAAA bool
foundOther bool
)
for _, e := range out {
switch e.Hostname {
case "dns.google":
switch e.QueryType {
case "A":
foundDNSGoogleA = true
if err := dnscheckgood(e); err != nil {
t.Fatal(err)
}
case "AAAA":
foundDNSGoogleAAAA = true
if err := dnscheckgood(e); err != nil {
t.Fatal(err)
}
default:
foundOther = true
}
case "dns.googlex":
switch e.QueryType {
case "A":
foundErrorA = true
if err := dnscheckbad(e); err != nil {
t.Fatal(err)
}
case "AAAA":
foundErrorAAAA = true
if err := dnscheckbad(e); err != nil {
t.Fatal(err)
}
default:
foundOther = true
}
default:
foundOther = true
}
}
if foundDNSGoogleA == false {
t.Fatal("missing A for dns.google")
}
if foundDNSGoogleAAAA == false {
t.Fatal("missing AAAA for dns.google")
}
if foundErrorA == false {
t.Fatal("missing A for invalid domain")
}
if foundErrorAAAA == false {
t.Fatal("missing AAAA for invalid domain")
}
if foundOther == true {
t.Fatal("seen something unexpected")
}
}
func dnscheckgood(e DNSQueryEntry) error {
if len(e.Answers) != 1 {
return errors.New("unexpected number of answers")
}
if e.Engine != "system" {
return errors.New("invalid engine")
}
if e.Failure != nil {
return errors.New("invalid failure")
}
if e.Hostname != "dns.google" {
return errors.New("invalid hostname")
}
switch e.QueryType {
case "A", "AAAA":
default:
return errors.New("invalid query type")
}
if e.Answers[0].AnswerType != e.QueryType {
return errors.New("AnswerType mismatch")
}
switch e.QueryType {
case "A":
if e.Answers[0].IPv4 != "8.8.4.4" {
return errors.New("unexpected IPv4 entry")
}
case "AAAA":
if e.Answers[0].IPv6 != "2001:4860:4860::8888" {
return errors.New("unexpected IPv6 entry")
}
}
if e.ResolverHostname != nil {
return errors.New("invalid resolver hostname")
}
if e.ResolverPort != nil {
return errors.New("invalid resolver port")
}
if e.ResolverAddress != "" {
return errors.New("invalid resolver address")
}
return nil
}
func dnscheckbad(e DNSQueryEntry) error {
if len(e.Answers) != 0 {
return errors.New("unexpected number of answers")
}
if e.Engine != "system" {
return errors.New("invalid engine")
}
if *e.Failure != errorsx.FailureDNSNXDOMAINError {
return errors.New("invalid failure")
}
if e.Hostname != "dns.googlex" {
return errors.New("invalid hostname")
}
switch e.QueryType {
case "A", "AAAA":
default:
return errors.New("invalid query type")
}
if e.ResolverHostname != nil {
return errors.New("invalid resolver hostname")
}
if e.ResolverPort != nil {
return errors.New("invalid resolver port")
}
if e.ResolverAddress != "" {
return errors.New("invalid resolver address")
}
return nil
}
func TestDNSQueryTypeIPOfType(t *testing.T) {
qtype := dnsQueryType("ANTANI")
if qtype.ipoftype("8.8.8.8") == true {
t.Fatal("ipoftype misbehaving")
}
}
func TestNewNetworkEventsListEmpty(t *testing.T) {
out := NewNetworkEventsList(oonitemplates.Results{})
if len(out) != 0 {
t.Fatal("unexpected output length")
}
}
func TestNewNetworkEventsListNoSuitableEvents(t *testing.T) {
out := NewNetworkEventsList(oonitemplates.Results{
NetworkEvents: []*modelx.Measurement{
{},
{},
{},
},
})
if len(out) != 0 {
t.Fatal("unexpected output length")
}
}
func TestNewNetworkEventsListGood(t *testing.T) {
out := NewNetworkEventsList(oonitemplates.Results{
NetworkEvents: []*modelx.Measurement{
{
Connect: &modelx.ConnectEvent{
DurationSinceBeginning: 10 * time.Millisecond,
RemoteAddress: "1.1.1.1:443",
},
},
{
Read: &modelx.ReadEvent{
DurationSinceBeginning: 20 * time.Millisecond,
NumBytes: 1789,
},
},
{
Write: &modelx.WriteEvent{
DurationSinceBeginning: 30 * time.Millisecond,
NumBytes: 17714,
},
},
},
})
if len(out) != 3 {
t.Fatal("unexpected output length")
}
if out[0].Address != "1.1.1.1:443" {
t.Fatal("wrong out[0].Address")
}
if out[0].Failure != nil {
t.Fatal("wrong out[0].Failure")
}
if out[0].NumBytes != 0 {
t.Fatal("wrong out[0].NumBytes")
}
if out[0].Operation != errorsx.ConnectOperation {
t.Fatal("wrong out[0].Operation")
}
if !floatEquals(out[0].T, 0.010) {
t.Fatal("wrong out[0].T")
}
if out[1].Address != "" {
t.Fatal("wrong out[1].Address")
}
if out[1].Failure != nil {
t.Fatal("wrong out[1].Failure")
}
if out[1].NumBytes != 1789 {
t.Fatal("wrong out[1].NumBytes")
}
if out[1].Operation != errorsx.ReadOperation {
t.Fatal("wrong out[1].Operation")
}
if !floatEquals(out[1].T, 0.020) {
t.Fatal("wrong out[1].T")
}
if out[2].Address != "" {
t.Fatal("wrong out[2].Address")
}
if out[2].Failure != nil {
t.Fatal("wrong out[2].Failure")
}
if out[2].NumBytes != 17714 {
t.Fatal("wrong out[2].NumBytes")
}
if out[2].Operation != errorsx.WriteOperation {
t.Fatal("wrong out[2].Operation")
}
if !floatEquals(out[2].T, 0.030) {
t.Fatal("wrong out[2].T")
}
}
func TestNewNetworkEventsListGoodUDPAndErrors(t *testing.T) {
out := NewNetworkEventsList(oonitemplates.Results{
NetworkEvents: []*modelx.Measurement{
{
Connect: &modelx.ConnectEvent{
DurationSinceBeginning: 10 * time.Millisecond,
Error: errors.New("mocked error"),
RemoteAddress: "1.1.1.1:443",
},
},
{
Read: &modelx.ReadEvent{
DurationSinceBeginning: 20 * time.Millisecond,
Error: errors.New("mocked error"),
NumBytes: 1789,
},
},
{
Write: &modelx.WriteEvent{
DurationSinceBeginning: 30 * time.Millisecond,
Error: errors.New("mocked error"),
NumBytes: 17714,
},
},
},
})
if len(out) != 3 {
t.Fatal("unexpected output length")
}
if out[0].Address != "1.1.1.1:443" {
t.Fatal("wrong out[0].Address")
}
if *out[0].Failure != "mocked error" {
t.Fatal("wrong out[0].Failure")
}
if out[0].NumBytes != 0 {
t.Fatal("wrong out[0].NumBytes")
}
if out[0].Operation != errorsx.ConnectOperation {
t.Fatal("wrong out[0].Operation")
}
if !floatEquals(out[0].T, 0.010) {
t.Fatal("wrong out[0].T")
}
if out[1].Address != "" {
t.Fatal("wrong out[1].Address")
}
if *out[1].Failure != "mocked error" {
t.Fatal("wrong out[1].Failure")
}
if out[1].NumBytes != 1789 {
t.Fatal("wrong out[1].NumBytes")
}
if out[1].Operation != errorsx.ReadOperation {
t.Fatal("wrong out[1].Operation")
}
if !floatEquals(out[1].T, 0.020) {
t.Fatal("wrong out[1].T")
}
if out[2].Address != "" {
t.Fatal("wrong out[2].Address")
}
if *out[2].Failure != "mocked error" {
t.Fatal("wrong out[2].Failure")
}
if out[2].NumBytes != 17714 {
t.Fatal("wrong out[2].NumBytes")
}
if out[2].Operation != errorsx.WriteOperation {
t.Fatal("wrong out[2].Operation")
}
if !floatEquals(out[2].T, 0.030) {
t.Fatal("wrong out[2].T")
}
}
func floatEquals(a, b float64) bool {
const c = 1e-03
return (a-b) < c && (b-a) < c
}
func TestNewTLSHandshakesListEmpty(t *testing.T) {
out := NewTLSHandshakesList(oonitemplates.Results{})
if len(out) != 0 {
t.Fatal("unexpected output length")
}
}
func TestNewTLSHandshakesListSuccess(t *testing.T) {
out := NewTLSHandshakesList(oonitemplates.Results{
TLSHandshakes: []*modelx.TLSHandshakeDoneEvent{
{},
{
Error: errors.New("mocked error"),
},
{
ConnectionState: modelx.TLSConnectionState{
CipherSuite: tls.TLS_AES_128_GCM_SHA256,
NegotiatedProtocol: "h2",
PeerCertificates: []modelx.X509Certificate{
{
Data: []byte("deadbeef"),
},
{
Data: []byte("abad1dea"),
},
},
Version: tls.VersionTLS11,
},
DurationSinceBeginning: 10 * time.Millisecond,
},
},
})
if len(out) != 3 {
t.Fatal("unexpected output length")
}
if out[0].CipherSuite != "" {
t.Fatal("invalid out[0].CipherSuite")
}
if out[0].Failure != nil {
t.Fatal("invalid out[0].Failure")
}
if out[0].NegotiatedProtocol != "" {
t.Fatal("invalid out[0].NegotiatedProtocol")
}
if len(out[0].PeerCertificates) != 0 {
t.Fatal("invalid out[0].PeerCertificates")
}
if !floatEquals(out[0].T, 0) {
t.Fatal("invalid out[0].T")
}
if out[0].TLSVersion != "" {
t.Fatal("invalid out[0].TLSVersion")
}
if out[1].CipherSuite != "" {
t.Fatal("invalid out[1].CipherSuite")
}
if *out[1].Failure != "mocked error" {
t.Fatal("invalid out[1].Failure")
}
if out[1].NegotiatedProtocol != "" {
t.Fatal("invalid out[1].NegotiatedProtocol")
}
if len(out[1].PeerCertificates) != 0 {
t.Fatal("invalid out[1].PeerCertificates")
}
if !floatEquals(out[1].T, 0) {
t.Fatal("invalid out[1].T")
}
if out[1].TLSVersion != "" {
t.Fatal("invalid out[1].TLSVersion")
}
if out[2].CipherSuite != "TLS_AES_128_GCM_SHA256" {
t.Fatal("invalid out[2].CipherSuite")
}
if out[2].Failure != nil {
t.Fatal("invalid out[2].Failure")
}
if out[2].NegotiatedProtocol != "h2" {
t.Fatal("invalid out[2].NegotiatedProtocol")
}
if len(out[2].PeerCertificates) != 2 {
t.Fatal("invalid out[2].PeerCertificates")
}
if !floatEquals(out[2].T, 0.010) {
t.Fatal("invalid out[2].T")
}
if out[2].TLSVersion != "TLSv1.1" {
t.Fatal("invalid out[2].TLSVersion")
}
for idx, mbv := range out[2].PeerCertificates {
if idx == 0 && mbv.Value != "deadbeef" {
t.Fatal("invalid first certificate")
}
if idx == 1 && mbv.Value != "abad1dea" {
t.Fatal("invalid second certificate")
}
if idx < 0 || idx > 1 {
t.Fatal("invalid index")
}
}
}