package webconnectivity_test

import (
	"io"
	"net/url"
	"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"
)

func TestDNSAnalysis(t *testing.T) {
	measurementFailure := netxlite.FailureDNSNXDOMAINError
	controlFailure := webconnectivity.DNSNameError
	eofFailure := io.EOF.Error()
	androidEaiNoData := netxlite.FailureAndroidDNSCacheNoData
	type args struct {
		URL         *url.URL
		measurement webconnectivity.DNSLookupResult
		control     webconnectivity.ControlResponse
	}
	tests := []struct {
		name    string
		args    args
		wantOut webconnectivity.DNSAnalysisResult
	}{{
		name: "when the URL contains an IP address",
		args: args{
			URL: &url.URL{
				Host: "10.0.0.1",
			},
			control: webconnectivity.ControlResponse{
				DNS: webconnectivity.ControlDNSResult{
					Failure: &controlFailure,
				},
			},
		},
		wantOut: webconnectivity.DNSAnalysisResult{
			DNSConsistency: &webconnectivity.DNSConsistent,
		},
	}, {
		name: "when the failures are not compatible",
		args: args{
			URL: &url.URL{
				Host: "www.kerneltrap.org",
			},
			measurement: webconnectivity.DNSLookupResult{
				Failure: &eofFailure,
			},
			control: webconnectivity.ControlResponse{
				DNS: webconnectivity.ControlDNSResult{
					Failure: &controlFailure,
				},
			},
		},
		wantOut: webconnectivity.DNSAnalysisResult{
			DNSConsistency: &webconnectivity.DNSInconsistent,
		},
	}, {
		name: "when the failures are compatible (NXDOMAIN case)",
		args: args{
			URL: &url.URL{
				Host: "www.kerneltrap.org",
			},
			measurement: webconnectivity.DNSLookupResult{
				Failure: &measurementFailure,
			},
			control: webconnectivity.ControlResponse{
				DNS: webconnectivity.ControlDNSResult{
					Failure: &controlFailure,
				},
			},
		},
		wantOut: webconnectivity.DNSAnalysisResult{
			DNSConsistency: &webconnectivity.DNSConsistent,
		},
	}, {
		name: "when the failures are compatible (Android EAI_NODATA case)",
		args: args{
			URL: &url.URL{
				Host: "www.kerneltrap.org",
			},
			measurement: webconnectivity.DNSLookupResult{
				Failure: &androidEaiNoData,
			},
			control: webconnectivity.ControlResponse{
				DNS: webconnectivity.ControlDNSResult{
					Failure: &controlFailure,
				},
			},
		},
		wantOut: webconnectivity.DNSAnalysisResult{
			DNSConsistency: &webconnectivity.DNSConsistent,
		},
	}, {
		name: "when the ASNs are equal",
		args: args{
			URL: &url.URL{
				Host: "fancy.dns",
			},
			measurement: webconnectivity.DNSLookupResult{
				Addrs: map[string]int64{
					"1.1.1.1": 15169,
					"8.8.8.8": 13335,
				},
			},
			control: webconnectivity.ControlResponse{
				DNS: webconnectivity.ControlDNSResult{
					ASNs: []int64{13335, 15169},
				},
			},
		},
		wantOut: webconnectivity.DNSAnalysisResult{
			DNSConsistency: &webconnectivity.DNSConsistent,
		},
	}, {
		name: "when the ASNs overlap",
		args: args{
			URL: &url.URL{
				Host: "fancy.dns",
			},
			measurement: webconnectivity.DNSLookupResult{
				Addrs: map[string]int64{
					"1.1.1.1": 15169,
					"8.8.8.8": 13335,
				},
			},
			control: webconnectivity.ControlResponse{
				DNS: webconnectivity.ControlDNSResult{
					ASNs: []int64{13335, 13335},
				},
			},
		},
		wantOut: webconnectivity.DNSAnalysisResult{
			DNSConsistency: &webconnectivity.DNSConsistent,
		},
	}, {
		name: "when the ASNs do not overlap",
		args: args{
			URL: &url.URL{
				Host: "fancy.dns",
			},
			measurement: webconnectivity.DNSLookupResult{
				Addrs: map[string]int64{
					"1.1.1.1": 15169,
					"8.8.8.8": 15169,
				},
			},
			control: webconnectivity.ControlResponse{
				DNS: webconnectivity.ControlDNSResult{
					ASNs: []int64{13335, 13335},
				},
			},
		},
		wantOut: webconnectivity.DNSAnalysisResult{
			DNSConsistency: &webconnectivity.DNSInconsistent,
		},
	}, {
		name: "when ASNs lookup fails but IPs overlap",
		args: args{
			URL: &url.URL{
				Host: "fancy.dns",
			},
			measurement: webconnectivity.DNSLookupResult{
				Addrs: map[string]int64{
					"2001:4860:4860::8844": 0,
					"8.8.4.4":              0,
				},
			},
			control: webconnectivity.ControlResponse{
				DNS: webconnectivity.ControlDNSResult{
					Addrs: []string{"8.8.8.8", "2001:4860:4860::8844"},
					ASNs:  []int64{0, 0},
				},
			},
		},
		wantOut: webconnectivity.DNSAnalysisResult{
			DNSConsistency: &webconnectivity.DNSConsistent,
		},
	}, {
		name: "when ASNs lookup fails and IPs do not overlap",
		args: args{
			URL: &url.URL{
				Host: "fancy.dns",
			},
			measurement: webconnectivity.DNSLookupResult{
				Addrs: map[string]int64{
					"2001:4860:4860::8888": 0,
					"8.8.8.8":              0,
				},
			},
			control: webconnectivity.ControlResponse{
				DNS: webconnectivity.ControlDNSResult{
					Addrs: []string{"8.8.4.4", "2001:4860:4860::8844"},
					ASNs:  []int64{0, 0},
				},
			},
		},
		wantOut: webconnectivity.DNSAnalysisResult{
			DNSConsistency: &webconnectivity.DNSInconsistent,
		},
	}}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotOut := webconnectivity.DNSAnalysis(tt.args.URL, tt.args.measurement, tt.args.control)
			if diff := cmp.Diff(tt.wantOut, gotOut); diff != "" {
				t.Fatal(diff)
			}
		})
	}
}
