ooni-probe-cli/internal/engine/legacy/oonidatamodel/oonidatamodel_test.go
Simone Basso d57c78bc71
chore: merge probe-engine into probe-cli (#201)
This is how I did it:

1. `git clone https://github.com/ooni/probe-engine internal/engine`

2. ```
(cd internal/engine && git describe --tags)
v0.23.0
```

3. `nvim go.mod` (merging `go.mod` with `internal/engine/go.mod`

4. `rm -rf internal/.git internal/engine/go.{mod,sum}`

5. `git add internal/engine`

6. `find . -type f -name \*.go -exec sed -i 's@/ooni/probe-engine@/ooni/probe-cli/v3/internal/engine@g' {} \;`

7. `go build ./...` (passes)

8. `go test -race ./...` (temporary failure on RiseupVPN)

9. `go mod tidy`

10. this commit message

Once this piece of work is done, we can build a new version of `ooniprobe` that
is using `internal/engine` directly. We need to do more work to ensure all the
other functionality in `probe-engine` (e.g. making mobile packages) are still WAI.

Part of https://github.com/ooni/probe/issues/1335
2021-02-02 12:05:47 +01:00

1158 lines
28 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{
ConnID: 555,
DurationSinceBeginning: 10 * time.Millisecond,
DialID: 17,
RemoteAddress: "1.1.1.1:443",
},
},
{
Read: &modelx.ReadEvent{
ConnID: 555,
DurationSinceBeginning: 20 * time.Millisecond,
NumBytes: 1789,
},
},
{
Write: &modelx.WriteEvent{
ConnID: 555,
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].ConnID != 555 {
t.Fatal("wrong out[0].ConnID")
}
if out[0].DialID != 17 {
t.Fatal("wrong out[0].DialID")
}
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 out[0].Proto != "tcp" {
t.Fatal("wrong out[0].Proto")
}
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].ConnID != 555 {
t.Fatal("wrong out[1].ConnID")
}
if out[1].DialID != 0 {
t.Fatal("wrong out[1].DialID")
}
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 out[1].Proto != "tcp" {
t.Fatal("wrong out[1].Proto")
}
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].ConnID != 555 {
t.Fatal("wrong out[2].ConnID")
}
if out[2].DialID != 0 {
t.Fatal("wrong out[2].DialID")
}
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 out[2].Proto != "tcp" {
t.Fatal("wrong out[2].Proto")
}
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{
ConnID: -555,
DurationSinceBeginning: 10 * time.Millisecond,
DialID: 17,
Error: errors.New("mocked error"),
RemoteAddress: "1.1.1.1:443",
},
},
{
Read: &modelx.ReadEvent{
ConnID: -555,
DurationSinceBeginning: 20 * time.Millisecond,
Error: errors.New("mocked error"),
NumBytes: 1789,
},
},
{
Write: &modelx.WriteEvent{
ConnID: -555,
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].ConnID != -555 {
t.Fatal("wrong out[0].ConnID")
}
if out[0].DialID != 17 {
t.Fatal("wrong out[0].DialID")
}
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 out[0].Proto != "udp" {
t.Fatal("wrong out[0].Proto")
}
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].ConnID != -555 {
t.Fatal("wrong out[1].ConnID")
}
if out[1].DialID != 0 {
t.Fatal("wrong out[1].DialID")
}
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 out[1].Proto != "udp" {
t.Fatal("wrong out[1].Proto")
}
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].ConnID != -555 {
t.Fatal("wrong out[2].ConnID")
}
if out[2].DialID != 0 {
t.Fatal("wrong out[2].DialID")
}
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 out[2].Proto != "udp" {
t.Fatal("wrong out[2].Proto")
}
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{
{},
{
ConnID: 12345,
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].ConnID != 0 {
t.Fatal("invalid out[0].ConnID")
}
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].ConnID != 12345 {
t.Fatal("invalid out[1].ConnID")
}
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].ConnID != 0 {
t.Fatal("invalid out[2].ConnID")
}
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")
}
}
}