package watcher_test import ( "slices" "testing" "sneak.berlin/go/dnswatcher/internal/state" "sneak.berlin/go/dnswatcher/internal/watcher" ) // hostnameState builds the state a check with these records leaves behind. func hostnameState( records map[string]map[string][]string, ) *state.HostnameState { hs := &state.HostnameState{ RecordsByNameserver: make(map[string]*state.NameserverRecordState), } for ns, recs := range records { hs.RecordsByNameserver[ns] = &state.NameserverRecordState{ Records: recs, Status: "ok", } } return hs } func TestNewlyDisagreeingPairs(t *testing.T) { t.Parallel() const ( nsA = "a.ns.example.net." nsB = "b.ns.example.net." ip1 = "192.0.2.1" ip2 = "192.0.2.2" ) onlyA := map[string]map[string][]string{nsA: {"A": {ip1}}} agree := map[string]map[string][]string{ nsA: {"A": {ip1}}, nsB: {"A": {ip1}}, } disagree := map[string]map[string][]string{ nsA: {"A": {ip1}}, nsB: {"A": {ip2}}, } alert := [][2]string{{nsA, nsB}} // Each case starts from the state loaded at startup and runs the // checks in order; want[i] is what check i alerts for. tests := []struct { name string loaded map[string]map[string][]string checks []map[string]map[string][]string want [][][2]string }{ { name: "disagreement persisting across checks alerts once", loaded: agree, checks: []map[string]map[string][]string{disagree, disagree, disagree}, want: [][][2]string{alert, nil, nil}, }, { name: "disagreement starting on a later check alerts on it", loaded: agree, checks: []map[string]map[string][]string{agree, agree, disagree}, want: [][][2]string{nil, nil, alert}, }, { name: "disagreement in the loaded state does not alert", loaded: disagree, checks: []map[string]map[string][]string{disagree, disagree}, want: [][][2]string{nil, nil}, }, { name: "nameserver new on this check does not alert", loaded: onlyA, checks: []map[string]map[string][]string{disagree}, want: [][][2]string{nil}, }, { name: "disagreement after agreeing again alerts again", loaded: agree, checks: []map[string]map[string][]string{disagree, agree, disagree}, want: [][][2]string{alert, nil, alert}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() prev := hostnameState(tt.loaded) for i, current := range tt.checks { got := watcher.NewlyDisagreeingPairs(prev, current) if !slices.Equal(got, tt.want[i]) { t.Errorf( "check %d: alerted for %v, want %v", i, got, tt.want[i], ) } prev = hostnameState(current) } }) } }