ooni-probe-cli/internal/engine/experiment/webconnectivity/summary_test.go
Simone Basso 58adb68b2c
refactor: move tracex outside of engine/netx (#782)
* refactor: move tracex outside of engine/netx

Consistently with https://github.com/ooni/probe/issues/2121 and
https://github.com/ooni/probe/issues/2115, we can now move tracex
outside of engine/netx. The main reason why this makes sense now
is that the package is now changed significantly from the one
that we imported from ooni/probe-engine.

We have improved its implementation, which had not been touched
significantly for quite some time, and converted it to unit
testing. I will document tomorrow some extra work I'd like to
do with this package but likely could not do $soon.

* go fmt

* regen tutorials
2022-06-02 00:50:55 +02:00

478 lines
13 KiB
Go

package webconnectivity_test
import (
"io"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/ooni/probe-cli/v3/internal/engine/experiment/webconnectivity"
"github.com/ooni/probe-cli/v3/internal/netxlite"
"github.com/ooni/probe-cli/v3/internal/tracex"
)
func TestSummarize(t *testing.T) {
var (
genericFailure = io.EOF.Error()
dns = "dns"
falseValue = false
httpDiff = "http-diff"
httpFailure = "http-failure"
nilstring *string
probeConnectionRefused = netxlite.FailureConnectionRefused
probeConnectionReset = netxlite.FailureConnectionReset
probeEOFError = netxlite.FailureEOFError
probeNXDOMAIN = netxlite.FailureDNSNXDOMAINError
probeTimeout = netxlite.FailureGenericTimeoutError
probeSSLInvalidHost = netxlite.FailureSSLInvalidHostname
probeSSLInvalidCert = netxlite.FailureSSLInvalidCertificate
probeSSLUnknownAuth = netxlite.FailureSSLUnknownAuthority
probeAndroidEaiNoData = netxlite.FailureAndroidDNSCacheNoData
tcpIP = "tcp_ip"
trueValue = true
)
type args struct {
tk *webconnectivity.TestKeys
}
tests := []struct {
name string
args args
wantOut webconnectivity.Summary
}{{
name: "with an HTTPS request with no failure",
args: args{
tk: &webconnectivity.TestKeys{
Requests: []tracex.RequestEntry{{
Request: tracex.HTTPRequest{
URL: "https://www.kernel.org/",
},
Failure: nil,
}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: nil,
Blocking: false,
Accessible: &trueValue,
Status: webconnectivity.StatusSuccessSecure,
},
}, {
name: "with failure in contacting the control",
args: args{
tk: &webconnectivity.TestKeys{
ControlFailure: &genericFailure,
},
},
wantOut: webconnectivity.Summary{
BlockingReason: nil,
Blocking: nilstring,
Accessible: nil,
Status: webconnectivity.StatusAnomalyControlUnreachable,
},
}, {
name: "with non-existing website (NXDOMAIN case)",
args: args{
tk: &webconnectivity.TestKeys{
DNSExperimentFailure: &probeNXDOMAIN,
DNSAnalysisResult: webconnectivity.DNSAnalysisResult{
DNSConsistency: &webconnectivity.DNSConsistent,
},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: nil,
Blocking: false,
Accessible: &trueValue,
Status: webconnectivity.StatusSuccessNXDOMAIN |
webconnectivity.StatusExperimentDNS,
},
}, {
name: "with non-existing website (Android EAI_NODATA case)",
args: args{
tk: &webconnectivity.TestKeys{
DNSExperimentFailure: &probeAndroidEaiNoData,
DNSAnalysisResult: webconnectivity.DNSAnalysisResult{
DNSConsistency: &webconnectivity.DNSConsistent,
},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: nil,
Blocking: false,
Accessible: &trueValue,
Status: webconnectivity.StatusSuccessNXDOMAIN |
webconnectivity.StatusExperimentDNS,
},
}, {
name: "with NXDOMAIN measured only by the probe",
args: args{
tk: &webconnectivity.TestKeys{
DNSExperimentFailure: &probeNXDOMAIN,
DNSAnalysisResult: webconnectivity.DNSAnalysisResult{
DNSConsistency: &webconnectivity.DNSInconsistent,
},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &dns,
Blocking: &dns,
Accessible: &falseValue,
Status: webconnectivity.StatusAnomalyDNS |
webconnectivity.StatusExperimentDNS,
},
}, {
name: "with TCP total failure and consistent DNS",
args: args{
tk: &webconnectivity.TestKeys{
DNSAnalysisResult: webconnectivity.DNSAnalysisResult{
DNSConsistency: &webconnectivity.DNSConsistent,
},
TCPConnectAttempts: 7,
TCPConnectSuccesses: 0,
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &tcpIP,
Blocking: &tcpIP,
Accessible: &falseValue,
Status: webconnectivity.StatusAnomalyConnect |
webconnectivity.StatusExperimentConnect,
},
}, {
name: "with TCP total failure and inconsistent DNS",
args: args{
tk: &webconnectivity.TestKeys{
DNSAnalysisResult: webconnectivity.DNSAnalysisResult{
DNSConsistency: &webconnectivity.DNSInconsistent,
},
TCPConnectAttempts: 7,
TCPConnectSuccesses: 0,
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &dns,
Blocking: &dns,
Accessible: &falseValue,
Status: webconnectivity.StatusAnomalyConnect |
webconnectivity.StatusExperimentConnect |
webconnectivity.StatusAnomalyDNS,
},
}, {
name: "with TCP total failure and unexpected DNS consistency",
args: args{
tk: &webconnectivity.TestKeys{
DNSAnalysisResult: webconnectivity.DNSAnalysisResult{
DNSConsistency: func() *string {
s := "ANTANI"
return &s
}(),
},
TCPConnectAttempts: 7,
TCPConnectSuccesses: 0,
},
},
wantOut: webconnectivity.Summary{
BlockingReason: nil,
Blocking: nilstring,
Accessible: nil,
Status: webconnectivity.StatusAnomalyConnect |
webconnectivity.StatusExperimentConnect |
webconnectivity.StatusAnomalyUnknown,
},
}, {
name: "with failed control HTTP request",
args: args{
tk: &webconnectivity.TestKeys{
Control: webconnectivity.ControlResponse{
HTTPRequest: webconnectivity.ControlHTTPRequestResult{
Failure: &genericFailure,
},
},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: nil,
Blocking: nilstring,
Accessible: nil,
Status: webconnectivity.StatusAnomalyControlFailure,
},
}, {
name: "with less that one request entry",
args: args{
tk: &webconnectivity.TestKeys{},
},
wantOut: webconnectivity.Summary{
BlockingReason: nil,
Blocking: nilstring,
Accessible: nil,
Status: webconnectivity.StatusBugNoRequests,
},
}, {
name: "with connection refused",
args: args{
tk: &webconnectivity.TestKeys{
Requests: []tracex.RequestEntry{{
Failure: &probeConnectionRefused,
}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &httpFailure,
Blocking: &httpFailure,
Accessible: &falseValue,
Status: webconnectivity.StatusExperimentHTTP |
webconnectivity.StatusAnomalyConnect,
},
}, {
name: "with connection reset",
args: args{
tk: &webconnectivity.TestKeys{
Requests: []tracex.RequestEntry{{
Failure: &probeConnectionReset,
}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &httpFailure,
Blocking: &httpFailure,
Accessible: &falseValue,
Status: webconnectivity.StatusExperimentHTTP |
webconnectivity.StatusAnomalyReadWrite,
},
}, {
name: "with NXDOMAIN",
args: args{
tk: &webconnectivity.TestKeys{
Requests: []tracex.RequestEntry{{
Failure: &probeNXDOMAIN,
}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &dns,
Blocking: &dns,
Accessible: &falseValue,
Status: webconnectivity.StatusExperimentHTTP |
webconnectivity.StatusAnomalyDNS,
},
}, {
name: "with EOF",
args: args{
tk: &webconnectivity.TestKeys{
Requests: []tracex.RequestEntry{{
Failure: &probeEOFError,
}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &httpFailure,
Blocking: &httpFailure,
Accessible: &falseValue,
Status: webconnectivity.StatusExperimentHTTP |
webconnectivity.StatusAnomalyReadWrite,
},
}, {
name: "with timeout",
args: args{
tk: &webconnectivity.TestKeys{
Requests: []tracex.RequestEntry{{
Failure: &probeTimeout,
}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &httpFailure,
Blocking: &httpFailure,
Accessible: &falseValue,
Status: webconnectivity.StatusExperimentHTTP |
webconnectivity.StatusAnomalyUnknown,
},
}, {
name: "with SSL invalid hostname",
args: args{
tk: &webconnectivity.TestKeys{
Requests: []tracex.RequestEntry{{
Failure: &probeSSLInvalidHost,
}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &httpFailure,
Blocking: &httpFailure,
Accessible: &falseValue,
Status: webconnectivity.StatusExperimentHTTP |
webconnectivity.StatusAnomalyTLSHandshake,
},
}, {
name: "with SSL invalid cert",
args: args{
tk: &webconnectivity.TestKeys{
Requests: []tracex.RequestEntry{{
Failure: &probeSSLInvalidCert,
}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &httpFailure,
Blocking: &httpFailure,
Accessible: &falseValue,
Status: webconnectivity.StatusExperimentHTTP |
webconnectivity.StatusAnomalyTLSHandshake,
},
}, {
name: "with SSL unknown auth",
args: args{
tk: &webconnectivity.TestKeys{
Requests: []tracex.RequestEntry{{
Failure: &probeSSLUnknownAuth,
}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &httpFailure,
Blocking: &httpFailure,
Accessible: &falseValue,
Status: webconnectivity.StatusExperimentHTTP |
webconnectivity.StatusAnomalyTLSHandshake,
},
}, {
name: "with SSL unknown auth _and_ untrustworthy DNS",
args: args{
tk: &webconnectivity.TestKeys{
DNSAnalysisResult: webconnectivity.DNSAnalysisResult{
DNSConsistency: &webconnectivity.DNSInconsistent,
},
Requests: []tracex.RequestEntry{{
Failure: &probeSSLUnknownAuth,
}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &dns,
Blocking: &dns,
Accessible: &falseValue,
Status: webconnectivity.StatusExperimentHTTP |
webconnectivity.StatusAnomalyTLSHandshake |
webconnectivity.StatusAnomalyDNS,
},
}, {
name: "with SSL unknown auth _and_ untrustworthy DNS _and_ a longer chain",
args: args{
tk: &webconnectivity.TestKeys{
DNSAnalysisResult: webconnectivity.DNSAnalysisResult{
DNSConsistency: &webconnectivity.DNSInconsistent,
},
Requests: []tracex.RequestEntry{{
Failure: &probeSSLUnknownAuth,
}, {}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &httpFailure,
Blocking: &httpFailure,
Accessible: &falseValue,
Status: webconnectivity.StatusExperimentHTTP |
webconnectivity.StatusAnomalyTLSHandshake,
},
}, {
name: "with status code and body length matching",
args: args{
tk: &webconnectivity.TestKeys{
HTTPAnalysisResult: webconnectivity.HTTPAnalysisResult{
StatusCodeMatch: &trueValue,
BodyLengthMatch: &trueValue,
},
Requests: []tracex.RequestEntry{{}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: nil,
Blocking: falseValue,
Accessible: &trueValue,
Status: webconnectivity.StatusSuccessCleartext,
},
}, {
name: "with status code and headers matching",
args: args{
tk: &webconnectivity.TestKeys{
HTTPAnalysisResult: webconnectivity.HTTPAnalysisResult{
StatusCodeMatch: &trueValue,
HeadersMatch: &trueValue,
},
Requests: []tracex.RequestEntry{{}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: nil,
Blocking: falseValue,
Accessible: &trueValue,
Status: webconnectivity.StatusSuccessCleartext,
},
}, {
name: "with status code and title matching",
args: args{
tk: &webconnectivity.TestKeys{
HTTPAnalysisResult: webconnectivity.HTTPAnalysisResult{
StatusCodeMatch: &trueValue,
TitleMatch: &trueValue,
},
Requests: []tracex.RequestEntry{{}},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: nil,
Blocking: falseValue,
Accessible: &trueValue,
Status: webconnectivity.StatusSuccessCleartext,
},
}, {
name: "with suspect http-diff and inconsistent DNS",
args: args{
tk: &webconnectivity.TestKeys{
HTTPAnalysisResult: webconnectivity.HTTPAnalysisResult{
StatusCodeMatch: &falseValue,
TitleMatch: &trueValue,
},
Requests: []tracex.RequestEntry{{}},
DNSAnalysisResult: webconnectivity.DNSAnalysisResult{
DNSConsistency: &webconnectivity.DNSInconsistent,
},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &dns,
Blocking: &dns,
Accessible: &falseValue,
Status: webconnectivity.StatusAnomalyHTTPDiff |
webconnectivity.StatusAnomalyDNS,
},
}, {
name: "with suspect http-diff and consistent DNS",
args: args{
tk: &webconnectivity.TestKeys{
HTTPAnalysisResult: webconnectivity.HTTPAnalysisResult{
StatusCodeMatch: &falseValue,
TitleMatch: &trueValue,
},
Requests: []tracex.RequestEntry{{}},
DNSAnalysisResult: webconnectivity.DNSAnalysisResult{
DNSConsistency: &webconnectivity.DNSConsistent,
},
},
},
wantOut: webconnectivity.Summary{
BlockingReason: &httpDiff,
Blocking: &httpDiff,
Accessible: &falseValue,
Status: webconnectivity.StatusAnomalyHTTPDiff,
},
}}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotOut := webconnectivity.Summarize(tt.args.tk)
if diff := cmp.Diff(tt.wantOut, gotOut); diff != "" {
t.Fatal(diff)
}
})
}
}