test(diff): пустой шаблон — массовый Delete управляемых, NS остаётся ReadOnly

This commit is contained in:
2026-07-03 12:57:36 +07:00
parent 86338f846a
commit cb2f826dc2
+40
View File
@@ -59,6 +59,46 @@ func TestDiffMarksReadOnlyForNSSOA(t *testing.T) {
}
}
// Global Constraint: an empty/nil template must not silently no-op — every managed
// record in the zone must surface as a Delete, while read-only records (NS/SOA)
// stay ReadOnly and excluded from Actionable(). This guards against mass deletion
// bugs where a missing template accidentally wipes the zone unattended.
func TestDiffEmptyTemplateDeletesAllManagedKeepsNSReadOnly(t *testing.T) {
actual := []model.Record{
{Type: model.A, Name: "a.example.com.", TTL: 300, Values: []string{"1.1.1.1"}},
{Type: model.A, Name: "b.example.com.", TTL: 300, Values: []string{"2.2.2.2"}},
{Type: model.NS, Name: "example.com.", TTL: 3600, Values: []string{"ns1.example.com."}},
}
cs := Diff(nil, actual)
if len(cs.Diffs) != 3 {
t.Fatalf("expected 3 diffs (2 A deletes + 1 NS), got %d: %+v", len(cs.Diffs), cs.Diffs)
}
da := find(cs, "A a.example.com.")
if da == nil || da.Kind != Delete || da.ReadOnly {
t.Fatalf("A a.example.com. must be a non-read-only Delete, got %+v", da)
}
db := find(cs, "A b.example.com.")
if db == nil || db.Kind != Delete || db.ReadOnly {
t.Fatalf("A b.example.com. must be a non-read-only Delete, got %+v", db)
}
dns := find(cs, "NS example.com.")
if dns == nil || dns.Kind != Delete || !dns.ReadOnly {
t.Fatalf("NS example.com. must be a ReadOnly Delete, got %+v", dns)
}
act := cs.Actionable()
if len(act) != 2 {
t.Fatalf("expected 2 actionable deletes (A records only), got %d: %+v", len(act), act)
}
for _, d := range act {
if d.Type == model.NS {
t.Fatalf("NS must be excluded from Actionable(), got %+v", d)
}
}
}
func TestActionableExcludesInSyncAndReadOnly(t *testing.T) {
tmpl := []model.Record{
{Type: model.A, Name: "a.example.com.", TTL: 300, Values: []string{"1.1.1.1"}}, // in sync