github.com/akamai/AkamaiOPEN-edgegrid-golang/v5@v5.0.0/pkg/papi/include_rule_test.go (about)

     1  package papi
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func TestGetIncludeRuleTree(t *testing.T) {
    16  	tests := map[string]struct {
    17  		params           GetIncludeRuleTreeRequest
    18  		responseStatus   int
    19  		responseBody     string
    20  		expectedPath     string
    21  		expectedResponse *GetIncludeRuleTreeResponse
    22  		withError        error
    23  	}{
    24  		"200 OK - get include": {
    25  			params: GetIncludeRuleTreeRequest{
    26  				IncludeID:      "inc_123456",
    27  				IncludeVersion: 2,
    28  				ContractID:     "test_contract",
    29  				GroupID:        "test_group",
    30  				ValidateMode:   "fast",
    31  				ValidateRules:  false,
    32  			},
    33  			responseStatus: http.StatusOK,
    34  			responseBody: `
    35  {
    36      "accountId": "test_account",
    37      "contractId": "test_contract",
    38      "groupId": "test_group",
    39      "includeId": "inc_123456",
    40      "includeVersion": 2,
    41  	"includeName": "test_include",
    42  	"includeType": "MICROSERVICES",
    43      "etag": "etag",
    44      "ruleFormat": "v2020-11-02",
    45      "rules": {
    46          "name": "default",
    47          "criteria": [],
    48          "children": [
    49              {
    50                  "name": "Compress Text Content",
    51                  "criteria": [
    52                      {
    53                          "name": "contentType",
    54                          "options": {
    55                              "matchOperator": "IS_ONE_OF",
    56                              "matchWildcard": true,
    57                              "matchCaseSensitive": false,
    58                              "values": [
    59                                  "text/html*",
    60                                  "text/css*",
    61                                  "application/x-javascript*"
    62                              ]
    63                          }
    64                      }
    65                  ],
    66                  "behaviors": [
    67                      {
    68                          "name": "gzipResponse",
    69                          "options": { "behavior": "ALWAYS" }
    70                      }
    71                  ]
    72              }
    73          ],
    74          "options": {
    75              "is_secure": false
    76          },
    77          "behaviors": [
    78              {
    79                  "name": "origin",
    80                  "options": {
    81                      "httpPort": 80,
    82                      "enableTrueClientIp": false,
    83                      "compress": true,
    84                      "cacheKeyHostname": "ORIGIN_HOSTNAME",
    85                      "forwardHostHeader": "REQUEST_HOST_HEADER",
    86                      "hostname": "origin.test.com",
    87                      "originType": "CUSTOMER"
    88                  }
    89              },
    90              {
    91                  "name": "cpCode",
    92                  "options": {
    93                      "value": {
    94                          "id": 12345,
    95                          "name": "my CP code"
    96                      }
    97                  }
    98              }
    99          ],
   100   		"customOverride": {
   101          	"overrideId": "cbo_12345",
   102          	"name": "mdc"
   103      	},
   104  		"variables": [
   105              {
   106                  "name": "VAR_NAME",
   107                  "value": "default value",
   108                  "description": "This is a sample Property Manager variable.",
   109                  "hidden": false,
   110                  "sensitive": false
   111              }
   112          ]
   113      }
   114  }`,
   115  			expectedPath: "/papi/v1/includes/inc_123456/versions/2/rules?contractId=test_contract&groupId=test_group&validateMode=fast&validateRules=false",
   116  			expectedResponse: &GetIncludeRuleTreeResponse{
   117  				Response: Response{
   118  					AccountID:  "test_account",
   119  					ContractID: "test_contract",
   120  					GroupID:    "test_group",
   121  				},
   122  				IncludeID:      "inc_123456",
   123  				IncludeVersion: 2,
   124  				IncludeName:    "test_include",
   125  				IncludeType:    IncludeTypeMicroServices,
   126  				Etag:           "etag",
   127  				RuleFormat:     "v2020-11-02",
   128  				Rules: Rules{
   129  					Behaviors: []RuleBehavior{
   130  						{
   131  							Name: "origin",
   132  							Options: RuleOptionsMap{
   133  								"httpPort":           float64(80),
   134  								"enableTrueClientIp": false,
   135  								"compress":           true,
   136  								"cacheKeyHostname":   "ORIGIN_HOSTNAME",
   137  								"forwardHostHeader":  "REQUEST_HOST_HEADER",
   138  								"hostname":           "origin.test.com",
   139  								"originType":         "CUSTOMER",
   140  							},
   141  						},
   142  						{
   143  							Name: "cpCode",
   144  							Options: RuleOptionsMap{
   145  								"value": map[string]interface{}{
   146  									"id":   float64(12345),
   147  									"name": "my CP code",
   148  								},
   149  							},
   150  						},
   151  					},
   152  					Children: []Rules{
   153  						{
   154  							Behaviors: []RuleBehavior{
   155  								{
   156  									Name: "gzipResponse",
   157  									Options: RuleOptionsMap{
   158  										"behavior": "ALWAYS",
   159  									},
   160  								},
   161  							},
   162  							Criteria: []RuleBehavior{
   163  								{
   164  									Locked: false,
   165  									Name:   "contentType",
   166  									Options: RuleOptionsMap{
   167  										"matchOperator":      "IS_ONE_OF",
   168  										"matchWildcard":      true,
   169  										"matchCaseSensitive": false,
   170  										"values":             []interface{}{"text/html*", "text/css*", "application/x-javascript*"},
   171  									},
   172  								},
   173  							},
   174  							Name: "Compress Text Content",
   175  						},
   176  					},
   177  					Criteria: []RuleBehavior{},
   178  					Name:     "default",
   179  					Options:  RuleOptions{IsSecure: false},
   180  					CustomOverride: &RuleCustomOverride{
   181  						OverrideID: "cbo_12345",
   182  						Name:       "mdc",
   183  					},
   184  					Variables: []RuleVariable{
   185  						{
   186  							Description: "This is a sample Property Manager variable.",
   187  							Hidden:      false,
   188  							Name:        "VAR_NAME",
   189  							Sensitive:   false,
   190  							Value:       "default value",
   191  						},
   192  					},
   193  				},
   194  			},
   195  		},
   196  		"200 OK - get include with ruleFormat": {
   197  			params: GetIncludeRuleTreeRequest{
   198  				IncludeID:      "inc_123456",
   199  				IncludeVersion: 2,
   200  				ContractID:     "test_contract",
   201  				GroupID:        "test_group",
   202  				RuleFormat:     "v2020-11-02",
   203  				ValidateMode:   "fast",
   204  				ValidateRules:  false,
   205  			},
   206  			responseStatus: http.StatusOK,
   207  			responseBody: `
   208  {
   209      "accountId": "test_account",
   210      "contractId": "test_contract",
   211      "groupId": "test_group",
   212      "includeId": "inc_123456",
   213      "includeVersion": 2,
   214  	"includeName": "test_include",
   215  	"includeType": "MICROSERVICES",
   216      "etag": "etag",
   217      "ruleFormat": "v2020-11-02",
   218      "rules": {
   219          "name": "default",
   220          "criteria": [],
   221          "children": [
   222              {
   223                  "name": "Compress Text Content",
   224                  "criteria": [
   225                      {
   226                          "name": "contentType",
   227                          "options": {
   228                              "matchOperator": "IS_ONE_OF",
   229                              "matchWildcard": true,
   230                              "matchCaseSensitive": false,
   231                              "values": [
   232                                  "text/html*",
   233                                  "text/css*",
   234                                  "application/x-javascript*"
   235                              ]
   236                          }
   237                      }
   238                  ],
   239                  "behaviors": [
   240                      {
   241                          "name": "gzipResponse",
   242                          "options": { "behavior": "ALWAYS" }
   243                      }
   244                  ]
   245              }
   246          ],
   247          "options": {
   248              "is_secure": false
   249          },
   250          "behaviors": [
   251              {
   252                  "name": "origin",
   253                  "options": {
   254                      "httpPort": 80,
   255                      "enableTrueClientIp": false,
   256                      "compress": true,
   257                      "cacheKeyHostname": "ORIGIN_HOSTNAME",
   258                      "forwardHostHeader": "REQUEST_HOST_HEADER",
   259                      "hostname": "origin.test.com",
   260                      "originType": "CUSTOMER"
   261                  }
   262              },
   263              {
   264                  "name": "cpCode",
   265                  "options": {
   266                      "value": {
   267                          "id": 12345,
   268                          "name": "my CP code"
   269                      }
   270                  }
   271              }
   272          ],
   273   		"customOverride": {
   274          	"overrideId": "cbo_12345",
   275          	"name": "mdc"
   276      	},
   277  		"variables": [
   278              {
   279                  "name": "VAR_NAME",
   280                  "value": "default value",
   281                  "description": "This is a sample Property Manager variable.",
   282                  "hidden": false,
   283                  "sensitive": false
   284              }
   285          ]
   286      }
   287  }`,
   288  			expectedPath: "/papi/v1/includes/inc_123456/versions/2/rules?contractId=test_contract&groupId=test_group&validateMode=fast&validateRules=false",
   289  			expectedResponse: &GetIncludeRuleTreeResponse{
   290  				Response: Response{
   291  					AccountID:  "test_account",
   292  					ContractID: "test_contract",
   293  					GroupID:    "test_group",
   294  				},
   295  				IncludeID:      "inc_123456",
   296  				IncludeVersion: 2,
   297  				IncludeName:    "test_include",
   298  				IncludeType:    IncludeTypeMicroServices,
   299  				Etag:           "etag",
   300  				RuleFormat:     "v2020-11-02",
   301  				Rules: Rules{
   302  					Behaviors: []RuleBehavior{
   303  						{
   304  							Name: "origin",
   305  							Options: RuleOptionsMap{
   306  								"httpPort":           float64(80),
   307  								"enableTrueClientIp": false,
   308  								"compress":           true,
   309  								"cacheKeyHostname":   "ORIGIN_HOSTNAME",
   310  								"forwardHostHeader":  "REQUEST_HOST_HEADER",
   311  								"hostname":           "origin.test.com",
   312  								"originType":         "CUSTOMER",
   313  							},
   314  						},
   315  						{
   316  							Name: "cpCode",
   317  							Options: RuleOptionsMap{
   318  								"value": map[string]interface{}{
   319  									"id":   float64(12345),
   320  									"name": "my CP code",
   321  								},
   322  							},
   323  						},
   324  					},
   325  					Children: []Rules{
   326  						{
   327  							Behaviors: []RuleBehavior{
   328  								{
   329  									Name: "gzipResponse",
   330  									Options: RuleOptionsMap{
   331  										"behavior": "ALWAYS",
   332  									},
   333  								},
   334  							},
   335  							Criteria: []RuleBehavior{
   336  								{
   337  									Locked: false,
   338  									Name:   "contentType",
   339  									Options: RuleOptionsMap{
   340  										"matchOperator":      "IS_ONE_OF",
   341  										"matchWildcard":      true,
   342  										"matchCaseSensitive": false,
   343  										"values":             []interface{}{"text/html*", "text/css*", "application/x-javascript*"},
   344  									},
   345  								},
   346  							},
   347  							Name: "Compress Text Content",
   348  						},
   349  					},
   350  					Criteria: []RuleBehavior{},
   351  					Name:     "default",
   352  					Options:  RuleOptions{IsSecure: false},
   353  					CustomOverride: &RuleCustomOverride{
   354  						OverrideID: "cbo_12345",
   355  						Name:       "mdc",
   356  					},
   357  					Variables: []RuleVariable{
   358  						{
   359  							Description: "This is a sample Property Manager variable.",
   360  							Hidden:      false,
   361  							Name:        "VAR_NAME",
   362  							Sensitive:   false,
   363  							Value:       "default value",
   364  						},
   365  					},
   366  				},
   367  			},
   368  		},
   369  		"500 Internal Server Error": {
   370  			params: GetIncludeRuleTreeRequest{
   371  				IncludeID:      "inc_123456",
   372  				IncludeVersion: 2,
   373  				ContractID:     "test_contract",
   374  				GroupID:        "test_group",
   375  				ValidateMode:   "fast",
   376  				ValidateRules:  false,
   377  			},
   378  			responseStatus: http.StatusInternalServerError,
   379  			responseBody: `
   380  {
   381  	"type": "internal_error",
   382      "title": "Internal Server Error",
   383      "detail": "Error fetching rule tree",
   384      "status": 500
   385  }`,
   386  			expectedPath: "/papi/v1/includes/inc_123456/versions/2/rules?contractId=test_contract&groupId=test_group&validateMode=fast&validateRules=false",
   387  			withError: &Error{
   388  				Type:       "internal_error",
   389  				Title:      "Internal Server Error",
   390  				Detail:     "Error fetching rule tree",
   391  				StatusCode: http.StatusInternalServerError,
   392  			},
   393  		},
   394  		"validation error - missing includeId": {
   395  			params: GetIncludeRuleTreeRequest{
   396  				IncludeVersion: 2,
   397  				ContractID:     "test_contract",
   398  				GroupID:        "test_group",
   399  				ValidateMode:   "fast",
   400  				ValidateRules:  false,
   401  			},
   402  			withError: ErrStructValidation,
   403  		},
   404  		"validation error - missing includeVersion": {
   405  			params: GetIncludeRuleTreeRequest{
   406  				IncludeID:     "inc_123456",
   407  				ContractID:    "test_contract",
   408  				GroupID:       "test_group",
   409  				ValidateMode:  "fast",
   410  				ValidateRules: false,
   411  			},
   412  			withError: ErrStructValidation,
   413  		},
   414  		"validation error - missing contractId": {
   415  			params: GetIncludeRuleTreeRequest{
   416  				IncludeID:      "inc_123456",
   417  				IncludeVersion: 2,
   418  				GroupID:        "test_group",
   419  				ValidateMode:   "fast",
   420  				ValidateRules:  false,
   421  			},
   422  			withError: ErrStructValidation,
   423  		},
   424  		"validation error - missing groupId": {
   425  			params: GetIncludeRuleTreeRequest{
   426  				IncludeID:      "inc_123456",
   427  				IncludeVersion: 2,
   428  				ContractID:     "test_contract",
   429  				ValidateMode:   "fast",
   430  				ValidateRules:  false,
   431  			},
   432  			withError: ErrStructValidation,
   433  		},
   434  		"validation error - invalid validation mode": {
   435  			params: GetIncludeRuleTreeRequest{
   436  				IncludeID:      "inc_123456",
   437  				IncludeVersion: 2,
   438  				ContractID:     "test_contract",
   439  				GroupID:        "test_group",
   440  				ValidateMode:   "test",
   441  				ValidateRules:  false,
   442  			},
   443  			withError: ErrStructValidation,
   444  		},
   445  		"validation error - invalid ruleFormat": {
   446  			params: GetIncludeRuleTreeRequest{
   447  				IncludeID:      "inc_123456",
   448  				IncludeVersion: 2,
   449  				ContractID:     "test_contract",
   450  				GroupID:        "test_group",
   451  				RuleFormat:     "invalid",
   452  				ValidateMode:   "fast",
   453  				ValidateRules:  false,
   454  			},
   455  			withError: ErrStructValidation,
   456  		},
   457  	}
   458  
   459  	for name, test := range tests {
   460  		t.Run(name, func(t *testing.T) {
   461  			mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   462  				assert.Equal(t, test.expectedPath, r.URL.String())
   463  				assert.Equal(t, http.MethodGet, r.Method)
   464  				w.WriteHeader(test.responseStatus)
   465  				_, err := w.Write([]byte(test.responseBody))
   466  				if test.params.RuleFormat != "" {
   467  					assert.Equal(t, r.Header.Get("Accept"), fmt.Sprintf("application/vnd.akamai.papirules.%s+json", test.params.RuleFormat))
   468  				}
   469  				assert.NoError(t, err)
   470  			}))
   471  			client := mockAPIClient(t, mockServer)
   472  			result, err := client.GetIncludeRuleTree(context.Background(), test.params)
   473  			if test.withError != nil {
   474  				assert.True(t, errors.Is(err, test.withError), "want: %s; got: %s", test.withError, err)
   475  				return
   476  			}
   477  			require.NoError(t, err)
   478  			assert.Equal(t, test.expectedResponse, result)
   479  		})
   480  	}
   481  }
   482  
   483  func TestUpdateIncludeRuleTree(t *testing.T) {
   484  	tests := map[string]struct {
   485  		params           UpdateIncludeRuleTreeRequest
   486  		responseStatus   int
   487  		responseBody     string
   488  		responseHeaders  map[string]string
   489  		expectedPath     string
   490  		expectedResponse *UpdateIncludeRuleTreeResponse
   491  		withError        error
   492  	}{
   493  		"200 OK - update rule tree": {
   494  			params: UpdateIncludeRuleTreeRequest{
   495  				IncludeID:      "inc_123456",
   496  				IncludeVersion: 2,
   497  				ContractID:     "test_contract",
   498  				GroupID:        "test_group",
   499  				DryRun:         true,
   500  				ValidateMode:   "fast",
   501  				ValidateRules:  false,
   502  				Rules: RulesUpdate{
   503  					Comments: "version comment",
   504  					Rules: Rules{
   505  						Comments: "default comment",
   506  						Behaviors: []RuleBehavior{
   507  							{
   508  								Name: "origin",
   509  								Options: RuleOptionsMap{
   510  									"httpPort":           float64(80),
   511  									"enableTrueClientIp": false,
   512  									"compress":           true,
   513  									"cacheKeyHostname":   "ORIGIN_HOSTNAME",
   514  									"forwardHostHeader":  "REQUEST_HOST_HEADER",
   515  									"hostname":           "origin.test.com",
   516  									"originType":         "CUSTOMER",
   517  								},
   518  							},
   519  							{
   520  								Name: "cpCode",
   521  								Options: RuleOptionsMap{
   522  									"value": map[string]interface{}{
   523  										"id":   float64(12345),
   524  										"name": "my CP code",
   525  									},
   526  								},
   527  							},
   528  						},
   529  						Children: []Rules{
   530  							{
   531  								Behaviors: []RuleBehavior{
   532  									{
   533  										Name: "gzipResponse",
   534  										Options: RuleOptionsMap{
   535  											"behavior": "ALWAYS",
   536  										},
   537  									},
   538  								},
   539  								Criteria: []RuleBehavior{
   540  									{
   541  										Locked: false,
   542  										Name:   "contentType",
   543  										Options: RuleOptionsMap{
   544  											"matchOperator":      "IS_ONE_OF",
   545  											"matchWildcard":      true,
   546  											"matchCaseSensitive": false,
   547  											"values":             []interface{}{"text/html*", "text/css*", "application/x-javascript*"},
   548  										},
   549  									},
   550  								},
   551  								Name: "Compress Text Content",
   552  							},
   553  						},
   554  						Criteria: []RuleBehavior{},
   555  						Name:     "default",
   556  						Options:  RuleOptions{IsSecure: false},
   557  						CustomOverride: &RuleCustomOverride{
   558  							OverrideID: "cbo_12345",
   559  							Name:       "mdc",
   560  						},
   561  						Variables: []RuleVariable{
   562  							{
   563  								Description: "This is a sample Property Manager variable.",
   564  								Hidden:      false,
   565  								Name:        "VAR_NAME",
   566  								Sensitive:   false,
   567  								Value:       "default value",
   568  							},
   569  						},
   570  					},
   571  				},
   572  			},
   573  			responseStatus: http.StatusOK,
   574  			responseHeaders: map[string]string{
   575  				"x-limit-elements-per-property-limit":            "3000",
   576  				"x-limit-elements-per-property-remaining":        "3000",
   577  				"x-limit-max-nested-rules-per-include-limit":     "4",
   578  				"x-limit-max-nested-rules-per-include-remaining": "4",
   579  			},
   580  			responseBody: `
   581  {
   582      "accountId": "test_account",
   583      "contractId": "test_contract",
   584      "groupId": "test_group",
   585      "includeId": "inc_123456",
   586      "includeVersion": 2,
   587     	"includeName": "test_include",
   588      "includeType": "MICROSERVICES",
   589      "etag": "etag",
   590      "ruleFormat": "v2020-11-02",
   591  	"comments": "version comment",
   592      "rules": {
   593          "name": "default",
   594          "comments": "default comment",
   595          "criteria": [],
   596          "children": [
   597              {
   598                  "name": "Compress Text Content",
   599                  "criteria": [
   600                      {
   601                          "name": "contentType",
   602                          "options": {
   603                              "matchOperator": "IS_ONE_OF",
   604                              "matchWildcard": true,
   605                              "matchCaseSensitive": false,
   606                              "values": [
   607                                  "text/html*",
   608                                  "text/css*",
   609                                  "application/x-javascript*"
   610                              ]
   611                          }
   612                      }
   613                  ],
   614                  "behaviors": [
   615                      {
   616                          "name": "gzipResponse",
   617                          "options": { "behavior": "ALWAYS" }
   618                      }
   619                  ]
   620              }
   621          ],
   622          "options": {
   623              "is_secure": false
   624          },
   625          "behaviors": [
   626              {
   627                  "name": "origin",
   628                  "options": {
   629                      "httpPort": 80,
   630                      "enableTrueClientIp": false,
   631                      "compress": true,
   632                      "cacheKeyHostname": "ORIGIN_HOSTNAME",
   633                      "forwardHostHeader": "REQUEST_HOST_HEADER",
   634                      "hostname": "origin.test.com",
   635                      "originType": "CUSTOMER"
   636                  }
   637              },
   638              {
   639                  "name": "cpCode",
   640                  "options": {
   641                      "value": {
   642                          "id": 12345,
   643                          "name": "my CP code"
   644                      }
   645                  }
   646              },
   647   			{
   648                  "name": "matchAdvanced",
   649                  "options": {
   650                      "value": {
   651                          "id": 12345,
   652                          "name": "my advanced match"
   653                      }
   654                  },
   655  				"locked": true,
   656  				"uuid": "fd6a63bc-120a-4891-a5f2-c479765d5553",
   657  				"templateUuid": "bedbac99-4ce1-43a3-96cc-b84c8cd30176"
   658              }
   659          ],
   660   		"customOverride": {
   661          	"overrideId": "cbo_12345",
   662          	"name": "mdc"
   663      	},
   664  		"templateUuid": "bedbac99-4ce1-43a3-96cc-b84c8cd30176",
   665  		"templateLink": "/platformtoolkit/service/ruletemplate/30582260/1?accountId=1-1TJZFB&gid=61726&ck=16.3.1.1",
   666  		"variables": [
   667              {
   668                  "name": "VAR_NAME",
   669                  "value": "default value",
   670                  "description": "This is a sample Property Manager variable.",
   671                  "hidden": false,
   672                  "sensitive": false
   673              }
   674          ]
   675      },
   676  	"errors": [
   677          {
   678              "type": "https://problems.example.net/papi/v0/validation/attribute_required",
   679              "errorLocation": "#/rules/behaviors/0/options/reason",
   680              "detail": "The Reason ID option on the Control Access behavior is required."
   681          }
   682      ]
   683  }`,
   684  			expectedPath: "/papi/v1/includes/inc_123456/versions/2/rules?contractId=test_contract&dryRun=true&groupId=test_group&validateMode=fast&validateRules=false",
   685  			expectedResponse: &UpdateIncludeRuleTreeResponse{
   686  				Response: Response{
   687  					AccountID:  "test_account",
   688  					ContractID: "test_contract",
   689  					GroupID:    "test_group",
   690  					Errors: []*Error{
   691  						{
   692  							Type:          "https://problems.example.net/papi/v0/validation/attribute_required",
   693  							ErrorLocation: "#/rules/behaviors/0/options/reason",
   694  							Detail:        "The Reason ID option on the Control Access behavior is required.",
   695  						},
   696  					},
   697  				},
   698  				ResponseHeaders: UpdateIncludeResponseHeaders{
   699  					ElementsPerPropertyRemaining:      "3000",
   700  					ElementsPerPropertyTotal:          "3000",
   701  					MaxNestedRulesPerIncludeRemaining: "4",
   702  					MaxNestedRulesPerIncludeTotal:     "4",
   703  				},
   704  				IncludeID:      "inc_123456",
   705  				IncludeVersion: 2,
   706  				IncludeName:    "test_include",
   707  				IncludeType:    IncludeTypeMicroServices,
   708  				Etag:           "etag",
   709  				RuleFormat:     "v2020-11-02",
   710  				Comments:       "version comment",
   711  				Rules: Rules{
   712  					Comments: "default comment",
   713  					Behaviors: []RuleBehavior{
   714  						{
   715  							Name: "origin",
   716  							Options: RuleOptionsMap{
   717  								"httpPort":           float64(80),
   718  								"enableTrueClientIp": false,
   719  								"compress":           true,
   720  								"cacheKeyHostname":   "ORIGIN_HOSTNAME",
   721  								"forwardHostHeader":  "REQUEST_HOST_HEADER",
   722  								"hostname":           "origin.test.com",
   723  								"originType":         "CUSTOMER",
   724  							},
   725  						},
   726  						{
   727  							Name: "cpCode",
   728  							Options: RuleOptionsMap{
   729  								"value": map[string]interface{}{
   730  									"id":   float64(12345),
   731  									"name": "my CP code",
   732  								},
   733  							},
   734  						},
   735  						{
   736  							Name: "matchAdvanced",
   737  							Options: RuleOptionsMap{
   738  								"value": map[string]interface{}{
   739  									"id":   float64(12345),
   740  									"name": "my advanced match",
   741  								},
   742  							},
   743  							Locked:       true,
   744  							UUID:         "fd6a63bc-120a-4891-a5f2-c479765d5553",
   745  							TemplateUuid: "bedbac99-4ce1-43a3-96cc-b84c8cd30176",
   746  						},
   747  					},
   748  					Children: []Rules{
   749  						{
   750  							Behaviors: []RuleBehavior{
   751  								{
   752  									Name: "gzipResponse",
   753  									Options: RuleOptionsMap{
   754  										"behavior": "ALWAYS",
   755  									},
   756  								},
   757  							},
   758  							Criteria: []RuleBehavior{
   759  								{
   760  									Locked: false,
   761  									Name:   "contentType",
   762  									Options: RuleOptionsMap{
   763  										"matchOperator":      "IS_ONE_OF",
   764  										"matchWildcard":      true,
   765  										"matchCaseSensitive": false,
   766  										"values":             []interface{}{"text/html*", "text/css*", "application/x-javascript*"},
   767  									},
   768  								},
   769  							},
   770  							Name: "Compress Text Content",
   771  						},
   772  					},
   773  					Criteria: []RuleBehavior{},
   774  					Name:     "default",
   775  					Options:  RuleOptions{IsSecure: false},
   776  					CustomOverride: &RuleCustomOverride{
   777  						OverrideID: "cbo_12345",
   778  						Name:       "mdc",
   779  					},
   780  					TemplateUuid: "bedbac99-4ce1-43a3-96cc-b84c8cd30176",
   781  					TemplateLink: "/platformtoolkit/service/ruletemplate/30582260/1?accountId=1-1TJZFB&gid=61726&ck=16.3.1.1",
   782  					Variables: []RuleVariable{
   783  						{
   784  							Description: "This is a sample Property Manager variable.",
   785  							Hidden:      false,
   786  							Name:        "VAR_NAME",
   787  							Sensitive:   false,
   788  							Value:       "default value",
   789  						},
   790  					},
   791  				},
   792  			},
   793  		},
   794  		"500 Internal Server Error": {
   795  			params: UpdateIncludeRuleTreeRequest{
   796  				IncludeID:      "inc_123456",
   797  				IncludeVersion: 2,
   798  				ContractID:     "test_contract",
   799  				GroupID:        "test_group",
   800  				DryRun:         true,
   801  				ValidateMode:   "fast",
   802  				ValidateRules:  false,
   803  				Rules: RulesUpdate{
   804  					Comments: "version comment",
   805  					Rules: Rules{
   806  						Comments: "default comment",
   807  						Behaviors: []RuleBehavior{
   808  							{
   809  								Name: "origin",
   810  								Options: RuleOptionsMap{
   811  									"httpPort":           float64(80),
   812  									"enableTrueClientIp": false,
   813  									"compress":           true,
   814  									"cacheKeyHostname":   "ORIGIN_HOSTNAME",
   815  									"forwardHostHeader":  "REQUEST_HOST_HEADER",
   816  									"hostname":           "origin.test.com",
   817  									"originType":         "CUSTOMER",
   818  								},
   819  							},
   820  							{
   821  								Name: "cpCode",
   822  								Options: RuleOptionsMap{
   823  									"value": map[string]interface{}{
   824  										"id":   float64(12345),
   825  										"name": "my CP code",
   826  									},
   827  								},
   828  							},
   829  						},
   830  						Children: []Rules{
   831  							{
   832  								Behaviors: []RuleBehavior{
   833  									{
   834  										Name: "gzipResponse",
   835  										Options: RuleOptionsMap{
   836  											"behavior": "ALWAYS",
   837  										},
   838  									},
   839  								},
   840  								Criteria: []RuleBehavior{
   841  									{
   842  										Locked: false,
   843  										Name:   "contentType",
   844  										Options: RuleOptionsMap{
   845  											"matchOperator":      "IS_ONE_OF",
   846  											"matchWildcard":      true,
   847  											"matchCaseSensitive": false,
   848  											"values":             []interface{}{"text/html*", "text/css*", "application/x-javascript*"},
   849  										},
   850  									},
   851  								},
   852  								Name: "Compress Text Content",
   853  							},
   854  						},
   855  						Criteria: []RuleBehavior{},
   856  						Name:     "default",
   857  						Options:  RuleOptions{IsSecure: false},
   858  						CustomOverride: &RuleCustomOverride{
   859  							OverrideID: "cbo_12345",
   860  							Name:       "mdc",
   861  						},
   862  						Variables: []RuleVariable{
   863  							{
   864  								Description: "This is a sample Property Manager variable.",
   865  								Hidden:      false,
   866  								Name:        "VAR_NAME",
   867  								Sensitive:   false,
   868  								Value:       "default value",
   869  							},
   870  						},
   871  					},
   872  				},
   873  			},
   874  			responseStatus: http.StatusInternalServerError,
   875  			responseBody: `
   876  {
   877  	"type": "internal_error",
   878      "title": "Internal Server Error",
   879      "detail": "Error updating rule tree",
   880      "status": 500
   881  }`,
   882  			expectedPath: "/papi/v1/includes/inc_123456/versions/2/rules?contractId=test_contract&dryRun=true&groupId=test_group&validateMode=fast&validateRules=false",
   883  			withError: &Error{
   884  				Type:       "internal_error",
   885  				Title:      "Internal Server Error",
   886  				Detail:     "Error updating rule tree",
   887  				StatusCode: http.StatusInternalServerError,
   888  			},
   889  		},
   890  		"validation error - missing includeId": {
   891  			params: UpdateIncludeRuleTreeRequest{
   892  				IncludeVersion: 2,
   893  				ContractID:     "test_contract",
   894  				GroupID:        "test_group",
   895  				DryRun:         true,
   896  				ValidateMode:   "fast",
   897  				ValidateRules:  false,
   898  				Rules: RulesUpdate{
   899  					Comments: "version comment",
   900  					Rules: Rules{
   901  						Comments: "default comment",
   902  						Behaviors: []RuleBehavior{
   903  							{
   904  								Name: "origin",
   905  								Options: RuleOptionsMap{
   906  									"httpPort":           float64(80),
   907  									"enableTrueClientIp": false,
   908  									"compress":           true,
   909  									"cacheKeyHostname":   "ORIGIN_HOSTNAME",
   910  									"forwardHostHeader":  "REQUEST_HOST_HEADER",
   911  									"hostname":           "origin.test.com",
   912  									"originType":         "CUSTOMER",
   913  								},
   914  							},
   915  							{
   916  								Name: "cpCode",
   917  								Options: RuleOptionsMap{
   918  									"value": map[string]interface{}{
   919  										"id":   float64(12345),
   920  										"name": "my CP code",
   921  									},
   922  								},
   923  							},
   924  						},
   925  						Children: []Rules{
   926  							{
   927  								Behaviors: []RuleBehavior{
   928  									{
   929  										Name: "gzipResponse",
   930  										Options: RuleOptionsMap{
   931  											"behavior": "ALWAYS",
   932  										},
   933  									},
   934  								},
   935  								Criteria: []RuleBehavior{
   936  									{
   937  										Locked: false,
   938  										Name:   "contentType",
   939  										Options: RuleOptionsMap{
   940  											"matchOperator":      "IS_ONE_OF",
   941  											"matchWildcard":      true,
   942  											"matchCaseSensitive": false,
   943  											"values":             []interface{}{"text/html*", "text/css*", "application/x-javascript*"},
   944  										},
   945  									},
   946  								},
   947  								Name: "Compress Text Content",
   948  							},
   949  						},
   950  						Criteria: []RuleBehavior{},
   951  						Name:     "default",
   952  						Options:  RuleOptions{IsSecure: false},
   953  						CustomOverride: &RuleCustomOverride{
   954  							OverrideID: "cbo_12345",
   955  							Name:       "mdc",
   956  						},
   957  						Variables: []RuleVariable{
   958  							{
   959  								Description: "This is a sample Property Manager variable.",
   960  								Hidden:      false,
   961  								Name:        "VAR_NAME",
   962  								Sensitive:   false,
   963  								Value:       "default value",
   964  							},
   965  						},
   966  					},
   967  				},
   968  			},
   969  			withError: ErrStructValidation,
   970  		},
   971  		"validation error - missing includeVersion": {
   972  			params: UpdateIncludeRuleTreeRequest{
   973  				IncludeID:     "inc_123456",
   974  				ContractID:    "test_contract",
   975  				GroupID:       "test_group",
   976  				DryRun:        true,
   977  				ValidateMode:  "fast",
   978  				ValidateRules: false,
   979  				Rules: RulesUpdate{
   980  					Comments: "version comment",
   981  					Rules: Rules{
   982  						Comments: "default comment",
   983  						Behaviors: []RuleBehavior{
   984  							{
   985  								Name: "origin",
   986  								Options: RuleOptionsMap{
   987  									"httpPort":           float64(80),
   988  									"enableTrueClientIp": false,
   989  									"compress":           true,
   990  									"cacheKeyHostname":   "ORIGIN_HOSTNAME",
   991  									"forwardHostHeader":  "REQUEST_HOST_HEADER",
   992  									"hostname":           "origin.test.com",
   993  									"originType":         "CUSTOMER",
   994  								},
   995  							},
   996  							{
   997  								Name: "cpCode",
   998  								Options: RuleOptionsMap{
   999  									"value": map[string]interface{}{
  1000  										"id":   float64(12345),
  1001  										"name": "my CP code",
  1002  									},
  1003  								},
  1004  							},
  1005  						},
  1006  						Children: []Rules{
  1007  							{
  1008  								Behaviors: []RuleBehavior{
  1009  									{
  1010  										Name: "gzipResponse",
  1011  										Options: RuleOptionsMap{
  1012  											"behavior": "ALWAYS",
  1013  										},
  1014  									},
  1015  								},
  1016  								Criteria: []RuleBehavior{
  1017  									{
  1018  										Locked: false,
  1019  										Name:   "contentType",
  1020  										Options: RuleOptionsMap{
  1021  											"matchOperator":      "IS_ONE_OF",
  1022  											"matchWildcard":      true,
  1023  											"matchCaseSensitive": false,
  1024  											"values":             []interface{}{"text/html*", "text/css*", "application/x-javascript*"},
  1025  										},
  1026  									},
  1027  								},
  1028  								Name: "Compress Text Content",
  1029  							},
  1030  						},
  1031  						Criteria: []RuleBehavior{},
  1032  						Name:     "default",
  1033  						Options:  RuleOptions{IsSecure: false},
  1034  						CustomOverride: &RuleCustomOverride{
  1035  							OverrideID: "cbo_12345",
  1036  							Name:       "mdc",
  1037  						},
  1038  						Variables: []RuleVariable{
  1039  							{
  1040  								Description: "This is a sample Property Manager variable.",
  1041  								Hidden:      false,
  1042  								Name:        "VAR_NAME",
  1043  								Sensitive:   false,
  1044  								Value:       "default value",
  1045  							},
  1046  						},
  1047  					},
  1048  				},
  1049  			},
  1050  			withError: ErrStructValidation,
  1051  		},
  1052  		"validation error - missing contractId": {
  1053  			params: UpdateIncludeRuleTreeRequest{
  1054  				IncludeID:      "inc_123456",
  1055  				IncludeVersion: 2,
  1056  				GroupID:        "test_group",
  1057  				DryRun:         true,
  1058  				ValidateMode:   "fast",
  1059  				ValidateRules:  false,
  1060  				Rules: RulesUpdate{
  1061  					Comments: "version comment",
  1062  					Rules: Rules{
  1063  						Comments: "default comment",
  1064  						Behaviors: []RuleBehavior{
  1065  							{
  1066  								Name: "origin",
  1067  								Options: RuleOptionsMap{
  1068  									"httpPort":           float64(80),
  1069  									"enableTrueClientIp": false,
  1070  									"compress":           true,
  1071  									"cacheKeyHostname":   "ORIGIN_HOSTNAME",
  1072  									"forwardHostHeader":  "REQUEST_HOST_HEADER",
  1073  									"hostname":           "origin.test.com",
  1074  									"originType":         "CUSTOMER",
  1075  								},
  1076  							},
  1077  							{
  1078  								Name: "cpCode",
  1079  								Options: RuleOptionsMap{
  1080  									"value": map[string]interface{}{
  1081  										"id":   float64(12345),
  1082  										"name": "my CP code",
  1083  									},
  1084  								},
  1085  							},
  1086  						},
  1087  						Children: []Rules{
  1088  							{
  1089  								Behaviors: []RuleBehavior{
  1090  									{
  1091  										Name: "gzipResponse",
  1092  										Options: RuleOptionsMap{
  1093  											"behavior": "ALWAYS",
  1094  										},
  1095  									},
  1096  								},
  1097  								Criteria: []RuleBehavior{
  1098  									{
  1099  										Locked: false,
  1100  										Name:   "contentType",
  1101  										Options: RuleOptionsMap{
  1102  											"matchOperator":      "IS_ONE_OF",
  1103  											"matchWildcard":      true,
  1104  											"matchCaseSensitive": false,
  1105  											"values":             []interface{}{"text/html*", "text/css*", "application/x-javascript*"},
  1106  										},
  1107  									},
  1108  								},
  1109  								Name: "Compress Text Content",
  1110  							},
  1111  						},
  1112  						Criteria: []RuleBehavior{},
  1113  						Name:     "default",
  1114  						Options:  RuleOptions{IsSecure: false},
  1115  						CustomOverride: &RuleCustomOverride{
  1116  							OverrideID: "cbo_12345",
  1117  							Name:       "mdc",
  1118  						},
  1119  						Variables: []RuleVariable{
  1120  							{
  1121  								Description: "This is a sample Property Manager variable.",
  1122  								Hidden:      false,
  1123  								Name:        "VAR_NAME",
  1124  								Sensitive:   false,
  1125  								Value:       "default value",
  1126  							},
  1127  						},
  1128  					},
  1129  				},
  1130  			},
  1131  			withError: ErrStructValidation,
  1132  		},
  1133  		"validation error - missing groupId": {
  1134  			params: UpdateIncludeRuleTreeRequest{
  1135  				IncludeID:      "inc_123456",
  1136  				IncludeVersion: 2,
  1137  				ContractID:     "test_contract",
  1138  				DryRun:         true,
  1139  				ValidateMode:   "fast",
  1140  				ValidateRules:  false,
  1141  				Rules: RulesUpdate{
  1142  					Comments: "version comment",
  1143  					Rules: Rules{
  1144  						Comments: "default comment",
  1145  						Behaviors: []RuleBehavior{
  1146  							{
  1147  								Name: "origin",
  1148  								Options: RuleOptionsMap{
  1149  									"httpPort":           float64(80),
  1150  									"enableTrueClientIp": false,
  1151  									"compress":           true,
  1152  									"cacheKeyHostname":   "ORIGIN_HOSTNAME",
  1153  									"forwardHostHeader":  "REQUEST_HOST_HEADER",
  1154  									"hostname":           "origin.test.com",
  1155  									"originType":         "CUSTOMER",
  1156  								},
  1157  							},
  1158  							{
  1159  								Name: "cpCode",
  1160  								Options: RuleOptionsMap{
  1161  									"value": map[string]interface{}{
  1162  										"id":   float64(12345),
  1163  										"name": "my CP code",
  1164  									},
  1165  								},
  1166  							},
  1167  						},
  1168  						Children: []Rules{
  1169  							{
  1170  								Behaviors: []RuleBehavior{
  1171  									{
  1172  										Name: "gzipResponse",
  1173  										Options: RuleOptionsMap{
  1174  											"behavior": "ALWAYS",
  1175  										},
  1176  									},
  1177  								},
  1178  								Criteria: []RuleBehavior{
  1179  									{
  1180  										Locked: false,
  1181  										Name:   "contentType",
  1182  										Options: RuleOptionsMap{
  1183  											"matchOperator":      "IS_ONE_OF",
  1184  											"matchWildcard":      true,
  1185  											"matchCaseSensitive": false,
  1186  											"values":             []interface{}{"text/html*", "text/css*", "application/x-javascript*"},
  1187  										},
  1188  									},
  1189  								},
  1190  								Name: "Compress Text Content",
  1191  							},
  1192  						},
  1193  						Criteria: []RuleBehavior{},
  1194  						Name:     "default",
  1195  						Options:  RuleOptions{IsSecure: false},
  1196  						CustomOverride: &RuleCustomOverride{
  1197  							OverrideID: "cbo_12345",
  1198  							Name:       "mdc",
  1199  						},
  1200  						Variables: []RuleVariable{
  1201  							{
  1202  								Description: "This is a sample Property Manager variable.",
  1203  								Hidden:      false,
  1204  								Name:        "VAR_NAME",
  1205  								Sensitive:   false,
  1206  								Value:       "default value",
  1207  							},
  1208  						},
  1209  					},
  1210  				},
  1211  			},
  1212  			withError: ErrStructValidation,
  1213  		},
  1214  		"validation error - invalid validation mode": {
  1215  			params: UpdateIncludeRuleTreeRequest{
  1216  				IncludeID:      "inc_123456",
  1217  				IncludeVersion: 2,
  1218  				ContractID:     "test_contract",
  1219  				GroupID:        "test_group",
  1220  				DryRun:         true,
  1221  				ValidateMode:   "test",
  1222  				ValidateRules:  false,
  1223  				Rules: RulesUpdate{
  1224  					Comments: "version comment",
  1225  					Rules: Rules{
  1226  						Comments: "default comment",
  1227  						Behaviors: []RuleBehavior{
  1228  							{
  1229  								Name: "origin",
  1230  								Options: RuleOptionsMap{
  1231  									"httpPort":           float64(80),
  1232  									"enableTrueClientIp": false,
  1233  									"compress":           true,
  1234  									"cacheKeyHostname":   "ORIGIN_HOSTNAME",
  1235  									"forwardHostHeader":  "REQUEST_HOST_HEADER",
  1236  									"hostname":           "origin.test.com",
  1237  									"originType":         "CUSTOMER",
  1238  								},
  1239  							},
  1240  							{
  1241  								Name: "cpCode",
  1242  								Options: RuleOptionsMap{
  1243  									"value": map[string]interface{}{
  1244  										"id":   float64(12345),
  1245  										"name": "my CP code",
  1246  									},
  1247  								},
  1248  							},
  1249  						},
  1250  						Children: []Rules{
  1251  							{
  1252  								Behaviors: []RuleBehavior{
  1253  									{
  1254  										Name: "gzipResponse",
  1255  										Options: RuleOptionsMap{
  1256  											"behavior": "ALWAYS",
  1257  										},
  1258  									},
  1259  								},
  1260  								Criteria: []RuleBehavior{
  1261  									{
  1262  										Locked: false,
  1263  										Name:   "contentType",
  1264  										Options: RuleOptionsMap{
  1265  											"matchOperator":      "IS_ONE_OF",
  1266  											"matchWildcard":      true,
  1267  											"matchCaseSensitive": false,
  1268  											"values":             []interface{}{"text/html*", "text/css*", "application/x-javascript*"},
  1269  										},
  1270  									},
  1271  								},
  1272  								Name: "Compress Text Content",
  1273  							},
  1274  						},
  1275  						Criteria: []RuleBehavior{},
  1276  						Name:     "default",
  1277  						Options:  RuleOptions{IsSecure: false},
  1278  						CustomOverride: &RuleCustomOverride{
  1279  							OverrideID: "cbo_12345",
  1280  							Name:       "mdc",
  1281  						},
  1282  						Variables: []RuleVariable{
  1283  							{
  1284  								Description: "This is a sample Property Manager variable.",
  1285  								Hidden:      false,
  1286  								Name:        "VAR_NAME",
  1287  								Sensitive:   false,
  1288  								Value:       "default value",
  1289  							},
  1290  						},
  1291  					},
  1292  				},
  1293  			},
  1294  			withError: ErrStructValidation,
  1295  		},
  1296  	}
  1297  
  1298  	for name, test := range tests {
  1299  		t.Run(name, func(t *testing.T) {
  1300  			mockServer := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  1301  				assert.Equal(t, test.expectedPath, r.URL.String())
  1302  				assert.Equal(t, http.MethodPut, r.Method)
  1303  
  1304  				if len(test.responseHeaders) > 0 {
  1305  					for header, value := range test.responseHeaders {
  1306  						w.Header().Set(header, value)
  1307  					}
  1308  				}
  1309  				w.WriteHeader(test.responseStatus)
  1310  				_, err := w.Write([]byte(test.responseBody))
  1311  				assert.NoError(t, err)
  1312  			}))
  1313  			client := mockAPIClient(t, mockServer)
  1314  			result, err := client.UpdateIncludeRuleTree(context.Background(), test.params)
  1315  			if test.withError != nil {
  1316  				assert.True(t, errors.Is(err, test.withError), "want: %s; got: %s", test.withError, err)
  1317  				return
  1318  			}
  1319  			require.NoError(t, err)
  1320  			assert.Equal(t, test.expectedResponse, result)
  1321  		})
  1322  	}
  1323  }