github.com/teknogeek/dnscontrol@v0.2.8/integrationTest/integration_test.go (about) 1 package main 2 3 import ( 4 "flag" 5 "testing" 6 7 "fmt" 8 9 "strconv" 10 "strings" 11 12 "github.com/StackExchange/dnscontrol/models" 13 "github.com/StackExchange/dnscontrol/pkg/nameservers" 14 "github.com/StackExchange/dnscontrol/providers" 15 _ "github.com/StackExchange/dnscontrol/providers/_all" 16 "github.com/StackExchange/dnscontrol/providers/config" 17 "github.com/miekg/dns/dnsutil" 18 "github.com/pkg/errors" 19 ) 20 21 var providerToRun = flag.String("provider", "", "Provider to run") 22 var startIdx = flag.Int("start", 0, "Test number to begin with") 23 var endIdx = flag.Int("end", 0, "Test index to stop after") 24 var verbose = flag.Bool("verbose", false, "Print corrections as you run them") 25 26 func init() { 27 flag.Parse() 28 } 29 30 func getProvider(t *testing.T) (providers.DNSServiceProvider, string, map[int]bool) { 31 if *providerToRun == "" { 32 t.Log("No provider specified with -provider") 33 return nil, "", nil 34 } 35 jsons, err := config.LoadProviderConfigs("providers.json") 36 if err != nil { 37 t.Fatalf("Error loading provider configs: %s", err) 38 } 39 fails := map[int]bool{} 40 for name, cfg := range jsons { 41 if *providerToRun != name { 42 continue 43 } 44 provider, err := providers.CreateDNSProvider(name, cfg, nil) 45 if err != nil { 46 t.Fatal(err) 47 } 48 if f := cfg["knownFailures"]; f != "" { 49 for _, s := range strings.Split(f, ",") { 50 i, err := strconv.Atoi(s) 51 if err != nil { 52 t.Fatal(err) 53 } 54 fails[i] = true 55 } 56 } 57 return provider, cfg["domain"], fails 58 } 59 t.Fatalf("Provider %s not found", *providerToRun) 60 return nil, "", nil 61 } 62 63 func TestDNSProviders(t *testing.T) { 64 provider, domain, fails := getProvider(t) 65 if provider == nil { 66 return 67 } 68 t.Run(fmt.Sprintf("%s", domain), func(t *testing.T) { 69 runTests(t, provider, domain, fails) 70 }) 71 72 } 73 74 func getDomainConfigWithNameservers(t *testing.T, prv providers.DNSServiceProvider, domainName string) *models.DomainConfig { 75 dc := &models.DomainConfig{ 76 Name: domainName, 77 } 78 // fix up nameservers 79 ns, err := prv.GetNameservers(domainName) 80 if err != nil { 81 t.Fatal("Failed getting nameservers", err) 82 } 83 dc.Nameservers = ns 84 nameservers.AddNSRecords(dc) 85 return dc 86 } 87 88 func runTests(t *testing.T, prv providers.DNSServiceProvider, domainName string, knownFailures map[int]bool) { 89 dc := getDomainConfigWithNameservers(t, prv, domainName) 90 // run tests one at a time 91 end := *endIdx 92 tests := makeTests(t) 93 if end == 0 || end >= len(tests) { 94 end = len(tests) - 1 95 } 96 for i := *startIdx; i <= end; i++ { 97 tst := tests[i] 98 if t.Failed() { 99 break 100 } 101 t.Run(fmt.Sprintf("%d: %s", i, tst.Desc), func(t *testing.T) { 102 skipVal := false 103 if knownFailures[i] { 104 t.Log("SKIPPING VALIDATION FOR KNOWN FAILURE CASE") 105 skipVal = true 106 } 107 dom, _ := dc.Copy() 108 for _, r := range tst.Records { 109 rc := models.RecordConfig(*r) 110 if strings.Contains(rc.GetTargetField(), "**current-domain**") { 111 rc.SetTarget(strings.Replace(rc.GetTargetField(), "**current-domain**", domainName, 1) + ".") 112 } 113 if strings.Contains(rc.GetLabelFQDN(), "**current-domain**") { 114 rc.SetLabelFromFQDN(strings.Replace(rc.GetLabelFQDN(), "**current-domain**", domainName, 1), domainName) 115 } 116 dom.Records = append(dom.Records, &rc) 117 } 118 dom.IgnoredLabels = tst.IgnoredLabels 119 models.PostProcessRecords(dom.Records) 120 dom2, _ := dom.Copy() 121 // get corrections for first time 122 corrections, err := prv.GetDomainCorrections(dom) 123 if err != nil { 124 t.Fatal(errors.Wrap(err, "runTests")) 125 } 126 if !skipVal && i != *startIdx && len(corrections) == 0 { 127 if tst.Desc != "Empty" { 128 // There are "no corrections" if the last test was programmatically 129 // skipped. We detect this (possibly inaccurately) by checking to 130 // see if .Desc is "Empty". 131 t.Fatalf("Expect changes for all tests, but got none") 132 } 133 } 134 for _, c := range corrections { 135 if *verbose { 136 t.Log(c.Msg) 137 } 138 err = c.F() 139 if !skipVal && err != nil { 140 t.Fatal(err) 141 } 142 } 143 // run a second time and expect zero corrections 144 corrections, err = prv.GetDomainCorrections(dom2) 145 if err != nil { 146 t.Fatal(err) 147 } 148 if !skipVal && len(corrections) != 0 { 149 t.Logf("Expected 0 corrections on second run, but found %d.", len(corrections)) 150 for i, c := range corrections { 151 t.Logf("#%d: %s", i, c.Msg) 152 } 153 t.FailNow() 154 } 155 }) 156 } 157 } 158 159 func TestDualProviders(t *testing.T) { 160 p, domain, _ := getProvider(t) 161 if p == nil { 162 return 163 } 164 dc := getDomainConfigWithNameservers(t, p, domain) 165 // clear everything 166 run := func() { 167 dom, _ := dc.Copy() 168 cs, err := p.GetDomainCorrections(dom) 169 if err != nil { 170 t.Fatal(err) 171 } 172 for i, c := range cs { 173 t.Logf("#%d: %s", i+1, c.Msg) 174 if err = c.F(); err != nil { 175 t.Fatal(err) 176 } 177 } 178 } 179 t.Log("Clearing everything") 180 run() 181 // add bogus nameservers 182 dc.Records = []*models.RecordConfig{} 183 dc.Nameservers = append(dc.Nameservers, models.StringsToNameservers([]string{"ns1.otherdomain.tld", "ns2.otherdomain.tld"})...) 184 nameservers.AddNSRecords(dc) 185 t.Log("Adding nameservers from another provider") 186 run() 187 // run again to make sure no corrections 188 t.Log("Running again to ensure stability") 189 cs, err := p.GetDomainCorrections(dc) 190 if err != nil { 191 t.Fatal(err) 192 } 193 if len(cs) != 0 { 194 t.Logf("Expect no corrections on second run, but found %d.", len(cs)) 195 for i, c := range cs { 196 t.Logf("#%d: %s", i, c.Msg) 197 } 198 t.FailNow() 199 } 200 } 201 202 type TestCase struct { 203 Desc string 204 Records []*rec 205 IgnoredLabels []string 206 } 207 208 type rec models.RecordConfig 209 210 func (r *rec) GetLabel() string { 211 return r.Name 212 } 213 214 func (r *rec) SetLabel(label, domain string) { 215 r.Name = label 216 r.NameFQDN = dnsutil.AddOrigin(label, "**current-domain**") 217 } 218 219 func (r *rec) SetTarget(target string) { 220 r.Target = target 221 } 222 223 func a(name, target string) *rec { 224 return makeRec(name, target, "A") 225 } 226 227 func cname(name, target string) *rec { 228 return makeRec(name, target, "CNAME") 229 } 230 231 func alias(name, target string) *rec { 232 return makeRec(name, target, "ALIAS") 233 } 234 235 func r53alias(name, aliasType, target string) *rec { 236 r := makeRec(name, target, "R53_ALIAS") 237 r.R53Alias = map[string]string{ 238 "type": aliasType, 239 } 240 return r 241 } 242 243 func ns(name, target string) *rec { 244 return makeRec(name, target, "NS") 245 } 246 247 func mx(name string, prio uint16, target string) *rec { 248 r := makeRec(name, target, "MX") 249 r.MxPreference = prio 250 return r 251 } 252 253 func ptr(name, target string) *rec { 254 return makeRec(name, target, "PTR") 255 } 256 257 func srv(name string, priority, weight, port uint16, target string) *rec { 258 r := makeRec(name, target, "SRV") 259 r.SrvPriority = priority 260 r.SrvWeight = weight 261 r.SrvPort = port 262 return r 263 } 264 265 func txt(name, target string) *rec { 266 // FYI: This must match the algorithm in pkg/js/helpers.js TXT. 267 r := makeRec(name, target, "TXT") 268 r.TxtStrings = []string{target} 269 return r 270 } 271 272 func txtmulti(name string, target []string) *rec { 273 // FYI: This must match the algorithm in pkg/js/helpers.js TXT. 274 r := makeRec(name, target[0], "TXT") 275 r.TxtStrings = target 276 return r 277 } 278 279 func caa(name string, tag string, flag uint8, target string) *rec { 280 r := makeRec(name, target, "CAA") 281 r.CaaFlag = flag 282 r.CaaTag = tag 283 return r 284 } 285 286 func tlsa(name string, usage, selector, matchingtype uint8, target string) *rec { 287 r := makeRec(name, target, "TLSA") 288 r.TlsaUsage = usage 289 r.TlsaSelector = selector 290 r.TlsaMatchingType = matchingtype 291 return r 292 } 293 294 func ignore(name string) *rec { 295 r := &rec{ 296 Type: "IGNORE", 297 } 298 r.SetLabel(name, "**current-domain**") 299 return r 300 } 301 302 func makeRec(name, target, typ string) *rec { 303 r := &rec{ 304 Type: typ, 305 TTL: 300, 306 } 307 r.SetLabel(name, "**current-domain**") 308 r.SetTarget(target) 309 return r 310 } 311 312 func (r *rec) ttl(t uint32) *rec { 313 r.TTL = t 314 return r 315 } 316 317 func tc(desc string, recs ...*rec) *TestCase { 318 var records []*rec 319 var ignored []string 320 for _, r := range recs { 321 if r.Type == "IGNORE" { 322 ignored = append(ignored, r.GetLabel()) 323 } else { 324 records = append(records, r) 325 } 326 } 327 return &TestCase{ 328 Desc: desc, 329 Records: records, 330 IgnoredLabels: ignored, 331 } 332 } 333 334 func manyA(namePattern, target string, n int) []*rec { 335 recs := []*rec{} 336 for i := 0; i < n; i++ { 337 recs = append(recs, makeRec(fmt.Sprintf(namePattern, i), target, "A")) 338 } 339 return recs 340 } 341 342 func makeTests(t *testing.T) []*TestCase { 343 // ALWAYS ADD TO BOTTOM OF LIST. Order and indexes matter. 344 tests := []*TestCase{ 345 // A 346 tc("Empty"), 347 tc("Create an A record", a("@", "1.1.1.1")), 348 tc("Change it", a("@", "1.2.3.4")), 349 tc("Add another", a("@", "1.2.3.4"), a("www", "1.2.3.4")), 350 tc("Add another(same name)", a("@", "1.2.3.4"), a("www", "1.2.3.4"), a("www", "5.6.7.8")), 351 tc("Change a ttl", a("@", "1.2.3.4").ttl(1000), a("www", "1.2.3.4"), a("www", "5.6.7.8")), 352 tc("Change single target from set", a("@", "1.2.3.4").ttl(1000), a("www", "2.2.2.2"), a("www", "5.6.7.8")), 353 tc("Change all ttls", a("@", "1.2.3.4").ttl(500), a("www", "2.2.2.2").ttl(400), a("www", "5.6.7.8").ttl(400)), 354 tc("Delete one", a("@", "1.2.3.4").ttl(500), a("www", "5.6.7.8").ttl(400)), 355 tc("Add back and change ttl", a("www", "5.6.7.8").ttl(700), a("www", "1.2.3.4").ttl(700)), 356 tc("Change targets and ttls", a("www", "1.1.1.1"), a("www", "2.2.2.2")), 357 tc("Create wildcard", a("*", "1.2.3.4"), a("www", "1.1.1.1")), 358 tc("Delete wildcard", a("www", "1.1.1.1")), 359 360 // CNAMES 361 tc("Empty"), 362 tc("Create a CNAME", cname("foo", "google.com.")), 363 tc("Change it", cname("foo", "google2.com.")), 364 tc("Change to A record", a("foo", "1.2.3.4")), 365 tc("Change back to CNAME", cname("foo", "google.com.")), 366 tc("Record pointing to @", cname("foo", "**current-domain**")), 367 368 // NS 369 tc("Empty"), 370 tc("NS for subdomain", ns("xyz", "ns2.foo.com.")), 371 tc("Dual NS for subdomain", ns("xyz", "ns2.foo.com."), ns("xyz", "ns1.foo.com.")), 372 tc("NS Record pointing to @", ns("foo", "**current-domain**")), 373 374 // IDNAs 375 tc("Empty"), 376 tc("Internationalized name", a("ööö", "1.2.3.4")), 377 tc("Change IDN", a("ööö", "2.2.2.2")), 378 tc("Internationalized CNAME Target", cname("a", "ööö.com.")), 379 tc("IDN CNAME AND Target", cname("öoö", "ööö.企业.")), 380 381 // MX 382 tc("Empty"), 383 tc("MX record", mx("@", 5, "foo.com.")), 384 tc("Second MX record, same prio", mx("@", 5, "foo.com."), mx("@", 5, "foo2.com.")), 385 tc("3 MX", mx("@", 5, "foo.com."), mx("@", 5, "foo2.com."), mx("@", 15, "foo3.com.")), 386 tc("Delete one", mx("@", 5, "foo2.com."), mx("@", 15, "foo3.com.")), 387 tc("Change to other name", mx("@", 5, "foo2.com."), mx("mail", 15, "foo3.com.")), 388 tc("Change Preference", mx("@", 7, "foo2.com."), mx("mail", 15, "foo3.com.")), 389 tc("Record pointing to @", mx("foo", 8, "**current-domain**")), 390 } 391 392 // PTR 393 if !providers.ProviderHasCabability(*providerToRun, providers.CanUsePTR) { 394 t.Log("Skipping PTR Tests because provider does not support them") 395 } else { 396 tests = append(tests, tc("Empty"), 397 tc("Create PTR record", ptr("4", "foo.com.")), 398 tc("Modify PTR record", ptr("4", "bar.com.")), 399 ) 400 } 401 402 // ALIAS 403 if !providers.ProviderHasCabability(*providerToRun, providers.CanUseAlias) { 404 t.Log("Skipping ALIAS Tests because provider does not support them") 405 } else { 406 tests = append(tests, tc("Empty"), 407 tc("ALIAS at root", alias("@", "foo.com.")), 408 tc("change it", alias("@", "foo2.com.")), 409 tc("ALIAS at subdomain", alias("test", "foo.com.")), 410 ) 411 } 412 413 // SRV 414 if !providers.ProviderHasCabability(*providerToRun, providers.CanUseSRV) { 415 t.Log("Skipping SRV Tests because provider does not support them") 416 } else { 417 tests = append(tests, tc("Empty"), 418 tc("SRV record", srv("_sip._tcp", 5, 6, 7, "foo.com.")), 419 tc("Second SRV record, same prio", srv("_sip._tcp", 5, 6, 7, "foo.com."), srv("_sip._tcp", 5, 60, 70, "foo2.com.")), 420 tc("3 SRV", srv("_sip._tcp", 5, 6, 7, "foo.com."), srv("_sip._tcp", 5, 60, 70, "foo2.com."), srv("_sip._tcp", 15, 65, 75, "foo3.com.")), 421 tc("Delete one", srv("_sip._tcp", 5, 6, 7, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo3.com.")), 422 tc("Change Target", srv("_sip._tcp", 5, 6, 7, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo4.com.")), 423 tc("Change Priority", srv("_sip._tcp", 52, 6, 7, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo4.com.")), 424 tc("Change Weight", srv("_sip._tcp", 52, 62, 7, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo4.com.")), 425 tc("Change Port", srv("_sip._tcp", 52, 62, 72, "foo.com."), srv("_sip._tcp", 15, 65, 75, "foo4.com.")), 426 ) 427 } 428 429 // CAA 430 if !providers.ProviderHasCabability(*providerToRun, providers.CanUseCAA) { 431 t.Log("Skipping CAA Tests because provider does not support them") 432 } else { 433 tests = append(tests, tc("Empty"), 434 tc("CAA record", caa("@", "issue", 0, "letsencrypt.org")), 435 tc("CAA change tag", caa("@", "issuewild", 0, "letsencrypt.org")), 436 tc("CAA change target", caa("@", "issuewild", 0, "example.com")), 437 tc("CAA change flag", caa("@", "issuewild", 128, "example.com")), 438 tc("CAA many records", caa("@", "issue", 0, "letsencrypt.org"), caa("@", "issuewild", 0, ";"), caa("@", "iodef", 128, "mailto:test@example.com")), 439 tc("CAA delete", caa("@", "issue", 0, "letsencrypt.org")), 440 ) 441 } 442 443 // TLSA 444 if !providers.ProviderHasCabability(*providerToRun, providers.CanUseTLSA) { 445 t.Log("Skipping TLSA Tests because provider does not support them") 446 } else { 447 sha256hash := strings.Repeat("0123456789abcdef", 4) 448 sha512hash := strings.Repeat("0123456789abcdef", 8) 449 reversedSha512 := strings.Repeat("fedcba9876543210", 8) 450 tests = append(tests, tc("Empty"), 451 tc("TLSA record", tlsa("_443._tcp", 3, 1, 1, sha256hash)), 452 tc("TLSA change usage", tlsa("_443._tcp", 2, 1, 1, sha256hash)), 453 tc("TLSA change selector", tlsa("_443._tcp", 2, 0, 1, sha256hash)), 454 tc("TLSA change matchingtype", tlsa("_443._tcp", 2, 0, 2, sha512hash)), 455 tc("TLSA change certificate", tlsa("_443._tcp", 2, 0, 2, reversedSha512)), 456 ) 457 } 458 459 // Case 460 tests = append(tests, tc("Empty"), 461 tc("Empty"), 462 tc("Create CAPS", mx("BAR", 5, "BAR.com.")), 463 tc("Downcase label", mx("bar", 5, "BAR.com."), a("decoy", "1.1.1.1")), 464 tc("Downcase target", mx("bar", 5, "bar.com."), a("decoy", "2.2.2.2")), 465 tc("Upcase both", mx("BAR", 5, "BAR.COM."), a("decoy", "3.3.3.3")), 466 // The decoys are required so that there is at least one actual change in each tc. 467 ) 468 469 // Test large zonefiles. 470 // Mostly to test paging. Many providers page at 100 471 // Known page sizes: 472 // - gandi: 100 473 skip := map[string]bool{ 474 "NS1": true, // ns1 free acct only allows 50 records 475 } 476 if skip[*providerToRun] { 477 t.Log("Skipping Large record count Tests because provider does not support them") 478 } else { 479 tests = append(tests, tc("Empty"), 480 tc("99 records", manyA("rec%04d", "1.2.3.4", 99)...), 481 tc("100 records", manyA("rec%04d", "1.2.3.4", 100)...), 482 tc("101 records", manyA("rec%04d", "1.2.3.4", 101)...), 483 ) 484 } 485 486 // NB(tlim): To temporarily skip most of the tests, insert a line like this: 487 //tests = nil 488 489 // TXT (single) 490 tests = append(tests, tc("Empty"), 491 tc("Empty"), 492 tc("Create a TXT", txt("foo", "simple")), 493 tc("Change a TXT", txt("foo", "changed")), 494 tc("Empty"), 495 tc("Create a TXT with spaces", txt("foo", "with spaces")), 496 tc("Change a TXT with spaces", txt("foo", "with whitespace")), 497 tc("Create 1 TXT as array", txtmulti("foo", []string{"simple"})), 498 tc("Empty"), 499 tc("Create a 255-byte TXT", txt("foo", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")), 500 ) 501 502 // TXTMulti 503 if !providers.ProviderHasCabability(*providerToRun, providers.CanUseTXTMulti) { 504 t.Log("Skipping TXTMulti Tests because provider does not support them") 505 } else { 506 tests = append(tests, 507 tc("Empty"), 508 tc("Create TXTMulti 1", 509 txtmulti("foo1", []string{"simple"}), 510 ), 511 tc("Create TXTMulti 2", 512 txtmulti("foo1", []string{"simple"}), 513 txtmulti("foo2", []string{"one", "two"}), 514 ), 515 tc("Create TXTMulti 3", 516 txtmulti("foo1", []string{"simple"}), 517 txtmulti("foo2", []string{"one", "two"}), 518 txtmulti("foo3", []string{"eh", "bee", "cee"}), 519 ), 520 tc("Create TXTMulti with quotes", 521 txtmulti("foo1", []string{"simple"}), 522 txtmulti("foo2", []string{"o\"ne", "tw\"o"}), 523 txtmulti("foo3", []string{"eh", "bee", "cee"}), 524 ), 525 tc("Change TXTMulti", 526 txtmulti("foo1", []string{"dimple"}), 527 txtmulti("foo2", []string{"fun", "two"}), 528 txtmulti("foo3", []string{"eh", "bzz", "cee"}), 529 ), 530 tc("Empty"), 531 tc("3x255-byte TXTMulti", 532 txtmulti("foo3", []string{"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY", "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ"}), 533 ), 534 tc("Empty"), 535 ) 536 } 537 538 // ignored records 539 tests = append(tests, 540 tc("Empty"), 541 tc("Create some records", txt("foo", "simple"), a("foo", "1.2.3.4")), 542 tc("Add a new record - ignoring foo", a("bar", "1.2.3.4"), ignore("foo")), 543 ) 544 545 // R53_ALIAS 546 if !providers.ProviderHasCabability(*providerToRun, providers.CanUseRoute53Alias) { 547 t.Log("Skipping Route53 ALIAS Tests because provider does not support them") 548 } else { 549 tests = append(tests, tc("Empty"), 550 tc("create dependent records", a("foo", "1.2.3.4"), a("quux", "2.3.4.5")), 551 tc("ALIAS to A record in same zone", a("foo", "1.2.3.4"), a("quux", "2.3.4.5"), r53alias("bar", "A", "foo.**current-domain**")), 552 tc("change it", a("foo", "1.2.3.4"), a("quux", "2.3.4.5"), r53alias("bar", "A", "quux.**current-domain**")), 553 ) 554 } 555 556 // Empty last 557 tc("Empty") 558 return tests 559 }