github.com/kyma-incubator/compass/components/director@v0.0.0-20230623144113-d764f56ff805/pkg/graphql/webhook_validation_test.go (about)

     1  package graphql_test
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/kyma-incubator/compass/components/director/pkg/str"
     7  
     8  	"github.com/kyma-incubator/compass/components/director/pkg/correlation"
     9  
    10  	"github.com/kyma-incubator/compass/components/director/pkg/graphql"
    11  	"github.com/kyma-incubator/compass/components/director/pkg/inputvalidation/inputvalidationtest"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  func TestWebhookInput_Validate_Type(t *testing.T) {
    16  	testCases := []struct {
    17  		Name          string
    18  		Value         graphql.WebhookType
    19  		ExpectedValid bool
    20  	}{
    21  		{
    22  			Name:          "ExpectedValid",
    23  			Value:         graphql.WebhookTypeConfigurationChanged,
    24  			ExpectedValid: true,
    25  		},
    26  		{
    27  			Name:          "Invalid - Empty",
    28  			Value:         inputvalidationtest.EmptyString,
    29  			ExpectedValid: false,
    30  		},
    31  		{
    32  			Name:          "Invalid - Not enum",
    33  			Value:         "invalid",
    34  			ExpectedValid: false,
    35  		},
    36  	}
    37  
    38  	for _, testCase := range testCases {
    39  		t.Run(testCase.Name, func(t *testing.T) {
    40  			//GIVEN
    41  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
    42  			sut.Type = testCase.Value
    43  			// WHEN
    44  			err := sut.Validate()
    45  			// THEN
    46  			if testCase.ExpectedValid {
    47  				require.NoError(t, err)
    48  			} else {
    49  				require.Error(t, err)
    50  			}
    51  		})
    52  	}
    53  }
    54  
    55  func TestWebhookInput_Validate_URL(t *testing.T) {
    56  	testCases := []struct {
    57  		Name          string
    58  		Value         string
    59  		ExpectedValid bool
    60  	}{
    61  		{
    62  			Name:          "ExpectedValid",
    63  			Value:         inputvalidationtest.ValidURL,
    64  			ExpectedValid: true,
    65  		},
    66  		{
    67  			Name:          "Invalid - Empty string",
    68  			Value:         inputvalidationtest.EmptyString,
    69  			ExpectedValid: false,
    70  		},
    71  		{
    72  			Name:          "Invalid - Invalid URL",
    73  			Value:         inputvalidationtest.InvalidURL,
    74  			ExpectedValid: false,
    75  		},
    76  		{
    77  			Name:          "Invalid - Too long",
    78  			Value:         inputvalidationtest.URL257Long,
    79  			ExpectedValid: false,
    80  		},
    81  	}
    82  
    83  	for _, testCase := range testCases {
    84  		t.Run(testCase.Name, func(t *testing.T) {
    85  			//GIVEN
    86  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
    87  			sut.URL = &testCase.Value
    88  			// WHEN
    89  			err := sut.Validate()
    90  			// THEN
    91  			if testCase.ExpectedValid {
    92  				require.NoError(t, err)
    93  			} else {
    94  				require.Error(t, err)
    95  			}
    96  		})
    97  	}
    98  }
    99  
   100  func TestWebhookInput_Validate_Auth(t *testing.T) {
   101  	auth := fixValidAuthInput()
   102  	testCases := []struct {
   103  		Name          string
   104  		Value         *graphql.AuthInput
   105  		ExpectedValid bool
   106  	}{
   107  		{
   108  			Name:          "ExpectedValid",
   109  			Value:         &auth,
   110  			ExpectedValid: true,
   111  		},
   112  		{
   113  			Name:          "ExpectedValid - nil",
   114  			Value:         nil,
   115  			ExpectedValid: true,
   116  		},
   117  		{
   118  			Name:          "Invalid - Nested validation error",
   119  			Value:         &graphql.AuthInput{Credential: &graphql.CredentialDataInput{}},
   120  			ExpectedValid: false,
   121  		},
   122  	}
   123  
   124  	for _, testCase := range testCases {
   125  		t.Run(testCase.Name, func(t *testing.T) {
   126  			//GIVEN
   127  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   128  			sut.Auth = testCase.Value
   129  			// WHEN
   130  			err := sut.Validate()
   131  			// THEN
   132  			if testCase.ExpectedValid {
   133  				require.NoError(t, err)
   134  			} else {
   135  				require.Error(t, err)
   136  			}
   137  		})
   138  	}
   139  }
   140  
   141  func TestWebhookInput_Validate_CorrelationIDKey(t *testing.T) {
   142  	testCases := []struct {
   143  		Name          string
   144  		Value         *string
   145  		ExpectedValid bool
   146  	}{
   147  		{
   148  			Name:          "ExpectedValid",
   149  			Value:         stringPtr(correlation.RequestIDHeaderKey),
   150  			ExpectedValid: true,
   151  		},
   152  		{
   153  			Name:          "Empty",
   154  			Value:         stringPtr(inputvalidationtest.EmptyString),
   155  			ExpectedValid: true,
   156  		},
   157  		{
   158  			Name:          "Nil",
   159  			Value:         nil,
   160  			ExpectedValid: true,
   161  		},
   162  	}
   163  
   164  	for _, testCase := range testCases {
   165  		t.Run(testCase.Name, func(t *testing.T) {
   166  			//GIVEN
   167  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   168  			sut.CorrelationIDKey = testCase.Value
   169  			// WHEN
   170  			err := sut.Validate()
   171  			// THEN
   172  			if testCase.ExpectedValid {
   173  				require.NoError(t, err)
   174  			} else {
   175  				require.Error(t, err)
   176  			}
   177  		})
   178  	}
   179  }
   180  
   181  func TestWebhookInput_Validate_Mode(t *testing.T) {
   182  	testCases := []struct {
   183  		Name          string
   184  		Value         *graphql.WebhookMode
   185  		Type          *graphql.WebhookType
   186  		ExpectedValid bool
   187  	}{
   188  		{
   189  			Name:          "ExpectedValidMode",
   190  			Value:         webhookModePtr(graphql.WebhookModeSync),
   191  			ExpectedValid: true,
   192  		},
   193  		{
   194  			Name:          "ExpectedValidSyncModeAndConfigurationChangedType",
   195  			Value:         webhookModePtr(graphql.WebhookModeSync),
   196  			Type:          webhookTypePtr(graphql.WebhookTypeConfigurationChanged),
   197  			ExpectedValid: true,
   198  		},
   199  		{
   200  			Name:          "ExpectedValidAsyncCallbackModeAndConfigurationChangedType",
   201  			Value:         webhookModePtr(graphql.WebhookModeAsyncCallback),
   202  			Type:          webhookTypePtr(graphql.WebhookTypeConfigurationChanged),
   203  			ExpectedValid: true,
   204  		},
   205  		{
   206  			Name:          "ExpectedValidAsyncCallbackModeAndAppTntMappingType",
   207  			Value:         webhookModePtr(graphql.WebhookModeAsyncCallback),
   208  			Type:          webhookTypePtr(graphql.WebhookTypeApplicationTenantMapping),
   209  			ExpectedValid: true,
   210  		},
   211  		{
   212  			Name:          "ExpectedValidSyncModeAndFormationLifecycleType",
   213  			Value:         webhookModePtr(graphql.WebhookModeSync),
   214  			Type:          webhookTypePtr(graphql.WebhookTypeFormationLifecycle),
   215  			ExpectedValid: true,
   216  		},
   217  		{
   218  			Name:          "ExpectedInvalidAsyncModeAndConfigurationChangedType",
   219  			Value:         webhookModePtr(graphql.WebhookModeAsync),
   220  			Type:          webhookTypePtr(graphql.WebhookTypeConfigurationChanged),
   221  			ExpectedValid: false,
   222  		},
   223  		{
   224  			Name:          "ExpectedInvalidAsyncModeAndAppTntMappingType",
   225  			Value:         webhookModePtr(graphql.WebhookModeAsync),
   226  			Type:          webhookTypePtr(graphql.WebhookTypeApplicationTenantMapping),
   227  			ExpectedValid: false,
   228  		},
   229  		{
   230  			Name:          "ExpectedInvalidAsyncCallbackModeAndRegisterAppType",
   231  			Value:         webhookModePtr(graphql.WebhookModeAsyncCallback),
   232  			Type:          webhookTypePtr(graphql.WebhookTypeRegisterApplication),
   233  			ExpectedValid: false,
   234  		},
   235  		{
   236  			Name:          "ExpectedValidAsyncCallbackModeAndFormationLifecycleType",
   237  			Value:         webhookModePtr(graphql.WebhookModeAsyncCallback),
   238  			Type:          webhookTypePtr(graphql.WebhookTypeFormationLifecycle),
   239  			ExpectedValid: true,
   240  		},
   241  		{
   242  			Name:          "Empty",
   243  			Value:         webhookModePtr(inputvalidationtest.EmptyString),
   244  			ExpectedValid: true,
   245  		},
   246  		{
   247  			Name:          "Nil",
   248  			Value:         nil,
   249  			ExpectedValid: true,
   250  		},
   251  		{
   252  			Name:          "Invalid - Not enum",
   253  			Value:         webhookModePtr("invalid"),
   254  			ExpectedValid: false,
   255  		},
   256  	}
   257  
   258  	for _, testCase := range testCases {
   259  		t.Run(testCase.Name, func(t *testing.T) {
   260  			//GIVEN
   261  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   262  			sut.Mode = testCase.Value
   263  			if testCase.Type != nil {
   264  				sut.Type = *testCase.Type
   265  			}
   266  			// WHEN
   267  			err := sut.Validate()
   268  			// THEN
   269  			if testCase.ExpectedValid {
   270  				require.NoError(t, err)
   271  			} else {
   272  				require.Error(t, err)
   273  			}
   274  		})
   275  	}
   276  }
   277  
   278  func TestWebhookInput_Validate_RetryInterval(t *testing.T) {
   279  	testCases := []struct {
   280  		Name          string
   281  		Value         *int
   282  		ExpectedValid bool
   283  	}{
   284  		{
   285  			Name:          "ExpectedValid",
   286  			Value:         intPtr(120),
   287  			ExpectedValid: true,
   288  		},
   289  		{
   290  			Name:          "Empty",
   291  			Value:         intPtr(0),
   292  			ExpectedValid: true,
   293  		},
   294  		{
   295  			Name:          "Nil",
   296  			Value:         nil,
   297  			ExpectedValid: true,
   298  		},
   299  		{
   300  			Name:          "Invalid - negative number",
   301  			Value:         intPtr(-1),
   302  			ExpectedValid: false,
   303  		},
   304  	}
   305  
   306  	for _, testCase := range testCases {
   307  		t.Run(testCase.Name, func(t *testing.T) {
   308  			//GIVEN
   309  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   310  			sut.RetryInterval = testCase.Value
   311  			// WHEN
   312  			err := sut.Validate()
   313  			// THEN
   314  			if testCase.ExpectedValid {
   315  				require.NoError(t, err)
   316  			} else {
   317  				require.Error(t, err)
   318  			}
   319  		})
   320  	}
   321  }
   322  
   323  func TestWebhookInput_Validate_Timeout(t *testing.T) {
   324  	testCases := []struct {
   325  		Name          string
   326  		Value         *int
   327  		ExpectedValid bool
   328  	}{
   329  		{
   330  			Name:          "ExpectedValid",
   331  			Value:         intPtr(120),
   332  			ExpectedValid: true,
   333  		},
   334  		{
   335  			Name:          "Empty",
   336  			Value:         intPtr(0),
   337  			ExpectedValid: true,
   338  		},
   339  		{
   340  			Name:          "Nil",
   341  			Value:         nil,
   342  			ExpectedValid: true,
   343  		},
   344  		{
   345  			Name:          "Invalid - negative number",
   346  			Value:         intPtr(-1),
   347  			ExpectedValid: false,
   348  		},
   349  	}
   350  
   351  	for _, testCase := range testCases {
   352  		t.Run(testCase.Name, func(t *testing.T) {
   353  			//GIVEN
   354  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   355  			sut.Timeout = testCase.Value
   356  			// WHEN
   357  			err := sut.Validate()
   358  			// THEN
   359  			if testCase.ExpectedValid {
   360  				require.NoError(t, err)
   361  			} else {
   362  				require.Error(t, err)
   363  			}
   364  		})
   365  	}
   366  }
   367  
   368  func TestWebhookInput_Validate_URLTemplate(t *testing.T) {
   369  	testCases := []struct {
   370  		Name          string
   371  		Value         *string
   372  		ExpectedValid bool
   373  	}{
   374  		{
   375  			Name: "ExpectedValid",
   376  			Value: stringPtr(`{
   377  	   "method": "POST",
   378  	   "path": "https://my-int-system/api/v1/{{.Application.ID}}/pairing"
   379   }`),
   380  			ExpectedValid: true,
   381  		},
   382  		{
   383  			Name: "InvalidURL",
   384  			Value: stringPtr(`{
   385  	   "method": "POST",
   386  	   "path": "abc"
   387   }`),
   388  			ExpectedValid: false,
   389  		},
   390  		{
   391  			Name: "MissingPath",
   392  			Value: stringPtr(`{
   393  	   "method": "POST"
   394   }`),
   395  			ExpectedValid: false,
   396  		},
   397  		{
   398  			Name: "MissingMethod",
   399  			Value: stringPtr(`{
   400  	   "path": "https://my-int-system/api/v1/{{.Application.ID}}/pairing"
   401   }`),
   402  			ExpectedValid: false,
   403  		},
   404  		{
   405  			Name: "MethodNotAllowed",
   406  			Value: stringPtr(`{
   407  		"method": "HEAD",
   408  	   	"path": "https://my-int-system/api/v1/{{.Application.ID}}/pairing"
   409   }`),
   410  			ExpectedValid: false,
   411  		},
   412  		{
   413  			Name:          "Empty",
   414  			Value:         stringPtr(""),
   415  			ExpectedValid: false, // it should not be valid due to the fact that we also discard the legacy URL from the webhook in the test below
   416  		},
   417  		{
   418  			Name:          "Nil",
   419  			Value:         nil,
   420  			ExpectedValid: false, // it should not be valid due to the fact that we also discard the legacy URL from the webhook in the test below
   421  		},
   422  		{
   423  			Name:          "Invalid - contains unexpected property",
   424  			Value:         stringPtr("https://my-int-system/api/v1/{{.Application.Group}}/pairing"),
   425  			ExpectedValid: false,
   426  		},
   427  	}
   428  
   429  	for _, testCase := range testCases {
   430  		t.Run(testCase.Name, func(t *testing.T) {
   431  			//GIVEN
   432  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   433  			sut.URL = nil
   434  			sut.URLTemplate = testCase.Value
   435  			// WHEN
   436  			err := sut.Validate()
   437  			// THEN
   438  			if testCase.ExpectedValid {
   439  				require.NoError(t, err)
   440  			} else {
   441  				require.Error(t, err)
   442  			}
   443  		})
   444  	}
   445  }
   446  
   447  func TestWebhookInput_Validate_InputTemplate(t *testing.T) {
   448  	testCases := []struct {
   449  		Name          string
   450  		Value         *string
   451  		ExpectedValid bool
   452  	}{
   453  		{
   454  			Name: "ExpectedValid",
   455  			Value: stringPtr(`{
   456  			  "app_id": "{{.Application.ID}}",
   457  			  "app_name": "{{.Application.Name}}"
   458  			}`),
   459  			ExpectedValid: true,
   460  		},
   461  		{
   462  			Name:          "Empty",
   463  			Value:         stringPtr(""),
   464  			ExpectedValid: false,
   465  		},
   466  		{
   467  			Name:          "Nil",
   468  			Value:         nil,
   469  			ExpectedValid: true,
   470  		},
   471  		{
   472  			Name: "Invalid - contains unexpected property",
   473  			Value: stringPtr(`{
   474  			  "app_id": "{{.Application.ID}}",
   475  			  "app_name": "{{.Application.Group}}"
   476  			}`),
   477  			ExpectedValid: false,
   478  		},
   479  	}
   480  
   481  	for _, testCase := range testCases {
   482  		t.Run(testCase.Name, func(t *testing.T) {
   483  			//GIVEN
   484  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   485  			sut.InputTemplate = testCase.Value
   486  			// WHEN
   487  			err := sut.Validate()
   488  			// THEN
   489  			if testCase.ExpectedValid {
   490  				require.NoError(t, err)
   491  			} else {
   492  				require.Error(t, err)
   493  			}
   494  		})
   495  	}
   496  }
   497  
   498  func TestWebhookInput_Validate_HeaderTemplate(t *testing.T) {
   499  	testCases := []struct {
   500  		Name          string
   501  		Value         *string
   502  		ExpectedValid bool
   503  	}{
   504  		{
   505  			Name:          "ExpectedValid",
   506  			Value:         stringPtr(`{"Content-Type":["application/json"],"client_user":["{{.Headers.User_id}}"]}`),
   507  			ExpectedValid: true,
   508  		},
   509  		{
   510  			Name:          "Empty",
   511  			Value:         stringPtr(""),
   512  			ExpectedValid: false,
   513  		},
   514  		{
   515  			Name:          "Nil",
   516  			Value:         nil,
   517  			ExpectedValid: true,
   518  		},
   519  		{
   520  			Name:          "Invalid - contains unexpected property",
   521  			Value:         stringPtr(`{"Content-Type":["application/json"],"client_user":["{{.Body.Group}}"]}`),
   522  			ExpectedValid: false,
   523  		},
   524  	}
   525  
   526  	for _, testCase := range testCases {
   527  		t.Run(testCase.Name, func(t *testing.T) {
   528  			//GIVEN
   529  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   530  			sut.HeaderTemplate = testCase.Value
   531  			// WHEN
   532  			err := sut.Validate()
   533  			// THEN
   534  			if testCase.ExpectedValid {
   535  				require.NoError(t, err)
   536  			} else {
   537  				require.Error(t, err)
   538  			}
   539  		})
   540  	}
   541  }
   542  
   543  func TestWebhookInput_Validate_OutputTemplate(t *testing.T) {
   544  	testCases := []struct {
   545  		Name          string
   546  		Value         *string
   547  		ExpectedValid bool
   548  	}{
   549  		{
   550  			Name: "ExpectedValid",
   551  			Value: stringPtr(`{
   552  			   "location": "{{.Headers.Location}}",
   553  			   "success_status_code": 202,
   554  			   "error": "{{.Body.error}}"
   555  			 }`),
   556  			ExpectedValid: true,
   557  		},
   558  		{
   559  			Name: "ExpectedValid - missing location when SYNC mode",
   560  			Value: stringPtr(`{
   561  			   "success_status_code": 202,
   562  			   "error": "{{.Body.error}}"
   563  			 }`),
   564  			ExpectedValid: true,
   565  		},
   566  		{
   567  			Name: "Invalid - missing success status code",
   568  			Value: stringPtr(`{
   569  			   "location": "{{.Headers.Location}}",
   570  			   "error": "{{.Body.error}}"
   571  			 }`),
   572  			ExpectedValid: false,
   573  		},
   574  		{
   575  			Name: "Invalid - missing error",
   576  			Value: stringPtr(`{
   577  			   "location": "{{.Headers.Location}}",
   578  			   "success_status_code": 202
   579  			 }`),
   580  			ExpectedValid: false,
   581  		},
   582  		{
   583  			Name:          "Empty",
   584  			Value:         stringPtr(""),
   585  			ExpectedValid: false,
   586  		},
   587  		{
   588  			Name:          "Nil",
   589  			Value:         nil,
   590  			ExpectedValid: false,
   591  		},
   592  	}
   593  
   594  	for _, testCase := range testCases {
   595  		t.Run(testCase.Name, func(t *testing.T) {
   596  			//GIVEN
   597  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   598  			sut.InputTemplate = nil
   599  			sut.OutputTemplate = testCase.Value
   600  			// WHEN
   601  			err := sut.Validate()
   602  			// THEN
   603  			if testCase.ExpectedValid {
   604  				require.NoError(t, err)
   605  			} else {
   606  				require.Error(t, err)
   607  			}
   608  		})
   609  	}
   610  }
   611  
   612  func TestWebhookInput_Validate_StatusTemplate(t *testing.T) {
   613  	testCases := []struct {
   614  		Name          string
   615  		Value         *string
   616  		ExpectedValid bool
   617  	}{
   618  		{
   619  			Name: "ExpectedValid",
   620  			Value: stringPtr(`{
   621  			   "status": "{{.Body.status}}",
   622  			   "success_status_code": 200,
   623  			   "success_status_identifier": "SUCCESS",
   624  			   "in_progress_status_identifier": "IN_PROGRESS",
   625  			   "failed_status_identifier": "FAILED",
   626  			   "error": "{{.Body.error}}"
   627  			}`),
   628  			ExpectedValid: true,
   629  		},
   630  		{
   631  			Name: "Invalid - missing error",
   632  			Value: stringPtr(`{
   633  			   "status": "{{.Body.status}}",
   634  			   "success_status_code": 200,
   635  			   "success_status_identifier": "SUCCESS",
   636  			   "in_progress_status_identifier": "IN_PROGRESS",
   637  			   "failed_status_identifier": "FAILED"
   638  			 }`),
   639  			ExpectedValid: false,
   640  		},
   641  		{
   642  			Name: "Invalid -  missing status",
   643  			Value: stringPtr(`{
   644  			   "success_status_code": 200,
   645  			   "success_status_identifier": "SUCCESS",
   646  			   "in_progress_status_identifier": "IN_PROGRESS",
   647  			   "failed_status_identifier": "FAILED",
   648  			   "error": "{{.Body.error}}"
   649  			 }`),
   650  			ExpectedValid: false,
   651  		},
   652  		{
   653  			Name: "Invalid - missing success status code",
   654  			Value: stringPtr(`{
   655  			   "status": "{{.Body.status}}",
   656  			   "success_status_identifier": "SUCCESS",
   657  			   "in_progress_status_identifier": "IN_PROGRESS",
   658  			   "failed_status_identifier": "FAILED",
   659  			   "error": "{{.Body.error}}"
   660  			 }`),
   661  			ExpectedValid: false,
   662  		},
   663  		{
   664  			Name: "Invalid - missing success status identifier",
   665  			Value: stringPtr(`{
   666  			   "status": "{{.Body.status}}",
   667  			   "success_status_code": 200,
   668  			   "in_progress_status_identifier": "IN_PROGRESS",
   669  			   "failed_status_identifier": "FAILED",
   670  			   "error": "{{.Body.error}}"
   671  			}`),
   672  			ExpectedValid: false,
   673  		},
   674  		{
   675  			Name: "Invalid - missing in progress status identifier",
   676  			Value: stringPtr(`{
   677  			   "status": "{{.Body.status}}",
   678  			   "success_status_code": 200,
   679  			   "success_status_identifier": "SUCCESS",
   680  			   "failed_status_identifier": "FAILED",
   681  			   "error": "{{.Body.error}}"
   682  			}`),
   683  			ExpectedValid: false,
   684  		},
   685  		{
   686  			Name: "Invalid - missing failed status identifier",
   687  			Value: stringPtr(`{
   688  			   "status": "{{.Body.status}}",
   689  			   "success_status_code": 200,
   690  			   "success_status_identifier": "SUCCESS",
   691  			   "in_progress_status_identifier": "IN_PROGRESS",
   692  			   "error": "{{.Body.error}}"
   693  			}`),
   694  			ExpectedValid: false,
   695  		},
   696  		{
   697  			Name:          "Empty",
   698  			Value:         stringPtr(""),
   699  			ExpectedValid: false,
   700  		},
   701  		{
   702  			Name:          "Nil",
   703  			Value:         nil,
   704  			ExpectedValid: false,
   705  		},
   706  	}
   707  
   708  	for _, testCase := range testCases {
   709  		t.Run(testCase.Name, func(t *testing.T) {
   710  			//GIVEN
   711  			sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   712  			sut.Mode = webhookModePtr(graphql.WebhookModeAsync)
   713  			sut.StatusTemplate = testCase.Value
   714  			// WHEN
   715  			err := sut.Validate()
   716  			// THEN
   717  			if testCase.ExpectedValid {
   718  				require.NoError(t, err)
   719  			} else {
   720  				require.Error(t, err)
   721  			}
   722  		})
   723  	}
   724  }
   725  
   726  func TestWebhookInput_Validate_BothURLAndURLTemplate(t *testing.T) {
   727  	sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   728  	sut.URL = stringPtr("https://my-int-system/api/v1/123/pairing")
   729  	sut.URLTemplate = stringPtr("https://my-int-system/api/v1/{{.Application.ID}}/pairing")
   730  	// WHEN
   731  	err := sut.Validate()
   732  	// THEN
   733  	require.Error(t, err)
   734  }
   735  
   736  func TestWebhookInput_Validate_AsyncWebhook_MissingLocationInOutputTemplate_ShouldReturnError(t *testing.T) {
   737  	sut := fixValidWebhookInput(inputvalidationtest.ValidURL)
   738  	sut.Mode = webhookModePtr(graphql.WebhookModeAsync)
   739  	sut.OutputTemplate = stringPtr(`{
   740  	   "success_status_code": 202,
   741  	   "error": "{{.Body.error}}"
   742  	}`)
   743  	// WHEN
   744  	err := sut.Validate()
   745  	// THEN
   746  	require.Error(t, err)
   747  }
   748  
   749  func TestWebhookInput_Validate_MissingOutputTemplateForCertainTypes(t *testing.T) {
   750  	webhookMode := graphql.WebhookModeSync
   751  	testCases := []struct {
   752  		Name          string
   753  		Input         graphql.WebhookInput
   754  		ExpectedValid bool
   755  	}{
   756  		{
   757  			Name:          "Success when missing for type: OPEN_RESOURCE_DISCOVERY",
   758  			ExpectedValid: true,
   759  			Input: graphql.WebhookInput{
   760  				Type: graphql.WebhookTypeOpenResourceDiscovery,
   761  				Mode: &webhookMode,
   762  				URL:  str.Ptr(inputvalidationtest.ValidURL),
   763  			},
   764  		},
   765  		{
   766  			Name:          "Success when missing for type: CONFIGURATION_CHANGED",
   767  			ExpectedValid: true,
   768  			Input: graphql.WebhookInput{
   769  				Type: graphql.WebhookTypeConfigurationChanged,
   770  				Mode: &webhookMode,
   771  				URL:  str.Ptr(inputvalidationtest.ValidURL),
   772  			},
   773  		},
   774  		{
   775  			Name:          "Fails when missing for type: UNREGISTER_APPLICATION",
   776  			ExpectedValid: false,
   777  			Input: graphql.WebhookInput{
   778  				Type: graphql.WebhookTypeUnregisterApplication,
   779  				Mode: &webhookMode,
   780  				URL:  str.Ptr(inputvalidationtest.ValidURL),
   781  			},
   782  		},
   783  		{
   784  			Name:          "Fails when missing for type: REGISTER_APPLICATION",
   785  			ExpectedValid: false,
   786  			Input: graphql.WebhookInput{
   787  				Type: graphql.WebhookTypeRegisterApplication,
   788  				Mode: &webhookMode,
   789  				URL:  str.Ptr(inputvalidationtest.ValidURL),
   790  			},
   791  		},
   792  	}
   793  
   794  	for _, testCase := range testCases {
   795  		t.Run(testCase.Name, func(t *testing.T) {
   796  			err := testCase.Input.Validate()
   797  			if testCase.ExpectedValid {
   798  				require.NoError(t, err)
   799  			} else {
   800  				require.Error(t, err)
   801  			}
   802  		})
   803  	}
   804  }
   805  
   806  func fixValidWebhookInput(url string) graphql.WebhookInput {
   807  	template := `{}`
   808  	outputTemplate := `{
   809  	   "location": "{{.Headers.Location}}",
   810  	   "success_status_code": 202,
   811  	   "error": "{{.Body.error}}"
   812   }`
   813  	webhookMode := graphql.WebhookModeSync
   814  	webhookInput := graphql.WebhookInput{
   815  		Type:           graphql.WebhookTypeUnregisterApplication,
   816  		Mode:           &webhookMode,
   817  		InputTemplate:  &template,
   818  		HeaderTemplate: &template,
   819  		OutputTemplate: &outputTemplate,
   820  	}
   821  
   822  	if url != "" {
   823  		webhookInput.URL = &url
   824  	} else {
   825  		webhookInput.URLTemplate = stringPtr(`{
   826  	   "method": "POST",
   827  	   "path": "https://my-int-system/api/v1/{{.Application.ID}}/pairing"
   828   }`)
   829  	}
   830  
   831  	return webhookInput
   832  }
   833  
   834  func stringPtr(s string) *string {
   835  	return &s
   836  }
   837  
   838  func intPtr(n int) *int {
   839  	return &n
   840  }
   841  
   842  func webhookModePtr(mode graphql.WebhookMode) *graphql.WebhookMode {
   843  	return &mode
   844  }
   845  
   846  func webhookTypePtr(whType graphql.WebhookType) *graphql.WebhookType {
   847  	return &whType
   848  }