github.com/argoproj/argo-cd/v3@v3.2.1/server/repository/repository_test.go (about)

     1  package repository
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/golang-jwt/jwt/v5"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/mock"
    12  	"github.com/stretchr/testify/require"
    13  	"google.golang.org/grpc/codes"
    14  	"google.golang.org/grpc/status"
    15  	corev1 "k8s.io/api/core/v1"
    16  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    17  	"k8s.io/apimachinery/pkg/runtime"
    18  	"k8s.io/client-go/kubernetes/fake"
    19  	k8scache "k8s.io/client-go/tools/cache"
    20  
    21  	"github.com/argoproj/argo-cd/v3/common"
    22  	"github.com/argoproj/argo-cd/v3/pkg/apiclient/repository"
    23  	appsv1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    24  	fakeapps "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/fake"
    25  	appinformer "github.com/argoproj/argo-cd/v3/pkg/client/informers/externalversions"
    26  	applisters "github.com/argoproj/argo-cd/v3/pkg/client/listers/application/v1alpha1"
    27  	"github.com/argoproj/argo-cd/v3/reposerver/apiclient"
    28  	"github.com/argoproj/argo-cd/v3/reposerver/apiclient/mocks"
    29  	"github.com/argoproj/argo-cd/v3/server/cache"
    30  	"github.com/argoproj/argo-cd/v3/util/assets"
    31  	cacheutil "github.com/argoproj/argo-cd/v3/util/cache"
    32  	appstatecache "github.com/argoproj/argo-cd/v3/util/cache/appstate"
    33  	"github.com/argoproj/argo-cd/v3/util/db"
    34  	dbmocks "github.com/argoproj/argo-cd/v3/util/db/mocks"
    35  	"github.com/argoproj/argo-cd/v3/util/rbac"
    36  	"github.com/argoproj/argo-cd/v3/util/settings"
    37  
    38  	"github.com/argoproj/argo-cd/v3/pkg/apis/application"
    39  )
    40  
    41  const testNamespace = "default"
    42  
    43  var (
    44  	argocdCM = corev1.ConfigMap{
    45  		ObjectMeta: metav1.ObjectMeta{
    46  			Namespace: testNamespace,
    47  			Name:      "argocd-cm",
    48  			Labels: map[string]string{
    49  				"app.kubernetes.io/part-of": "argocd",
    50  			},
    51  		},
    52  	}
    53  	argocdSecret = corev1.Secret{
    54  		ObjectMeta: metav1.ObjectMeta{
    55  			Name:      "argocd-secret",
    56  			Namespace: testNamespace,
    57  		},
    58  		Data: map[string][]byte{
    59  			"admin.password":   []byte("test"),
    60  			"server.secretkey": []byte("test"),
    61  		},
    62  	}
    63  	defaultProj = &appsv1.AppProject{
    64  		TypeMeta: metav1.TypeMeta{
    65  			Kind:       application.AppProjectKind,
    66  			APIVersion: "argoproj.io/v1alpha1",
    67  		},
    68  		ObjectMeta: metav1.ObjectMeta{
    69  			Name:      "default",
    70  			Namespace: testNamespace,
    71  		},
    72  		Spec: appsv1.AppProjectSpec{
    73  			SourceRepos:  []string{"*"},
    74  			Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
    75  		},
    76  	}
    77  
    78  	defaultProjNoSources = &appsv1.AppProject{
    79  		TypeMeta: metav1.TypeMeta{
    80  			Kind:       application.AppProjectKind,
    81  			APIVersion: "argoproj.io/v1alpha1",
    82  		},
    83  		ObjectMeta: metav1.ObjectMeta{
    84  			Name:      "default",
    85  			Namespace: testNamespace,
    86  		},
    87  		Spec: appsv1.AppProjectSpec{
    88  			SourceRepos:  []string{},
    89  			Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
    90  		},
    91  	}
    92  	fakeRepo = appsv1.Repository{
    93  		Repo:           "https://test",
    94  		Type:           "test",
    95  		Name:           "test",
    96  		Username:       "argo",
    97  		Insecure:       false,
    98  		EnableLFS:      false,
    99  		EnableOCI:      false,
   100  		Proxy:          "test",
   101  		Project:        "argocd",
   102  		InheritedCreds: true,
   103  	}
   104  	guestbookApp = &appsv1.Application{
   105  		TypeMeta: metav1.TypeMeta{
   106  			Kind:       application.ApplicationKind,
   107  			APIVersion: "argoproj.io/v1alpha1",
   108  		},
   109  		ObjectMeta: metav1.ObjectMeta{
   110  			Name:      "guestbook",
   111  			Namespace: testNamespace,
   112  		},
   113  		Spec: appsv1.ApplicationSpec{
   114  			Project: "default",
   115  			Source: &appsv1.ApplicationSource{
   116  				RepoURL:        "https://test",
   117  				TargetRevision: "HEAD",
   118  				Helm: &appsv1.ApplicationSourceHelm{
   119  					ValueFiles: []string{"values.yaml"},
   120  				},
   121  			},
   122  		},
   123  		Status: appsv1.ApplicationStatus{
   124  			History: appsv1.RevisionHistories{
   125  				{
   126  					Revision: "abcdef123567",
   127  					Source: appsv1.ApplicationSource{
   128  						RepoURL:        "https://test",
   129  						TargetRevision: "HEAD",
   130  						Helm: &appsv1.ApplicationSourceHelm{
   131  							ValueFiles: []string{"values-old.yaml"},
   132  						},
   133  					},
   134  				},
   135  			},
   136  		},
   137  	}
   138  	multiSourceApp001AppName = "msa-two-helm-types"
   139  	multiSourceApp001        = &appsv1.Application{
   140  		TypeMeta: metav1.TypeMeta{
   141  			Kind:       application.ApplicationKind,
   142  			APIVersion: "argoproj.io/v1alpha1",
   143  		},
   144  		ObjectMeta: metav1.ObjectMeta{
   145  			Name:      multiSourceApp001AppName,
   146  			Namespace: testNamespace,
   147  		},
   148  		Spec: appsv1.ApplicationSpec{
   149  			Project: "default",
   150  			Sources: []appsv1.ApplicationSource{
   151  				{
   152  					RepoURL:        "https://helm.elastic.co",
   153  					TargetRevision: "7.7.0",
   154  					Chart:          "elasticsearch",
   155  					Helm: &appsv1.ApplicationSourceHelm{
   156  						ValueFiles: []string{"values.yaml"},
   157  					},
   158  				},
   159  				{
   160  					RepoURL:        "https://helm.elastic.co",
   161  					TargetRevision: "7.6.0",
   162  					Chart:          "elasticsearch",
   163  					Helm: &appsv1.ApplicationSourceHelm{
   164  						ValueFiles: []string{"values.yaml"},
   165  					},
   166  				},
   167  			},
   168  		},
   169  		Status: appsv1.ApplicationStatus{
   170  			History: appsv1.RevisionHistories{
   171  				{
   172  					ID: 1,
   173  					Revisions: []string{
   174  						"abcdef123567",
   175  					},
   176  					Sources: []appsv1.ApplicationSource{
   177  						{
   178  							RepoURL:        "https://helm.elastic.co",
   179  							TargetRevision: "7.6.0",
   180  							Helm: &appsv1.ApplicationSourceHelm{
   181  								ValueFiles: []string{"values-old.yaml"},
   182  							},
   183  						},
   184  					},
   185  				},
   186  			},
   187  		},
   188  	}
   189  	multiSourceApp002AppName = "msa-one-plugin-one-helm"
   190  	multiSourceApp002        = &appsv1.Application{
   191  		TypeMeta: metav1.TypeMeta{
   192  			Kind:       application.ApplicationKind,
   193  			APIVersion: "argoproj.io/v1alpha1",
   194  		},
   195  		ObjectMeta: metav1.ObjectMeta{
   196  			Name:      multiSourceApp002AppName,
   197  			Namespace: testNamespace,
   198  		},
   199  		Spec: appsv1.ApplicationSpec{
   200  			Project: "default",
   201  			Sources: []appsv1.ApplicationSource{
   202  				{
   203  					RepoURL:        "https://github.com/argoproj/argocd-example-apps.git",
   204  					Path:           "sock-shop",
   205  					TargetRevision: "HEAD",
   206  				},
   207  				{
   208  					RepoURL:        "https://helm.elastic.co",
   209  					TargetRevision: "7.7.0",
   210  					Chart:          "elasticsearch",
   211  					Helm: &appsv1.ApplicationSourceHelm{
   212  						ValueFiles: []string{"values.yaml"},
   213  					},
   214  				},
   215  			},
   216  		},
   217  		Status: appsv1.ApplicationStatus{
   218  			History: appsv1.RevisionHistories{
   219  				{
   220  					Revision: "HEAD",
   221  					Sources: []appsv1.ApplicationSource{
   222  						{
   223  							RepoURL:        "https://github.com/argoproj/argocd-example-apps.git",
   224  							TargetRevision: "1.0.0",
   225  						},
   226  					},
   227  				},
   228  			},
   229  		},
   230  	}
   231  )
   232  
   233  func newAppAndProjLister(objects ...runtime.Object) (applisters.ApplicationLister, k8scache.SharedIndexInformer) {
   234  	fakeAppsClientset := fakeapps.NewSimpleClientset(objects...)
   235  	factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(""), appinformer.WithTweakListOptions(func(_ *metav1.ListOptions) {}))
   236  	projInformer := factory.Argoproj().V1alpha1().AppProjects()
   237  	appsInformer := factory.Argoproj().V1alpha1().Applications()
   238  	for _, obj := range objects {
   239  		switch obj.(type) {
   240  		case *appsv1.AppProject:
   241  			_ = projInformer.Informer().GetStore().Add(obj)
   242  		case *appsv1.Application:
   243  			_ = appsInformer.Informer().GetStore().Add(obj)
   244  		}
   245  	}
   246  	appLister := appsInformer.Lister()
   247  	return appLister, projInformer.Informer()
   248  }
   249  
   250  func Test_createRBACObject(t *testing.T) {
   251  	object := createRBACObject("test-prj", "test-repo")
   252  	assert.Equal(t, "test-prj/test-repo", object)
   253  	objectWithoutPrj := createRBACObject("", "test-repo")
   254  	assert.Equal(t, "test-repo", objectWithoutPrj)
   255  }
   256  
   257  func TestRepositoryServer(t *testing.T) {
   258  	kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret)
   259  	settingsMgr := settings.NewSettingsManager(t.Context(), kubeclientset, testNamespace)
   260  	enforcer := newEnforcer(kubeclientset)
   261  	appLister, projInformer := newAppAndProjLister(defaultProj)
   262  	argoDB := db.NewDB("default", settingsMgr, kubeclientset)
   263  
   264  	t.Run("Test_getRepo", func(t *testing.T) {
   265  		repoServerClient := mocks.RepoServerServiceClient{}
   266  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   267  
   268  		s := NewServer(&repoServerClientset, argoDB, enforcer, nil, appLister, projInformer, testNamespace, settingsMgr, false)
   269  		url := "https://test"
   270  		repo, _ := s.getRepo(t.Context(), url, "")
   271  		assert.Equal(t, repo.Repo, url)
   272  	})
   273  
   274  	t.Run("Test_validateAccess", func(t *testing.T) {
   275  		repoServerClient := mocks.RepoServerServiceClient{}
   276  		repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
   277  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   278  
   279  		s := NewServer(&repoServerClientset, argoDB, enforcer, nil, appLister, projInformer, testNamespace, settingsMgr, false)
   280  		url := "https://test"
   281  		_, err := s.ValidateAccess(t.Context(), &repository.RepoAccessQuery{
   282  			Repo: url,
   283  		})
   284  		require.NoError(t, err)
   285  	})
   286  
   287  	t.Run("Test_Get", func(t *testing.T) {
   288  		repoServerClient := mocks.RepoServerServiceClient{}
   289  		repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
   290  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   291  
   292  		url := "https://test"
   293  		db := &dbmocks.ArgoDB{}
   294  		db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: url}}, nil)
   295  		db.On("GetRepository", t.Context(), url, "").Return(&appsv1.Repository{Repo: url}, nil)
   296  		db.On("RepositoryExists", t.Context(), url, "").Return(true, nil)
   297  
   298  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false)
   299  		repo, err := s.Get(t.Context(), &repository.RepoQuery{
   300  			Repo: url,
   301  		})
   302  		require.NoError(t, err)
   303  		assert.Equal(t, repo.Repo, url)
   304  	})
   305  
   306  	t.Run("Test_GetInherited", func(t *testing.T) {
   307  		repoServerClient := mocks.RepoServerServiceClient{}
   308  		repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
   309  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   310  
   311  		url := "https://test"
   312  		db := &dbmocks.ArgoDB{}
   313  		testRepo := &appsv1.Repository{
   314  			Repo:           url,
   315  			Type:           "git",
   316  			Username:       "",
   317  			InheritedCreds: true,
   318  		}
   319  		db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{testRepo}, nil)
   320  		db.On("GetRepository", t.Context(), url, "").Return(testRepo, nil)
   321  		db.On("RepositoryExists", t.Context(), url, "").Return(true, nil)
   322  
   323  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false)
   324  		repo, err := s.Get(t.Context(), &repository.RepoQuery{
   325  			Repo: url,
   326  		})
   327  		require.NoError(t, err)
   328  
   329  		testRepo.ConnectionState = repo.ConnectionState // overwrite connection state on our test object to simplify comparison below
   330  
   331  		assert.Equal(t, testRepo, repo)
   332  	})
   333  
   334  	t.Run("Test_GetWithErrorShouldReturn403", func(t *testing.T) {
   335  		repoServerClient := mocks.RepoServerServiceClient{}
   336  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   337  
   338  		url := "https://test"
   339  		db := &dbmocks.ArgoDB{}
   340  		db.On("ListRepositories", t.Context()).Return(nil, nil)
   341  		db.On("GetRepository", t.Context(), url, "").Return(nil, errors.New("some error"))
   342  		db.On("RepositoryExists", t.Context(), url, "").Return(true, nil)
   343  
   344  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false)
   345  		repo, err := s.Get(t.Context(), &repository.RepoQuery{
   346  			Repo: url,
   347  		})
   348  		assert.Nil(t, repo)
   349  		assert.Equal(t, err, common.PermissionDeniedAPIError)
   350  	})
   351  
   352  	t.Run("Test_GetWithNotExistRepoShouldReturn404", func(t *testing.T) {
   353  		repoServerClient := mocks.RepoServerServiceClient{}
   354  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   355  		repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
   356  
   357  		url := "https://test"
   358  		db := &dbmocks.ArgoDB{}
   359  		db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: url}}, nil)
   360  		db.On("GetRepository", t.Context(), url, "").Return(&appsv1.Repository{Repo: url}, nil)
   361  		db.On("RepositoryExists", t.Context(), url, "").Return(false, nil)
   362  
   363  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false)
   364  		repo, err := s.Get(t.Context(), &repository.RepoQuery{
   365  			Repo: url,
   366  		})
   367  		assert.Nil(t, repo)
   368  		assert.EqualError(t, err, "rpc error: code = NotFound desc = repo 'https://test' not found")
   369  	})
   370  
   371  	t.Run("Test_GetRepoIsSanitized", func(t *testing.T) {
   372  		repoServerClient := mocks.RepoServerServiceClient{}
   373  		repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
   374  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   375  
   376  		url := "https://test"
   377  		db := &dbmocks.ArgoDB{}
   378  		db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: url, Username: "test", Password: "it's a secret", GitHubAppEnterpriseBaseURL: "https://ghe.example.com/api/v3", GithubAppId: 123456, GithubAppInstallationId: 789}}, nil)
   379  		db.On("GetRepository", t.Context(), url, "").Return(&appsv1.Repository{Repo: url, Username: "test", Password: "it's a secret"}, nil)
   380  		db.On("RepositoryExists", t.Context(), url, "").Return(true, nil)
   381  
   382  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false)
   383  		repo, err := s.Get(t.Context(), &repository.RepoQuery{
   384  			Repo: url,
   385  		})
   386  		require.NoError(t, err)
   387  		assert.Equal(t, "https://test", repo.Repo)
   388  		assert.Equal(t, "https://ghe.example.com/api/v3", repo.GitHubAppEnterpriseBaseURL)
   389  		assert.Equal(t, int64(123456), repo.GithubAppId)
   390  		assert.Equal(t, int64(789), repo.GithubAppInstallationId)
   391  		assert.Empty(t, repo.Password)
   392  	})
   393  
   394  	t.Run("Test_GetRepoIsNormalized", func(t *testing.T) {
   395  		repoServerClient := mocks.RepoServerServiceClient{}
   396  		repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
   397  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   398  
   399  		url := "https://test"
   400  		db := &dbmocks.ArgoDB{}
   401  		db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: url}}, nil)
   402  		db.On("GetRepository", t.Context(), url, "").Return(&appsv1.Repository{Repo: url, Username: "test"}, nil)
   403  		db.On("RepositoryExists", t.Context(), url, "").Return(true, nil)
   404  
   405  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false)
   406  		repo, err := s.Get(t.Context(), &repository.RepoQuery{
   407  			Repo: url,
   408  		})
   409  		require.NoError(t, err)
   410  		assert.Equal(t, "https://test", repo.Repo)
   411  		assert.Equal(t, common.DefaultRepoType, repo.Type)
   412  	})
   413  
   414  	t.Run("Test_GetRepoHasConnectionState", func(t *testing.T) {
   415  		repoServerClient := mocks.RepoServerServiceClient{}
   416  		repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{
   417  			VerifiedRepository: true,
   418  		}, nil)
   419  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   420  
   421  		url := "https://test"
   422  		db := &dbmocks.ArgoDB{}
   423  		db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: url}}, nil)
   424  		db.On("GetRepository", t.Context(), url, "").Return(&appsv1.Repository{Repo: url}, nil)
   425  		db.On("RepositoryExists", t.Context(), url, "").Return(true, nil)
   426  
   427  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false)
   428  		repo, err := s.Get(t.Context(), &repository.RepoQuery{
   429  			Repo: url,
   430  		})
   431  		require.NoError(t, err)
   432  		require.NotNil(t, repo.ConnectionState)
   433  		assert.Equal(t, appsv1.ConnectionStatusSuccessful, repo.ConnectionState.Status)
   434  	})
   435  
   436  	t.Run("Test_CreateRepositoryWithoutUpsert", func(t *testing.T) {
   437  		repoServerClient := mocks.RepoServerServiceClient{}
   438  		repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
   439  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   440  
   441  		db := &dbmocks.ArgoDB{}
   442  		db.On("GetRepository", t.Context(), "test").Return(nil, errors.New("not found"))
   443  		db.On("CreateRepository", t.Context(), mock.Anything).Return(&apiclient.TestRepositoryResponse{}).Return(&appsv1.Repository{
   444  			Repo:    "repo",
   445  			Project: "proj",
   446  		}, nil)
   447  
   448  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false)
   449  		repo, err := s.CreateRepository(t.Context(), &repository.RepoCreateRequest{
   450  			Repo: &appsv1.Repository{
   451  				Repo:     "test",
   452  				Username: "test",
   453  			},
   454  		})
   455  		require.NoError(t, err)
   456  		assert.Equal(t, "repo", repo.Repo)
   457  	})
   458  
   459  	t.Run("Test_CreateRepositoryWithUpsert", func(t *testing.T) {
   460  		repoServerClient := mocks.RepoServerServiceClient{}
   461  		repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
   462  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   463  
   464  		r := &appsv1.Repository{
   465  			Repo:     "test",
   466  			Username: "test",
   467  		}
   468  
   469  		db := &dbmocks.ArgoDB{}
   470  		db.On("GetRepository", t.Context(), "test", "").Return(&appsv1.Repository{
   471  			Repo:     "test",
   472  			Username: "test",
   473  		}, nil)
   474  		db.On("CreateRepository", t.Context(), mock.Anything).Return(nil, status.Errorf(codes.AlreadyExists, "repository already exists"))
   475  		db.On("UpdateRepository", t.Context(), mock.Anything).Return(r, nil)
   476  
   477  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false)
   478  		repo, err := s.CreateRepository(t.Context(), &repository.RepoCreateRequest{
   479  			Repo:   r,
   480  			Upsert: true,
   481  		})
   482  
   483  		require.NoError(t, err)
   484  		require.NotNil(t, repo)
   485  		assert.Equal(t, "test", repo.Repo)
   486  	})
   487  
   488  	t.Run("Test_ListRepositories", func(t *testing.T) {
   489  		repoServerClient := mocks.RepoServerServiceClient{}
   490  		repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
   491  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   492  		enforcer := newEnforcer(kubeclientset)
   493  
   494  		url := "https://test"
   495  		db := &dbmocks.ArgoDB{}
   496  		db.On("GetRepository", t.Context(), url, "argocd").Return(nil, nil)
   497  		db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(nil, nil)
   498  		db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{&fakeRepo, &fakeRepo}, nil)
   499  
   500  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projInformer, testNamespace, settingsMgr, false)
   501  		resp, err := s.ListRepositories(t.Context(), &repository.RepoQuery{})
   502  		require.NoError(t, err)
   503  		assert.Len(t, resp.Items, 2)
   504  	})
   505  }
   506  
   507  func TestRepositoryServerListApps(t *testing.T) {
   508  	kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret)
   509  	settingsMgr := settings.NewSettingsManager(t.Context(), kubeclientset, testNamespace)
   510  
   511  	t.Run("Test_WithoutAppCreateUpdatePrivileges", func(t *testing.T) {
   512  		repoServerClient := mocks.RepoServerServiceClient{}
   513  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   514  		enforcer := newEnforcer(kubeclientset)
   515  		enforcer.SetDefaultRole("role:readonly")
   516  
   517  		url := "https://test"
   518  		db := &dbmocks.ArgoDB{}
   519  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   520  		appLister, projLister := newAppAndProjLister(defaultProj)
   521  
   522  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   523  		resp, err := s.ListApps(t.Context(), &repository.RepoAppsQuery{
   524  			Repo:       "https://test",
   525  			Revision:   "HEAD",
   526  			AppName:    "foo",
   527  			AppProject: "default",
   528  		})
   529  		assert.Nil(t, resp)
   530  		assert.Equal(t, err, common.PermissionDeniedAPIError)
   531  	})
   532  
   533  	t.Run("Test_WithAppCreateUpdatePrivileges", func(t *testing.T) {
   534  		repoServerClient := mocks.RepoServerServiceClient{}
   535  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   536  		enforcer := newEnforcer(kubeclientset)
   537  		enforcer.SetDefaultRole("role:admin")
   538  		appLister, projLister := newAppAndProjLister(defaultProj)
   539  
   540  		url := "https://test"
   541  		db := &dbmocks.ArgoDB{}
   542  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   543  		db.On("GetProjectRepositories", "default").Return(nil, nil)
   544  		db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil)
   545  		repoServerClient.On("ListApps", t.Context(), mock.Anything).Return(&apiclient.AppList{
   546  			Apps: map[string]string{
   547  				"path/to/dir": "Kustomize",
   548  			},
   549  		}, nil)
   550  
   551  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   552  		resp, err := s.ListApps(t.Context(), &repository.RepoAppsQuery{
   553  			Repo:       "https://test",
   554  			Revision:   "HEAD",
   555  			AppName:    "foo",
   556  			AppProject: "default",
   557  		})
   558  		require.NoError(t, err)
   559  		require.Len(t, resp.Items, 1)
   560  		assert.Equal(t, "path/to/dir", resp.Items[0].Path)
   561  		assert.Equal(t, "Kustomize", resp.Items[0].Type)
   562  	})
   563  
   564  	t.Run("Test_WithAppCreateUpdatePrivilegesRepoNotAllowed", func(t *testing.T) {
   565  		repoServerClient := mocks.RepoServerServiceClient{}
   566  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   567  		enforcer := newEnforcer(kubeclientset)
   568  		enforcer.SetDefaultRole("role:admin")
   569  		appLister, projLister := newAppAndProjLister(defaultProjNoSources)
   570  
   571  		url := "https://test"
   572  		db := &dbmocks.ArgoDB{}
   573  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   574  		db.On("GetProjectRepositories", "default").Return(nil, nil)
   575  		db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil)
   576  		repoServerClient.On("ListApps", t.Context(), mock.Anything).Return(&apiclient.AppList{
   577  			Apps: map[string]string{
   578  				"path/to/dir": "Kustomize",
   579  			},
   580  		}, nil)
   581  
   582  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   583  		resp, err := s.ListApps(t.Context(), &repository.RepoAppsQuery{
   584  			Repo:       "https://test",
   585  			Revision:   "HEAD",
   586  			AppName:    "foo",
   587  			AppProject: "default",
   588  		})
   589  		assert.Nil(t, resp)
   590  		require.Error(t, err, "repository 'https://test' not permitted in project 'default'")
   591  	})
   592  }
   593  
   594  func TestRepositoryServerGetAppDetails(t *testing.T) {
   595  	kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret)
   596  	settingsMgr := settings.NewSettingsManager(t.Context(), kubeclientset, testNamespace)
   597  
   598  	t.Run("Test_WithoutRepoReadPrivileges", func(t *testing.T) {
   599  		repoServerClient := mocks.RepoServerServiceClient{}
   600  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   601  		enforcer := newEnforcer(kubeclientset)
   602  		enforcer.SetDefaultRole("")
   603  
   604  		url := "https://test"
   605  		db := &dbmocks.ArgoDB{}
   606  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   607  		appLister, projLister := newAppAndProjLister(defaultProj)
   608  
   609  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   610  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   611  			Source: &appsv1.ApplicationSource{
   612  				RepoURL: url,
   613  			},
   614  			AppName:    "newapp",
   615  			AppProject: "default",
   616  		})
   617  		assert.Nil(t, resp)
   618  		require.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: repositories, get, https://test")
   619  	})
   620  	t.Run("Test_WithoutAppReadPrivileges", func(t *testing.T) {
   621  		repoServerClient := mocks.RepoServerServiceClient{}
   622  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   623  		enforcer := newEnforcer(kubeclientset)
   624  		_ = enforcer.SetUserPolicy("p, role:readrepos, repositories, get, *, allow")
   625  		enforcer.SetDefaultRole("role:readrepos")
   626  
   627  		url := "https://test"
   628  		db := &dbmocks.ArgoDB{}
   629  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   630  		appLister, projLister := newAppAndProjLister(defaultProj)
   631  
   632  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   633  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   634  			Source: &appsv1.ApplicationSource{
   635  				RepoURL: url,
   636  			},
   637  			AppName:    "newapp",
   638  			AppProject: "default",
   639  		})
   640  		assert.Nil(t, resp)
   641  		require.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, get, default/newapp")
   642  	})
   643  	t.Run("Test_WithoutCreatePrivileges", func(t *testing.T) {
   644  		repoServerClient := mocks.RepoServerServiceClient{}
   645  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   646  		enforcer := newEnforcer(kubeclientset)
   647  		enforcer.SetDefaultRole("role:readonly")
   648  
   649  		url := "https://test"
   650  		db := &dbmocks.ArgoDB{}
   651  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   652  		appLister, projLister := newAppAndProjLister(defaultProj)
   653  
   654  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   655  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   656  			Source: &appsv1.ApplicationSource{
   657  				RepoURL: url,
   658  			},
   659  			AppName:    "newapp",
   660  			AppProject: "default",
   661  		})
   662  		assert.Nil(t, resp)
   663  		require.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, create, default/newapp")
   664  	})
   665  	t.Run("Test_WithCreatePrivileges", func(t *testing.T) {
   666  		repoServerClient := mocks.RepoServerServiceClient{}
   667  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   668  		enforcer := newEnforcer(kubeclientset)
   669  
   670  		url := "https://test"
   671  		db := &dbmocks.ArgoDB{}
   672  		db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(nil, nil)
   673  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   674  		db.On("GetProjectRepositories", "default").Return(nil, nil)
   675  		db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil)
   676  		expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
   677  		repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil)
   678  		appLister, projLister := newAppAndProjLister(defaultProj)
   679  
   680  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   681  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   682  			Source: &appsv1.ApplicationSource{
   683  				RepoURL: url,
   684  			},
   685  			AppName:    "newapp",
   686  			AppProject: "default",
   687  		})
   688  		require.NoError(t, err)
   689  		assert.Equal(t, expectedResp, *resp)
   690  	})
   691  	t.Run("Test_RepoNotPermitted", func(t *testing.T) {
   692  		repoServerClient := mocks.RepoServerServiceClient{}
   693  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   694  		enforcer := newEnforcer(kubeclientset)
   695  
   696  		url := "https://test"
   697  		db := &dbmocks.ArgoDB{}
   698  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   699  		db.On("GetProjectRepositories", "default").Return(nil, nil)
   700  		db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil)
   701  		expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
   702  		repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil)
   703  		appLister, projLister := newAppAndProjLister(defaultProjNoSources)
   704  
   705  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   706  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   707  			Source: &appsv1.ApplicationSource{
   708  				RepoURL: url,
   709  			},
   710  			AppName:    "newapp",
   711  			AppProject: "default",
   712  		})
   713  		require.Error(t, err, "repository 'https://test' not permitted in project 'default'")
   714  		assert.Nil(t, resp)
   715  	})
   716  	t.Run("Test_ExistingApp", func(t *testing.T) {
   717  		repoServerClient := mocks.RepoServerServiceClient{}
   718  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   719  		enforcer := newEnforcer(kubeclientset)
   720  
   721  		url := "https://test"
   722  		db := &dbmocks.ArgoDB{}
   723  		db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(nil, nil)
   724  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   725  		db.On("GetProjectRepositories", "default").Return(nil, nil)
   726  		db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil)
   727  		expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
   728  		repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil)
   729  		appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
   730  
   731  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   732  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   733  			Source:     guestbookApp.Spec.GetSourcePtrByIndex(0),
   734  			AppName:    "guestbook",
   735  			AppProject: "default",
   736  		})
   737  		require.NoError(t, err)
   738  		assert.Equal(t, expectedResp, *resp)
   739  	})
   740  	t.Run("Test_ExistingMultiSourceApp001", func(t *testing.T) {
   741  		repoServerClient := mocks.RepoServerServiceClient{}
   742  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   743  		enforcer := newEnforcer(kubeclientset)
   744  
   745  		url := "https://helm.elastic.co"
   746  		helmRepos := []*appsv1.Repository{{Repo: url}, {Repo: url}}
   747  		db := &dbmocks.ArgoDB{}
   748  		db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(helmRepos, nil)
   749  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   750  		db.On("GetProjectRepositories", "default").Return(nil, nil)
   751  		db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil)
   752  		expectedResp := apiclient.RepoAppDetailsResponse{Type: "Helm"}
   753  		repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil)
   754  		appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp001)
   755  
   756  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   757  		sources := multiSourceApp001.Spec.GetSources()
   758  		assert.Len(t, sources, 2)
   759  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   760  			Source:     &sources[0],
   761  			AppName:    multiSourceApp001AppName,
   762  			AppProject: "default",
   763  		})
   764  		require.NoError(t, err)
   765  		assert.Equal(t, expectedResp, *resp)
   766  		assert.Equal(t, "Helm", resp.Type)
   767  		// Next source
   768  		resp, err = s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   769  			Source:     &sources[1],
   770  			AppName:    multiSourceApp001AppName,
   771  			AppProject: "default",
   772  		})
   773  		require.NoError(t, err)
   774  		assert.Equal(t, expectedResp, *resp)
   775  		assert.Equal(t, "Helm", resp.Type)
   776  	})
   777  	t.Run("Test_ExistingMultiSourceApp002", func(t *testing.T) {
   778  		repoServerClient := mocks.RepoServerServiceClient{}
   779  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   780  		enforcer := newEnforcer(kubeclientset)
   781  
   782  		url0 := "https://github.com/argoproj/argocd-example-apps.git"
   783  		url1 := "https://helm.elastic.co"
   784  		helmRepos := []*appsv1.Repository{{Repo: url0}, {Repo: url1}}
   785  		db := &dbmocks.ArgoDB{}
   786  		db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(helmRepos, nil)
   787  		db.On("GetRepository", t.Context(), url0, "default").Return(&appsv1.Repository{Repo: url0}, nil)
   788  		db.On("GetRepository", t.Context(), url1, "default").Return(&appsv1.Repository{Repo: url1}, nil)
   789  		db.On("GetProjectRepositories", "default").Return(nil, nil)
   790  		db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil)
   791  		expectedResp0 := apiclient.RepoAppDetailsResponse{Type: "Plugin"}
   792  		expectedResp1 := apiclient.RepoAppDetailsResponse{Type: "Helm"}
   793  		repoServerClient.On("GetAppDetails", t.Context(), mock.MatchedBy(func(req *apiclient.RepoServerAppDetailsQuery) bool { return req.Source.RepoURL == url0 })).Return(&expectedResp0, nil)
   794  		repoServerClient.On("GetAppDetails", t.Context(), mock.MatchedBy(func(req *apiclient.RepoServerAppDetailsQuery) bool { return req.Source.RepoURL == url1 })).Return(&expectedResp1, nil)
   795  		appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp002)
   796  
   797  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   798  		sources := multiSourceApp002.Spec.GetSources()
   799  		assert.Len(t, sources, 2)
   800  
   801  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   802  			Source:     &sources[0],
   803  			AppName:    multiSourceApp002AppName,
   804  			AppProject: "default",
   805  		})
   806  		require.NoError(t, err)
   807  		assert.Equal(t, "Plugin", resp.Type)
   808  		assert.Equal(t, expectedResp0, *resp)
   809  		// Next source
   810  		resp, err = s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   811  			Source:     &sources[1],
   812  			AppName:    multiSourceApp002AppName,
   813  			AppProject: "default",
   814  		})
   815  		require.NoError(t, err)
   816  		assert.Equal(t, expectedResp1, *resp)
   817  		assert.Equal(t, "Helm", resp.Type)
   818  	})
   819  	t.Run("Test_ExistingAppMismatchedProjectName", func(t *testing.T) {
   820  		repoServerClient := mocks.RepoServerServiceClient{}
   821  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   822  		enforcer := newEnforcer(kubeclientset)
   823  
   824  		url := "https://test"
   825  		db := &dbmocks.ArgoDB{}
   826  		db.On("GetRepository", t.Context(), url, "mismatch").Return(&appsv1.Repository{Repo: url}, nil)
   827  		appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
   828  
   829  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   830  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   831  			Source:     guestbookApp.Spec.GetSourcePtrByIndex(0),
   832  			AppName:    "guestbook",
   833  			AppProject: "mismatch",
   834  		})
   835  		assert.Equal(t, err, common.PermissionDeniedAPIError)
   836  		assert.Nil(t, resp)
   837  	})
   838  	t.Run("Test_ExistingAppSourceNotInHistory", func(t *testing.T) {
   839  		repoServerClient := mocks.RepoServerServiceClient{}
   840  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   841  		enforcer := newEnforcer(kubeclientset)
   842  
   843  		url := "https://test"
   844  		db := &dbmocks.ArgoDB{}
   845  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   846  		appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
   847  		differentSource := guestbookApp.Spec.Source.DeepCopy()
   848  		differentSource.Helm.ValueFiles = []string{"/etc/passwd"}
   849  
   850  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   851  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   852  			Source:     differentSource,
   853  			AppName:    "guestbook",
   854  			AppProject: "default",
   855  		})
   856  		assert.Equal(t, err, common.PermissionDeniedAPIError)
   857  		assert.Nil(t, resp)
   858  	})
   859  	t.Run("Test_ExistingAppSourceInHistory", func(t *testing.T) {
   860  		repoServerClient := mocks.RepoServerServiceClient{}
   861  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   862  		enforcer := newEnforcer(kubeclientset)
   863  
   864  		url := "https://test"
   865  		db := &dbmocks.ArgoDB{}
   866  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   867  		db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(nil, nil)
   868  		db.On("GetProjectRepositories", "default").Return(nil, nil)
   869  		db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil)
   870  		expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
   871  		repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil)
   872  		appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
   873  		previousSource := guestbookApp.Status.History[0].Source.DeepCopy()
   874  		previousSource.TargetRevision = guestbookApp.Status.History[0].Revision
   875  
   876  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   877  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   878  			Source:     previousSource,
   879  			AppName:    "guestbook",
   880  			AppProject: "default",
   881  		})
   882  		require.NoError(t, err)
   883  		assert.Equal(t, expectedResp, *resp)
   884  	})
   885  
   886  	t.Run("Test_ExistingAppMultiSourceNotInHistory", func(t *testing.T) {
   887  		repoServerClient := mocks.RepoServerServiceClient{}
   888  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   889  		enforcer := newEnforcer(kubeclientset)
   890  
   891  		url := "https://helm.elastic.co"
   892  		helmRepos := []*appsv1.Repository{{Repo: url}, {Repo: url}}
   893  		db := &dbmocks.ArgoDB{}
   894  		db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(helmRepos, nil)
   895  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   896  		db.On("GetProjectRepositories", "default").Return(nil, nil)
   897  		db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil)
   898  		expectedResp := apiclient.RepoAppDetailsResponse{Type: "Helm"}
   899  		repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil)
   900  		appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp001)
   901  
   902  		differentSource := multiSourceApp001.Spec.Sources[0].DeepCopy()
   903  		differentSource.Helm.ValueFiles = []string{"/etc/passwd"}
   904  
   905  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   906  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   907  			Source:      differentSource,
   908  			AppName:     multiSourceApp001AppName,
   909  			AppProject:  "default",
   910  			SourceIndex: 0,
   911  			VersionId:   1,
   912  		})
   913  		assert.Equal(t, err, common.PermissionDeniedAPIError)
   914  		assert.Nil(t, resp)
   915  	})
   916  	t.Run("Test_ExistingAppMultiSourceInHistory", func(t *testing.T) {
   917  		repoServerClient := mocks.RepoServerServiceClient{}
   918  		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
   919  		enforcer := newEnforcer(kubeclientset)
   920  
   921  		url := "https://helm.elastic.co"
   922  		db := &dbmocks.ArgoDB{}
   923  		db.On("GetRepository", t.Context(), url, "default").Return(&appsv1.Repository{Repo: url}, nil)
   924  		db.On("ListHelmRepositories", t.Context(), mock.Anything).Return(nil, nil)
   925  		db.On("GetProjectRepositories", "default").Return(nil, nil)
   926  		db.On("GetProjectClusters", t.Context(), "default").Return(nil, nil)
   927  		expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
   928  		repoServerClient.On("GetAppDetails", t.Context(), mock.Anything).Return(&expectedResp, nil)
   929  		appLister, projLister := newAppAndProjLister(defaultProj, multiSourceApp001)
   930  		previousSource := multiSourceApp001.Status.History[0].Sources[0].DeepCopy()
   931  		previousSource.TargetRevision = multiSourceApp001.Status.History[0].Revisions[0]
   932  
   933  		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
   934  		resp, err := s.GetAppDetails(t.Context(), &repository.RepoAppDetailsQuery{
   935  			Source:      previousSource,
   936  			AppName:     multiSourceApp001AppName,
   937  			AppProject:  "default",
   938  			SourceIndex: 0,
   939  			VersionId:   1,
   940  		})
   941  		require.NoError(t, err)
   942  		assert.Equal(t, expectedResp, *resp)
   943  	})
   944  }
   945  
   946  type fixtures struct {
   947  	*cache.Cache
   948  }
   949  
   950  func newFixtures() *fixtures {
   951  	return &fixtures{cache.NewCache(
   952  		appstatecache.NewCache(
   953  			cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Hour)),
   954  			1*time.Minute,
   955  		),
   956  		1*time.Minute,
   957  		1*time.Minute,
   958  	)}
   959  }
   960  
   961  func newEnforcer(kubeclientset *fake.Clientset) *rbac.Enforcer {
   962  	enforcer := rbac.NewEnforcer(kubeclientset, testNamespace, common.ArgoCDRBACConfigMapName, nil)
   963  	_ = enforcer.SetBuiltinPolicy(assets.BuiltinPolicyCSV)
   964  	enforcer.SetDefaultRole("role:admin")
   965  	enforcer.SetClaimsEnforcerFunc(func(_ jwt.Claims, _ ...any) bool {
   966  		return true
   967  	})
   968  	return enforcer
   969  }
   970  
   971  func TestGetRepository(t *testing.T) {
   972  	type args struct {
   973  		ctx              context.Context
   974  		listRepositories func(context.Context, *repository.RepoQuery) (*appsv1.RepositoryList, error)
   975  		q                *repository.RepoQuery
   976  	}
   977  	tests := []struct {
   978  		name  string
   979  		args  args
   980  		want  *appsv1.Repository
   981  		error error
   982  	}{
   983  		{
   984  			name: "empty project and no repos",
   985  			args: args{
   986  				ctx: t.Context(),
   987  				listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) {
   988  					return &appsv1.RepositoryList{
   989  						Items: []*appsv1.Repository{
   990  							{Repo: "something-else"},
   991  						},
   992  					}, nil
   993  				},
   994  				q: &repository.RepoQuery{},
   995  			},
   996  			want:  nil,
   997  			error: common.PermissionDeniedAPIError,
   998  		},
   999  		{
  1000  			name: "empty project and no matching repos",
  1001  			args: args{
  1002  				ctx: t.Context(),
  1003  				listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) {
  1004  					return &appsv1.RepositoryList{}, nil
  1005  				},
  1006  				q: &repository.RepoQuery{
  1007  					Repo: "foobar",
  1008  				},
  1009  			},
  1010  			want:  nil,
  1011  			error: common.PermissionDeniedAPIError,
  1012  		},
  1013  		{
  1014  			name: "empty project + matching repo with an empty project",
  1015  			args: args{
  1016  				ctx: t.Context(),
  1017  				listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) {
  1018  					return &appsv1.RepositoryList{
  1019  						Items: []*appsv1.Repository{
  1020  							{Repo: "foobar", Project: ""},
  1021  						},
  1022  					}, nil
  1023  				},
  1024  				q: &repository.RepoQuery{
  1025  					Repo:       "foobar",
  1026  					AppProject: "",
  1027  				},
  1028  			},
  1029  			want: &appsv1.Repository{
  1030  				Repo:    "foobar",
  1031  				Project: "",
  1032  			},
  1033  			error: nil,
  1034  		},
  1035  		{
  1036  			name: "empty project + matching repo with a non-empty project",
  1037  			args: args{
  1038  				ctx: t.Context(),
  1039  				listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) {
  1040  					return &appsv1.RepositoryList{
  1041  						Items: []*appsv1.Repository{
  1042  							{Repo: "foobar", Project: "foobar"},
  1043  						},
  1044  					}, nil
  1045  				},
  1046  				q: &repository.RepoQuery{
  1047  					Repo:       "foobar",
  1048  					AppProject: "",
  1049  				},
  1050  			},
  1051  			want: &appsv1.Repository{
  1052  				Repo:    "foobar",
  1053  				Project: "foobar",
  1054  			},
  1055  			error: nil,
  1056  		},
  1057  		{
  1058  			name: "non-empty project + matching repo with an empty project",
  1059  			args: args{
  1060  				ctx: t.Context(),
  1061  				listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) {
  1062  					return &appsv1.RepositoryList{
  1063  						Items: []*appsv1.Repository{
  1064  							{Repo: "foobar", Project: ""},
  1065  						},
  1066  					}, nil
  1067  				},
  1068  				q: &repository.RepoQuery{
  1069  					Repo:       "foobar",
  1070  					AppProject: "foobar",
  1071  				},
  1072  			},
  1073  			want:  nil,
  1074  			error: errors.New(`repository not found for url "foobar" and project "foobar"`),
  1075  		},
  1076  		{
  1077  			name: "non-empty project + matching repo with a matching project",
  1078  			args: args{
  1079  				ctx: t.Context(),
  1080  				listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) {
  1081  					return &appsv1.RepositoryList{
  1082  						Items: []*appsv1.Repository{
  1083  							{Repo: "foobar", Project: "foobar"},
  1084  						},
  1085  					}, nil
  1086  				},
  1087  				q: &repository.RepoQuery{
  1088  					Repo:       "foobar",
  1089  					AppProject: "foobar",
  1090  				},
  1091  			},
  1092  			want: &appsv1.Repository{
  1093  				Repo:    "foobar",
  1094  				Project: "foobar",
  1095  			},
  1096  			error: nil,
  1097  		},
  1098  		{
  1099  			name: "non-empty project + matching repo with a non-matching project",
  1100  			args: args{
  1101  				ctx: t.Context(),
  1102  				listRepositories: func(_ context.Context, _ *repository.RepoQuery) (*appsv1.RepositoryList, error) {
  1103  					return &appsv1.RepositoryList{
  1104  						Items: []*appsv1.Repository{
  1105  							{Repo: "foobar", Project: "something-else"},
  1106  						},
  1107  					}, nil
  1108  				},
  1109  				q: &repository.RepoQuery{
  1110  					Repo:       "foobar",
  1111  					AppProject: "foobar",
  1112  				},
  1113  			},
  1114  			want:  nil,
  1115  			error: errors.New(`repository not found for url "foobar" and project "foobar"`),
  1116  		},
  1117  	}
  1118  	for _, tt := range tests {
  1119  		t.Run(tt.name, func(t *testing.T) {
  1120  			got, err := getRepository(tt.args.ctx, tt.args.listRepositories, tt.args.q)
  1121  			assert.Equal(t, tt.error, err)
  1122  			assert.Equalf(t, tt.want, got, "getRepository(%v, %v) = %v", tt.args.ctx, tt.args.q, got)
  1123  		})
  1124  	}
  1125  }
  1126  
  1127  func TestDeleteRepository(t *testing.T) {
  1128  	repositories := map[string]string{
  1129  		"valid": "https://bitbucket.org/workspace/repo.git",
  1130  		// Check a wrongly formatter repo as well, see https://github.com/argoproj/argo-cd/issues/20921
  1131  		"invalid": "git clone https://bitbucket.org/workspace/repo.git",
  1132  	}
  1133  
  1134  	kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret)
  1135  	settingsMgr := settings.NewSettingsManager(t.Context(), kubeclientset, testNamespace)
  1136  
  1137  	for name, repo := range repositories {
  1138  		t.Run(name, func(t *testing.T) {
  1139  			repoServerClient := mocks.RepoServerServiceClient{}
  1140  			repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
  1141  
  1142  			repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
  1143  			enforcer := newEnforcer(kubeclientset)
  1144  
  1145  			db := &dbmocks.ArgoDB{}
  1146  			db.On("DeleteRepository", t.Context(), repo, "default").Return(nil)
  1147  			db.On("ListRepositories", t.Context()).Return([]*appsv1.Repository{{Repo: repo, Project: "default"}}, nil)
  1148  			db.On("GetRepository", t.Context(), repo, "default").Return(&appsv1.Repository{Repo: repo, Project: "default"}, nil)
  1149  			appLister, projLister := newAppAndProjLister(defaultProj)
  1150  
  1151  			s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, testNamespace, settingsMgr, false)
  1152  			resp, err := s.DeleteRepository(t.Context(), &repository.RepoQuery{Repo: repo, AppProject: "default"})
  1153  			require.NoError(t, err)
  1154  			assert.Equal(t, repository.RepoResponse{}, *resp)
  1155  		})
  1156  	}
  1157  }