ooni-probe-cli/internal/engine/legacy/oonidatamodel/oonidatamodel_test.go
Simone Basso c74c94d616
cleanup: remove ConnID, DialID, TransactionID (#395)
We are not using them anymore. The only nettest still using the
legacy netx implementation is tor, for which setting these fields
is useless, because it performs each measurement into a separate
goroutine. Hence, let us start removing this part of the legacy
netx codebase, which is hampering progress in other areas.

Occurred to me while doing testing for the recent changes in
error mapping (https://github.com/ooni/probe/issues/1505).
2021-06-23 13:36:45 +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/engine/netx/errorx"
)
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(errorx.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 != errorx.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(errorx.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 != errorx.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(errorx.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 != errorx.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 != errorx.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 != errorx.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 != errorx.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 != errorx.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 != errorx.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 != errorx.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")
}
}
}