sigs.k8s.io/external-dns@v0.14.1/provider/civo/civo_test.go (about) 1 /* 2 Copyright 2020 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 package civo 17 18 import ( 19 "context" 20 "fmt" 21 "os" 22 "reflect" 23 "strings" 24 "testing" 25 26 "github.com/civo/civogo" 27 "github.com/google/go-cmp/cmp" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 "sigs.k8s.io/external-dns/endpoint" 31 "sigs.k8s.io/external-dns/plan" 32 ) 33 34 func TestNewCivoProvider(t *testing.T) { 35 _ = os.Setenv("CIVO_TOKEN", "xxxxxxxxxxxxxxx") 36 _, err := NewCivoProvider(endpoint.NewDomainFilter([]string{"test.civo.com"}), true) 37 require.NoError(t, err) 38 39 _ = os.Unsetenv("CIVO_TOKEN") 40 } 41 42 func TestNewCivoProviderNoToken(t *testing.T) { 43 _, err := NewCivoProvider(endpoint.NewDomainFilter([]string{"test.civo.com"}), true) 44 assert.Error(t, err) 45 46 assert.Equal(t, "no token found", err.Error()) 47 } 48 49 func TestCivoProviderZones(t *testing.T) { 50 client, server, _ := civogo.NewClientForTesting(map[string]string{ 51 "/v2/dns": `[ 52 {"id": "12345", "account_id": "1", "name": "example.com"}, 53 {"id": "12346", "account_id": "1", "name": "example.net"} 54 ]`, 55 }) 56 defer server.Close() 57 provider := &CivoProvider{ 58 Client: *client, 59 } 60 61 expected, err := client.ListDNSDomains() 62 assert.NoError(t, err) 63 64 zones, err := provider.Zones(context.Background()) 65 assert.NoError(t, err) 66 67 // Check if the return is a DNSDomain type 68 assert.Equal(t, reflect.TypeOf(zones), reflect.TypeOf(expected)) 69 assert.ElementsMatch(t, zones, expected) 70 } 71 72 func TestCivoProviderZonesWithError(t *testing.T) { 73 client, server, _ := civogo.NewClientForTesting(map[string]string{ 74 "/v2/dns-error": `[]`, 75 }) 76 defer server.Close() 77 provider := &CivoProvider{ 78 Client: *client, 79 } 80 81 _, err := provider.Zones(context.Background()) 82 assert.Error(t, err) 83 } 84 85 func TestCivoProviderRecords(t *testing.T) { 86 client, server, _ := civogo.NewAdvancedClientForTesting([]civogo.ConfigAdvanceClientForTesting{ 87 { 88 Method: "GET", 89 Value: []civogo.ValueAdvanceClientForTesting{ 90 { 91 RequestBody: ``, 92 URL: "/v2/dns/12345/records", 93 ResponseBody: `[ 94 {"id": "1", "domain_id":"12345", "account_id": "1", "name": "www", "type": "A", "value": "10.0.0.0", "ttl": 600}, 95 {"id": "2", "account_id": "1", "domain_id":"12345", "name": "mail", "type": "A", "value": "10.0.0.1", "ttl": 600} 96 ]`, 97 }, 98 { 99 RequestBody: ``, 100 URL: "/v2/dns", 101 ResponseBody: `[ 102 {"id": "12345", "account_id": "1", "name": "example.com"}, 103 {"id": "12346", "account_id": "1", "name": "example.net"} 104 ]`, 105 }, 106 }, 107 }, 108 }) 109 110 defer server.Close() 111 provider := &CivoProvider{ 112 Client: *client, 113 domainFilter: endpoint.NewDomainFilter([]string{"example.com"}), 114 } 115 116 expected, err := client.ListDNSRecords("12345") 117 assert.NoError(t, err) 118 119 records, err := provider.Records(context.Background()) 120 assert.NoError(t, err) 121 122 assert.Equal(t, strings.TrimSuffix(records[0].DNSName, ".example.com"), expected[0].Name) 123 assert.Equal(t, records[0].RecordType, string(expected[0].Type)) 124 assert.Equal(t, int(records[0].RecordTTL), expected[0].TTL) 125 126 assert.Equal(t, strings.TrimSuffix(records[1].DNSName, ".example.com"), expected[1].Name) 127 assert.Equal(t, records[1].RecordType, string(expected[1].Type)) 128 assert.Equal(t, int(records[1].RecordTTL), expected[1].TTL) 129 } 130 131 func TestCivoProviderWithoutRecords(t *testing.T) { 132 client, server, _ := civogo.NewClientForTesting(map[string]string{ 133 "/v2/dns/12345/records": `[]`, 134 "/v2/dns": `[ 135 {"id": "12345", "account_id": "1", "name": "example.com"}, 136 {"id": "12346", "account_id": "1", "name": "example.net"} 137 ]`, 138 }) 139 defer server.Close() 140 provider := &CivoProvider{ 141 Client: *client, 142 domainFilter: endpoint.NewDomainFilter([]string{"example.com"}), 143 } 144 145 records, err := provider.Records(context.Background()) 146 assert.NoError(t, err) 147 148 assert.Equal(t, len(records), 0) 149 } 150 151 func TestCivoProcessCreateActions(t *testing.T) { 152 zoneByID := map[string]civogo.DNSDomain{ 153 "example.com": { 154 ID: "1", 155 AccountID: "1", 156 Name: "example.com", 157 }, 158 } 159 160 recordsByZoneID := map[string][]civogo.DNSRecord{ 161 "example.com": { 162 { 163 ID: "1", 164 AccountID: "1", 165 DNSDomainID: "1", 166 Name: "txt", 167 Value: "12.12.12.1", 168 Type: "A", 169 TTL: 600, 170 }, 171 }, 172 } 173 174 createsByZone := map[string][]*endpoint.Endpoint{ 175 "example.com": { 176 endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "1.2.3.4"), 177 endpoint.NewEndpoint("txt.example.com", endpoint.RecordTypeCNAME, "foo.example.com"), 178 }, 179 } 180 181 var changes CivoChanges 182 err := processCreateActions(zoneByID, recordsByZoneID, createsByZone, &changes) 183 require.NoError(t, err) 184 185 assert.Equal(t, 2, len(changes.Creates)) 186 assert.Equal(t, 0, len(changes.Updates)) 187 assert.Equal(t, 0, len(changes.Deletes)) 188 189 expectedCreates := []*CivoChangeCreate{ 190 { 191 Domain: civogo.DNSDomain{ 192 ID: "1", 193 AccountID: "1", 194 Name: "example.com", 195 }, 196 Options: &civogo.DNSRecordConfig{ 197 Type: "A", 198 Name: "foo", 199 Value: "1.2.3.4", 200 }, 201 }, 202 { 203 Domain: civogo.DNSDomain{ 204 ID: "1", 205 AccountID: "1", 206 Name: "example.com", 207 }, 208 Options: &civogo.DNSRecordConfig{ 209 Type: "CNAME", 210 Name: "txt", 211 Value: "foo.example.com", 212 }, 213 }, 214 } 215 216 if !elementsMatch(t, expectedCreates, changes.Creates) { 217 assert.Failf(t, "diff: %s", cmp.Diff(expectedCreates, changes.Creates)) 218 } 219 } 220 221 func TestCivoProcessCreateActionsWithError(t *testing.T) { 222 zoneByID := map[string]civogo.DNSDomain{ 223 "example.com": { 224 ID: "1", 225 AccountID: "1", 226 Name: "example.com", 227 }, 228 } 229 230 recordsByZoneID := map[string][]civogo.DNSRecord{ 231 "example.com": { 232 { 233 ID: "1", 234 AccountID: "1", 235 DNSDomainID: "1", 236 Name: "txt", 237 Value: "12.12.12.1", 238 Type: "A", 239 TTL: 600, 240 }, 241 }, 242 } 243 244 createsByZone := map[string][]*endpoint.Endpoint{ 245 "example.com": { 246 endpoint.NewEndpoint("foo.example.com", "AAAA", "1.2.3.4"), 247 endpoint.NewEndpoint("txt.example.com", endpoint.RecordTypeCNAME, "foo.example.com"), 248 }, 249 } 250 251 var changes CivoChanges 252 err := processCreateActions(zoneByID, recordsByZoneID, createsByZone, &changes) 253 require.Error(t, err) 254 assert.Equal(t, "invalid Record Type: AAAA", err.Error()) 255 } 256 257 func TestCivoProcessUpdateActions(t *testing.T) { 258 zoneByID := map[string]civogo.DNSDomain{ 259 "example.com": { 260 ID: "1", 261 AccountID: "1", 262 Name: "example.com", 263 }, 264 } 265 266 recordsByZoneID := map[string][]civogo.DNSRecord{ 267 "example.com": { 268 { 269 ID: "1", 270 AccountID: "1", 271 DNSDomainID: "1", 272 Name: "txt", 273 Value: "1.2.3.4", 274 Type: "A", 275 TTL: 600, 276 }, 277 { 278 ID: "2", 279 AccountID: "1", 280 DNSDomainID: "1", 281 Name: "foo", 282 Value: "foo.example.com", 283 Type: "CNAME", 284 TTL: 600, 285 }, 286 { 287 ID: "3", 288 AccountID: "1", 289 DNSDomainID: "1", 290 Name: "bar", 291 Value: "10.10.10.1", 292 Type: "A", 293 TTL: 600, 294 }, 295 }, 296 } 297 298 updatesByZone := map[string][]*endpoint.Endpoint{ 299 "example.com": { 300 endpoint.NewEndpoint("txt.example.com", endpoint.RecordTypeA, "10.20.30.40"), 301 endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeCNAME, "bar.example.com"), 302 }, 303 } 304 305 var changes CivoChanges 306 err := processUpdateActions(zoneByID, recordsByZoneID, updatesByZone, &changes) 307 require.NoError(t, err) 308 309 assert.Equal(t, 2, len(changes.Creates)) 310 assert.Equal(t, 0, len(changes.Updates)) 311 assert.Equal(t, 2, len(changes.Deletes)) 312 313 expectedUpdate := []*CivoChangeCreate{ 314 { 315 Domain: civogo.DNSDomain{ 316 ID: "1", 317 AccountID: "1", 318 Name: "example.com", 319 }, 320 Options: &civogo.DNSRecordConfig{ 321 Type: "A", 322 Name: "txt", 323 Value: "10.20.30.40", 324 }, 325 }, 326 { 327 Domain: civogo.DNSDomain{ 328 ID: "1", 329 AccountID: "1", 330 Name: "example.com", 331 }, 332 Options: &civogo.DNSRecordConfig{ 333 Type: "CNAME", 334 Name: "foo", 335 Value: "bar.example.com", 336 }, 337 }, 338 } 339 340 if !elementsMatch(t, expectedUpdate, changes.Creates) { 341 assert.Failf(t, "diff: %s", cmp.Diff(expectedUpdate, changes.Creates)) 342 } 343 344 expectedDelete := []*CivoChangeDelete{ 345 { 346 Domain: civogo.DNSDomain{ 347 ID: "1", 348 AccountID: "1", 349 Name: "example.com", 350 }, 351 DomainRecord: civogo.DNSRecord{ 352 ID: "1", 353 AccountID: "1", 354 DNSDomainID: "1", 355 Name: "txt", 356 Value: "1.2.3.4", 357 Type: "A", 358 Priority: 0, 359 TTL: 600, 360 }, 361 }, 362 { 363 Domain: civogo.DNSDomain{ 364 ID: "1", 365 AccountID: "1", 366 Name: "example.com", 367 }, 368 DomainRecord: civogo.DNSRecord{ 369 ID: "2", 370 AccountID: "1", 371 DNSDomainID: "1", 372 Name: "foo", 373 Value: "foo.example.com", 374 Type: "CNAME", 375 Priority: 0, 376 TTL: 600, 377 }, 378 }, 379 } 380 381 if !elementsMatch(t, expectedDelete, changes.Deletes) { 382 assert.Failf(t, "diff: %s", cmp.Diff(expectedDelete, changes.Deletes)) 383 } 384 } 385 386 func TestCivoProcessDeleteAction(t *testing.T) { 387 zoneByID := map[string]civogo.DNSDomain{ 388 "example.com": { 389 ID: "1", 390 AccountID: "1", 391 Name: "example.com", 392 }, 393 } 394 395 recordsByZoneID := map[string][]civogo.DNSRecord{ 396 "example.com": { 397 { 398 ID: "1", 399 AccountID: "1", 400 DNSDomainID: "1", 401 Name: "txt", 402 Value: "1.2.3.4", 403 Type: "A", 404 TTL: 600, 405 }, 406 { 407 ID: "2", 408 AccountID: "1", 409 DNSDomainID: "1", 410 Name: "foo", 411 Value: "5.6.7.8", 412 Type: "A", 413 TTL: 600, 414 }, 415 { 416 ID: "3", 417 AccountID: "1", 418 DNSDomainID: "1", 419 Name: "bar", 420 Value: "10.10.10.1", 421 Type: "A", 422 TTL: 600, 423 }, 424 }, 425 } 426 427 deleteByDomain := map[string][]*endpoint.Endpoint{ 428 "example.com": { 429 endpoint.NewEndpoint("txt.example.com", endpoint.RecordTypeA, "1.2.3.4"), 430 endpoint.NewEndpoint("foo.example.com", endpoint.RecordTypeA, "5.6.7.8"), 431 }, 432 } 433 434 var changes CivoChanges 435 err := processDeleteActions(zoneByID, recordsByZoneID, deleteByDomain, &changes) 436 require.NoError(t, err) 437 438 assert.Equal(t, 0, len(changes.Creates)) 439 assert.Equal(t, 0, len(changes.Updates)) 440 assert.Equal(t, 2, len(changes.Deletes)) 441 442 expectedDelete := []*CivoChangeDelete{ 443 { 444 Domain: civogo.DNSDomain{ 445 ID: "1", 446 AccountID: "1", 447 Name: "example.com", 448 }, 449 DomainRecord: civogo.DNSRecord{ 450 ID: "1", 451 AccountID: "1", 452 DNSDomainID: "1", 453 Name: "txt", 454 Value: "1.2.3.4", 455 Type: "A", 456 TTL: 600, 457 }, 458 }, 459 { 460 Domain: civogo.DNSDomain{ 461 ID: "1", 462 AccountID: "1", 463 Name: "example.com", 464 }, 465 DomainRecord: civogo.DNSRecord{ 466 ID: "2", 467 AccountID: "1", 468 DNSDomainID: "1", 469 Type: "A", 470 Name: "foo", 471 Value: "5.6.7.8", 472 TTL: 600, 473 }, 474 }, 475 } 476 477 if !elementsMatch(t, expectedDelete, changes.Deletes) { 478 assert.Failf(t, "diff: %s", cmp.Diff(expectedDelete, changes.Deletes)) 479 } 480 } 481 482 func TestCivoApplyChanges(t *testing.T) { 483 client, server, _ := civogo.NewAdvancedClientForTesting([]civogo.ConfigAdvanceClientForTesting{ 484 { 485 Method: "GET", 486 Value: []civogo.ValueAdvanceClientForTesting{ 487 { 488 RequestBody: "", 489 URL: "/v2/dns", 490 ResponseBody: `[{"id": "12345", "account_id": "1", "name": "example.com"}]`, 491 }, 492 { 493 RequestBody: "", 494 URL: "/v2/dns/12345/records", 495 ResponseBody: `[]`, 496 }, 497 }, 498 }, 499 }) 500 defer server.Close() 501 502 changes := &plan.Changes{} 503 provider := &CivoProvider{ 504 Client: *client, 505 } 506 changes.Create = []*endpoint.Endpoint{ 507 {DNSName: "new.ext-dns-test.example.com", Targets: endpoint.Targets{"target"}, RecordType: endpoint.RecordTypeA}, 508 {DNSName: "new.ext-dns-test-with-ttl.example.com", Targets: endpoint.Targets{"target"}, RecordType: endpoint.RecordTypeA, RecordTTL: 100}, 509 } 510 changes.Delete = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.example.com", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"target"}}} 511 changes.UpdateOld = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.example.de", RecordType: endpoint.RecordTypeA, Targets: endpoint.Targets{"target-old"}}} 512 changes.UpdateNew = []*endpoint.Endpoint{{DNSName: "foobar.ext-dns-test.foo.com", Targets: endpoint.Targets{"target-new"}, RecordType: endpoint.RecordTypeCNAME, RecordTTL: 100}} 513 err := provider.ApplyChanges(context.Background(), changes) 514 assert.NoError(t, err) 515 } 516 517 func TestCivoProviderFetchZones(t *testing.T) { 518 client, server, _ := civogo.NewClientForTesting(map[string]string{ 519 "/v2/dns": `[ 520 {"id": "12345", "account_id": "1", "name": "example.com"}, 521 {"id": "12346", "account_id": "1", "name": "example.net"} 522 ]`, 523 }) 524 defer server.Close() 525 provider := &CivoProvider{ 526 Client: *client, 527 } 528 529 expected, err := client.ListDNSDomains() 530 if err != nil { 531 t.Errorf("should not fail, %s", err) 532 } 533 zones, err := provider.fetchZones(context.Background()) 534 if err != nil { 535 t.Fatal(err) 536 } 537 assert.ElementsMatch(t, zones, expected) 538 } 539 func TestCivoProviderFetchZonesWithFilter(t *testing.T) { 540 client, server, _ := civogo.NewClientForTesting(map[string]string{ 541 "/v2/dns": `[ 542 {"id": "12345", "account_id": "1", "name": "example.com"}, 543 {"id": "12346", "account_id": "1", "name": "example.net"} 544 ]`, 545 }) 546 defer server.Close() 547 provider := &CivoProvider{ 548 Client: *client, 549 domainFilter: endpoint.NewDomainFilter([]string{".com"}), 550 } 551 552 expected := []civogo.DNSDomain{ 553 {ID: "12345", Name: "example.com", AccountID: "1"}, 554 } 555 556 actual, err := provider.fetchZones(context.Background()) 557 if err != nil { 558 t.Fatal(err) 559 } 560 assert.ElementsMatch(t, expected, actual) 561 } 562 563 func TestCivoProviderFetchRecords(t *testing.T) { 564 client, server, _ := civogo.NewClientForTesting(map[string]string{ 565 "/v2/dns/12345/records": `[ 566 {"id": "1", "domain_id":"12345", "account_id": "1", "name": "www", "type": "A", "value": "10.0.0.0", "ttl": 600}, 567 {"id": "2", "account_id": "1", "domain_id":"12345", "name": "mail", "type": "A", "value": "10.0.0.1", "ttl": 600} 568 ]`, 569 }) 570 defer server.Close() 571 provider := &CivoProvider{ 572 Client: *client, 573 } 574 575 expected, err := client.ListDNSRecords("12345") 576 assert.NoError(t, err) 577 578 actual, err := provider.fetchRecords(context.Background(), "12345") 579 assert.NoError(t, err) 580 581 assert.ElementsMatch(t, expected, actual) 582 } 583 584 func TestCivoProviderFetchRecordsWithError(t *testing.T) { 585 client, server, _ := civogo.NewClientForTesting(map[string]string{ 586 "/v2/dns/12345/records": `[ 587 {"id": "1", "domain_id":"12345", "account_id": "1", "name": "www", "type": "A", "value": "10.0.0.0", "ttl": 600}, 588 {"id": "2", "account_id": "1", "domain_id":"12345", "name": "mail", "type": "A", "value": "10.0.0.1", "ttl": 600} 589 ]`, 590 }) 591 defer server.Close() 592 provider := &CivoProvider{ 593 Client: *client, 594 } 595 596 _, err := provider.fetchRecords(context.Background(), "235698") 597 assert.Error(t, err) 598 } 599 600 func TestCivo_getStrippedRecordName(t *testing.T) { 601 assert.Equal(t, "", getStrippedRecordName(civogo.DNSDomain{ 602 Name: "foo.com", 603 }, endpoint.Endpoint{ 604 DNSName: "foo.com", 605 })) 606 607 assert.Equal(t, "api", getStrippedRecordName(civogo.DNSDomain{ 608 Name: "foo.com", 609 }, endpoint.Endpoint{ 610 DNSName: "api.foo.com", 611 })) 612 } 613 614 func TestCivo_convertRecordType(t *testing.T) { 615 record, err := convertRecordType("A") 616 recordA := civogo.DNSRecordType(civogo.DNSRecordTypeA) 617 require.NoError(t, err) 618 assert.Equal(t, recordA, record) 619 620 record, err = convertRecordType("CNAME") 621 recordCName := civogo.DNSRecordType(civogo.DNSRecordTypeCName) 622 require.NoError(t, err) 623 assert.Equal(t, recordCName, record) 624 625 record, err = convertRecordType("TXT") 626 recordTXT := civogo.DNSRecordType(civogo.DNSRecordTypeTXT) 627 require.NoError(t, err) 628 assert.Equal(t, recordTXT, record) 629 630 record, err = convertRecordType("SRV") 631 recordSRV := civogo.DNSRecordType(civogo.DNSRecordTypeSRV) 632 require.NoError(t, err) 633 assert.Equal(t, recordSRV, record) 634 635 _, err = convertRecordType("INVALID") 636 require.Error(t, err) 637 638 assert.Equal(t, "invalid Record Type: INVALID", err.Error()) 639 } 640 641 func TestCivoProviderGetRecordID(t *testing.T) { 642 zone := civogo.DNSDomain{ 643 ID: "12345", 644 Name: "test.com", 645 } 646 647 record := []civogo.DNSRecord{{ 648 ID: "1", 649 Type: "A", 650 Name: "www", 651 Value: "10.0.0.0", 652 DNSDomainID: "12345", 653 TTL: 600, 654 }, { 655 ID: "2", 656 Type: "A", 657 Name: "api", 658 Value: "10.0.0.1", 659 DNSDomainID: "12345", 660 TTL: 600, 661 }} 662 663 endPoint := endpoint.Endpoint{DNSName: "www.test.com", Targets: endpoint.Targets{"10.0.0.0"}, RecordType: "A"} 664 id := getRecordID(record, zone, endPoint) 665 666 assert.Equal(t, id[0].ID, record[0].ID) 667 } 668 669 func TestCivo_submitChangesCreate(t *testing.T) { 670 client, server, _ := civogo.NewAdvancedClientForTesting([]civogo.ConfigAdvanceClientForTesting{ 671 { 672 Method: "POST", 673 Value: []civogo.ValueAdvanceClientForTesting{ 674 { 675 RequestBody: `{"type":"MX","name":"mail","value":"10.0.0.1","priority":10,"ttl":600}`, 676 URL: "/v2/dns/12345/records", 677 ResponseBody: `{ 678 "id": "76cc107f-fbef-4e2b-b97f-f5d34f4075d3", 679 "account_id": "1", 680 "domain_id": "12345", 681 "name": "mail", 682 "value": "10.0.0.1", 683 "type": "MX", 684 "priority": 10, 685 "ttl": 600 686 }`, 687 }, 688 }, 689 }, 690 }) 691 defer server.Close() 692 693 provider := &CivoProvider{ 694 Client: *client, 695 DryRun: false, 696 } 697 698 changes := CivoChanges{ 699 Creates: []*CivoChangeCreate{ 700 { 701 Domain: civogo.DNSDomain{ 702 ID: "12345", 703 AccountID: "1", 704 Name: "example.com", 705 }, 706 Options: &civogo.DNSRecordConfig{ 707 Type: "MX", 708 Name: "mail", 709 Value: "10.0.0.1", 710 Priority: 10, 711 TTL: 600, 712 }, 713 }, 714 }, 715 } 716 717 err := provider.submitChanges(context.Background(), changes) 718 assert.NoError(t, err) 719 } 720 721 func TestCivo_submitChangesUpdate(t *testing.T) { 722 client, server, _ := civogo.NewAdvancedClientForTesting([]civogo.ConfigAdvanceClientForTesting{ 723 { 724 Method: "PUT", 725 Value: []civogo.ValueAdvanceClientForTesting{ 726 { 727 RequestBody: `{"type":"MX","name":"mail","value":"10.0.0.2","priority":10,"ttl":600}`, 728 URL: "/v2/dns/12345/records/76cc107f-fbef-4e2b-b97f-f5d34f4075d3", 729 ResponseBody: `{ 730 "id": "76cc107f-fbef-4e2b-b97f-f5d34f4075d3", 731 "account_id": "1", 732 "domain_id": "12345", 733 "name": "mail", 734 "value": "10.0.0.2", 735 "type": "MX", 736 "priority": 10, 737 "ttl": 600 738 }`, 739 }, 740 }, 741 }, 742 }) 743 defer server.Close() 744 745 provider := &CivoProvider{ 746 Client: *client, 747 DryRun: false, 748 } 749 750 changes := CivoChanges{ 751 Updates: []*CivoChangeUpdate{ 752 { 753 Domain: civogo.DNSDomain{ID: "12345", AccountID: "1", Name: "example.com"}, 754 DomainRecord: civogo.DNSRecord{ 755 ID: "76cc107f-fbef-4e2b-b97f-f5d34f4075d3", 756 AccountID: "1", 757 DNSDomainID: "12345", 758 Name: "mail", 759 Value: "10.0.0.1", 760 Type: "MX", 761 Priority: 10, 762 TTL: 600, 763 }, 764 Options: civogo.DNSRecordConfig{ 765 Type: "MX", 766 Name: "mail", 767 Value: "10.0.0.2", 768 Priority: 10, 769 TTL: 600, 770 }, 771 }, 772 }, 773 } 774 775 err := provider.submitChanges(context.Background(), changes) 776 assert.NoError(t, err) 777 } 778 779 func TestCivo_submitChangesDelete(t *testing.T) { 780 client, server, _ := civogo.NewClientForTesting(map[string]string{ 781 "/v2/dns/12345/records/76cc107f-fbef-4e2b-b97f-f5d34f4075d3": `{"result": "success"}`, 782 }) 783 defer server.Close() 784 785 provider := &CivoProvider{ 786 Client: *client, 787 DryRun: false, 788 } 789 790 changes := CivoChanges{ 791 Deletes: []*CivoChangeDelete{ 792 { 793 Domain: civogo.DNSDomain{ID: "12345", AccountID: "1", Name: "example.com"}, 794 DomainRecord: civogo.DNSRecord{ 795 ID: "76cc107f-fbef-4e2b-b97f-f5d34f4075d3", 796 AccountID: "1", 797 DNSDomainID: "12345", 798 Name: "mail", 799 Value: "10.0.0.2", 800 Type: "MX", 801 Priority: 10, 802 TTL: 600, 803 }, 804 }, 805 }, 806 } 807 808 err := provider.submitChanges(context.Background(), changes) 809 assert.NoError(t, err) 810 } 811 812 func TestCivoChangesEmpty(t *testing.T) { 813 // Test empty CivoChanges 814 c := &CivoChanges{} 815 assert.True(t, c.Empty()) 816 817 // Test CivoChanges with Creates 818 c = &CivoChanges{ 819 Creates: []*CivoChangeCreate{ 820 { 821 Domain: civogo.DNSDomain{ 822 ID: "12345", 823 AccountID: "1", 824 Name: "example.com", 825 }, 826 Options: &civogo.DNSRecordConfig{ 827 Type: civogo.DNSRecordTypeA, 828 Name: "www", 829 Value: "192.1.1.1", 830 Priority: 0, 831 TTL: 600, 832 }, 833 }, 834 }, 835 } 836 assert.False(t, c.Empty()) 837 838 // Test CivoChanges with Updates 839 c = &CivoChanges{ 840 Updates: []*CivoChangeUpdate{ 841 { 842 Domain: civogo.DNSDomain{ 843 ID: "12345", 844 AccountID: "1", 845 Name: "example.com", 846 }, 847 DomainRecord: civogo.DNSRecord{ 848 ID: "76cc107f-fbef-4e2b-b97f-f5d34f4075d3", 849 AccountID: "1", 850 DNSDomainID: "12345", 851 Name: "mail", 852 Value: "192.168.1.2", 853 Type: "MX", 854 Priority: 10, 855 TTL: 600, 856 }, 857 Options: civogo.DNSRecordConfig{ 858 Type: "MX", 859 Name: "mail", 860 Value: "192.168.1.3", 861 Priority: 10, 862 TTL: 600, 863 }, 864 }, 865 }, 866 } 867 assert.False(t, c.Empty()) 868 869 // Test CivoChanges with Deletes 870 c = &CivoChanges{ 871 Deletes: []*CivoChangeDelete{ 872 { 873 Domain: civogo.DNSDomain{ 874 ID: "12345", 875 AccountID: "1", 876 Name: "example.com", 877 }, 878 DomainRecord: civogo.DNSRecord{ 879 ID: "76cc107f-fbef-4e2b-b97f-f5d34f4075d3", 880 AccountID: "1", 881 DNSDomainID: "12345", 882 Name: "mail", 883 Value: "192.168.1.3", 884 Type: "MX", 885 Priority: 10, 886 TTL: 600, 887 }, 888 }, 889 }, 890 } 891 assert.False(t, c.Empty()) 892 893 // Test CivoChanges with Creates, Updates, and Deletes 894 c = &CivoChanges{ 895 Creates: []*CivoChangeCreate{ 896 { 897 Domain: civogo.DNSDomain{ 898 ID: "12345", 899 AccountID: "1", 900 Name: "example.com", 901 }, 902 Options: &civogo.DNSRecordConfig{ 903 Type: civogo.DNSRecordTypeA, 904 Name: "www", 905 Value: "192.1.1.1", 906 Priority: 0, 907 TTL: 600, 908 }, 909 }, 910 }, 911 Updates: []*CivoChangeUpdate{ 912 { 913 Domain: civogo.DNSDomain{ 914 ID: "12345", 915 AccountID: "1", 916 Name: "example.com", 917 }, 918 DomainRecord: civogo.DNSRecord{ 919 ID: "76cc107f-fbef-4e2b-b97f-f5d34f4075d3", 920 AccountID: "1", 921 DNSDomainID: "12345", 922 Name: "mail", 923 Value: "192.168.1.2", 924 Type: "MX", 925 Priority: 10, 926 TTL: 600, 927 }, 928 Options: civogo.DNSRecordConfig{ 929 Type: "MX", 930 Name: "mail", 931 Value: "192.168.1.3", 932 Priority: 10, 933 TTL: 600, 934 }, 935 }, 936 }, 937 Deletes: []*CivoChangeDelete{ 938 { 939 Domain: civogo.DNSDomain{ 940 ID: "12345", 941 AccountID: "1", 942 Name: "example.com", 943 }, 944 DomainRecord: civogo.DNSRecord{ 945 ID: "76cc107f-fbef-4e2b-b97f-f5d34f4075d3", 946 AccountID: "1", 947 DNSDomainID: "12345", 948 Name: "mail", 949 Value: "192.168.1.3", 950 Type: "MX", 951 Priority: 10, 952 TTL: 600, 953 }, 954 }, 955 }, 956 } 957 assert.False(t, c.Empty()) 958 } 959 960 // This function is an adapted copy of the testify package's ElementsMatch function with the 961 // call to ObjectsAreEqual replaced with cmp.Equal which better handles struct's with pointers to 962 // other structs. It also ignores ordering when comparing unlike cmp.Equal. 963 func elementsMatch(t *testing.T, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) { 964 if listA == nil && listB == nil { 965 return true 966 } else if listA == nil { 967 return isEmpty(listB) 968 } else if listB == nil { 969 return isEmpty(listA) 970 } 971 972 aKind := reflect.TypeOf(listA).Kind() 973 bKind := reflect.TypeOf(listB).Kind() 974 975 if aKind != reflect.Array && aKind != reflect.Slice { 976 return assert.Fail(t, fmt.Sprintf("%q has an unsupported type %s", listA, aKind), msgAndArgs...) 977 } 978 979 if bKind != reflect.Array && bKind != reflect.Slice { 980 return assert.Fail(t, fmt.Sprintf("%q has an unsupported type %s", listB, bKind), msgAndArgs...) 981 } 982 983 aValue := reflect.ValueOf(listA) 984 bValue := reflect.ValueOf(listB) 985 986 aLen := aValue.Len() 987 bLen := bValue.Len() 988 989 if aLen != bLen { 990 return assert.Fail(t, fmt.Sprintf("lengths don't match: %d != %d", aLen, bLen), msgAndArgs...) 991 } 992 993 // Mark indexes in bValue that we already used 994 visited := make([]bool, bLen) 995 for i := 0; i < aLen; i++ { 996 element := aValue.Index(i).Interface() 997 found := false 998 for j := 0; j < bLen; j++ { 999 if visited[j] { 1000 continue 1001 } 1002 if cmp.Equal(bValue.Index(j).Interface(), element) { 1003 visited[j] = true 1004 found = true 1005 break 1006 } 1007 } 1008 if !found { 1009 return assert.Fail(t, fmt.Sprintf("element %s appears more times in %s than in %s", element, aValue, bValue), msgAndArgs...) 1010 } 1011 } 1012 1013 return true 1014 } 1015 1016 func isEmpty(xs interface{}) bool { 1017 if xs != nil { 1018 objValue := reflect.ValueOf(xs) 1019 return objValue.Len() == 0 1020 } 1021 return true 1022 }