github.com/argoproj/argo-cd/v2@v2.10.9/applicationset/utils/utils_test.go (about)

     1  package utils
     2  
     3  import (
     4  	"crypto/x509"
     5  	"encoding/json"
     6  	"os"
     7  	"path"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/sirupsen/logrus"
    12  	logtest "github.com/sirupsen/logrus/hooks/test"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  	"k8s.io/apimachinery/pkg/runtime"
    18  	"k8s.io/apimachinery/pkg/types"
    19  
    20  	argoappsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
    21  )
    22  
    23  func TestRenderTemplateParams(t *testing.T) {
    24  
    25  	// Believe it or not, this is actually less complex than the equivalent solution using reflection
    26  	fieldMap := map[string]func(app *argoappsv1.Application) *string{}
    27  	fieldMap["Path"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.Path }
    28  	fieldMap["RepoURL"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.RepoURL }
    29  	fieldMap["TargetRevision"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.TargetRevision }
    30  	fieldMap["Chart"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.Chart }
    31  
    32  	fieldMap["Server"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Server }
    33  	fieldMap["Namespace"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Namespace }
    34  	fieldMap["Name"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Name }
    35  
    36  	fieldMap["Project"] = func(app *argoappsv1.Application) *string { return &app.Spec.Project }
    37  
    38  	emptyApplication := &argoappsv1.Application{
    39  		ObjectMeta: metav1.ObjectMeta{
    40  			Annotations:       map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"},
    41  			Labels:            map[string]string{"label-key": "label-value", "label-key2": "label-value2"},
    42  			CreationTimestamp: metav1.NewTime(time.Now()),
    43  			UID:               types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"),
    44  			Name:              "application-one",
    45  			Namespace:         "default",
    46  		},
    47  		Spec: argoappsv1.ApplicationSpec{
    48  			Source: &argoappsv1.ApplicationSource{
    49  				Path:           "",
    50  				RepoURL:        "",
    51  				TargetRevision: "",
    52  				Chart:          "",
    53  			},
    54  			Destination: argoappsv1.ApplicationDestination{
    55  				Server:    "",
    56  				Namespace: "",
    57  				Name:      "",
    58  			},
    59  			Project: "",
    60  		},
    61  	}
    62  
    63  	tests := []struct {
    64  		name        string
    65  		fieldVal    string
    66  		params      map[string]interface{}
    67  		expectedVal string
    68  	}{
    69  		{
    70  			name:        "simple substitution",
    71  			fieldVal:    "{{one}}",
    72  			expectedVal: "two",
    73  			params: map[string]interface{}{
    74  				"one": "two",
    75  			},
    76  		},
    77  		{
    78  			name:        "simple substitution with whitespace",
    79  			fieldVal:    "{{ one }}",
    80  			expectedVal: "two",
    81  			params: map[string]interface{}{
    82  				"one": "two",
    83  			},
    84  		},
    85  
    86  		{
    87  			name:        "template characters but not in a template",
    88  			fieldVal:    "}} {{",
    89  			expectedVal: "}} {{",
    90  			params: map[string]interface{}{
    91  				"one": "two",
    92  			},
    93  		},
    94  
    95  		{
    96  			name:        "nested template",
    97  			fieldVal:    "{{ }}",
    98  			expectedVal: "{{ }}",
    99  			params: map[string]interface{}{
   100  				"one": "{{ }}",
   101  			},
   102  		},
   103  		{
   104  			name:        "field with whitespace",
   105  			fieldVal:    "{{ }}",
   106  			expectedVal: "{{ }}",
   107  			params: map[string]interface{}{
   108  				" ": "two",
   109  				"":  "three",
   110  			},
   111  		},
   112  
   113  		{
   114  			name:        "template contains itself, containing itself",
   115  			fieldVal:    "{{one}}",
   116  			expectedVal: "{{one}}",
   117  			params: map[string]interface{}{
   118  				"{{one}}": "{{one}}",
   119  			},
   120  		},
   121  
   122  		{
   123  			name:        "template contains itself, containing something else",
   124  			fieldVal:    "{{one}}",
   125  			expectedVal: "{{one}}",
   126  			params: map[string]interface{}{
   127  				"{{one}}": "{{two}}",
   128  			},
   129  		},
   130  
   131  		{
   132  			name:        "templates are case sensitive",
   133  			fieldVal:    "{{ONE}}",
   134  			expectedVal: "{{ONE}}",
   135  			params: map[string]interface{}{
   136  				"{{one}}": "two",
   137  			},
   138  		},
   139  		{
   140  			name:        "multiple on a line",
   141  			fieldVal:    "{{one}}{{one}}",
   142  			expectedVal: "twotwo",
   143  			params: map[string]interface{}{
   144  				"one": "two",
   145  			},
   146  		},
   147  		{
   148  			name:        "multiple different on a line",
   149  			fieldVal:    "{{one}}{{three}}",
   150  			expectedVal: "twofour",
   151  			params: map[string]interface{}{
   152  				"one":   "two",
   153  				"three": "four",
   154  			},
   155  		},
   156  		{
   157  			name:        "multiple different on a line with quote",
   158  			fieldVal:    "{{one}} {{three}}",
   159  			expectedVal: "\"hello\" world four",
   160  			params: map[string]interface{}{
   161  				"one":   "\"hello\" world",
   162  				"three": "four",
   163  			},
   164  		},
   165  	}
   166  
   167  	for _, test := range tests {
   168  
   169  		t.Run(test.name, func(t *testing.T) {
   170  
   171  			for fieldName, getPtrFunc := range fieldMap {
   172  
   173  				// Clone the template application
   174  				application := emptyApplication.DeepCopy()
   175  
   176  				// Set the value of the target field, to the test value
   177  				*getPtrFunc(application) = test.fieldVal
   178  
   179  				// Render the cloned application, into a new application
   180  				render := Render{}
   181  				newApplication, err := render.RenderTemplateParams(application, nil, test.params, false, nil)
   182  
   183  				// Retrieve the value of the target field from the newApplication, then verify that
   184  				// the target field has been templated into the expected value
   185  				actualValue := *getPtrFunc(newApplication)
   186  				assert.Equal(t, test.expectedVal, actualValue, "Field '%s' had an unexpected value. expected: '%s' value: '%s'", fieldName, test.expectedVal, actualValue)
   187  				assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-key"], "annotation-value")
   188  				assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-key2"], "annotation-value2")
   189  				assert.Equal(t, newApplication.ObjectMeta.Labels["label-key"], "label-value")
   190  				assert.Equal(t, newApplication.ObjectMeta.Labels["label-key2"], "label-value2")
   191  				assert.Equal(t, newApplication.ObjectMeta.Name, "application-one")
   192  				assert.Equal(t, newApplication.ObjectMeta.Namespace, "default")
   193  				assert.Equal(t, newApplication.ObjectMeta.UID, types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"))
   194  				assert.Equal(t, newApplication.ObjectMeta.CreationTimestamp, application.ObjectMeta.CreationTimestamp)
   195  				assert.NoError(t, err)
   196  			}
   197  		})
   198  	}
   199  
   200  }
   201  
   202  func TestRenderHelmValuesObjectJson(t *testing.T) {
   203  
   204  	params := map[string]interface{}{
   205  		"test": "Hello world",
   206  	}
   207  
   208  	application := &argoappsv1.Application{
   209  		ObjectMeta: metav1.ObjectMeta{
   210  			Annotations:       map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"},
   211  			Labels:            map[string]string{"label-key": "label-value", "label-key2": "label-value2"},
   212  			CreationTimestamp: metav1.NewTime(time.Now()),
   213  			UID:               types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"),
   214  			Name:              "application-one",
   215  			Namespace:         "default",
   216  		},
   217  		Spec: argoappsv1.ApplicationSpec{
   218  			Source: &argoappsv1.ApplicationSource{
   219  				Path:           "",
   220  				RepoURL:        "",
   221  				TargetRevision: "",
   222  				Chart:          "",
   223  				Helm: &argoappsv1.ApplicationSourceHelm{
   224  					ValuesObject: &runtime.RawExtension{
   225  						Raw: []byte(`{
   226  								"some": {
   227  									"string": "{{.test}}"
   228  								}
   229  							  }`),
   230  					},
   231  				},
   232  			},
   233  			Destination: argoappsv1.ApplicationDestination{
   234  				Server:    "",
   235  				Namespace: "",
   236  				Name:      "",
   237  			},
   238  			Project: "",
   239  		},
   240  	}
   241  
   242  	// Render the cloned application, into a new application
   243  	render := Render{}
   244  	newApplication, err := render.RenderTemplateParams(application, nil, params, true, []string{})
   245  
   246  	assert.NoError(t, err)
   247  	assert.NotNil(t, newApplication)
   248  
   249  	var unmarshaled interface{}
   250  	err = json.Unmarshal(newApplication.Spec.Source.Helm.ValuesObject.Raw, &unmarshaled)
   251  
   252  	assert.NoError(t, err)
   253  	assert.Equal(t, unmarshaled.(map[string]interface{})["some"].(map[string]interface{})["string"], "Hello world")
   254  
   255  }
   256  
   257  func TestRenderHelmValuesObjectYaml(t *testing.T) {
   258  
   259  	params := map[string]interface{}{
   260  		"test": "Hello world",
   261  	}
   262  
   263  	application := &argoappsv1.Application{
   264  		ObjectMeta: metav1.ObjectMeta{
   265  			Annotations:       map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"},
   266  			Labels:            map[string]string{"label-key": "label-value", "label-key2": "label-value2"},
   267  			CreationTimestamp: metav1.NewTime(time.Now()),
   268  			UID:               types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"),
   269  			Name:              "application-one",
   270  			Namespace:         "default",
   271  		},
   272  		Spec: argoappsv1.ApplicationSpec{
   273  			Source: &argoappsv1.ApplicationSource{
   274  				Path:           "",
   275  				RepoURL:        "",
   276  				TargetRevision: "",
   277  				Chart:          "",
   278  				Helm: &argoappsv1.ApplicationSourceHelm{
   279  					ValuesObject: &runtime.RawExtension{
   280  						Raw: []byte(`some:
   281    string: "{{.test}}"`),
   282  					},
   283  				},
   284  			},
   285  			Destination: argoappsv1.ApplicationDestination{
   286  				Server:    "",
   287  				Namespace: "",
   288  				Name:      "",
   289  			},
   290  			Project: "",
   291  		},
   292  	}
   293  
   294  	// Render the cloned application, into a new application
   295  	render := Render{}
   296  	newApplication, err := render.RenderTemplateParams(application, nil, params, true, []string{})
   297  
   298  	assert.NoError(t, err)
   299  	assert.NotNil(t, newApplication)
   300  
   301  	var unmarshaled interface{}
   302  	err = json.Unmarshal(newApplication.Spec.Source.Helm.ValuesObject.Raw, &unmarshaled)
   303  
   304  	assert.NoError(t, err)
   305  	assert.Equal(t, unmarshaled.(map[string]interface{})["some"].(map[string]interface{})["string"], "Hello world")
   306  
   307  }
   308  
   309  func TestRenderTemplateParamsGoTemplate(t *testing.T) {
   310  
   311  	// Believe it or not, this is actually less complex than the equivalent solution using reflection
   312  	fieldMap := map[string]func(app *argoappsv1.Application) *string{}
   313  	fieldMap["Path"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.Path }
   314  	fieldMap["RepoURL"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.RepoURL }
   315  	fieldMap["TargetRevision"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.TargetRevision }
   316  	fieldMap["Chart"] = func(app *argoappsv1.Application) *string { return &app.Spec.Source.Chart }
   317  
   318  	fieldMap["Server"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Server }
   319  	fieldMap["Namespace"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Namespace }
   320  	fieldMap["Name"] = func(app *argoappsv1.Application) *string { return &app.Spec.Destination.Name }
   321  
   322  	fieldMap["Project"] = func(app *argoappsv1.Application) *string { return &app.Spec.Project }
   323  
   324  	emptyApplication := &argoappsv1.Application{
   325  		ObjectMeta: metav1.ObjectMeta{
   326  			Annotations:       map[string]string{"annotation-key": "annotation-value", "annotation-key2": "annotation-value2"},
   327  			Labels:            map[string]string{"label-key": "label-value", "label-key2": "label-value2"},
   328  			CreationTimestamp: metav1.NewTime(time.Now()),
   329  			UID:               types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"),
   330  			Name:              "application-one",
   331  			Namespace:         "default",
   332  		},
   333  		Spec: argoappsv1.ApplicationSpec{
   334  			Source: &argoappsv1.ApplicationSource{
   335  				Path:           "",
   336  				RepoURL:        "",
   337  				TargetRevision: "",
   338  				Chart:          "",
   339  			},
   340  			Destination: argoappsv1.ApplicationDestination{
   341  				Server:    "",
   342  				Namespace: "",
   343  				Name:      "",
   344  			},
   345  			Project: "",
   346  		},
   347  	}
   348  
   349  	tests := []struct {
   350  		name            string
   351  		fieldVal        string
   352  		params          map[string]interface{}
   353  		expectedVal     string
   354  		errorMessage    string
   355  		templateOptions []string
   356  	}{
   357  		{
   358  			name:        "simple substitution",
   359  			fieldVal:    "{{ .one }}",
   360  			expectedVal: "two",
   361  			params: map[string]interface{}{
   362  				"one": "two",
   363  			},
   364  		},
   365  		{
   366  			name:        "simple substitution with whitespace",
   367  			fieldVal:    "{{ .one }}",
   368  			expectedVal: "two",
   369  			params: map[string]interface{}{
   370  				"one": "two",
   371  			},
   372  		},
   373  		{
   374  			name:        "template contains itself, containing itself",
   375  			fieldVal:    "{{ .one }}",
   376  			expectedVal: "{{one}}",
   377  			params: map[string]interface{}{
   378  				"one": "{{one}}",
   379  			},
   380  		},
   381  
   382  		{
   383  			name:        "template contains itself, containing something else",
   384  			fieldVal:    "{{ .one }}",
   385  			expectedVal: "{{two}}",
   386  			params: map[string]interface{}{
   387  				"one": "{{two}}",
   388  			},
   389  		},
   390  		{
   391  			name:        "multiple on a line",
   392  			fieldVal:    "{{.one}}{{.one}}",
   393  			expectedVal: "twotwo",
   394  			params: map[string]interface{}{
   395  				"one": "two",
   396  			},
   397  		},
   398  		{
   399  			name:        "multiple different on a line",
   400  			fieldVal:    "{{.one}}{{.three}}",
   401  			expectedVal: "twofour",
   402  			params: map[string]interface{}{
   403  				"one":   "two",
   404  				"three": "four",
   405  			},
   406  		},
   407  		{
   408  			name:        "multiple different on a line with quote",
   409  			fieldVal:    "{{.one}} {{.three}}",
   410  			expectedVal: "\"hello\" world four",
   411  			params: map[string]interface{}{
   412  				"one":   "\"hello\" world",
   413  				"three": "four",
   414  			},
   415  		},
   416  		{
   417  			name:        "depth",
   418  			fieldVal:    "{{ .image.version }}",
   419  			expectedVal: "latest",
   420  			params: map[string]interface{}{
   421  				"replicas": 3,
   422  				"image": map[string]interface{}{
   423  					"name":    "busybox",
   424  					"version": "latest",
   425  				},
   426  			},
   427  		},
   428  		{
   429  			name:        "multiple depth",
   430  			fieldVal:    "{{ .image.name }}:{{ .image.version }}",
   431  			expectedVal: "busybox:latest",
   432  			params: map[string]interface{}{
   433  				"replicas": 3,
   434  				"image": map[string]interface{}{
   435  					"name":    "busybox",
   436  					"version": "latest",
   437  				},
   438  			},
   439  		},
   440  		{
   441  			name:        "if ok",
   442  			fieldVal:    "{{ if .hpa.enabled }}{{ .hpa.maxReplicas }}{{ else }}{{ .replicas }}{{ end }}",
   443  			expectedVal: "5",
   444  			params: map[string]interface{}{
   445  				"replicas": 3,
   446  				"hpa": map[string]interface{}{
   447  					"enabled":     true,
   448  					"minReplicas": 1,
   449  					"maxReplicas": 5,
   450  				},
   451  			},
   452  		},
   453  		{
   454  			name:        "if not ok",
   455  			fieldVal:    "{{ if .hpa.enabled }}{{ .hpa.maxReplicas }}{{ else }}{{ .replicas }}{{ end }}",
   456  			expectedVal: "3",
   457  			params: map[string]interface{}{
   458  				"replicas": 3,
   459  				"hpa": map[string]interface{}{
   460  					"enabled":     false,
   461  					"minReplicas": 1,
   462  					"maxReplicas": 5,
   463  				},
   464  			},
   465  		},
   466  		{
   467  			name:        "loop",
   468  			fieldVal:    "{{ range .volumes }}[{{ .name }}]{{ end }}",
   469  			expectedVal: "[volume-one][volume-two]",
   470  			params: map[string]interface{}{
   471  				"replicas": 3,
   472  				"volumes": []map[string]interface{}{
   473  					{
   474  						"name":     "volume-one",
   475  						"emptyDir": map[string]interface{}{},
   476  					},
   477  					{
   478  						"name":     "volume-two",
   479  						"emptyDir": map[string]interface{}{},
   480  					},
   481  				},
   482  			},
   483  		},
   484  		{
   485  			name:        "Index",
   486  			fieldVal:    `{{ index .admin "admin-ca" }}, {{ index .admin "admin-jks" }}`,
   487  			expectedVal: "value admin ca, value admin jks",
   488  			params: map[string]interface{}{
   489  				"admin": map[string]interface{}{
   490  					"admin-ca":  "value admin ca",
   491  					"admin-jks": "value admin jks",
   492  				},
   493  			},
   494  		},
   495  		{
   496  			name:        "Index",
   497  			fieldVal:    `{{ index .admin "admin-ca" }}, \\ "Hello world", {{ index .admin "admin-jks" }}`,
   498  			expectedVal: `value "admin" ca with \, \\ "Hello world", value admin jks`,
   499  			params: map[string]interface{}{
   500  				"admin": map[string]interface{}{
   501  					"admin-ca":  `value "admin" ca with \`,
   502  					"admin-jks": "value admin jks",
   503  				},
   504  			},
   505  		},
   506  		{
   507  			name:        "quote",
   508  			fieldVal:    `{{.quote}}`,
   509  			expectedVal: `"`,
   510  			params: map[string]interface{}{
   511  				"quote": `"`,
   512  			},
   513  		},
   514  		{
   515  			name:        "Test No Data",
   516  			fieldVal:    `{{.data}}`,
   517  			expectedVal: "{{.data}}",
   518  			params:      map[string]interface{}{},
   519  		},
   520  		{
   521  			name:        "Test Parse Error",
   522  			fieldVal:    `{{functiondoesnotexist}}`,
   523  			expectedVal: "",
   524  			params: map[string]interface{}{
   525  				"data": `a data string`,
   526  			},
   527  			errorMessage: `failed to parse template {{functiondoesnotexist}}: template: :1: function "functiondoesnotexist" not defined`,
   528  		},
   529  		{
   530  			name:        "Test template error",
   531  			fieldVal:    `{{.data.test}}`,
   532  			expectedVal: "",
   533  			params: map[string]interface{}{
   534  				"data": `a data string`,
   535  			},
   536  			errorMessage: `failed to execute go template {{.data.test}}: template: :1:7: executing "" at <.data.test>: can't evaluate field test in type interface {}`,
   537  		},
   538  		{
   539  			name:        "lookup missing value with missingkey=default",
   540  			fieldVal:    `--> {{.doesnotexist}} <--`,
   541  			expectedVal: `--> <no value> <--`,
   542  			params: map[string]interface{}{
   543  				// if no params are passed then for some reason templating is skipped
   544  				"unused": "this is not used",
   545  			},
   546  		},
   547  		{
   548  			name:        "lookup missing value with missingkey=error",
   549  			fieldVal:    `--> {{.doesnotexist}} <--`,
   550  			expectedVal: "",
   551  			params: map[string]interface{}{
   552  				// if no params are passed then for some reason templating is skipped
   553  				"unused": "this is not used",
   554  			},
   555  			templateOptions: []string{"missingkey=error"},
   556  			errorMessage:    `failed to execute go template --> {{.doesnotexist}} <--: template: :1:6: executing "" at <.doesnotexist>: map has no entry for key "doesnotexist"`,
   557  		},
   558  		{
   559  			name:        "toYaml",
   560  			fieldVal:    `{{ toYaml . | indent 2 }}`,
   561  			expectedVal: "  foo:\n    bar:\n      bool: true\n      number: 2\n      str: Hello world",
   562  			params: map[string]interface{}{
   563  				"foo": map[string]interface{}{
   564  					"bar": map[string]interface{}{
   565  						"bool":   true,
   566  						"number": 2,
   567  						"str":    "Hello world",
   568  					},
   569  				},
   570  			},
   571  		},
   572  		{
   573  			name:         "toYaml Error",
   574  			fieldVal:     `{{ toYaml . | indent 2 }}`,
   575  			expectedVal:  "  foo:\n    bar:\n      bool: true\n      number: 2\n      str: Hello world",
   576  			errorMessage: "failed to execute go template {{ toYaml . | indent 2 }}: template: :1:3: executing \"\" at <toYaml .>: error calling toYaml: error marshaling into JSON: json: unsupported type: func(*string)",
   577  			params: map[string]interface{}{
   578  				"foo": func(test *string) {
   579  				},
   580  			},
   581  		},
   582  		{
   583  			name:        "fromYaml",
   584  			fieldVal:    `{{ get (fromYaml .value) "hello" }}`,
   585  			expectedVal: "world",
   586  			params: map[string]interface{}{
   587  				"value": "hello: world",
   588  			},
   589  		},
   590  		{
   591  			name:         "fromYaml error",
   592  			fieldVal:     `{{ get (fromYaml .value) "hello" }}`,
   593  			expectedVal:  "world",
   594  			errorMessage: "failed to execute go template {{ get (fromYaml .value) \"hello\" }}: template: :1:8: executing \"\" at <fromYaml .value>: error calling fromYaml: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type map[string]interface {}",
   595  			params: map[string]interface{}{
   596  				"value": "non\n compliant\n yaml",
   597  			},
   598  		},
   599  		{
   600  			name:        "fromYamlArray",
   601  			fieldVal:    `{{ fromYamlArray .value | last }}`,
   602  			expectedVal: "bonjour tout le monde",
   603  			params: map[string]interface{}{
   604  				"value": "- hello world\n- bonjour tout le monde",
   605  			},
   606  		},
   607  		{
   608  			name:         "fromYamlArray error",
   609  			fieldVal:     `{{ fromYamlArray .value | last }}`,
   610  			expectedVal:  "bonjour tout le monde",
   611  			errorMessage: "failed to execute go template {{ fromYamlArray .value | last }}: template: :1:3: executing \"\" at <fromYamlArray .value>: error calling fromYamlArray: error unmarshaling JSON: while decoding JSON: json: cannot unmarshal string into Go value of type []interface {}",
   612  			params: map[string]interface{}{
   613  				"value": "non\n compliant\n yaml",
   614  			},
   615  		},
   616  	}
   617  
   618  	for _, test := range tests {
   619  
   620  		t.Run(test.name, func(t *testing.T) {
   621  
   622  			for fieldName, getPtrFunc := range fieldMap {
   623  
   624  				// Clone the template application
   625  				application := emptyApplication.DeepCopy()
   626  
   627  				// Set the value of the target field, to the test value
   628  				*getPtrFunc(application) = test.fieldVal
   629  
   630  				// Render the cloned application, into a new application
   631  				render := Render{}
   632  				newApplication, err := render.RenderTemplateParams(application, nil, test.params, true, test.templateOptions)
   633  
   634  				// Retrieve the value of the target field from the newApplication, then verify that
   635  				// the target field has been templated into the expected value
   636  				if test.errorMessage != "" {
   637  					assert.Error(t, err)
   638  					assert.Equal(t, test.errorMessage, err.Error())
   639  				} else {
   640  					assert.NoError(t, err)
   641  					actualValue := *getPtrFunc(newApplication)
   642  					assert.Equal(t, test.expectedVal, actualValue, "Field '%s' had an unexpected value. expected: '%s' value: '%s'", fieldName, test.expectedVal, actualValue)
   643  					assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-key"], "annotation-value")
   644  					assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-key2"], "annotation-value2")
   645  					assert.Equal(t, newApplication.ObjectMeta.Labels["label-key"], "label-value")
   646  					assert.Equal(t, newApplication.ObjectMeta.Labels["label-key2"], "label-value2")
   647  					assert.Equal(t, newApplication.ObjectMeta.Name, "application-one")
   648  					assert.Equal(t, newApplication.ObjectMeta.Namespace, "default")
   649  					assert.Equal(t, newApplication.ObjectMeta.UID, types.UID("d546da12-06b7-4f9a-8ea2-3adb16a20e2b"))
   650  					assert.Equal(t, newApplication.ObjectMeta.CreationTimestamp, application.ObjectMeta.CreationTimestamp)
   651  				}
   652  			}
   653  		})
   654  	}
   655  }
   656  
   657  func TestRenderGeneratorParams_does_not_panic(t *testing.T) {
   658  	// This test verifies that the RenderGeneratorParams function does not panic when the value in a map is a non-
   659  	// nillable type. This is a regression test.
   660  	render := Render{}
   661  	params := map[string]interface{}{
   662  		"branch": "master",
   663  	}
   664  	generator := &argoappsv1.ApplicationSetGenerator{
   665  		Plugin: &argoappsv1.PluginGenerator{
   666  			ConfigMapRef: argoappsv1.PluginConfigMapRef{
   667  				Name: "cm-plugin",
   668  			},
   669  			Input: argoappsv1.PluginInput{
   670  				Parameters: map[string]apiextensionsv1.JSON{
   671  					"branch": {
   672  						Raw: []byte(`"{{.branch}}"`),
   673  					},
   674  					"repo": {
   675  						Raw: []byte(`"argo-test"`),
   676  					},
   677  				},
   678  			},
   679  		},
   680  	}
   681  	_, err := render.RenderGeneratorParams(generator, params, true, []string{})
   682  	assert.NoError(t, err)
   683  }
   684  
   685  func TestRenderTemplateKeys(t *testing.T) {
   686  	t.Run("fasttemplate", func(t *testing.T) {
   687  		application := &argoappsv1.Application{
   688  			ObjectMeta: metav1.ObjectMeta{
   689  				Annotations: map[string]string{
   690  					"annotation-{{key}}": "annotation-{{value}}",
   691  				},
   692  			},
   693  		}
   694  
   695  		params := map[string]interface{}{
   696  			"key":   "some-key",
   697  			"value": "some-value",
   698  		}
   699  
   700  		render := Render{}
   701  		newApplication, err := render.RenderTemplateParams(application, nil, params, false, nil)
   702  		require.NoError(t, err)
   703  		require.Contains(t, newApplication.ObjectMeta.Annotations, "annotation-some-key")
   704  		assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-some-key"], "annotation-some-value")
   705  	})
   706  	t.Run("gotemplate", func(t *testing.T) {
   707  		application := &argoappsv1.Application{
   708  			ObjectMeta: metav1.ObjectMeta{
   709  				Annotations: map[string]string{
   710  					"annotation-{{ .key }}": "annotation-{{ .value }}",
   711  				},
   712  			},
   713  		}
   714  
   715  		params := map[string]interface{}{
   716  			"key":   "some-key",
   717  			"value": "some-value",
   718  		}
   719  
   720  		render := Render{}
   721  		newApplication, err := render.RenderTemplateParams(application, nil, params, true, nil)
   722  		require.NoError(t, err)
   723  		require.Contains(t, newApplication.ObjectMeta.Annotations, "annotation-some-key")
   724  		assert.Equal(t, newApplication.ObjectMeta.Annotations["annotation-some-key"], "annotation-some-value")
   725  	})
   726  }
   727  
   728  func Test_Render_Replace_no_panic_on_missing_closing_brace(t *testing.T) {
   729  	r := &Render{}
   730  	assert.NotPanics(t, func() {
   731  		_, err := r.Replace("{{properly.closed}} {{improperly.closed}", nil, false, []string{})
   732  		assert.Error(t, err)
   733  	})
   734  }
   735  
   736  func TestRenderTemplateParamsFinalizers(t *testing.T) {
   737  
   738  	emptyApplication := &argoappsv1.Application{
   739  		Spec: argoappsv1.ApplicationSpec{
   740  			Source: &argoappsv1.ApplicationSource{
   741  				Path:           "",
   742  				RepoURL:        "",
   743  				TargetRevision: "",
   744  				Chart:          "",
   745  			},
   746  			Destination: argoappsv1.ApplicationDestination{
   747  				Server:    "",
   748  				Namespace: "",
   749  				Name:      "",
   750  			},
   751  			Project: "",
   752  		},
   753  	}
   754  
   755  	for _, c := range []struct {
   756  		testName           string
   757  		syncPolicy         *argoappsv1.ApplicationSetSyncPolicy
   758  		existingFinalizers []string
   759  		expectedFinalizers []string
   760  	}{
   761  		{
   762  			testName:           "existing finalizer should be preserved",
   763  			existingFinalizers: []string{"existing-finalizer"},
   764  			syncPolicy:         nil,
   765  			expectedFinalizers: []string{"existing-finalizer"},
   766  		},
   767  		{
   768  			testName:           "background finalizer should be preserved",
   769  			existingFinalizers: []string{"resources-finalizer.argocd.argoproj.io/background"},
   770  			syncPolicy:         nil,
   771  			expectedFinalizers: []string{"resources-finalizer.argocd.argoproj.io/background"},
   772  		},
   773  
   774  		{
   775  			testName:           "empty finalizer and empty sync should use standard finalizer",
   776  			existingFinalizers: nil,
   777  			syncPolicy:         nil,
   778  			expectedFinalizers: []string{"resources-finalizer.argocd.argoproj.io"},
   779  		},
   780  
   781  		{
   782  			testName:           "standard finalizer should be preserved",
   783  			existingFinalizers: []string{"resources-finalizer.argocd.argoproj.io"},
   784  			syncPolicy:         nil,
   785  			expectedFinalizers: []string{"resources-finalizer.argocd.argoproj.io"},
   786  		},
   787  		{
   788  			testName:           "empty array finalizers should use standard finalizer",
   789  			existingFinalizers: []string{},
   790  			syncPolicy:         nil,
   791  			expectedFinalizers: []string{"resources-finalizer.argocd.argoproj.io"},
   792  		},
   793  		{
   794  			testName:           "non-nil sync policy should use standard finalizer",
   795  			existingFinalizers: nil,
   796  			syncPolicy:         &argoappsv1.ApplicationSetSyncPolicy{},
   797  			expectedFinalizers: []string{"resources-finalizer.argocd.argoproj.io"},
   798  		},
   799  		{
   800  			testName:           "preserveResourcesOnDeletion should not have a finalizer",
   801  			existingFinalizers: nil,
   802  			syncPolicy: &argoappsv1.ApplicationSetSyncPolicy{
   803  				PreserveResourcesOnDeletion: true,
   804  			},
   805  			expectedFinalizers: nil,
   806  		},
   807  		{
   808  			testName:           "user-specified finalizer should overwrite preserveResourcesOnDeletion",
   809  			existingFinalizers: []string{"resources-finalizer.argocd.argoproj.io/background"},
   810  			syncPolicy: &argoappsv1.ApplicationSetSyncPolicy{
   811  				PreserveResourcesOnDeletion: true,
   812  			},
   813  			expectedFinalizers: []string{"resources-finalizer.argocd.argoproj.io/background"},
   814  		},
   815  	} {
   816  
   817  		t.Run(c.testName, func(t *testing.T) {
   818  
   819  			// Clone the template application
   820  			application := emptyApplication.DeepCopy()
   821  			application.Finalizers = c.existingFinalizers
   822  
   823  			params := map[string]interface{}{
   824  				"one": "two",
   825  			}
   826  
   827  			// Render the cloned application, into a new application
   828  			render := Render{}
   829  
   830  			res, err := render.RenderTemplateParams(application, c.syncPolicy, params, true, nil)
   831  			assert.Nil(t, err)
   832  
   833  			assert.ElementsMatch(t, res.Finalizers, c.expectedFinalizers)
   834  
   835  		})
   836  
   837  	}
   838  
   839  }
   840  
   841  func TestCheckInvalidGenerators(t *testing.T) {
   842  
   843  	scheme := runtime.NewScheme()
   844  	err := argoappsv1.AddToScheme(scheme)
   845  	assert.Nil(t, err)
   846  	err = argoappsv1.AddToScheme(scheme)
   847  	assert.Nil(t, err)
   848  
   849  	for _, c := range []struct {
   850  		testName    string
   851  		appSet      argoappsv1.ApplicationSet
   852  		expectedMsg string
   853  	}{
   854  		{
   855  			testName: "invalid generator, without annotation",
   856  			appSet: argoappsv1.ApplicationSet{
   857  				ObjectMeta: metav1.ObjectMeta{
   858  					Name:      "test-app-set",
   859  					Namespace: "namespace",
   860  				},
   861  				Spec: argoappsv1.ApplicationSetSpec{
   862  					Generators: []argoappsv1.ApplicationSetGenerator{
   863  						{
   864  							List:     &argoappsv1.ListGenerator{},
   865  							Clusters: nil,
   866  							Git:      nil,
   867  						},
   868  						{
   869  							List:     nil,
   870  							Clusters: nil,
   871  							Git:      nil,
   872  						},
   873  						{
   874  							List:     nil,
   875  							Clusters: nil,
   876  							Git:      &argoappsv1.GitGenerator{},
   877  						},
   878  					},
   879  				},
   880  			},
   881  			expectedMsg: "ApplicationSet test-app-set contains unrecognized generators",
   882  		},
   883  		{
   884  			testName: "invalid generator, with annotation",
   885  			appSet: argoappsv1.ApplicationSet{
   886  				ObjectMeta: metav1.ObjectMeta{
   887  					Name:      "test-app-set",
   888  					Namespace: "namespace",
   889  					Annotations: map[string]string{
   890  						"kubectl.kubernetes.io/last-applied-configuration": `{
   891  							"spec":{
   892  								"generators":[
   893  									{"list":{}},
   894  									{"bbb":{}},
   895  									{"git":{}},
   896  									{"aaa":{}}
   897  								]
   898  							}
   899  						}`,
   900  					},
   901  				},
   902  				Spec: argoappsv1.ApplicationSetSpec{
   903  					Generators: []argoappsv1.ApplicationSetGenerator{
   904  						{
   905  							List:     &argoappsv1.ListGenerator{},
   906  							Clusters: nil,
   907  							Git:      nil,
   908  						},
   909  						{
   910  							List:     nil,
   911  							Clusters: nil,
   912  							Git:      nil,
   913  						},
   914  						{
   915  							List:     nil,
   916  							Clusters: nil,
   917  							Git:      &argoappsv1.GitGenerator{},
   918  						},
   919  						{
   920  							List:     nil,
   921  							Clusters: nil,
   922  							Git:      nil,
   923  						},
   924  					},
   925  				},
   926  			},
   927  			expectedMsg: "ApplicationSet test-app-set contains unrecognized generators: aaa, bbb",
   928  		},
   929  	} {
   930  		oldhooks := logrus.StandardLogger().ReplaceHooks(logrus.LevelHooks{})
   931  		defer logrus.StandardLogger().ReplaceHooks(oldhooks)
   932  		hook := logtest.NewGlobal()
   933  
   934  		_ = CheckInvalidGenerators(&c.appSet)
   935  		assert.True(t, len(hook.Entries) >= 1, c.testName)
   936  		assert.NotNil(t, hook.LastEntry(), c.testName)
   937  		if hook.LastEntry() != nil {
   938  			assert.Equal(t, logrus.WarnLevel, hook.LastEntry().Level, c.testName)
   939  			assert.Equal(t, c.expectedMsg, hook.LastEntry().Message, c.testName)
   940  		}
   941  		hook.Reset()
   942  	}
   943  }
   944  
   945  func TestInvalidGenerators(t *testing.T) {
   946  
   947  	scheme := runtime.NewScheme()
   948  	err := argoappsv1.AddToScheme(scheme)
   949  	assert.Nil(t, err)
   950  	err = argoappsv1.AddToScheme(scheme)
   951  	assert.Nil(t, err)
   952  
   953  	for _, c := range []struct {
   954  		testName        string
   955  		appSet          argoappsv1.ApplicationSet
   956  		expectedInvalid bool
   957  		expectedNames   map[string]bool
   958  	}{
   959  		{
   960  			testName: "valid generators, with annotation",
   961  			appSet: argoappsv1.ApplicationSet{
   962  				ObjectMeta: metav1.ObjectMeta{
   963  					Name:      "name",
   964  					Namespace: "namespace",
   965  					Annotations: map[string]string{
   966  						"kubectl.kubernetes.io/last-applied-configuration": `{
   967  							"spec":{
   968  								"generators":[
   969  									{"list":{}},
   970  									{"cluster":{}},
   971  									{"git":{}}
   972  								]
   973  							}
   974  						}`,
   975  					},
   976  				},
   977  				Spec: argoappsv1.ApplicationSetSpec{
   978  					Generators: []argoappsv1.ApplicationSetGenerator{
   979  						{
   980  							List:     &argoappsv1.ListGenerator{},
   981  							Clusters: nil,
   982  							Git:      nil,
   983  						},
   984  						{
   985  							List:     nil,
   986  							Clusters: &argoappsv1.ClusterGenerator{},
   987  							Git:      nil,
   988  						},
   989  						{
   990  							List:     nil,
   991  							Clusters: nil,
   992  							Git:      &argoappsv1.GitGenerator{},
   993  						},
   994  					},
   995  				},
   996  			},
   997  			expectedInvalid: false,
   998  			expectedNames:   map[string]bool{},
   999  		},
  1000  		{
  1001  			testName: "invalid generators, no annotation",
  1002  			appSet: argoappsv1.ApplicationSet{
  1003  				ObjectMeta: metav1.ObjectMeta{
  1004  					Name:      "name",
  1005  					Namespace: "namespace",
  1006  				},
  1007  				Spec: argoappsv1.ApplicationSetSpec{
  1008  					Generators: []argoappsv1.ApplicationSetGenerator{
  1009  						{
  1010  							List:     nil,
  1011  							Clusters: nil,
  1012  							Git:      nil,
  1013  						},
  1014  						{
  1015  							List:     nil,
  1016  							Clusters: nil,
  1017  							Git:      nil,
  1018  						},
  1019  					},
  1020  				},
  1021  			},
  1022  			expectedInvalid: true,
  1023  			expectedNames:   map[string]bool{},
  1024  		},
  1025  		{
  1026  			testName: "valid and invalid generators, no annotation",
  1027  			appSet: argoappsv1.ApplicationSet{
  1028  				ObjectMeta: metav1.ObjectMeta{
  1029  					Name:      "name",
  1030  					Namespace: "namespace",
  1031  				},
  1032  				Spec: argoappsv1.ApplicationSetSpec{
  1033  					Generators: []argoappsv1.ApplicationSetGenerator{
  1034  						{
  1035  							List:     nil,
  1036  							Clusters: &argoappsv1.ClusterGenerator{},
  1037  							Git:      nil,
  1038  						},
  1039  						{
  1040  							List:     nil,
  1041  							Clusters: nil,
  1042  							Git:      nil,
  1043  						},
  1044  						{
  1045  							List:     nil,
  1046  							Clusters: nil,
  1047  							Git:      &argoappsv1.GitGenerator{},
  1048  						},
  1049  					},
  1050  				},
  1051  			},
  1052  			expectedInvalid: true,
  1053  			expectedNames:   map[string]bool{},
  1054  		},
  1055  		{
  1056  			testName: "valid and invalid generators, with annotation",
  1057  			appSet: argoappsv1.ApplicationSet{
  1058  				ObjectMeta: metav1.ObjectMeta{
  1059  					Name:      "name",
  1060  					Namespace: "namespace",
  1061  					Annotations: map[string]string{
  1062  						"kubectl.kubernetes.io/last-applied-configuration": `{
  1063  							"spec":{
  1064  								"generators":[
  1065  									{"cluster":{}},
  1066  									{"bbb":{}},
  1067  									{"git":{}},
  1068  									{"aaa":{}}
  1069  								]
  1070  							}
  1071  						}`,
  1072  					},
  1073  				},
  1074  				Spec: argoappsv1.ApplicationSetSpec{
  1075  					Generators: []argoappsv1.ApplicationSetGenerator{
  1076  						{
  1077  							List:     nil,
  1078  							Clusters: &argoappsv1.ClusterGenerator{},
  1079  							Git:      nil,
  1080  						},
  1081  						{
  1082  							List:     nil,
  1083  							Clusters: nil,
  1084  							Git:      nil,
  1085  						},
  1086  						{
  1087  							List:     nil,
  1088  							Clusters: nil,
  1089  							Git:      &argoappsv1.GitGenerator{},
  1090  						},
  1091  						{
  1092  							List:     nil,
  1093  							Clusters: nil,
  1094  							Git:      nil,
  1095  						},
  1096  					},
  1097  				},
  1098  			},
  1099  			expectedInvalid: true,
  1100  			expectedNames: map[string]bool{
  1101  				"aaa": true,
  1102  				"bbb": true,
  1103  			},
  1104  		},
  1105  		{
  1106  			testName: "invalid generator, annotation with missing spec",
  1107  			appSet: argoappsv1.ApplicationSet{
  1108  				ObjectMeta: metav1.ObjectMeta{
  1109  					Name:      "name",
  1110  					Namespace: "namespace",
  1111  					Annotations: map[string]string{
  1112  						"kubectl.kubernetes.io/last-applied-configuration": `{
  1113  						}`,
  1114  					},
  1115  				},
  1116  				Spec: argoappsv1.ApplicationSetSpec{
  1117  					Generators: []argoappsv1.ApplicationSetGenerator{
  1118  						{
  1119  							List:     nil,
  1120  							Clusters: nil,
  1121  							Git:      nil,
  1122  						},
  1123  					},
  1124  				},
  1125  			},
  1126  			expectedInvalid: true,
  1127  			expectedNames:   map[string]bool{},
  1128  		},
  1129  		{
  1130  			testName: "invalid generator, annotation with missing generators array",
  1131  			appSet: argoappsv1.ApplicationSet{
  1132  				ObjectMeta: metav1.ObjectMeta{
  1133  					Name:      "name",
  1134  					Namespace: "namespace",
  1135  					Annotations: map[string]string{
  1136  						"kubectl.kubernetes.io/last-applied-configuration": `{
  1137  							"spec":{
  1138  							}
  1139  						}`,
  1140  					},
  1141  				},
  1142  				Spec: argoappsv1.ApplicationSetSpec{
  1143  					Generators: []argoappsv1.ApplicationSetGenerator{
  1144  						{
  1145  							List:     nil,
  1146  							Clusters: nil,
  1147  							Git:      nil,
  1148  						},
  1149  					},
  1150  				},
  1151  			},
  1152  			expectedInvalid: true,
  1153  			expectedNames:   map[string]bool{},
  1154  		},
  1155  		{
  1156  			testName: "invalid generator, annotation with empty generators array",
  1157  			appSet: argoappsv1.ApplicationSet{
  1158  				ObjectMeta: metav1.ObjectMeta{
  1159  					Name:      "name",
  1160  					Namespace: "namespace",
  1161  					Annotations: map[string]string{
  1162  						"kubectl.kubernetes.io/last-applied-configuration": `{
  1163  							"spec":{
  1164  								"generators":[
  1165  								]
  1166  							}
  1167  						}`,
  1168  					},
  1169  				},
  1170  				Spec: argoappsv1.ApplicationSetSpec{
  1171  					Generators: []argoappsv1.ApplicationSetGenerator{
  1172  						{
  1173  							List:     nil,
  1174  							Clusters: nil,
  1175  							Git:      nil,
  1176  						},
  1177  					},
  1178  				},
  1179  			},
  1180  			expectedInvalid: true,
  1181  			expectedNames:   map[string]bool{},
  1182  		},
  1183  		{
  1184  			testName: "invalid generator, annotation with empty generator",
  1185  			appSet: argoappsv1.ApplicationSet{
  1186  				ObjectMeta: metav1.ObjectMeta{
  1187  					Name:      "name",
  1188  					Namespace: "namespace",
  1189  					Annotations: map[string]string{
  1190  						"kubectl.kubernetes.io/last-applied-configuration": `{
  1191  							"spec":{
  1192  								"generators":[
  1193  									{}
  1194  								]
  1195  							}
  1196  						}`,
  1197  					},
  1198  				},
  1199  				Spec: argoappsv1.ApplicationSetSpec{
  1200  					Generators: []argoappsv1.ApplicationSetGenerator{
  1201  						{
  1202  							List:     nil,
  1203  							Clusters: nil,
  1204  							Git:      nil,
  1205  						},
  1206  					},
  1207  				},
  1208  			},
  1209  			expectedInvalid: true,
  1210  			expectedNames:   map[string]bool{},
  1211  		},
  1212  	} {
  1213  		hasInvalid, names := invalidGenerators(&c.appSet)
  1214  		assert.Equal(t, c.expectedInvalid, hasInvalid, c.testName)
  1215  		assert.Equal(t, c.expectedNames, names, c.testName)
  1216  	}
  1217  }
  1218  
  1219  func TestNormalizeBitbucketBasePath(t *testing.T) {
  1220  	for _, c := range []struct {
  1221  		testName         string
  1222  		basePath         string
  1223  		expectedBasePath string
  1224  	}{
  1225  		{
  1226  			testName:         "default api url",
  1227  			basePath:         "https://company.bitbucket.com",
  1228  			expectedBasePath: "https://company.bitbucket.com/rest",
  1229  		},
  1230  		{
  1231  			testName:         "with /rest suffix",
  1232  			basePath:         "https://company.bitbucket.com/rest",
  1233  			expectedBasePath: "https://company.bitbucket.com/rest",
  1234  		},
  1235  		{
  1236  			testName:         "with /rest/ suffix",
  1237  			basePath:         "https://company.bitbucket.com/rest/",
  1238  			expectedBasePath: "https://company.bitbucket.com/rest",
  1239  		},
  1240  	} {
  1241  		result := NormalizeBitbucketBasePath(c.basePath)
  1242  		assert.Equal(t, c.expectedBasePath, result, c.testName)
  1243  	}
  1244  }
  1245  
  1246  func TestSlugify(t *testing.T) {
  1247  	for _, c := range []struct {
  1248  		branch           string
  1249  		smartTruncate    bool
  1250  		length           int
  1251  		expectedBasePath string
  1252  	}{
  1253  		{
  1254  			branch:           "feat/a_really+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
  1255  			smartTruncate:    false,
  1256  			length:           50,
  1257  			expectedBasePath: "feat-a-really-long-pull-request-name-to-test-argo",
  1258  		},
  1259  		{
  1260  			branch:           "feat/a_really+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
  1261  			smartTruncate:    true,
  1262  			length:           53,
  1263  			expectedBasePath: "feat-a-really-long-pull-request-name-to-test-argo",
  1264  		},
  1265  		{
  1266  			branch:           "feat/areallylongpullrequestnametotestargoslugificationandbranchnameshorteningfeature",
  1267  			smartTruncate:    true,
  1268  			length:           50,
  1269  			expectedBasePath: "feat",
  1270  		},
  1271  		{
  1272  			branch:           "feat/areallylongpullrequestnametotestargoslugificationandbranchnameshorteningfeature",
  1273  			smartTruncate:    false,
  1274  			length:           50,
  1275  			expectedBasePath: "feat-areallylongpullrequestnametotestargoslugifica",
  1276  		},
  1277  	} {
  1278  		result := SlugifyName(c.length, c.smartTruncate, c.branch)
  1279  		assert.Equal(t, c.expectedBasePath, result, c.branch)
  1280  	}
  1281  }
  1282  
  1283  func TestGetTLSConfig(t *testing.T) {
  1284  	// certParsed, err := tls.X509KeyPair(test.Cert, test.PrivateKey)
  1285  	// require.NoError(t, err)
  1286  
  1287  	temppath := t.TempDir()
  1288  	cert := `
  1289  -----BEGIN CERTIFICATE-----
  1290  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
  1321  -----END CERTIFICATE-----
  1322  `
  1323  
  1324  	rootCAPath := path.Join(temppath, "foo.example.com")
  1325  	err := os.WriteFile(rootCAPath, []byte(cert), 0666)
  1326  	if err != nil {
  1327  		panic(err)
  1328  	}
  1329  
  1330  	certPool := x509.NewCertPool()
  1331  	ok := certPool.AppendCertsFromPEM([]byte(cert))
  1332  	assert.True(t, ok)
  1333  
  1334  	testCases := []struct {
  1335  		name                    string
  1336  		scmRootCAPath           string
  1337  		insecure                bool
  1338  		validateCertInTlsConfig bool
  1339  	}{
  1340  		{
  1341  			name:                    "Insecure mode configured, SCM Root CA Path not set",
  1342  			scmRootCAPath:           "",
  1343  			insecure:                true,
  1344  			validateCertInTlsConfig: false,
  1345  		},
  1346  		{
  1347  			name:                    "SCM Root CA Path set, Insecure mode set to false",
  1348  			scmRootCAPath:           rootCAPath,
  1349  			insecure:                false,
  1350  			validateCertInTlsConfig: true,
  1351  		},
  1352  		{
  1353  			name:                    "SCM Root CA Path set, Insecure mode set to true",
  1354  			scmRootCAPath:           rootCAPath,
  1355  			insecure:                true,
  1356  			validateCertInTlsConfig: true,
  1357  		},
  1358  	}
  1359  
  1360  	for _, testCase := range testCases {
  1361  		t.Run(testCase.name, func(t *testing.T) {
  1362  			tlsConfig := GetTlsConfig(testCase.scmRootCAPath, testCase.insecure)
  1363  			assert.Equal(t, testCase.insecure, tlsConfig.InsecureSkipVerify)
  1364  			if testCase.validateCertInTlsConfig {
  1365  				assert.NotNil(t, tlsConfig)
  1366  				assert.True(t, tlsConfig.RootCAs.Equal(certPool))
  1367  			}
  1368  		})
  1369  	}
  1370  }