1
0
mirror of https://github.com/StackExchange/dnscontrol.git synced 2024-05-11 05:55:12 +00:00
2022-12-30 21:33:14 -05:00

610 lines
18 KiB
Go

package diff2
import (
"reflect"
"strings"
"testing"
"github.com/StackExchange/dnscontrol/v3/models"
"github.com/kylelemons/godebug/diff"
)
var testDataAA1234 = makeRec("laba", "A", "1.2.3.4") // [0]
var testDataAA5678 = makeRec("laba", "A", "5.6.7.8") //
var testDataAA1234ttl700 = makeRecTTL("laba", "A", "1.2.3.4", 700) //
var testDataAA5678ttl700 = makeRecTTL("laba", "A", "5.6.7.8", 700) //
var testDataAMX10a = makeRec("laba", "MX", "10 laba") // [1]
var testDataCCa = makeRec("labc", "CNAME", "laba") // [2]
var testDataEA15 = makeRec("labe", "A", "10.10.10.15") // [3]
var e4 = makeRec("labe", "A", "10.10.10.16") // [4]
var e5 = makeRec("labe", "A", "10.10.10.17") // [5]
var e6 = makeRec("labe", "A", "10.10.10.18") // [6]
var e7 = makeRec("labg", "NS", "10.10.10.15") // [7]
var e8 = makeRec("labg", "NS", "10.10.10.16") // [8]
var e9 = makeRec("labg", "NS", "10.10.10.17") // [9]
var e10 = makeRec("labg", "NS", "10.10.10.18") // [10]
var e11mx = makeRec("labh", "MX", "22 ttt") // [11]
var e11 = makeRec("labh", "CNAME", "labd") // [11]
var testDataApexMX1aaa = makeRec("", "MX", "1 aaa")
var testDataAA1234clone = makeRec("laba", "A", "1.2.3.4") // [0']
var testDataAA12345 = makeRec("laba", "A", "1.2.3.5") // [1']
var testDataAMX20b = makeRec("laba", "MX", "20 labb") // [2']
var d3 = makeRec("labe", "A", "10.10.10.95") // [3']
var d4 = makeRec("labe", "A", "10.10.10.96") // [4']
var d5 = makeRec("labe", "A", "10.10.10.97") // [5']
var d6 = makeRec("labe", "A", "10.10.10.98") // [6']
var d7 = makeRec("labf", "TXT", "foo") // [7']
var d8 = makeRec("labg", "NS", "10.10.10.10") // [8']
var d9 = makeRec("labg", "NS", "10.10.10.15") // [9']
var d10 = makeRec("labg", "NS", "10.10.10.16") // [10']
var d11 = makeRec("labg", "NS", "10.10.10.97") // [11']
var d12 = makeRec("labh", "A", "1.2.3.4") // [12']
var testDataApexMX22bbb = makeRec("", "MX", "22 bbb")
var d0tc = mkTargetConfig(testDataAA1234clone)
func makeChange(v Verb, l, t string, old, new models.Records, msgs []string) Change {
c := Change{
Type: v,
Old: old,
New: new,
Msgs: msgs,
}
c.Key.NameFQDN = l
c.Key.Type = t
return c
}
func compareMsgs(t *testing.T, fnname, testname, testpart string, gotcc ChangeList, wantstring string) {
t.Helper()
gs := strings.TrimSpace(justMsgString(gotcc))
ws := strings.TrimSpace(wantstring)
d := diff.Diff(gs, ws)
if d != "" {
t.Errorf("%s()/%s (wantMsgs:%s):\n===got===\n%s\n===want===\n%s\n===diff===\n%s\n===", fnname, testname, testpart, gs, ws, d)
}
}
func compareCL(t *testing.T, fnname, testname, testpart string, gotcl ChangeList, wantstring string) {
t.Helper()
gs := strings.TrimSpace(gotcl.String())
ws := strings.TrimSpace(wantstring)
d := diff.Diff(gs, ws)
if d != "" {
t.Errorf("%s()/%s (wantChange%s):\n===got===\n%s\n===want===\n%s\n===diff===\n%s\n===", fnname, testname, testpart, gs, ws, d)
}
}
func Test_analyzeByRecordSet(t *testing.T) {
type args struct {
origin string
existing, desired models.Records
compFn ComparableFunc
}
origin := "f.com"
existing := models.Records{testDataAA1234, testDataAMX10a, testDataCCa, testDataEA15, e4, e5, e6, e7, e8, e9, e10, e11}
desired := models.Records{testDataAA1234clone, testDataAA12345, testDataAMX20b, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12}
tests := []struct {
name string
args args
wantMsgs string
wantChangeRSet string
wantChangeLabel string
wantChangeRec string
wantChangeZone string
}{
{
name: "oneequal",
args: args{
origin: origin,
existing: models.Records{testDataAA1234},
desired: models.Records{testDataAA1234clone},
},
wantMsgs: "", // Empty
wantChangeRSet: "ChangeList: len=0",
wantChangeLabel: "ChangeList: len=0",
wantChangeRec: "ChangeList: len=0",
},
{
name: "onediff",
args: args{
origin: origin,
existing: models.Records{testDataAA1234, testDataAMX10a},
desired: models.Records{testDataAA1234clone, testDataAMX20b},
},
wantMsgs: "CHANGE laba.f.com MX (10 laba) -> (20 labb)",
wantChangeRSet: `
ChangeList: len=1
00: Change: verb=CHANGE
key={laba.f.com MX}
old=[10 laba]
new=[20 labb]
msg=["CHANGE laba.f.com MX (10 laba) -> (20 labb)"]
`,
wantChangeLabel: `
ChangeList: len=1
00: Change: verb=CHANGE
key={laba.f.com }
old=[1.2.3.4 10 laba]
new=[1.2.3.4 20 labb]
msg=["CHANGE laba.f.com MX (10 laba) -> (20 labb)"]
`,
wantChangeRec: `
ChangeList: len=1
00: Change: verb=CHANGE
key={laba.f.com MX}
old=[10 laba]
new=[20 labb]
msg=["CHANGE laba.f.com MX (10 laba) -> (20 labb)"]
`,
},
{
name: "apex",
args: args{
origin: origin,
existing: models.Records{testDataAA1234, testDataApexMX1aaa},
desired: models.Records{testDataAA1234clone, testDataApexMX22bbb},
},
wantMsgs: "CHANGE f.com MX (1 aaa) -> (22 bbb)",
wantChangeRSet: `
ChangeList: len=1
00: Change: verb=CHANGE
key={f.com MX}
old=[1 aaa]
new=[22 bbb]
msg=["CHANGE f.com MX (1 aaa) -> (22 bbb)"]
`,
wantChangeLabel: `
ChangeList: len=1
00: Change: verb=CHANGE
key={f.com }
old=[1 aaa]
new=[22 bbb]
msg=["CHANGE f.com MX (1 aaa) -> (22 bbb)"]
`,
wantChangeRec: `
ChangeList: len=1
00: Change: verb=CHANGE
key={f.com MX}
old=[1 aaa]
new=[22 bbb]
msg=["CHANGE f.com MX (1 aaa) -> (22 bbb)"]
`,
},
{
name: "firsta",
args: args{
origin: origin,
existing: models.Records{testDataAA1234, testDataAMX10a},
desired: models.Records{testDataAA1234clone, testDataAA12345, testDataAMX20b},
},
wantMsgs: `
CREATE laba.f.com A 1.2.3.5
CHANGE laba.f.com MX (10 laba) -> (20 labb)
`,
wantChangeRSet: `
ChangeList: len=2
00: Change: verb=CHANGE
key={laba.f.com A}
old=[1.2.3.4]
new=[1.2.3.4 1.2.3.5]
msg=["CREATE laba.f.com A 1.2.3.5"]
01: Change: verb=CHANGE
key={laba.f.com MX}
old=[10 laba]
new=[20 labb]
msg=["CHANGE laba.f.com MX (10 laba) -> (20 labb)"]
`,
wantChangeLabel: `
ChangeList: len=1
00: Change: verb=CHANGE
key={laba.f.com }
old=[1.2.3.4 10 laba]
new=[1.2.3.4 1.2.3.5 20 labb]
msg=["CREATE laba.f.com A 1.2.3.5" "CHANGE laba.f.com MX (10 laba) -> (20 labb)"]
`,
wantChangeRec: `
ChangeList: len=2
00: Change: verb=CREATE
key={laba.f.com A}
new=[1.2.3.5]
msg=["CREATE laba.f.com A 1.2.3.5"]
01: Change: verb=CHANGE
key={laba.f.com MX}
old=[10 laba]
new=[20 labb]
msg=["CHANGE laba.f.com MX (10 laba) -> (20 labb)"]
`,
},
{
name: "big",
args: args{
origin: origin,
existing: existing,
desired: desired,
},
wantMsgs: `
CREATE laba.f.com A 1.2.3.5
CHANGE laba.f.com MX (10 laba) -> (20 labb)
DELETE labc.f.com CNAME laba
CHANGE labe.f.com A (10.10.10.15) -> (10.10.10.95)
CHANGE labe.f.com A (10.10.10.16) -> (10.10.10.96)
CHANGE labe.f.com A (10.10.10.17) -> (10.10.10.97)
CHANGE labe.f.com A (10.10.10.18) -> (10.10.10.98)
CREATE labf.f.com TXT "foo"
CHANGE labg.f.com NS (10.10.10.17) -> (10.10.10.10)
CHANGE labg.f.com NS (10.10.10.18) -> (10.10.10.97)
DELETE labh.f.com CNAME labd
CREATE labh.f.com A 1.2.3.4
`,
wantChangeRSet: `
ChangeList: len=8
00: Change: verb=CHANGE
key={laba.f.com A}
old=[1.2.3.4]
new=[1.2.3.4 1.2.3.5]
msg=["CREATE laba.f.com A 1.2.3.5"]
01: Change: verb=CHANGE
key={laba.f.com MX}
old=[10 laba]
new=[20 labb]
msg=["CHANGE laba.f.com MX (10 laba) -> (20 labb)"]
02: Change: verb=DELETE
key={labc.f.com CNAME}
old=[laba]
msg=["DELETE labc.f.com CNAME laba"]
03: Change: verb=CHANGE
key={labe.f.com A}
old=[10.10.10.15 10.10.10.16 10.10.10.17 10.10.10.18]
new=[10.10.10.95 10.10.10.96 10.10.10.97 10.10.10.98]
msg=["CHANGE labe.f.com A (10.10.10.15) -> (10.10.10.95)" "CHANGE labe.f.com A (10.10.10.16) -> (10.10.10.96)" "CHANGE labe.f.com A (10.10.10.17) -> (10.10.10.97)" "CHANGE labe.f.com A (10.10.10.18) -> (10.10.10.98)"]
04: Change: verb=CREATE
key={labf.f.com TXT}
new=["foo"]
msg=["CREATE labf.f.com TXT \"foo\""]
05: Change: verb=CHANGE
key={labg.f.com NS}
old=[10.10.10.15 10.10.10.16 10.10.10.17 10.10.10.18]
new=[10.10.10.10 10.10.10.15 10.10.10.16 10.10.10.97]
msg=["CHANGE labg.f.com NS (10.10.10.17) -> (10.10.10.10)" "CHANGE labg.f.com NS (10.10.10.18) -> (10.10.10.97)"]
06: Change: verb=DELETE
key={labh.f.com CNAME}
old=[labd]
msg=["DELETE labh.f.com CNAME labd"]
07: Change: verb=CREATE
key={labh.f.com A}
new=[1.2.3.4]
msg=["CREATE labh.f.com A 1.2.3.4"]
`,
wantChangeLabel: `
ChangeList: len=6
00: Change: verb=CHANGE
key={laba.f.com }
old=[1.2.3.4 10 laba]
new=[1.2.3.4 1.2.3.5 20 labb]
msg=["CREATE laba.f.com A 1.2.3.5" "CHANGE laba.f.com MX (10 laba) -> (20 labb)"]
01: Change: verb=DELETE
key={labc.f.com }
old=[laba]
msg=["DELETE labc.f.com CNAME laba"]
02: Change: verb=CHANGE
key={labe.f.com }
old=[10.10.10.15 10.10.10.16 10.10.10.17 10.10.10.18]
new=[10.10.10.95 10.10.10.96 10.10.10.97 10.10.10.98]
msg=["CHANGE labe.f.com A (10.10.10.15) -> (10.10.10.95)" "CHANGE labe.f.com A (10.10.10.16) -> (10.10.10.96)" "CHANGE labe.f.com A (10.10.10.17) -> (10.10.10.97)" "CHANGE labe.f.com A (10.10.10.18) -> (10.10.10.98)"]
03: Change: verb=CREATE
key={labf.f.com }
new=["foo"]
msg=["CREATE labf.f.com TXT \"foo\""]
04: Change: verb=CHANGE
key={labg.f.com }
old=[10.10.10.15 10.10.10.16 10.10.10.17 10.10.10.18]
new=[10.10.10.10 10.10.10.15 10.10.10.16 10.10.10.97]
msg=["CHANGE labg.f.com NS (10.10.10.17) -> (10.10.10.10)" "CHANGE labg.f.com NS (10.10.10.18) -> (10.10.10.97)"]
05: Change: verb=CHANGE
key={labh.f.com }
old=[labd]
new=[1.2.3.4]
msg=["DELETE labh.f.com CNAME labd" "CREATE labh.f.com A 1.2.3.4"]
`,
wantChangeRec: `
ChangeList: len=12
00: Change: verb=CREATE
key={laba.f.com A}
new=[1.2.3.5]
msg=["CREATE laba.f.com A 1.2.3.5"]
01: Change: verb=CHANGE
key={laba.f.com MX}
old=[10 laba]
new=[20 labb]
msg=["CHANGE laba.f.com MX (10 laba) -> (20 labb)"]
02: Change: verb=DELETE
key={labc.f.com CNAME}
old=[laba]
msg=["DELETE labc.f.com CNAME laba"]
03: Change: verb=CHANGE
key={labe.f.com A}
old=[10.10.10.15]
new=[10.10.10.95]
msg=["CHANGE labe.f.com A (10.10.10.15) -> (10.10.10.95)"]
04: Change: verb=CHANGE
key={labe.f.com A}
old=[10.10.10.16]
new=[10.10.10.96]
msg=["CHANGE labe.f.com A (10.10.10.16) -> (10.10.10.96)"]
05: Change: verb=CHANGE
key={labe.f.com A}
old=[10.10.10.17]
new=[10.10.10.97]
msg=["CHANGE labe.f.com A (10.10.10.17) -> (10.10.10.97)"]
06: Change: verb=CHANGE
key={labe.f.com A}
old=[10.10.10.18]
new=[10.10.10.98]
msg=["CHANGE labe.f.com A (10.10.10.18) -> (10.10.10.98)"]
07: Change: verb=CREATE
key={labf.f.com TXT}
new=["foo"]
msg=["CREATE labf.f.com TXT \"foo\""]
08: Change: verb=CHANGE
key={labg.f.com NS}
old=[10.10.10.17]
new=[10.10.10.10]
msg=["CHANGE labg.f.com NS (10.10.10.17) -> (10.10.10.10)"]
09: Change: verb=CHANGE
key={labg.f.com NS}
old=[10.10.10.18]
new=[10.10.10.97]
msg=["CHANGE labg.f.com NS (10.10.10.18) -> (10.10.10.97)"]
10: Change: verb=DELETE
key={labh.f.com CNAME}
old=[labd]
msg=["DELETE labh.f.com CNAME labd"]
11: Change: verb=CREATE
key={labh.f.com A}
new=[1.2.3.4]
msg=["CREATE labh.f.com A 1.2.3.4"]
`,
},
}
for _, tt := range tests {
// Each "analyze*()" should return the same msgs, but a different ChangeList.
// Sadly the analyze*() functions are destructive to the CompareConfig struct.
// Therefore we have to run NewCompareConfig() each time.
t.Run(tt.name, func(t *testing.T) {
cl := analyzeByRecordSet(NewCompareConfig(tt.args.origin, tt.args.existing, tt.args.desired, tt.args.compFn))
compareMsgs(t, "analyzeByRecordSet", tt.name, "RSet", cl, tt.wantMsgs)
compareCL(t, "analyzeByRecordSet", tt.name, "RSet", cl, tt.wantChangeRSet)
})
t.Run(tt.name, func(t *testing.T) {
cl := analyzeByLabel(NewCompareConfig(tt.args.origin, tt.args.existing, tt.args.desired, tt.args.compFn))
compareMsgs(t, "analyzeByLabel", tt.name, "Label", cl, tt.wantMsgs)
compareCL(t, "analyzeByLabel", tt.name, "Label", cl, tt.wantChangeLabel)
})
t.Run(tt.name, func(t *testing.T) {
cl := analyzeByRecord(NewCompareConfig(tt.args.origin, tt.args.existing, tt.args.desired, tt.args.compFn))
compareMsgs(t, "analyzeByRecord", tt.name, "Rec", cl, tt.wantMsgs)
compareCL(t, "analyzeByRecord", tt.name, "Rec", cl, tt.wantChangeRec)
})
// NB(tlim): There is no analyzeByZone(). ByZone() uses analyzeByRecord().
}
}
func mkTargetConfig(x ...*models.RecordConfig) []targetConfig {
var tc []targetConfig
for _, r := range x {
tc = append(tc, targetConfig{
compareable: comparable(r, nil),
rec: r,
})
}
return tc
}
func mkTargetConfigMap(x ...*models.RecordConfig) map[string]*targetConfig {
var m = map[string]*targetConfig{}
for _, v := range mkTargetConfig(x...) {
m[v.compareable] = &v
}
return m
}
func Test_diffTargets(t *testing.T) {
type args struct {
existing []targetConfig
desired []targetConfig
}
tests := []struct {
name string
args args
want ChangeList
}{
{
name: "single",
args: args{
existing: mkTargetConfig(testDataAA1234),
desired: mkTargetConfig(testDataAA1234),
},
//want: ,
},
{
name: "add1",
args: args{
existing: mkTargetConfig(testDataAA1234),
desired: mkTargetConfig(testDataAA1234, testDataAMX10a),
},
want: ChangeList{
Change{Type: CREATE,
Key: models.RecordKey{NameFQDN: "laba.f.com", Type: "MX"},
New: models.Records{testDataAMX10a},
Msgs: []string{"CREATE laba.f.com MX 10 laba"},
},
},
},
{
name: "del1",
args: args{
existing: mkTargetConfig(testDataAA1234, testDataAMX10a),
desired: mkTargetConfig(testDataAA1234),
},
want: ChangeList{
Change{Type: DELETE,
Key: models.RecordKey{NameFQDN: "laba.f.com", Type: "MX"},
Old: models.Records{testDataAMX10a},
Msgs: []string{"DELETE laba.f.com MX 10 laba"},
},
},
},
{
name: "change2nd",
args: args{
existing: mkTargetConfig(testDataAA1234, testDataAMX10a),
desired: mkTargetConfig(testDataAA1234, testDataAMX20b),
},
want: ChangeList{
Change{Type: CHANGE,
Key: models.RecordKey{NameFQDN: "laba.f.com", Type: "MX"},
Old: models.Records{testDataAMX10a},
New: models.Records{testDataAMX20b},
Msgs: []string{"CHANGE laba.f.com MX (10 laba) -> (20 labb)"},
},
},
},
{
name: "del2nd",
args: args{
existing: mkTargetConfig(testDataAA1234, testDataAA5678),
desired: mkTargetConfig(testDataAA1234),
},
want: ChangeList{
Change{Type: CHANGE,
Key: models.RecordKey{NameFQDN: "laba.f.com", Type: "A"},
Old: models.Records{testDataAA1234, testDataAA5678},
New: models.Records{testDataAA1234},
Msgs: []string{"DELETE laba.f.com A 5.6.7.8"},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
//fmt.Printf("DEBUG: Test %02d\n", i)
got := diffTargets(tt.args.existing, tt.args.desired)
d := diff.Diff(strings.TrimSpace(justMsgString(got)), strings.TrimSpace(justMsgString(tt.want)))
if d != "" {
//fmt.Printf("DEBUG: %d %d\n", len(got), len(tt.want))
t.Errorf("diffTargets()\n diff=%s", d)
}
})
}
}
func Test_removeCommon(t *testing.T) {
type args struct {
existing []targetConfig
desired []targetConfig
}
tests := []struct {
name string
args args
want []targetConfig
want1 []targetConfig
}{
{
name: "same",
args: args{
existing: d0tc,
desired: d0tc,
},
want: []targetConfig{},
want1: []targetConfig{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1 := removeCommon(tt.args.existing, tt.args.desired)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("removeCommon() got = %v, want %v", got, tt.want)
}
if (!(got1 == nil && tt.want1 == nil)) && !reflect.DeepEqual(got1, tt.want1) {
t.Errorf("removeCommon() got1 = %v, want %v", got1, tt.want1)
}
})
}
}
func comparables(s []targetConfig) []string {
var r []string
for _, j := range s {
r = append(r, j.compareable)
}
return r
}
func Test_filterBy(t *testing.T) {
type args struct {
s []targetConfig
m map[string]*targetConfig
}
tests := []struct {
name string
args args
want []targetConfig
}{
{
name: "removeall",
args: args{
s: mkTargetConfig(testDataAA1234, testDataAMX10a),
m: mkTargetConfigMap(testDataAA1234, testDataAMX10a),
},
want: []targetConfig{},
},
{
name: "keepall",
args: args{
s: mkTargetConfig(testDataAA1234, testDataAMX10a),
m: mkTargetConfigMap(),
},
want: mkTargetConfig(testDataAA1234, testDataAMX10a),
},
{
name: "keepsome",
args: args{
s: mkTargetConfig(testDataAA1234, testDataAMX10a),
m: mkTargetConfigMap(testDataAMX10a),
},
want: mkTargetConfig(testDataAA1234),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := filterBy(tt.args.s, tt.args.m); !reflect.DeepEqual(comparables(got), comparables(tt.want)) {
t.Errorf("filterBy() = %v, want %v", got, tt.want)
}
})
}
}