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  }