github.com/argoproj/argo-cd/v3@v3.2.1/applicationset/generators/pull_request_test.go (about)

     1  package generators
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	"github.com/stretchr/testify/require"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  
    12  	pullrequest "github.com/argoproj/argo-cd/v3/applicationset/services/pull_request"
    13  	argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    14  )
    15  
    16  func TestPullRequestGithubGenerateParams(t *testing.T) {
    17  	ctx := t.Context()
    18  	cases := []struct {
    19  		selectFunc                  func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error)
    20  		values                      map[string]string
    21  		expected                    []map[string]any
    22  		expectedErr                 error
    23  		applicationSet              argoprojiov1alpha1.ApplicationSet
    24  		continueOnRepoNotFoundError bool
    25  	}{
    26  		{
    27  			selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
    28  				return pullrequest.NewFakeService(
    29  					ctx,
    30  					[]*pullrequest.PullRequest{
    31  						{
    32  							Number:       1,
    33  							Title:        "title1",
    34  							Branch:       "branch1",
    35  							TargetBranch: "master",
    36  							HeadSHA:      "089d92cbf9ff857a39e6feccd32798ca700fb958",
    37  							Author:       "testName",
    38  						},
    39  					},
    40  					nil,
    41  				)
    42  			},
    43  			expected: []map[string]any{
    44  				{
    45  					"number":             "1",
    46  					"title":              "title1",
    47  					"branch":             "branch1",
    48  					"branch_slug":        "branch1",
    49  					"target_branch":      "master",
    50  					"target_branch_slug": "master",
    51  					"head_sha":           "089d92cbf9ff857a39e6feccd32798ca700fb958",
    52  					"head_short_sha":     "089d92cb",
    53  					"head_short_sha_7":   "089d92c",
    54  					"author":             "testName",
    55  				},
    56  			},
    57  			expectedErr: nil,
    58  		},
    59  		{
    60  			selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
    61  				return pullrequest.NewFakeService(
    62  					ctx,
    63  					[]*pullrequest.PullRequest{
    64  						{
    65  							Number:       2,
    66  							Title:        "title2",
    67  							Branch:       "feat/areally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
    68  							TargetBranch: "feat/anotherreally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
    69  							HeadSHA:      "9b34ff5bd418e57d58891eb0aa0728043ca1e8be",
    70  							Author:       "testName",
    71  						},
    72  					},
    73  					nil,
    74  				)
    75  			},
    76  			expected: []map[string]any{
    77  				{
    78  					"number":             "2",
    79  					"title":              "title2",
    80  					"branch":             "feat/areally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
    81  					"branch_slug":        "feat-areally-long-pull-request-name-to-test-argo",
    82  					"target_branch":      "feat/anotherreally+long_pull_request_name_to_test_argo_slugification_and_branch_name_shortening_feature",
    83  					"target_branch_slug": "feat-anotherreally-long-pull-request-name-to-test",
    84  					"head_sha":           "9b34ff5bd418e57d58891eb0aa0728043ca1e8be",
    85  					"head_short_sha":     "9b34ff5b",
    86  					"head_short_sha_7":   "9b34ff5",
    87  					"author":             "testName",
    88  				},
    89  			},
    90  			expectedErr: nil,
    91  		},
    92  		{
    93  			selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
    94  				return pullrequest.NewFakeService(
    95  					ctx,
    96  					[]*pullrequest.PullRequest{
    97  						{
    98  							Number:       1,
    99  							Title:        "title1",
   100  							Branch:       "a-very-short-sha",
   101  							TargetBranch: "master",
   102  							HeadSHA:      "abcd",
   103  							Author:       "testName",
   104  						},
   105  					},
   106  					nil,
   107  				)
   108  			},
   109  			expected: []map[string]any{
   110  				{
   111  					"number":             "1",
   112  					"title":              "title1",
   113  					"branch":             "a-very-short-sha",
   114  					"branch_slug":        "a-very-short-sha",
   115  					"target_branch":      "master",
   116  					"target_branch_slug": "master",
   117  					"head_sha":           "abcd",
   118  					"head_short_sha":     "abcd",
   119  					"head_short_sha_7":   "abcd",
   120  					"author":             "testName",
   121  				},
   122  			},
   123  			expectedErr: nil,
   124  		},
   125  		{
   126  			selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
   127  				return pullrequest.NewFakeService(
   128  					ctx,
   129  					[]*pullrequest.PullRequest{
   130  						{
   131  							Number:       1,
   132  							Title:        "title1",
   133  							Branch:       "my_branch",
   134  							TargetBranch: "master",
   135  							HeadSHA:      "abcd",
   136  							Author:       "testName",
   137  						},
   138  					},
   139  					nil,
   140  				)
   141  			},
   142  			values: map[string]string{
   143  				"foo":       "bar",
   144  				"pr_branch": "{{ branch }}",
   145  			},
   146  			expected: []map[string]any{
   147  				{
   148  					"number":             "1",
   149  					"title":              "title1",
   150  					"branch":             "my_branch",
   151  					"branch_slug":        "my-branch",
   152  					"target_branch":      "master",
   153  					"target_branch_slug": "master",
   154  					"head_sha":           "abcd",
   155  					"head_short_sha":     "abcd",
   156  					"head_short_sha_7":   "abcd",
   157  					"author":             "testName",
   158  					"values.foo":         "bar",
   159  					"values.pr_branch":   "my_branch",
   160  				},
   161  			},
   162  			expectedErr: nil,
   163  		},
   164  		{
   165  			selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
   166  				return pullrequest.NewFakeService(
   167  					ctx,
   168  					nil,
   169  					errors.New("fake error"),
   170  				)
   171  			},
   172  			expected:    nil,
   173  			expectedErr: errors.New("error listing repos: fake error"),
   174  		},
   175  		{
   176  			selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
   177  				return pullrequest.NewFakeService(
   178  					ctx,
   179  					nil,
   180  					pullrequest.NewRepositoryNotFoundError(errors.New("repository not found")),
   181  				)
   182  			},
   183  			expected:                    []map[string]any{},
   184  			expectedErr:                 nil,
   185  			continueOnRepoNotFoundError: true,
   186  		},
   187  		{
   188  			selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
   189  				return pullrequest.NewFakeService(
   190  					ctx,
   191  					nil,
   192  					pullrequest.NewRepositoryNotFoundError(errors.New("repository not found")),
   193  				)
   194  			},
   195  			expected:                    nil,
   196  			expectedErr:                 errors.New("error listing repos: repository not found"),
   197  			continueOnRepoNotFoundError: false,
   198  		},
   199  		{
   200  			selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
   201  				return pullrequest.NewFakeService(
   202  					ctx,
   203  					[]*pullrequest.PullRequest{
   204  						{
   205  							Number:       1,
   206  							Title:        "title1",
   207  							Branch:       "branch1",
   208  							TargetBranch: "master",
   209  							HeadSHA:      "089d92cbf9ff857a39e6feccd32798ca700fb958",
   210  							Labels:       []string{"preview"},
   211  							Author:       "testName",
   212  						},
   213  					},
   214  					nil,
   215  				)
   216  			},
   217  			expected: []map[string]any{
   218  				{
   219  					"number":             "1",
   220  					"title":              "title1",
   221  					"branch":             "branch1",
   222  					"branch_slug":        "branch1",
   223  					"target_branch":      "master",
   224  					"target_branch_slug": "master",
   225  					"head_sha":           "089d92cbf9ff857a39e6feccd32798ca700fb958",
   226  					"head_short_sha":     "089d92cb",
   227  					"head_short_sha_7":   "089d92c",
   228  					"labels":             []string{"preview"},
   229  					"author":             "testName",
   230  				},
   231  			},
   232  			expectedErr: nil,
   233  			applicationSet: argoprojiov1alpha1.ApplicationSet{
   234  				Spec: argoprojiov1alpha1.ApplicationSetSpec{
   235  					// Application set is using Go Template.
   236  					GoTemplate: true,
   237  				},
   238  			},
   239  		},
   240  		{
   241  			selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
   242  				return pullrequest.NewFakeService(
   243  					ctx,
   244  					[]*pullrequest.PullRequest{
   245  						{
   246  							Number:       1,
   247  							Title:        "title1",
   248  							Branch:       "branch1",
   249  							TargetBranch: "master",
   250  							HeadSHA:      "089d92cbf9ff857a39e6feccd32798ca700fb958",
   251  							Labels:       []string{"preview"},
   252  							Author:       "testName",
   253  						},
   254  					},
   255  					nil,
   256  				)
   257  			},
   258  			expected: []map[string]any{
   259  				{
   260  					"number":             "1",
   261  					"title":              "title1",
   262  					"branch":             "branch1",
   263  					"branch_slug":        "branch1",
   264  					"target_branch":      "master",
   265  					"target_branch_slug": "master",
   266  					"head_sha":           "089d92cbf9ff857a39e6feccd32798ca700fb958",
   267  					"head_short_sha":     "089d92cb",
   268  					"head_short_sha_7":   "089d92c",
   269  					"author":             "testName",
   270  				},
   271  			},
   272  			expectedErr: nil,
   273  			applicationSet: argoprojiov1alpha1.ApplicationSet{
   274  				Spec: argoprojiov1alpha1.ApplicationSetSpec{
   275  					// Application set is using fasttemplate.
   276  					GoTemplate: false,
   277  				},
   278  			},
   279  		},
   280  		{
   281  			selectFunc: func(context.Context, *argoprojiov1alpha1.PullRequestGenerator, *argoprojiov1alpha1.ApplicationSet) (pullrequest.PullRequestService, error) {
   282  				return pullrequest.NewFakeService(
   283  					ctx,
   284  					[]*pullrequest.PullRequest{
   285  						{
   286  							Number:       1,
   287  							Title:        "title1",
   288  							Branch:       "my_branch",
   289  							TargetBranch: "master",
   290  							HeadSHA:      "abcd",
   291  							Author:       "testName",
   292  							Labels:       []string{"preview", "preview:team1"},
   293  						},
   294  					},
   295  					nil,
   296  				)
   297  			},
   298  			values: map[string]string{
   299  				"preview_env": "{{ regexFind \"(team1|team2)\" (.labels | join \",\") }}",
   300  			},
   301  			expected: []map[string]any{
   302  				{
   303  					"number":             "1",
   304  					"title":              "title1",
   305  					"branch":             "my_branch",
   306  					"branch_slug":        "my-branch",
   307  					"target_branch":      "master",
   308  					"target_branch_slug": "master",
   309  					"head_sha":           "abcd",
   310  					"head_short_sha":     "abcd",
   311  					"head_short_sha_7":   "abcd",
   312  					"author":             "testName",
   313  					"labels":             []string{"preview", "preview:team1"},
   314  					"values":             map[string]string{"preview_env": "team1"},
   315  				},
   316  			},
   317  			expectedErr: nil,
   318  			applicationSet: argoprojiov1alpha1.ApplicationSet{
   319  				Spec: argoprojiov1alpha1.ApplicationSetSpec{
   320  					// Application set is using fasttemplate.
   321  					GoTemplate: true,
   322  				},
   323  			},
   324  		},
   325  	}
   326  
   327  	for _, c := range cases {
   328  		gen := PullRequestGenerator{
   329  			selectServiceProviderFunc: c.selectFunc,
   330  		}
   331  		generatorConfig := argoprojiov1alpha1.ApplicationSetGenerator{
   332  			PullRequest: &argoprojiov1alpha1.PullRequestGenerator{
   333  				Values:                      c.values,
   334  				ContinueOnRepoNotFoundError: c.continueOnRepoNotFoundError,
   335  			},
   336  		}
   337  
   338  		got, gotErr := gen.GenerateParams(&generatorConfig, &c.applicationSet, nil)
   339  		if c.expectedErr != nil {
   340  			require.EqualError(t, gotErr, c.expectedErr.Error())
   341  		} else {
   342  			require.NoError(t, gotErr)
   343  		}
   344  		assert.ElementsMatch(t, c.expected, got)
   345  	}
   346  }
   347  
   348  func TestAllowedSCMProviderPullRequest(t *testing.T) {
   349  	t.Parallel()
   350  
   351  	cases := []struct {
   352  		name           string
   353  		providerConfig *argoprojiov1alpha1.PullRequestGenerator
   354  	}{
   355  		{
   356  			name: "Error Github",
   357  			providerConfig: &argoprojiov1alpha1.PullRequestGenerator{
   358  				Github: &argoprojiov1alpha1.PullRequestGeneratorGithub{
   359  					API: "https://myservice.mynamespace.svc.cluster.local",
   360  				},
   361  			},
   362  		},
   363  		{
   364  			name: "Error Gitlab",
   365  			providerConfig: &argoprojiov1alpha1.PullRequestGenerator{
   366  				GitLab: &argoprojiov1alpha1.PullRequestGeneratorGitLab{
   367  					API: "https://myservice.mynamespace.svc.cluster.local",
   368  				},
   369  			},
   370  		},
   371  		{
   372  			name: "Error Gitea",
   373  			providerConfig: &argoprojiov1alpha1.PullRequestGenerator{
   374  				Gitea: &argoprojiov1alpha1.PullRequestGeneratorGitea{
   375  					API: "https://myservice.mynamespace.svc.cluster.local",
   376  				},
   377  			},
   378  		},
   379  		{
   380  			name: "Error Bitbucket",
   381  			providerConfig: &argoprojiov1alpha1.PullRequestGenerator{
   382  				BitbucketServer: &argoprojiov1alpha1.PullRequestGeneratorBitbucketServer{
   383  					API: "https://myservice.mynamespace.svc.cluster.local",
   384  				},
   385  			},
   386  		},
   387  	}
   388  
   389  	for _, testCase := range cases {
   390  		testCaseCopy := testCase
   391  
   392  		t.Run(testCaseCopy.name, func(t *testing.T) {
   393  			t.Parallel()
   394  
   395  			pullRequestGenerator := NewPullRequestGenerator(nil, NewSCMConfig("", []string{
   396  				"github.myorg.com",
   397  				"gitlab.myorg.com",
   398  				"gitea.myorg.com",
   399  				"bitbucket.myorg.com",
   400  				"azuredevops.myorg.com",
   401  			}, true, true, nil, true))
   402  
   403  			applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
   404  				ObjectMeta: metav1.ObjectMeta{
   405  					Name: "set",
   406  				},
   407  				Spec: argoprojiov1alpha1.ApplicationSetSpec{
   408  					Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
   409  						PullRequest: testCaseCopy.providerConfig,
   410  					}},
   411  				},
   412  			}
   413  
   414  			_, err := pullRequestGenerator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil)
   415  
   416  			require.Error(t, err, "Must return an error")
   417  			var expectedError ErrDisallowedSCMProvider
   418  			assert.ErrorAs(t, err, &expectedError)
   419  		})
   420  	}
   421  }
   422  
   423  func TestSCMProviderDisabled_PRGenerator(t *testing.T) {
   424  	generator := NewPullRequestGenerator(nil, NewSCMConfig("", []string{}, false, true, nil, true))
   425  
   426  	applicationSetInfo := argoprojiov1alpha1.ApplicationSet{
   427  		ObjectMeta: metav1.ObjectMeta{
   428  			Name: "set",
   429  		},
   430  		Spec: argoprojiov1alpha1.ApplicationSetSpec{
   431  			Generators: []argoprojiov1alpha1.ApplicationSetGenerator{{
   432  				PullRequest: &argoprojiov1alpha1.PullRequestGenerator{
   433  					Github: &argoprojiov1alpha1.PullRequestGeneratorGithub{
   434  						API: "https://myservice.mynamespace.svc.cluster.local",
   435  					},
   436  				},
   437  			}},
   438  		},
   439  	}
   440  
   441  	_, err := generator.GenerateParams(&applicationSetInfo.Spec.Generators[0], &applicationSetInfo, nil)
   442  	assert.ErrorIs(t, err, ErrSCMProvidersDisabled)
   443  }