github.com/akamai/AkamaiOPEN-edgegrid-golang/v8@v8.1.0/pkg/gtm/property_test.go (about)

     1  package gtm
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"errors"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"testing"
    11  
    12  	"github.com/akamai/AkamaiOPEN-edgegrid-golang/v8/pkg/session"
    13  	"github.com/akamai/AkamaiOPEN-edgegrid-golang/v8/pkg/tools"
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func TestGTM_ListProperties(t *testing.T) {
    19  	var result PropertyList
    20  
    21  	respData, err := loadTestData("TestGTM_ListProperties.resp.json")
    22  	if err != nil {
    23  		t.Fatal(err)
    24  	}
    25  
    26  	if err := json.NewDecoder(bytes.NewBuffer(respData)).Decode(&result); err != nil {
    27  		t.Fatal(err)
    28  	}
    29  
    30  	tests := map[string]struct {
    31  		domain           string
    32  		responseStatus   int
    33  		responseBody     string
    34  		expectedPath     string
    35  		expectedResponse []*Property
    36  		withError        error
    37  		headers          http.Header
    38  	}{
    39  		"200 OK": {
    40  			domain: "example.akadns.net",
    41  			headers: http.Header{
    42  				"Content-Type": []string{"application/vnd.config-gtm.v1.4+json;charset=UTF-8"},
    43  			},
    44  			responseStatus:   http.StatusOK,
    45  			responseBody:     string(respData),
    46  			expectedPath:     "/config-gtm/v1/domains/example.akadns.net/properties",
    47  			expectedResponse: result.PropertyItems,
    48  		},
    49  		"500 internal server error": {
    50  			domain:         "example.akadns.net",
    51  			headers:        http.Header{},
    52  			responseStatus: http.StatusInternalServerError,
    53  			responseBody: `
    54  {
    55      "type": "internal_error",
    56      "title": "Internal Server Error",
    57      "detail": "Error fetching propertys",
    58      "status": 500
    59  }`,
    60  			expectedPath: "/config-gtm/v1/domains/example.akadns.net/properties",
    61  			withError: &Error{
    62  				Type:       "internal_error",
    63  				Title:      "Internal Server Error",
    64  				Detail:     "Error fetching propertys",
    65  				StatusCode: http.StatusInternalServerError,
    66  			},
    67  		},
    68  	}
    69  
    70  	for name, test := range tests {
    71  		t.Run(name, func(t *testing.T) {
    72  			mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    73  				assert.Equal(t, test.expectedPath, r.URL.String())
    74  				assert.Equal(t, http.MethodGet, r.Method)
    75  				w.WriteHeader(test.responseStatus)
    76  				_, err := w.Write([]byte(test.responseBody))
    77  				assert.NoError(t, err)
    78  			}))
    79  			client := mockAPIClient(t, mockServer)
    80  			result, err := client.ListProperties(
    81  				session.ContextWithOptions(
    82  					context.Background(),
    83  					session.WithContextHeaders(test.headers)), test.domain)
    84  			if test.withError != nil {
    85  				assert.True(t, errors.Is(err, test.withError), "want: %s; got: %s", test.withError, err)
    86  				return
    87  			}
    88  			require.NoError(t, err)
    89  			assert.Equal(t, test.expectedResponse, result)
    90  		})
    91  	}
    92  }
    93  
    94  func TestGTM_GetProperty(t *testing.T) {
    95  	var result Property
    96  
    97  	respData, err := loadTestData("TestGTM_GetProperty.resp.json")
    98  	if err != nil {
    99  		t.Fatal(err)
   100  	}
   101  
   102  	if err := json.NewDecoder(bytes.NewBuffer(respData)).Decode(&result); err != nil {
   103  		t.Fatal(err)
   104  	}
   105  
   106  	tests := map[string]struct {
   107  		name             string
   108  		domain           string
   109  		responseStatus   int
   110  		responseBody     []byte
   111  		expectedPath     string
   112  		expectedResponse *Property
   113  		withError        error
   114  	}{
   115  		"200 OK": {
   116  			name:             "www",
   117  			domain:           "example.akadns.net",
   118  			responseStatus:   http.StatusOK,
   119  			responseBody:     respData,
   120  			expectedPath:     "/config-gtm/v1/domains/example.akadns.net/properties/www",
   121  			expectedResponse: &result,
   122  		},
   123  		"500 internal server error": {
   124  			name:           "www",
   125  			domain:         "example.akadns.net",
   126  			responseStatus: http.StatusInternalServerError,
   127  			responseBody: []byte(`
   128  {
   129      "type": "internal_error",
   130      "title": "Internal Server Error",
   131      "detail": "Error fetching property"
   132  }`),
   133  			expectedPath: "/config-gtm/v1/domains/example.akadns.net/properties/www",
   134  			withError: &Error{
   135  				Type:       "internal_error",
   136  				Title:      "Internal Server Error",
   137  				Detail:     "Error fetching property",
   138  				StatusCode: http.StatusInternalServerError,
   139  			},
   140  		},
   141  	}
   142  
   143  	for name, test := range tests {
   144  		t.Run(name, func(t *testing.T) {
   145  			mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   146  				assert.Equal(t, test.expectedPath, r.URL.String())
   147  				assert.Equal(t, http.MethodGet, r.Method)
   148  				w.WriteHeader(test.responseStatus)
   149  				_, err := w.Write(test.responseBody)
   150  				assert.NoError(t, err)
   151  			}))
   152  			client := mockAPIClient(t, mockServer)
   153  			result, err := client.GetProperty(context.Background(), test.name, test.domain)
   154  			if test.withError != nil {
   155  				assert.True(t, errors.Is(err, test.withError), "want: %s; got: %s", test.withError, err)
   156  				return
   157  			}
   158  			require.NoError(t, err)
   159  			assert.Equal(t, test.expectedResponse, result)
   160  		})
   161  	}
   162  }
   163  
   164  func TestGTM_CreateProperty(t *testing.T) {
   165  	tests := map[string]struct {
   166  		domain           string
   167  		property         *Property
   168  		responseStatus   int
   169  		responseBody     string
   170  		expectedPath     string
   171  		expectedResponse *PropertyResponse
   172  		withError        bool
   173  		assertError      func(*testing.T, error)
   174  		headers          http.Header
   175  	}{
   176  		"201 Created": {
   177  			property: &Property{
   178  				BalanceByDownloadScore: false,
   179  				HandoutMode:            "normal",
   180  				IPv6:                   false,
   181  				Name:                   "origin",
   182  				ScoreAggregationType:   "mean",
   183  				StaticTTL:              600,
   184  				Type:                   "weighted-round-robin",
   185  				UseComputedTargets:     false,
   186  				LivenessTests: []*LivenessTest{
   187  					{
   188  						DisableNonstandardPortWarning: false,
   189  						HTTPError3xx:                  true,
   190  						HTTPError4xx:                  true,
   191  						HTTPError5xx:                  true,
   192  						Name:                          "health-check",
   193  						TestInterval:                  60,
   194  						TestObject:                    "/status",
   195  						TestObjectPort:                80,
   196  						TestObjectProtocol:            "HTTP",
   197  						TestTimeout:                   25.0,
   198  					},
   199  				},
   200  				TrafficTargets: []*TrafficTarget{
   201  					{
   202  						DatacenterID: 3134,
   203  						Enabled:      true,
   204  						Weight:       50.0,
   205  						Servers:      []string{"1.2.3.5"},
   206  					},
   207  					{
   208  						DatacenterID: 3133,
   209  						Enabled:      true,
   210  						Weight:       50.0,
   211  						Servers:      []string{"1.2.3.4"},
   212  						Precedence:   nil,
   213  					},
   214  				},
   215  			},
   216  			domain: "example.akadns.net",
   217  			headers: http.Header{
   218  				"Content-Type": []string{"application/vnd.config-gtm.v1.4+json;charset=UTF-8"},
   219  			},
   220  			responseStatus: http.StatusCreated,
   221  			responseBody: `
   222  {
   223      "resource": {
   224          "backupCName": null,
   225          "backupIp": null,
   226          "balanceByDownloadScore": false,
   227          "cname": null,
   228          "comments": null,
   229          "dynamicTTL": 300,
   230          "failbackDelay": 0,
   231          "failoverDelay": 0,
   232          "handoutMode": "normal",
   233          "healthMax": null,
   234          "healthMultiplier": null,
   235          "healthThreshold": null,
   236          "ipv6": false,
   237          "lastModified": null,
   238          "loadImbalancePercentage": null,
   239          "mapName": null,
   240          "maxUnreachablePenalty": null,
   241          "name": "origin",
   242          "scoreAggregationType": "mean",
   243          "staticTTL": 600,
   244          "stickinessBonusConstant": 0,
   245          "stickinessBonusPercentage": 0,
   246          "type": "weighted-round-robin",
   247          "unreachableThreshold": null,
   248          "useComputedTargets": false,
   249          "mxRecords": [],
   250          "links": [
   251              {
   252                  "href": "/config-gtm/v1/domains/example.akadns.net/properties/origin",
   253                  "rel": "self"
   254              }
   255          ],
   256          "livenessTests": [
   257              {
   258                  "disableNonstandardPortWarning": false,
   259                  "hostHeader": "foo.example.com",
   260                  "httpError3xx": true,
   261                  "httpError4xx": true,
   262                  "httpError5xx": true,
   263                  "name": "health-check",
   264                  "requestString": null,
   265                  "responseString": null,
   266                  "sslClientCertificate": null,
   267                  "sslClientPrivateKey": null,
   268                  "testInterval": 60,
   269                  "testObject": "/status",
   270                  "testObjectPassword": null,
   271                  "testObjectPort": 80,
   272                  "testObjectProtocol": "HTTP",
   273                  "testObjectUsername": null,
   274                  "testTimeout": 25.0
   275              }
   276          ],
   277          "trafficTargets": [
   278              {
   279                  "datacenterId": 3134,
   280                  "enabled": true,
   281                  "handoutCName": null,
   282                  "name": null,
   283                  "weight": 50.0,
   284                  "servers": [
   285                      "1.2.3.5"
   286                  ],
   287                  "precedence": null
   288              },
   289              {
   290                  "datacenterId": 3133,
   291                  "enabled": true,
   292                  "handoutCName": null,
   293                  "name": null,
   294                  "weight": 50.0,
   295                  "servers": [
   296                      "1.2.3.4"
   297                  ],
   298                  "precedence": null
   299              }
   300          ]
   301      },
   302      "status": {
   303          "changeId": "eee0c3b4-0e45-4f4b-822c-7dbc60764d18",
   304          "message": "Change Pending",
   305          "passingValidation": true,
   306          "propagationStatus": "PENDING",
   307          "propagationStatusDate": "2014-04-15T11:30:27.000+0000",
   308          "links": [
   309              {
   310                  "href": "/config-gtm/v1/domains/example.akadns.net/status/current",
   311                  "rel": "self"
   312              }
   313          ]
   314      }
   315  }
   316  `,
   317  			expectedResponse: &PropertyResponse{
   318  				Resource: &Property{
   319  					BalanceByDownloadScore: false,
   320  					HandoutMode:            "normal",
   321  					IPv6:                   false,
   322  					Name:                   "origin",
   323  					ScoreAggregationType:   "mean",
   324  					StaticTTL:              600,
   325  					DynamicTTL:             300,
   326  					Type:                   "weighted-round-robin",
   327  					UseComputedTargets:     false,
   328  					LivenessTests: []*LivenessTest{
   329  						{
   330  							DisableNonstandardPortWarning: false,
   331  							HTTPError3xx:                  true,
   332  							HTTPError4xx:                  true,
   333  							HTTPError5xx:                  true,
   334  							Name:                          "health-check",
   335  							TestInterval:                  60,
   336  							TestObject:                    "/status",
   337  							TestObjectPort:                80,
   338  							TestObjectProtocol:            "HTTP",
   339  							TestTimeout:                   25.0,
   340  						},
   341  					},
   342  					TrafficTargets: []*TrafficTarget{
   343  						{
   344  							DatacenterID: 3134,
   345  							Enabled:      true,
   346  							Weight:       50.0,
   347  							Servers:      []string{"1.2.3.5"},
   348  						},
   349  						{
   350  							DatacenterID: 3133,
   351  							Enabled:      true,
   352  							Weight:       50.0,
   353  							Servers:      []string{"1.2.3.4"},
   354  						},
   355  					},
   356  					Links: []*Link{
   357  						{
   358  							Href: "/config-gtm/v1/domains/example.akadns.net/properties/origin",
   359  							Rel:  "self",
   360  						},
   361  					},
   362  				},
   363  				Status: &ResponseStatus{
   364  					ChangeID:              "eee0c3b4-0e45-4f4b-822c-7dbc60764d18",
   365  					Message:               "Change Pending",
   366  					PassingValidation:     true,
   367  					PropagationStatus:     "PENDING",
   368  					PropagationStatusDate: "2014-04-15T11:30:27.000+0000",
   369  					Links: &[]Link{
   370  						{
   371  							Href: "/config-gtm/v1/domains/example.akadns.net/status/current",
   372  							Rel:  "self",
   373  						},
   374  					},
   375  				},
   376  			},
   377  			expectedPath: "/config-gtm/v1/domains/example.akadns.net?contractId=1-2ABCDE",
   378  		},
   379  		"201 Created - ranked-failover": {
   380  			property: &Property{
   381  				BalanceByDownloadScore: false,
   382  				HandoutMode:            "normal",
   383  				IPv6:                   false,
   384  				Name:                   "origin",
   385  				ScoreAggregationType:   "mean",
   386  				StaticTTL:              600,
   387  				Type:                   "ranked-failover",
   388  				UseComputedTargets:     false,
   389  				LivenessTests: []*LivenessTest{
   390  					{
   391  						DisableNonstandardPortWarning: false,
   392  						HTTPError3xx:                  true,
   393  						HTTPError4xx:                  true,
   394  						HTTPError5xx:                  true,
   395  						HTTPMethod:                    tools.StringPtr("GET"),
   396  						HTTPRequestBody:               tools.StringPtr("TestBody"),
   397  						Name:                          "health-check",
   398  						TestInterval:                  60,
   399  						TestObject:                    "/status",
   400  						TestObjectPort:                80,
   401  						TestObjectProtocol:            "HTTP",
   402  						TestTimeout:                   25.0,
   403  						Pre2023SecurityPosture:        true,
   404  						AlternateCACertificates:       []string{"test1"},
   405  					},
   406  				},
   407  				TrafficTargets: []*TrafficTarget{
   408  					{
   409  						DatacenterID: 3134,
   410  						Enabled:      true,
   411  						Weight:       50.0,
   412  						Servers:      []string{"1.2.3.5"},
   413  						Precedence:   tools.IntPtr(255),
   414  					},
   415  					{
   416  						DatacenterID: 3133,
   417  						Enabled:      true,
   418  						Weight:       50.0,
   419  						Servers:      []string{"1.2.3.4"},
   420  						Precedence:   nil,
   421  					},
   422  				},
   423  			},
   424  			domain: "example.akadns.net",
   425  			headers: http.Header{
   426  				"Content-Type": []string{"application/vnd.config-gtm.v1.4+json;charset=UTF-8"},
   427  			},
   428  			responseStatus: http.StatusCreated,
   429  			responseBody: `
   430  {
   431      "resource": {
   432          "backupCName": null,
   433          "backupIp": null,
   434          "balanceByDownloadScore": false,
   435          "cname": null,
   436          "comments": null,
   437          "dynamicTTL": 300,
   438          "failbackDelay": 0,
   439          "failoverDelay": 0,
   440          "handoutMode": "normal",
   441          "healthMax": null,
   442          "healthMultiplier": null,
   443          "healthThreshold": null,
   444          "ipv6": false,
   445          "lastModified": null,
   446          "loadImbalancePercentage": null,
   447          "mapName": null,
   448          "maxUnreachablePenalty": null,
   449          "name": "origin",
   450          "scoreAggregationType": "mean",
   451          "staticTTL": 600,
   452          "stickinessBonusConstant": 0,
   453          "stickinessBonusPercentage": 0,
   454          "type": "weighted-round-robin",
   455          "unreachableThreshold": null,
   456          "useComputedTargets": false,
   457          "mxRecords": [],
   458          "links": [
   459              {
   460                  "href": "/config-gtm/v1/domains/example.akadns.net/properties/origin",
   461                  "rel": "self"
   462              }
   463          ],
   464          "livenessTests": [
   465              {
   466                  "disableNonstandardPortWarning": false,
   467                  "hostHeader": "foo.example.com",
   468                  "httpError3xx": true,
   469                  "httpError4xx": true,
   470                  "httpError5xx": true,
   471  				"httpMethod": "GET",
   472  				"httpRequestBody": "TestBody",
   473  				"pre2023SecurityPosture": true,
   474  				"alternateCACertificates": ["test1"],
   475                  "name": "health-check",
   476                  "requestString": null,
   477                  "responseString": null,
   478                  "sslClientCertificate": null,
   479                  "sslClientPrivateKey": null,
   480                  "testInterval": 60,
   481                  "testObject": "/status",
   482                  "testObjectPassword": null,
   483                  "testObjectPort": 80,
   484                  "testObjectProtocol": "HTTP",
   485                  "testObjectUsername": null,
   486                  "testTimeout": 25.0
   487              }
   488          ],
   489          "trafficTargets": [
   490              {
   491                  "datacenterId": 3134,
   492                  "enabled": true,
   493                  "handoutCName": null,
   494                  "name": null,
   495                  "weight": 50.0,
   496                  "servers": [
   497                      "1.2.3.5"
   498                  ],
   499                  "precedence": 255
   500              },
   501              {
   502                  "datacenterId": 3133,
   503                  "enabled": true,
   504                  "handoutCName": null,
   505                  "name": null,
   506                  "weight": 50.0,
   507                  "servers": [
   508                      "1.2.3.4"
   509                  ],
   510                  "precedence": null
   511              }
   512          ]
   513      },
   514      "status": {
   515          "changeId": "eee0c3b4-0e45-4f4b-822c-7dbc60764d18",
   516          "message": "Change Pending",
   517          "passingValidation": true,
   518          "propagationStatus": "PENDING",
   519          "propagationStatusDate": "2014-04-15T11:30:27.000+0000",
   520          "links": [
   521              {
   522                  "href": "/config-gtm/v1/domains/example.akadns.net/status/current",
   523                  "rel": "self"
   524              }
   525          ]
   526      }
   527  }
   528  `,
   529  			expectedResponse: &PropertyResponse{
   530  				Resource: &Property{
   531  					BalanceByDownloadScore: false,
   532  					HandoutMode:            "normal",
   533  					IPv6:                   false,
   534  					Name:                   "origin",
   535  					ScoreAggregationType:   "mean",
   536  					StaticTTL:              600,
   537  					DynamicTTL:             300,
   538  					Type:                   "weighted-round-robin",
   539  					UseComputedTargets:     false,
   540  					LivenessTests: []*LivenessTest{
   541  						{
   542  							DisableNonstandardPortWarning: false,
   543  							HTTPError3xx:                  true,
   544  							HTTPError4xx:                  true,
   545  							HTTPError5xx:                  true,
   546  							HTTPMethod:                    tools.StringPtr("GET"),
   547  							HTTPRequestBody:               tools.StringPtr("TestBody"),
   548  							Pre2023SecurityPosture:        true,
   549  							AlternateCACertificates:       []string{"test1"},
   550  							Name:                          "health-check",
   551  							TestInterval:                  60,
   552  							TestObject:                    "/status",
   553  							TestObjectPort:                80,
   554  							TestObjectProtocol:            "HTTP",
   555  							TestTimeout:                   25.0,
   556  						},
   557  					},
   558  					TrafficTargets: []*TrafficTarget{
   559  						{
   560  							DatacenterID: 3134,
   561  							Enabled:      true,
   562  							Weight:       50.0,
   563  							Servers:      []string{"1.2.3.5"},
   564  							Precedence:   tools.IntPtr(255),
   565  						},
   566  						{
   567  							DatacenterID: 3133,
   568  							Enabled:      true,
   569  							Weight:       50.0,
   570  							Servers:      []string{"1.2.3.4"},
   571  							Precedence:   nil,
   572  						},
   573  					},
   574  					Links: []*Link{
   575  						{
   576  							Href: "/config-gtm/v1/domains/example.akadns.net/properties/origin",
   577  							Rel:  "self",
   578  						},
   579  					},
   580  				},
   581  				Status: &ResponseStatus{
   582  					ChangeID:              "eee0c3b4-0e45-4f4b-822c-7dbc60764d18",
   583  					Message:               "Change Pending",
   584  					PassingValidation:     true,
   585  					PropagationStatus:     "PENDING",
   586  					PropagationStatusDate: "2014-04-15T11:30:27.000+0000",
   587  					Links: &[]Link{
   588  						{
   589  							Href: "/config-gtm/v1/domains/example.akadns.net/status/current",
   590  							Rel:  "self",
   591  						},
   592  					},
   593  				},
   594  			},
   595  			expectedPath: "/config-gtm/v1/domains/example.akadns.net?contractId=1-2ABCDE",
   596  		},
   597  		"validation error - missing precedence for ranked-failover property type": {
   598  			property: &Property{
   599  				Type:                 "ranked-failover",
   600  				Name:                 "property",
   601  				HandoutMode:          "normal",
   602  				ScoreAggregationType: "mean",
   603  				TrafficTargets: []*TrafficTarget{
   604  					{
   605  						DatacenterID: 1,
   606  						Enabled:      false,
   607  						Precedence:   nil,
   608  					},
   609  					{
   610  						DatacenterID: 2,
   611  						Enabled:      false,
   612  						Precedence:   nil,
   613  					},
   614  				},
   615  			},
   616  			withError: true,
   617  			assertError: func(t *testing.T, err error) {
   618  				assert.ErrorContains(t, err, "TrafficTargets: property cannot have multiple primary traffic targets (targets with lowest precedence)")
   619  			},
   620  		},
   621  		"validation error - precedence value over the limit": {
   622  			property: &Property{
   623  				Type:                 "ranked-failover",
   624  				Name:                 "property",
   625  				HandoutMode:          "normal",
   626  				ScoreAggregationType: "mean",
   627  				TrafficTargets: []*TrafficTarget{
   628  					{
   629  						DatacenterID: 1,
   630  						Enabled:      false,
   631  						Precedence:   tools.IntPtr(256),
   632  					},
   633  				},
   634  			},
   635  			withError: true,
   636  			assertError: func(t *testing.T, err error) {
   637  				assert.ErrorContains(t, err, "property validation failed. TrafficTargets: 'Precedence' value has to be between 0 and 255")
   638  			},
   639  		},
   640  		"500 internal server error": {
   641  			property: &Property{
   642  				Name:                 "testName",
   643  				HandoutMode:          "normal",
   644  				ScoreAggregationType: "mean",
   645  				Type:                 "failover",
   646  			},
   647  			domain:         "example.akadns.net",
   648  			responseStatus: http.StatusInternalServerError,
   649  			responseBody: `
   650  		{
   651  		   "type": "internal_error",
   652  		   "title": "Internal Server Error",
   653  		   "detail": "Error creating domain"
   654  		}`,
   655  			expectedPath: "/config-gtm/v1/domains/example.akadns.net?contractId=1-2ABCDE",
   656  			withError:    true,
   657  			assertError: func(t *testing.T, err error) {
   658  				want := &Error{
   659  					Type:       "internal_error",
   660  					Title:      "Internal Server Error",
   661  					Detail:     "Error creating domain",
   662  					StatusCode: http.StatusInternalServerError,
   663  				}
   664  				assert.ErrorIs(t, err, want)
   665  			},
   666  		},
   667  	}
   668  
   669  	for name, test := range tests {
   670  		t.Run(name, func(t *testing.T) {
   671  			mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   672  				assert.Equal(t, http.MethodPut, r.Method)
   673  				w.WriteHeader(test.responseStatus)
   674  				if len(test.responseBody) > 0 {
   675  					_, err := w.Write([]byte(test.responseBody))
   676  					assert.NoError(t, err)
   677  				}
   678  			}))
   679  			client := mockAPIClient(t, mockServer)
   680  			result, err := client.CreateProperty(
   681  				session.ContextWithOptions(
   682  					context.Background(),
   683  					session.WithContextHeaders(test.headers)), test.property, test.domain)
   684  			if test.withError {
   685  				test.assertError(t, err)
   686  				return
   687  			}
   688  			require.NoError(t, err)
   689  			assert.Equal(t, test.expectedResponse, result)
   690  		})
   691  	}
   692  }
   693  
   694  func TestGTM_UpdateProperty(t *testing.T) {
   695  	tests := map[string]struct {
   696  		property         *Property
   697  		domain           string
   698  		responseStatus   int
   699  		responseBody     string
   700  		expectedPath     string
   701  		expectedResponse *ResponseStatus
   702  		withError        bool
   703  		assertError      func(*testing.T, error)
   704  		headers          http.Header
   705  	}{
   706  		"200 Success": {
   707  			property: &Property{
   708  				BalanceByDownloadScore: false,
   709  				HandoutMode:            "normal",
   710  				IPv6:                   false,
   711  				Name:                   "origin",
   712  				ScoreAggregationType:   "mean",
   713  				StaticTTL:              600,
   714  				Type:                   "weighted-round-robin",
   715  				UseComputedTargets:     false,
   716  				LivenessTests: []*LivenessTest{
   717  					{
   718  						DisableNonstandardPortWarning: false,
   719  						HTTPError3xx:                  true,
   720  						HTTPError4xx:                  true,
   721  						HTTPError5xx:                  true,
   722  						Name:                          "health-check",
   723  						TestInterval:                  60,
   724  						TestObject:                    "/status",
   725  						TestObjectPort:                80,
   726  						TestObjectProtocol:            "HTTP",
   727  						TestTimeout:                   25.0,
   728  					},
   729  				},
   730  				TrafficTargets: []*TrafficTarget{
   731  					{
   732  						DatacenterID: 3134,
   733  						Enabled:      true,
   734  						Weight:       50.0,
   735  						Servers:      []string{"1.2.3.5"},
   736  						Precedence:   tools.IntPtr(255),
   737  					},
   738  					{
   739  						DatacenterID: 3133,
   740  						Enabled:      true,
   741  						Weight:       50.0,
   742  						Servers:      []string{"1.2.3.4"},
   743  						Precedence:   nil,
   744  					},
   745  				},
   746  			},
   747  			domain: "example.akadns.net",
   748  			headers: http.Header{
   749  				"Content-Type": []string{"application/vnd.config-gtm.v1.4+json;charset=UTF-8"},
   750  			},
   751  			responseStatus: http.StatusCreated,
   752  			responseBody: `
   753  {
   754      "resource": {
   755          "backupCName": null,
   756          "backupIp": null,
   757          "balanceByDownloadScore": false,
   758          "cname": null,
   759          "comments": null,
   760          "dynamicTTL": 300,
   761          "failbackDelay": 0,
   762          "failoverDelay": 0,
   763          "handoutMode": "normal",
   764          "healthMax": null,
   765          "healthMultiplier": null,
   766          "healthThreshold": null,
   767          "ipv6": false,
   768          "lastModified": null,
   769          "loadImbalancePercentage": null,
   770          "mapName": null,
   771          "maxUnreachablePenalty": null,
   772          "name": "origin",
   773          "scoreAggregationType": "mean",
   774          "staticTTL": 600,
   775          "stickinessBonusConstant": 0,
   776          "stickinessBonusPercentage": 0,
   777          "type": "weighted-round-robin",
   778          "unreachableThreshold": null,
   779          "useComputedTargets": false,
   780          "mxRecords": [],
   781          "links": [
   782              {
   783                  "href": "/config-gtm/v1/domains/example.akadns.net/properties/origin",
   784                  "rel": "self"
   785              }
   786          ],
   787          "livenessTests": [
   788              {
   789                  "disableNonstandardPortWarning": false,
   790                  "hostHeader": "foo.example.com",
   791                  "httpError3xx": true,
   792                  "httpError4xx": true,
   793                  "httpError5xx": true,
   794                  "name": "health-check",
   795                  "requestString": null,
   796                  "responseString": null,
   797                  "sslClientCertificate": null,
   798                  "sslClientPrivateKey": null,
   799                  "testInterval": 60,
   800                  "testObject": "/status",
   801                  "testObjectPassword": null,
   802                  "testObjectPort": 80,
   803                  "testObjectProtocol": "HTTP",
   804                  "testObjectUsername": null,
   805                  "testTimeout": 25.0
   806              }
   807          ],
   808          "trafficTargets": [
   809              {
   810                  "datacenterId": 3134,
   811                  "enabled": true,
   812                  "handoutCName": null,
   813                  "name": null,
   814                  "weight": 50.0,
   815                  "servers": [
   816                      "1.2.3.5"
   817                  ],
   818                  "precedence": 255
   819              },
   820              {
   821                  "datacenterId": 3133,
   822                  "enabled": true,
   823                  "handoutCName": null,
   824                  "name": null,
   825                  "weight": 50.0,
   826                  "servers": [
   827                      "1.2.3.4"
   828                  ],
   829                  "precedence": null
   830              }
   831          ]
   832      },
   833      "status": {
   834          "changeId": "eee0c3b4-0e45-4f4b-822c-7dbc60764d18",
   835          "message": "Change Pending",
   836          "passingValidation": true,
   837          "propagationStatus": "PENDING",
   838          "propagationStatusDate": "2014-04-15T11:30:27.000+0000",
   839          "links": [
   840              {
   841                  "href": "/config-gtm/v1/domains/example.akadns.net/status/current",
   842                  "rel": "self"
   843              }
   844          ]
   845      }
   846  }
   847  `,
   848  			expectedResponse: &ResponseStatus{
   849  				ChangeID:              "eee0c3b4-0e45-4f4b-822c-7dbc60764d18",
   850  				Message:               "Change Pending",
   851  				PassingValidation:     true,
   852  				PropagationStatus:     "PENDING",
   853  				PropagationStatusDate: "2014-04-15T11:30:27.000+0000",
   854  				Links: &[]Link{
   855  					{
   856  						Href: "/config-gtm/v1/domains/example.akadns.net/status/current",
   857  						Rel:  "self",
   858  					},
   859  				},
   860  			},
   861  			expectedPath: "/config-gtm/v1/domains/example.akadns.net?contractId=1-2ABCDE",
   862  		},
   863  		"validation error - missing precedence for ranked-failover property type": {
   864  			property: &Property{
   865  				Type:                 "ranked-failover",
   866  				Name:                 "property",
   867  				HandoutMode:          "normal",
   868  				ScoreAggregationType: "mean",
   869  				TrafficTargets: []*TrafficTarget{
   870  					{
   871  						DatacenterID: 1,
   872  						Enabled:      false,
   873  						Precedence:   nil,
   874  					},
   875  					{
   876  						DatacenterID: 2,
   877  						Enabled:      false,
   878  						Precedence:   nil,
   879  					},
   880  				},
   881  			},
   882  			withError: true,
   883  			assertError: func(t *testing.T, err error) {
   884  				assert.ErrorContains(t, err, "TrafficTargets: property cannot have multiple primary traffic targets (targets with lowest precedence)")
   885  			},
   886  		},
   887  		"validation error - no traffic targets": {
   888  			property: &Property{
   889  				Type:                 "ranked-failover",
   890  				Name:                 "property",
   891  				HandoutMode:          "normal",
   892  				ScoreAggregationType: "mean",
   893  			},
   894  			withError: true,
   895  			assertError: func(t *testing.T, err error) {
   896  				assert.ErrorContains(t, err, "property validation failed. TrafficTargets: no traffic targets are enabled")
   897  			},
   898  		},
   899  		"500 internal server error": {
   900  			property: &Property{
   901  				Name:                 "testName",
   902  				HandoutMode:          "normal",
   903  				ScoreAggregationType: "mean",
   904  				Type:                 "failover",
   905  			},
   906  			domain:         "example.akadns.net",
   907  			responseStatus: http.StatusInternalServerError,
   908  			responseBody: `
   909  {
   910      "type": "internal_error",
   911      "title": "Internal Server Error",
   912      "detail": "Error creating zone"
   913  }`,
   914  			expectedPath: "/config-gtm/v1/domains/example.akadns.net?contractId=1-2ABCDE",
   915  			withError:    true,
   916  			assertError: func(t *testing.T, err error) {
   917  				want := &Error{
   918  					Type:       "internal_error",
   919  					Title:      "Internal Server Error",
   920  					Detail:     "Error creating zone",
   921  					StatusCode: http.StatusInternalServerError,
   922  				}
   923  				assert.ErrorIs(t, err, want)
   924  			},
   925  		},
   926  	}
   927  
   928  	for name, test := range tests {
   929  		t.Run(name, func(t *testing.T) {
   930  			mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   931  				assert.Equal(t, http.MethodPut, r.Method)
   932  				w.WriteHeader(test.responseStatus)
   933  				if len(test.responseBody) > 0 {
   934  					_, err := w.Write([]byte(test.responseBody))
   935  					assert.NoError(t, err)
   936  				}
   937  			}))
   938  			client := mockAPIClient(t, mockServer)
   939  			result, err := client.UpdateProperty(
   940  				session.ContextWithOptions(
   941  					context.Background(),
   942  					session.WithContextHeaders(test.headers)), test.property, test.domain)
   943  			if test.withError {
   944  				test.assertError(t, err)
   945  				return
   946  			}
   947  			require.NoError(t, err)
   948  			assert.Equal(t, test.expectedResponse, result)
   949  		})
   950  	}
   951  }
   952  
   953  func TestGTM_DeleteProperty(t *testing.T) {
   954  	var result PropertyResponse
   955  	var req Property
   956  
   957  	respData, err := loadTestData("TestGTM_CreateProperty.resp.json")
   958  	if err != nil {
   959  		t.Fatal(err)
   960  	}
   961  
   962  	if err := json.NewDecoder(bytes.NewBuffer(respData)).Decode(&result); err != nil {
   963  		t.Fatal(err)
   964  	}
   965  
   966  	reqData, err := loadTestData("TestGTM_CreateProperty.req.json")
   967  	if err != nil {
   968  		t.Fatal(err)
   969  	}
   970  
   971  	if err := json.NewDecoder(bytes.NewBuffer(reqData)).Decode(&req); err != nil {
   972  		t.Fatal(err)
   973  	}
   974  
   975  	tests := map[string]struct {
   976  		prop             *Property
   977  		domain           string
   978  		responseStatus   int
   979  		responseBody     []byte
   980  		expectedPath     string
   981  		expectedResponse *ResponseStatus
   982  		withError        error
   983  		headers          http.Header
   984  	}{
   985  		"200 Success": {
   986  			prop:   &req,
   987  			domain: "example.akadns.net",
   988  			headers: http.Header{
   989  				"Content-Type": []string{"application/vnd.config-gtm.v1.4+json;charset=UTF-8"},
   990  			},
   991  			responseStatus:   http.StatusOK,
   992  			responseBody:     respData,
   993  			expectedResponse: result.Status,
   994  			expectedPath:     "/config-gtm/v1/domains/example.akadns.net?contractId=1-2ABCDE",
   995  		},
   996  		"500 internal server error": {
   997  			prop:           &req,
   998  			domain:         "example.akadns.net",
   999  			responseStatus: http.StatusInternalServerError,
  1000  			responseBody: []byte(`
  1001  {
  1002      "type": "internal_error",
  1003      "title": "Internal Server Error",
  1004      "detail": "Error creating zone"
  1005  }`),
  1006  			expectedPath: "/config-gtm/v1/domains/example.akadns.net?contractId=1-2ABCDE",
  1007  			withError: &Error{
  1008  				Type:       "internal_error",
  1009  				Title:      "Internal Server Error",
  1010  				Detail:     "Error creating zone",
  1011  				StatusCode: http.StatusInternalServerError,
  1012  			},
  1013  		},
  1014  	}
  1015  
  1016  	for name, test := range tests {
  1017  		t.Run(name, func(t *testing.T) {
  1018  			mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1019  				assert.Equal(t, http.MethodDelete, r.Method)
  1020  				w.WriteHeader(test.responseStatus)
  1021  				if len(test.responseBody) > 0 {
  1022  					_, err := w.Write(test.responseBody)
  1023  					assert.NoError(t, err)
  1024  				}
  1025  			}))
  1026  			client := mockAPIClient(t, mockServer)
  1027  			result, err := client.DeleteProperty(
  1028  				session.ContextWithOptions(
  1029  					context.Background(),
  1030  					session.WithContextHeaders(test.headers)), test.prop, test.domain)
  1031  			if test.withError != nil {
  1032  				assert.True(t, errors.Is(err, test.withError), "want: %s; got: %s", test.withError, err)
  1033  				return
  1034  			}
  1035  			require.NoError(t, err)
  1036  			assert.Equal(t, test.expectedResponse, result)
  1037  		})
  1038  	}
  1039  }