31e478b04e
* fix(pkg.go.dev): import a subpackage containing the assets We're trying to fix this issue that pkg.go.dev does not build. Thanks to @hellais for this very neat idea! Let's keep our fingers crossed and see whether it fixes! * feat: use embedded geoip databases Closes https://github.com/ooni/probe/issues/1372. Work done as part of https://github.com/ooni/probe/issues/1369. * fix(assetsx): add tests * feat: simplify and just vendor uncompressed DBs * remove tests that seems not necessary anymore * fix: run go mod tidy * Address https://github.com/ooni/probe-cli/pull/260/files#r605181364 * rewrite a test in a better way * fix: gently cleanup the legacy assetsdir Do not remove the whole directory with brute force. Just zap the files whose name we know. Then attempt to delete the legacy directory as well. If not empty, just fail. This is fine because it means the user has stored other files inside the directory. * fix: create .miniooni if missing
1056 lines
24 KiB
Go
1056 lines
24 KiB
Go
package archival_test
|
|
|
|
import (
|
|
"context"
|
|
"crypto/x509"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/ooni/probe-cli/v3/internal/engine/model"
|
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/archival"
|
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/errorx"
|
|
"github.com/ooni/probe-cli/v3/internal/engine/netx/trace"
|
|
)
|
|
|
|
func TestNewTCPConnectList(t *testing.T) {
|
|
begin := time.Now()
|
|
type args struct {
|
|
begin time.Time
|
|
events []trace.Event
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want []archival.TCPConnectEntry
|
|
}{{
|
|
name: "empty run",
|
|
args: args{
|
|
begin: begin,
|
|
events: nil,
|
|
},
|
|
want: nil,
|
|
}, {
|
|
name: "realistic run",
|
|
args: args{
|
|
begin: begin,
|
|
events: []trace.Event{{
|
|
Addresses: []string{"8.8.8.8", "8.8.4.4"},
|
|
Hostname: "dns.google.com",
|
|
Name: "resolve_done",
|
|
Time: begin.Add(100 * time.Millisecond),
|
|
}, {
|
|
Address: "8.8.8.8:853",
|
|
Duration: 30 * time.Millisecond,
|
|
Name: errorx.ConnectOperation,
|
|
Proto: "tcp",
|
|
Time: begin.Add(130 * time.Millisecond),
|
|
}, {
|
|
Address: "8.8.8.8:853",
|
|
Duration: 55 * time.Millisecond,
|
|
Name: errorx.ConnectOperation,
|
|
Proto: "udp",
|
|
Time: begin.Add(130 * time.Millisecond),
|
|
}, {
|
|
Address: "8.8.4.4:53",
|
|
Duration: 50 * time.Millisecond,
|
|
Err: io.EOF,
|
|
Name: errorx.ConnectOperation,
|
|
Proto: "tcp",
|
|
Time: begin.Add(180 * time.Millisecond),
|
|
}},
|
|
},
|
|
want: []archival.TCPConnectEntry{{
|
|
IP: "8.8.8.8",
|
|
Port: 853,
|
|
Status: archival.TCPConnectStatus{
|
|
Success: true,
|
|
},
|
|
T: 0.13,
|
|
}, {
|
|
IP: "8.8.4.4",
|
|
Port: 53,
|
|
Status: archival.TCPConnectStatus{
|
|
Failure: archival.NewFailure(io.EOF),
|
|
Success: false,
|
|
},
|
|
T: 0.18,
|
|
}},
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := archival.NewTCPConnectList(tt.args.begin, tt.args.events); !reflect.DeepEqual(got, tt.want) {
|
|
t.Error(cmp.Diff(got, tt.want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewRequestList(t *testing.T) {
|
|
begin := time.Now()
|
|
type args struct {
|
|
begin time.Time
|
|
events []trace.Event
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want []archival.RequestEntry
|
|
}{{
|
|
name: "empty run",
|
|
args: args{
|
|
begin: begin,
|
|
events: nil,
|
|
},
|
|
want: nil,
|
|
}, {
|
|
name: "realistic run",
|
|
args: args{
|
|
begin: begin,
|
|
events: []trace.Event{{
|
|
Name: "http_transaction_start",
|
|
Time: begin.Add(10 * time.Millisecond),
|
|
}, {
|
|
Name: "http_request_body_snapshot",
|
|
Data: []byte("deadbeef"),
|
|
DataIsTruncated: false,
|
|
}, {
|
|
Name: "http_request_metadata",
|
|
HTTPHeaders: http.Header{
|
|
"User-Agent": []string{"miniooni/0.1.0-dev"},
|
|
},
|
|
HTTPMethod: "POST",
|
|
HTTPURL: "https://www.example.com/submit",
|
|
}, {
|
|
Name: "http_response_metadata",
|
|
HTTPHeaders: http.Header{
|
|
"Server": []string{"orchestra/0.1.0-dev"},
|
|
},
|
|
HTTPStatusCode: 200,
|
|
}, {
|
|
Name: "http_response_body_snapshot",
|
|
Data: []byte("{}"),
|
|
DataIsTruncated: false,
|
|
}, {
|
|
Name: "http_transaction_done",
|
|
}, {
|
|
Name: "http_transaction_start",
|
|
Time: begin.Add(20 * time.Millisecond),
|
|
}, {
|
|
Name: "http_request_metadata",
|
|
HTTPHeaders: http.Header{
|
|
"User-Agent": []string{"miniooni/0.1.0-dev"},
|
|
},
|
|
HTTPMethod: "GET",
|
|
HTTPURL: "https://www.example.com/result",
|
|
}, {
|
|
Name: "http_transaction_done",
|
|
Err: io.EOF,
|
|
}},
|
|
},
|
|
want: []archival.RequestEntry{{
|
|
Failure: archival.NewFailure(io.EOF),
|
|
Request: archival.HTTPRequest{
|
|
HeadersList: []archival.HTTPHeader{{
|
|
Key: "User-Agent",
|
|
Value: archival.MaybeBinaryValue{
|
|
Value: "miniooni/0.1.0-dev",
|
|
},
|
|
}},
|
|
Headers: map[string]archival.MaybeBinaryValue{
|
|
"User-Agent": {Value: "miniooni/0.1.0-dev"},
|
|
},
|
|
Method: "GET",
|
|
URL: "https://www.example.com/result",
|
|
},
|
|
T: 0.02,
|
|
}, {
|
|
Request: archival.HTTPRequest{
|
|
Body: archival.MaybeBinaryValue{
|
|
Value: "deadbeef",
|
|
},
|
|
HeadersList: []archival.HTTPHeader{{
|
|
Key: "User-Agent",
|
|
Value: archival.MaybeBinaryValue{
|
|
Value: "miniooni/0.1.0-dev",
|
|
},
|
|
}},
|
|
Headers: map[string]archival.MaybeBinaryValue{
|
|
"User-Agent": {Value: "miniooni/0.1.0-dev"},
|
|
},
|
|
Method: "POST",
|
|
URL: "https://www.example.com/submit",
|
|
},
|
|
Response: archival.HTTPResponse{
|
|
Body: archival.MaybeBinaryValue{
|
|
Value: "{}",
|
|
},
|
|
Code: 200,
|
|
HeadersList: []archival.HTTPHeader{{
|
|
Key: "Server",
|
|
Value: archival.MaybeBinaryValue{
|
|
Value: "orchestra/0.1.0-dev",
|
|
},
|
|
}},
|
|
Headers: map[string]archival.MaybeBinaryValue{
|
|
"Server": {Value: "orchestra/0.1.0-dev"},
|
|
},
|
|
Locations: nil,
|
|
},
|
|
T: 0.01,
|
|
}},
|
|
}, {
|
|
// for an example of why we need to sort headers, see
|
|
// https://github.com/ooni/probe-cli/v3/internal/engine/pull/751/checks?check_run_id=853562310
|
|
name: "run with redirect and headers to sort",
|
|
args: args{
|
|
begin: begin,
|
|
events: []trace.Event{{
|
|
Name: "http_transaction_start",
|
|
Time: begin.Add(10 * time.Millisecond),
|
|
}, {
|
|
Name: "http_request_metadata",
|
|
HTTPHeaders: http.Header{
|
|
"User-Agent": []string{"miniooni/0.1.0-dev"},
|
|
},
|
|
HTTPMethod: "GET",
|
|
HTTPURL: "https://www.example.com/",
|
|
}, {
|
|
Name: "http_response_metadata",
|
|
HTTPHeaders: http.Header{
|
|
"Server": []string{"orchestra/0.1.0-dev"},
|
|
"Location": []string{"https://x.example.com", "https://y.example.com"},
|
|
},
|
|
HTTPStatusCode: 302,
|
|
}, {
|
|
Name: "http_transaction_done",
|
|
}},
|
|
},
|
|
want: []archival.RequestEntry{{
|
|
Request: archival.HTTPRequest{
|
|
HeadersList: []archival.HTTPHeader{{
|
|
Key: "User-Agent",
|
|
Value: archival.MaybeBinaryValue{
|
|
Value: "miniooni/0.1.0-dev",
|
|
},
|
|
}},
|
|
Headers: map[string]archival.MaybeBinaryValue{
|
|
"User-Agent": {Value: "miniooni/0.1.0-dev"},
|
|
},
|
|
Method: "GET",
|
|
URL: "https://www.example.com/",
|
|
},
|
|
Response: archival.HTTPResponse{
|
|
Code: 302,
|
|
HeadersList: []archival.HTTPHeader{{
|
|
Key: "Location",
|
|
Value: archival.MaybeBinaryValue{
|
|
Value: "https://x.example.com",
|
|
},
|
|
}, {
|
|
Key: "Location",
|
|
Value: archival.MaybeBinaryValue{
|
|
Value: "https://y.example.com",
|
|
},
|
|
}, {
|
|
Key: "Server",
|
|
Value: archival.MaybeBinaryValue{
|
|
Value: "orchestra/0.1.0-dev",
|
|
},
|
|
}},
|
|
Headers: map[string]archival.MaybeBinaryValue{
|
|
"Server": {Value: "orchestra/0.1.0-dev"},
|
|
"Location": {Value: "https://x.example.com"},
|
|
},
|
|
Locations: []string{
|
|
"https://x.example.com", "https://y.example.com",
|
|
},
|
|
},
|
|
T: 0.01,
|
|
}},
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := archival.NewRequestList(tt.args.begin, tt.args.events); !reflect.DeepEqual(got, tt.want) {
|
|
t.Error(cmp.Diff(got, tt.want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewDNSQueriesList(t *testing.T) {
|
|
begin := time.Now()
|
|
type args struct {
|
|
begin time.Time
|
|
events []trace.Event
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want []archival.DNSQueryEntry
|
|
}{{
|
|
name: "empty run",
|
|
args: args{
|
|
begin: begin,
|
|
events: nil,
|
|
},
|
|
want: nil,
|
|
}, {
|
|
name: "realistic run",
|
|
args: args{
|
|
begin: begin,
|
|
events: []trace.Event{{
|
|
Address: "1.1.1.1:853",
|
|
Addresses: []string{"8.8.8.8", "8.8.4.4"},
|
|
Hostname: "dns.google.com",
|
|
Name: "resolve_done",
|
|
Proto: "dot",
|
|
Time: begin.Add(100 * time.Millisecond),
|
|
}, {
|
|
Address: "8.8.8.8:853",
|
|
Duration: 30 * time.Millisecond,
|
|
Name: errorx.ConnectOperation,
|
|
Proto: "tcp",
|
|
Time: begin.Add(130 * time.Millisecond),
|
|
}, {
|
|
Address: "8.8.4.4:53",
|
|
Duration: 50 * time.Millisecond,
|
|
Err: io.EOF,
|
|
Name: errorx.ConnectOperation,
|
|
Proto: "tcp",
|
|
Time: begin.Add(180 * time.Millisecond),
|
|
}},
|
|
},
|
|
want: []archival.DNSQueryEntry{{
|
|
Answers: []archival.DNSAnswerEntry{{
|
|
ASN: 15169,
|
|
ASOrgName: "Google LLC",
|
|
AnswerType: "A",
|
|
IPv4: "8.8.8.8",
|
|
}, {
|
|
ASN: 15169,
|
|
ASOrgName: "Google LLC",
|
|
AnswerType: "A",
|
|
IPv4: "8.8.4.4",
|
|
}},
|
|
Engine: "dot",
|
|
Hostname: "dns.google.com",
|
|
QueryType: "A",
|
|
ResolverAddress: "1.1.1.1:853",
|
|
T: 0.1,
|
|
}},
|
|
}, {
|
|
name: "run with IPv6 results",
|
|
args: args{
|
|
begin: begin,
|
|
events: []trace.Event{{
|
|
Addresses: []string{"2001:4860:4860::8888"},
|
|
Hostname: "dns.google.com",
|
|
Name: "resolve_done",
|
|
Time: begin.Add(200 * time.Millisecond),
|
|
}},
|
|
},
|
|
want: []archival.DNSQueryEntry{{
|
|
Answers: []archival.DNSAnswerEntry{{
|
|
ASN: 15169,
|
|
ASOrgName: "Google LLC",
|
|
AnswerType: "AAAA",
|
|
IPv6: "2001:4860:4860::8888",
|
|
}},
|
|
Hostname: "dns.google.com",
|
|
QueryType: "AAAA",
|
|
T: 0.2,
|
|
}},
|
|
}, {
|
|
name: "run with errors",
|
|
args: args{
|
|
begin: begin,
|
|
events: []trace.Event{{
|
|
Err: &errorx.ErrWrapper{Failure: errorx.FailureDNSNXDOMAINError},
|
|
Hostname: "dns.google.com",
|
|
Name: "resolve_done",
|
|
Time: begin.Add(200 * time.Millisecond),
|
|
}},
|
|
},
|
|
want: []archival.DNSQueryEntry{{
|
|
Answers: nil,
|
|
Failure: archival.NewFailure(
|
|
&errorx.ErrWrapper{Failure: errorx.FailureDNSNXDOMAINError}),
|
|
Hostname: "dns.google.com",
|
|
QueryType: "A",
|
|
T: 0.2,
|
|
}, {
|
|
Answers: nil,
|
|
Failure: archival.NewFailure(
|
|
&errorx.ErrWrapper{Failure: errorx.FailureDNSNXDOMAINError}),
|
|
Hostname: "dns.google.com",
|
|
QueryType: "AAAA",
|
|
T: 0.2,
|
|
}},
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := archival.NewDNSQueriesList(tt.args.begin, tt.args.events)
|
|
if diff := cmp.Diff(tt.want, got); diff != "" {
|
|
t.Fatal(diff)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewNetworkEventsList(t *testing.T) {
|
|
begin := time.Now()
|
|
type args struct {
|
|
begin time.Time
|
|
events []trace.Event
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want []archival.NetworkEvent
|
|
}{{
|
|
name: "empty run",
|
|
args: args{
|
|
begin: begin,
|
|
events: nil,
|
|
},
|
|
want: nil,
|
|
}, {
|
|
name: "realistic run",
|
|
args: args{
|
|
begin: begin,
|
|
events: []trace.Event{{
|
|
Name: errorx.ConnectOperation,
|
|
Address: "8.8.8.8:853",
|
|
Err: io.EOF,
|
|
Proto: "tcp",
|
|
Time: begin.Add(7 * time.Millisecond),
|
|
}, {
|
|
Name: errorx.ReadOperation,
|
|
Err: context.Canceled,
|
|
NumBytes: 7117,
|
|
Time: begin.Add(11 * time.Millisecond),
|
|
}, {
|
|
Address: "8.8.8.8:853",
|
|
Name: errorx.ReadFromOperation,
|
|
Err: context.Canceled,
|
|
NumBytes: 7117,
|
|
Time: begin.Add(11 * time.Millisecond),
|
|
}, {
|
|
Name: errorx.WriteOperation,
|
|
Err: websocket.ErrBadHandshake,
|
|
NumBytes: 4114,
|
|
Time: begin.Add(14 * time.Millisecond),
|
|
}, {
|
|
Address: "8.8.8.8:853",
|
|
Name: errorx.WriteToOperation,
|
|
Err: websocket.ErrBadHandshake,
|
|
NumBytes: 4114,
|
|
Time: begin.Add(14 * time.Millisecond),
|
|
}, {
|
|
Name: errorx.CloseOperation,
|
|
Err: websocket.ErrReadLimit,
|
|
Time: begin.Add(17 * time.Millisecond),
|
|
}},
|
|
},
|
|
want: []archival.NetworkEvent{{
|
|
Address: "8.8.8.8:853",
|
|
Failure: archival.NewFailure(io.EOF),
|
|
Operation: errorx.ConnectOperation,
|
|
Proto: "tcp",
|
|
T: 0.007,
|
|
}, {
|
|
Failure: archival.NewFailure(context.Canceled),
|
|
NumBytes: 7117,
|
|
Operation: errorx.ReadOperation,
|
|
T: 0.011,
|
|
}, {
|
|
Address: "8.8.8.8:853",
|
|
Failure: archival.NewFailure(context.Canceled),
|
|
NumBytes: 7117,
|
|
Operation: errorx.ReadFromOperation,
|
|
T: 0.011,
|
|
}, {
|
|
Failure: archival.NewFailure(websocket.ErrBadHandshake),
|
|
NumBytes: 4114,
|
|
Operation: errorx.WriteOperation,
|
|
T: 0.014,
|
|
}, {
|
|
Address: "8.8.8.8:853",
|
|
Failure: archival.NewFailure(websocket.ErrBadHandshake),
|
|
NumBytes: 4114,
|
|
Operation: errorx.WriteToOperation,
|
|
T: 0.014,
|
|
}, {
|
|
Failure: archival.NewFailure(websocket.ErrReadLimit),
|
|
Operation: errorx.CloseOperation,
|
|
T: 0.017,
|
|
}},
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := archival.NewNetworkEventsList(tt.args.begin, tt.args.events); !reflect.DeepEqual(got, tt.want) {
|
|
t.Error(cmp.Diff(got, tt.want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewTLSHandshakesList(t *testing.T) {
|
|
begin := time.Now()
|
|
type args struct {
|
|
begin time.Time
|
|
events []trace.Event
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want []archival.TLSHandshake
|
|
}{{
|
|
name: "empty run",
|
|
args: args{
|
|
begin: begin,
|
|
events: nil,
|
|
},
|
|
want: nil,
|
|
}, {
|
|
name: "realistic run",
|
|
args: args{
|
|
begin: begin,
|
|
events: []trace.Event{{
|
|
Name: errorx.CloseOperation,
|
|
Err: websocket.ErrReadLimit,
|
|
Time: begin.Add(17 * time.Millisecond),
|
|
}, {
|
|
Name: "tls_handshake_done",
|
|
Err: io.EOF,
|
|
NoTLSVerify: false,
|
|
TLSCipherSuite: "SUITE",
|
|
TLSNegotiatedProto: "h2",
|
|
TLSPeerCerts: []*x509.Certificate{{
|
|
Raw: []byte("deadbeef"),
|
|
}, {
|
|
Raw: []byte("abad1dea"),
|
|
}},
|
|
TLSServerName: "x.org",
|
|
TLSVersion: "TLSv1.3",
|
|
Time: begin.Add(55 * time.Millisecond),
|
|
}},
|
|
},
|
|
want: []archival.TLSHandshake{{
|
|
CipherSuite: "SUITE",
|
|
Failure: archival.NewFailure(io.EOF),
|
|
NegotiatedProtocol: "h2",
|
|
NoTLSVerify: false,
|
|
PeerCertificates: []archival.MaybeBinaryValue{{
|
|
Value: "deadbeef",
|
|
}, {
|
|
Value: "abad1dea",
|
|
}},
|
|
ServerName: "x.org",
|
|
T: 0.055,
|
|
TLSVersion: "TLSv1.3",
|
|
}},
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := archival.NewTLSHandshakesList(tt.args.begin, tt.args.events); !reflect.DeepEqual(got, tt.want) {
|
|
t.Error(cmp.Diff(got, tt.want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExtSpec_AddTo(t *testing.T) {
|
|
m := new(model.Measurement)
|
|
archival.ExtDNS.AddTo(m)
|
|
expected := map[string]int64{"dnst": 0}
|
|
if d := cmp.Diff(m.Extensions, expected); d != "" {
|
|
t.Fatal(d)
|
|
}
|
|
}
|
|
|
|
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,
|
|
}
|
|
|
|
var encodedBinaryInput = []byte(`{"data":"V+V5+6a7DbzOvaeguqR4eBJZ7mg5pAeYxT68Vcv+NDx+G1qzIp3BLW7KW/EQJUceROItYAjqsArMBUig9Xg48Ns/nZ8lb4kAlpOvQ6xNyawT2yK+en3ZJKJSadiJwdFXqgQrotixGfbVETm7gM+G+V+djKv1xXQkOqLUQE7XEB8=","format":"base64"}`)
|
|
|
|
func TestMaybeBinaryValue_MarshalJSON(t *testing.T) {
|
|
type fields struct {
|
|
Value string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
want []byte
|
|
wantErr bool
|
|
}{{
|
|
name: "with string input",
|
|
fields: fields{
|
|
Value: "antani",
|
|
},
|
|
want: []byte(`"antani"`),
|
|
wantErr: false,
|
|
}, {
|
|
name: "with binary input",
|
|
fields: fields{
|
|
Value: string(binaryInput),
|
|
},
|
|
want: encodedBinaryInput,
|
|
wantErr: false,
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
hb := archival.MaybeBinaryValue{
|
|
Value: tt.fields.Value,
|
|
}
|
|
got, err := hb.MarshalJSON()
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("MaybeBinaryValue.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Error(cmp.Diff(got, tt.want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMaybeBinaryValue_UnmarshalJSON(t *testing.T) {
|
|
type fields struct {
|
|
WantValue string
|
|
}
|
|
type args struct {
|
|
d []byte
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
wantErr bool
|
|
}{{
|
|
name: "with string input",
|
|
fields: fields{
|
|
WantValue: "xo",
|
|
},
|
|
args: args{d: []byte(`"xo"`)},
|
|
wantErr: false,
|
|
}, {
|
|
name: "with nil input",
|
|
fields: fields{
|
|
WantValue: "",
|
|
},
|
|
args: args{d: nil},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with missing/invalid format",
|
|
fields: fields{
|
|
WantValue: "",
|
|
},
|
|
args: args{d: []byte(`{"format": "foo"}`)},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with missing data",
|
|
fields: fields{
|
|
WantValue: "",
|
|
},
|
|
args: args{d: []byte(`{"format": "base64"}`)},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with invalid base64 data",
|
|
fields: fields{
|
|
WantValue: "",
|
|
},
|
|
args: args{d: []byte(`{"format": "base64", "data": "x"}`)},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with valid base64 data",
|
|
fields: fields{
|
|
WantValue: string(binaryInput),
|
|
},
|
|
args: args{d: encodedBinaryInput},
|
|
wantErr: false,
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
hb := &archival.MaybeBinaryValue{}
|
|
if err := hb.UnmarshalJSON(tt.args.d); (err != nil) != tt.wantErr {
|
|
t.Errorf("MaybeBinaryValue.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
if d := cmp.Diff(tt.fields.WantValue, hb.Value); d != "" {
|
|
t.Error(d)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHTTPHeader_MarshalJSON(t *testing.T) {
|
|
type fields struct {
|
|
Key string
|
|
Value archival.MaybeBinaryValue
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
want []byte
|
|
wantErr bool
|
|
}{{
|
|
name: "with string value",
|
|
fields: fields{
|
|
Key: "Content-Type",
|
|
Value: archival.MaybeBinaryValue{
|
|
Value: "text/plain",
|
|
},
|
|
},
|
|
want: []byte(`["Content-Type","text/plain"]`),
|
|
wantErr: false,
|
|
}, {
|
|
name: "with binary value",
|
|
fields: fields{
|
|
Key: "Content-Type",
|
|
Value: archival.MaybeBinaryValue{
|
|
Value: string(binaryInput),
|
|
},
|
|
},
|
|
want: []byte(`["Content-Type",` + string(encodedBinaryInput) + `]`),
|
|
wantErr: false,
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
hh := archival.HTTPHeader{
|
|
Key: tt.fields.Key,
|
|
Value: tt.fields.Value,
|
|
}
|
|
got, err := hh.MarshalJSON()
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("HTTPHeader.MarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Error(cmp.Diff(got, tt.want))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHTTPHeader_UnmarshalJSON(t *testing.T) {
|
|
type fields struct {
|
|
WantKey string
|
|
WantValue archival.MaybeBinaryValue
|
|
}
|
|
type args struct {
|
|
d []byte
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
wantErr bool
|
|
}{{
|
|
name: "with invalid input",
|
|
fields: fields{
|
|
WantKey: "",
|
|
WantValue: archival.MaybeBinaryValue{
|
|
Value: "",
|
|
},
|
|
},
|
|
args: args{
|
|
d: []byte(`{}`),
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with unexpected number of items",
|
|
fields: fields{
|
|
WantKey: "",
|
|
WantValue: archival.MaybeBinaryValue{
|
|
Value: "",
|
|
},
|
|
},
|
|
args: args{
|
|
d: []byte(`[]`),
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with first item not being a string",
|
|
fields: fields{
|
|
WantKey: "",
|
|
WantValue: archival.MaybeBinaryValue{
|
|
Value: "",
|
|
},
|
|
},
|
|
args: args{
|
|
d: []byte(`[0,0]`),
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with both items being a string",
|
|
fields: fields{
|
|
WantKey: "x",
|
|
WantValue: archival.MaybeBinaryValue{
|
|
Value: "y",
|
|
},
|
|
},
|
|
args: args{
|
|
d: []byte(`["x","y"]`),
|
|
},
|
|
wantErr: false,
|
|
}, {
|
|
name: "with second item not being a map[string]interface{}",
|
|
fields: fields{
|
|
WantKey: "",
|
|
WantValue: archival.MaybeBinaryValue{
|
|
Value: "",
|
|
},
|
|
},
|
|
args: args{
|
|
d: []byte(`["x",[]]`),
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with missing format key in second item",
|
|
fields: fields{
|
|
WantKey: "",
|
|
WantValue: archival.MaybeBinaryValue{
|
|
Value: "",
|
|
},
|
|
},
|
|
args: args{
|
|
d: []byte(`["x",{}]`),
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with format value not being base64",
|
|
fields: fields{
|
|
WantKey: "",
|
|
WantValue: archival.MaybeBinaryValue{
|
|
Value: "",
|
|
},
|
|
},
|
|
args: args{
|
|
d: []byte(`["x",{"format":1}]`),
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with missing data field",
|
|
fields: fields{
|
|
WantKey: "",
|
|
WantValue: archival.MaybeBinaryValue{
|
|
Value: "",
|
|
},
|
|
},
|
|
args: args{
|
|
d: []byte(`["x",{"format":"base64"}]`),
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with data not being a string",
|
|
fields: fields{
|
|
WantKey: "",
|
|
WantValue: archival.MaybeBinaryValue{
|
|
Value: "",
|
|
},
|
|
},
|
|
args: args{
|
|
d: []byte(`["x",{"format":"base64","data":1}]`),
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with data not being base64",
|
|
fields: fields{
|
|
WantKey: "",
|
|
WantValue: archival.MaybeBinaryValue{
|
|
Value: "",
|
|
},
|
|
},
|
|
args: args{
|
|
d: []byte(`["x",{"format":"base64","data":"xx"}]`),
|
|
},
|
|
wantErr: true,
|
|
}, {
|
|
name: "with correctly encoded base64 data",
|
|
fields: fields{
|
|
WantKey: "x",
|
|
WantValue: archival.MaybeBinaryValue{
|
|
Value: string(binaryInput),
|
|
},
|
|
},
|
|
args: args{
|
|
d: []byte(`["x",` + string(encodedBinaryInput) + `]`),
|
|
},
|
|
wantErr: false,
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
hh := &archival.HTTPHeader{}
|
|
if err := hh.UnmarshalJSON(tt.args.d); (err != nil) != tt.wantErr {
|
|
t.Errorf("HTTPHeader.UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
|
}
|
|
expect := &archival.HTTPHeader{
|
|
Key: tt.fields.WantKey,
|
|
Value: tt.fields.WantValue,
|
|
}
|
|
if d := cmp.Diff(hh, expect); d != "" {
|
|
t.Error(d)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewFailure(t *testing.T) {
|
|
type args struct {
|
|
err error
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want *string
|
|
}{{
|
|
name: "when error is nil",
|
|
args: args{
|
|
err: nil,
|
|
},
|
|
want: nil,
|
|
}, {
|
|
name: "when error is wrapped and failure meaningful",
|
|
args: args{
|
|
err: &errorx.ErrWrapper{
|
|
Failure: errorx.FailureConnectionRefused,
|
|
},
|
|
},
|
|
want: func() *string {
|
|
s := errorx.FailureConnectionRefused
|
|
return &s
|
|
}(),
|
|
}, {
|
|
name: "when error is wrapped and failure is not meaningful",
|
|
args: args{
|
|
err: &errorx.ErrWrapper{},
|
|
},
|
|
want: func() *string {
|
|
s := "unknown_failure: errWrapper.Failure is empty"
|
|
return &s
|
|
}(),
|
|
}, {
|
|
name: "when error is not wrapped but wrappable",
|
|
args: args{err: io.EOF},
|
|
want: func() *string {
|
|
s := "eof_error"
|
|
return &s
|
|
}(),
|
|
}, {
|
|
name: "when the error is not wrapped and not wrappable",
|
|
args: args{
|
|
err: errors.New("use of closed socket 127.0.0.1:8080->10.0.0.1:22"),
|
|
},
|
|
want: func() *string {
|
|
s := "unknown_failure: use of closed socket [scrubbed]->[scrubbed]"
|
|
return &s
|
|
}(),
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := archival.NewFailure(tt.args.err)
|
|
if tt.want == nil && got == nil {
|
|
return
|
|
}
|
|
if tt.want == nil && got != nil {
|
|
t.Errorf("NewFailure: want %+v, got %s", tt.want, *got)
|
|
return
|
|
}
|
|
if tt.want != nil && got == nil {
|
|
t.Errorf("NewFailure: want %s, got %+v", *tt.want, got)
|
|
return
|
|
}
|
|
if *tt.want != *got {
|
|
t.Errorf("NewFailure: want %s, got %s", *tt.want, *got)
|
|
return
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNewFailedOperation(t *testing.T) {
|
|
type args struct {
|
|
err error
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want *string
|
|
}{{
|
|
name: "With no error",
|
|
args: args{
|
|
err: nil, // explicit
|
|
},
|
|
want: nil, // explicit
|
|
}, {
|
|
name: "With wrapped error and non-empty operation",
|
|
args: args{
|
|
err: &errorx.ErrWrapper{
|
|
Failure: errorx.FailureConnectionRefused,
|
|
Operation: errorx.ConnectOperation,
|
|
},
|
|
},
|
|
want: (func() *string {
|
|
s := errorx.ConnectOperation
|
|
return &s
|
|
})(),
|
|
}, {
|
|
name: "With wrapped error and empty operation",
|
|
args: args{
|
|
err: &errorx.ErrWrapper{
|
|
Failure: errorx.FailureConnectionRefused,
|
|
},
|
|
},
|
|
want: (func() *string {
|
|
s := errorx.UnknownOperation
|
|
return &s
|
|
})(),
|
|
}, {
|
|
name: "With non wrapped error",
|
|
args: args{
|
|
err: io.EOF,
|
|
},
|
|
want: (func() *string {
|
|
s := errorx.UnknownOperation
|
|
return &s
|
|
})(),
|
|
}}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got := archival.NewFailedOperation(tt.args.err)
|
|
if got == nil && tt.want == nil {
|
|
return
|
|
}
|
|
if got == nil && tt.want != nil {
|
|
t.Errorf("NewFailedOperation() = %v, want %v", got, tt.want)
|
|
return
|
|
}
|
|
if got != nil && tt.want == nil {
|
|
t.Errorf("NewFailedOperation() = %v, want %v", got, tt.want)
|
|
return
|
|
}
|
|
if got != nil && tt.want != nil && *got != *tt.want {
|
|
t.Errorf("NewFailedOperation() = %v, want %v", got, tt.want)
|
|
return
|
|
}
|
|
})
|
|
}
|
|
}
|