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

     1  package badge
     2  
     3  import (
     4  	"fmt"
     5  	"image/color"
     6  	"net/http"
     7  	"net/http/httptest"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    12  	appclientset "github.com/argoproj/argo-cd/v3/pkg/client/clientset/versioned/fake"
    13  	"github.com/argoproj/argo-cd/v3/util/settings"
    14  
    15  	"github.com/argoproj/gitops-engine/pkg/health"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  	corev1 "k8s.io/api/core/v1"
    19  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    20  	"k8s.io/apimachinery/pkg/runtime"
    21  	"k8s.io/client-go/kubernetes/fake"
    22  )
    23  
    24  func argoCDSecret() *corev1.Secret {
    25  	return &corev1.Secret{
    26  		ObjectMeta: metav1.ObjectMeta{Name: "argocd-secret", Namespace: "default"},
    27  		Data: map[string][]byte{
    28  			"admin.password":   []byte("test"),
    29  			"server.secretkey": []byte("test"),
    30  		},
    31  	}
    32  }
    33  
    34  func argoCDCm() *corev1.ConfigMap {
    35  	return &corev1.ConfigMap{
    36  		ObjectMeta: metav1.ObjectMeta{
    37  			Name:      "argocd-cm",
    38  			Namespace: "default",
    39  			Labels: map[string]string{
    40  				"app.kubernetes.io/part-of": "argocd",
    41  			},
    42  		},
    43  		Data: map[string]string{
    44  			"statusbadge.enabled": "true",
    45  		},
    46  	}
    47  }
    48  
    49  func testApp() *v1alpha1.Application {
    50  	return &v1alpha1.Application{
    51  		ObjectMeta: metav1.ObjectMeta{Name: "test-app", Namespace: "default"},
    52  		Status: v1alpha1.ApplicationStatus{
    53  			Sync:   v1alpha1.SyncStatus{Status: v1alpha1.SyncStatusCodeSynced},
    54  			Health: v1alpha1.AppHealthStatus{Status: health.HealthStatusHealthy},
    55  			OperationState: &v1alpha1.OperationState{
    56  				SyncResult: &v1alpha1.SyncOperationResult{
    57  					Revision: "aa29b85",
    58  				},
    59  			},
    60  		},
    61  	}
    62  }
    63  
    64  func testApp2() *v1alpha1.Application {
    65  	return &v1alpha1.Application{
    66  		ObjectMeta: metav1.ObjectMeta{Name: "test-app", Namespace: "argocd-test"},
    67  		Status: v1alpha1.ApplicationStatus{
    68  			Sync:   v1alpha1.SyncStatus{Status: v1alpha1.SyncStatusCodeSynced},
    69  			Health: v1alpha1.AppHealthStatus{Status: health.HealthStatusHealthy},
    70  			OperationState: &v1alpha1.OperationState{
    71  				SyncResult: &v1alpha1.SyncOperationResult{
    72  					Revision: "aa29b85",
    73  				},
    74  			},
    75  		},
    76  	}
    77  }
    78  
    79  func testApp3() *v1alpha1.Application {
    80  	return &v1alpha1.Application{
    81  		ObjectMeta: metav1.ObjectMeta{Name: "test-app", Namespace: "argocd-test"},
    82  		Status: v1alpha1.ApplicationStatus{
    83  			Sync:   v1alpha1.SyncStatus{Status: v1alpha1.SyncStatusCodeSynced},
    84  			Health: v1alpha1.AppHealthStatus{Status: health.HealthStatusHealthy},
    85  			OperationState: &v1alpha1.OperationState{
    86  				SyncResult: &v1alpha1.SyncOperationResult{
    87  					Revision: "aa29b85ababababababab",
    88  				},
    89  			},
    90  		},
    91  	}
    92  }
    93  
    94  func testProject() *v1alpha1.AppProject {
    95  	return &v1alpha1.AppProject{
    96  		ObjectMeta: metav1.ObjectMeta{Name: "test-project", Namespace: "default"},
    97  		Spec:       v1alpha1.AppProjectSpec{},
    98  	}
    99  }
   100  
   101  func TestHandlerFeatureIsEnabled(t *testing.T) {
   102  	settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm(), argoCDSecret()), "default")
   103  	handler := NewHandler(appclientset.NewSimpleClientset(testApp()), settingsMgr, "default", []string{})
   104  	req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app", http.NoBody)
   105  	require.NoError(t, err)
   106  
   107  	rr := httptest.NewRecorder()
   108  	handler.ServeHTTP(rr, req)
   109  
   110  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   111  	assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
   112  
   113  	response := rr.Body.String()
   114  	assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
   115  	assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
   116  	assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
   117  	assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
   118  	assert.NotContains(t, response, "test-app")
   119  	assert.NotContains(t, response, "(aa29b85)")
   120  }
   121  
   122  func TestHandlerFeatureProjectIsEnabled(t *testing.T) {
   123  	projectTests := []struct {
   124  		testApp     []*v1alpha1.Application
   125  		response    int
   126  		apiEndPoint string
   127  		namespace   string
   128  		health      string
   129  		status      string
   130  		healthColor color.RGBA
   131  		statusColor color.RGBA
   132  	}{
   133  		{
   134  			createApplications([]string{"Healthy:Synced", "Healthy:Synced"}, []string{"default", "default"}, "test"),
   135  			http.StatusOK, "/api/badge?project=default", "test", "Healthy", "Synced", Green, Green,
   136  		},
   137  		{
   138  			createApplications([]string{"Healthy:Synced", "Healthy:OutOfSync"}, []string{"test-project", "test-project"}, "default"),
   139  			http.StatusOK, "/api/badge?project=test-project", "default", "Healthy", "OutOfSync", Green, Orange,
   140  		},
   141  		{
   142  			createApplications([]string{"Healthy:Synced", "Degraded:Synced"}, []string{"default", "default"}, "test"),
   143  			http.StatusOK, "/api/badge?project=default", "test", "Degraded", "Synced", Red, Green,
   144  		},
   145  		{
   146  			createApplications([]string{"Healthy:Synced", "Degraded:OutOfSync"}, []string{"test-project", "test-project"}, "default"),
   147  			http.StatusOK, "/api/badge?project=test-project", "default", "Degraded", "OutOfSync", Red, Orange,
   148  		},
   149  		{
   150  			createApplications([]string{"Healthy:Synced", "Healthy:Synced"}, []string{"test-project", "default"}, "test"),
   151  			http.StatusOK, "/api/badge?project=default&project=test-project", "test", "Healthy", "Synced", Green, Green,
   152  		},
   153  		{
   154  			createApplications([]string{"Healthy:OutOfSync", "Healthy:Synced"}, []string{"test-project", "default"}, "default"),
   155  			http.StatusOK, "/api/badge?project=default&project=test-project", "default", "Healthy", "OutOfSync", Green, Orange,
   156  		},
   157  		{
   158  			createApplications([]string{"Degraded:Synced", "Healthy:Synced"}, []string{"test-project", "default"}, "test"),
   159  			http.StatusOK, "/api/badge?project=default&project=test-project", "test", "Degraded", "Synced", Red, Green,
   160  		},
   161  		{
   162  			createApplications([]string{"Degraded:OutOfSync", "Healthy:OutOfSync"}, []string{"test-project", "default"}, "default"),
   163  			http.StatusOK, "/api/badge?project=default&project=test-project", "default", "Degraded", "OutOfSync", Red, Orange,
   164  		},
   165  		{
   166  			createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"test-project", "default"}, "default"),
   167  			http.StatusOK, "/api/badge?project=", "default", "Unknown", "Unknown", Purple, Purple,
   168  		},
   169  		{
   170  			createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"test-project", "default"}, "default"),
   171  			http.StatusBadRequest, "/api/badge?project=test$project", "default", "Unknown", "Unknown", Purple, Purple,
   172  		},
   173  		{
   174  			createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"test-project", "default"}, "default"),
   175  			http.StatusOK, "/api/badge?project=unknown", "default", "Unknown", "Unknown", Purple, Purple,
   176  		},
   177  		{
   178  			createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"test-project", "default"}, "default"),
   179  			http.StatusBadRequest, "/api/badge?name=foo_bar", "default", "Unknown", "Unknown", Purple, Purple,
   180  		},
   181  		{
   182  			createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"test-project", "default"}, "default"),
   183  			http.StatusOK, "/api/badge?name=foobar", "default", "Not Found", "", Purple, Purple,
   184  		},
   185  		{
   186  			createApplicationsWithName([]string{"Healthy:Synced"}, []string{"default"}, "test", "test.application"),
   187  			http.StatusOK, "/api/badge?name=test.application-0", "test", "Healthy", "Synced", Green, Green,
   188  		},
   189  		{
   190  			createApplicationsWithName([]string{"Healthy:Synced"}, []string{"default"}, "test", "test.invalid_name"),
   191  			http.StatusBadRequest, "/api/badge?name=test.invalid_name-0", "test", "Healthy", "Synced", Green, Green,
   192  		},
   193  	}
   194  	for _, tt := range projectTests {
   195  		argoCDCm := argoCDCm()
   196  		argoCDSecret := argoCDSecret()
   197  		argoCDCm.Namespace = tt.namespace
   198  		argoCDSecret.Namespace = tt.namespace
   199  
   200  		settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm, argoCDSecret), tt.namespace)
   201  		objects := []runtime.Object{testProject()}
   202  		for _, v := range tt.testApp {
   203  			objects = append(objects, v)
   204  		}
   205  		handler := NewHandler(appclientset.NewSimpleClientset(objects...), settingsMgr, tt.namespace, []string{})
   206  		rr := httptest.NewRecorder()
   207  		req, err := http.NewRequest(http.MethodGet, tt.apiEndPoint, http.NoBody)
   208  		require.NoError(t, err)
   209  		handler.ServeHTTP(rr, req)
   210  		require.Equal(t, tt.response, rr.Result().StatusCode)
   211  		if rr.Result().StatusCode != http.StatusBadRequest {
   212  			assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   213  			assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
   214  			response := rr.Body.String()
   215  			require.Greater(t, len(response), 2)
   216  			assert.Equal(t, toRGBString(tt.healthColor), leftRectColorPattern.FindStringSubmatch(response)[1])
   217  			assert.Equal(t, toRGBString(tt.statusColor), rightRectColorPattern.FindStringSubmatch(response)[1])
   218  			assert.Equal(t, tt.health, leftTextPattern.FindStringSubmatch(response)[1])
   219  			assert.Equal(t, tt.status, rightTextPattern.FindStringSubmatch(response)[1])
   220  			assert.Equal(t, "\"20\"", svgHeightPattern.FindStringSubmatch(response)[2])
   221  		}
   222  	}
   223  }
   224  
   225  func TestHandlerNamespacesIsEnabled(t *testing.T) {
   226  	t.Run("Application in allowed namespace", func(t *testing.T) {
   227  		settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm(), argoCDSecret()), "default")
   228  		handler := NewHandler(appclientset.NewSimpleClientset(testApp2()), settingsMgr, "default", []string{"argocd-test"})
   229  		req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&namespace=argocd-test", http.NoBody)
   230  		require.NoError(t, err)
   231  
   232  		rr := httptest.NewRecorder()
   233  		handler.ServeHTTP(rr, req)
   234  
   235  		assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   236  		assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
   237  
   238  		response := rr.Body.String()
   239  		assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
   240  		assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
   241  		assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
   242  		assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
   243  		assert.NotContains(t, response, "test-app")
   244  		assert.NotContains(t, response, "(aa29b85)")
   245  	})
   246  
   247  	t.Run("Application in disallowed namespace", func(t *testing.T) {
   248  		settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm(), argoCDSecret()), "default")
   249  		handler := NewHandler(appclientset.NewSimpleClientset(testApp2()), settingsMgr, "default", []string{"argocd-test"})
   250  		req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&namespace=kube-system", http.NoBody)
   251  		require.NoError(t, err)
   252  
   253  		rr := httptest.NewRecorder()
   254  		handler.ServeHTTP(rr, req)
   255  
   256  		require.Equal(t, http.StatusOK, rr.Result().StatusCode)
   257  		response := rr.Body.String()
   258  		assert.Equal(t, toRGBString(Purple), leftRectColorPattern.FindStringSubmatch(response)[1])
   259  		assert.Equal(t, toRGBString(Purple), rightRectColorPattern.FindStringSubmatch(response)[1])
   260  		assert.Equal(t, "Not Found", leftTextPattern.FindStringSubmatch(response)[1])
   261  		assert.Empty(t, rightTextPattern.FindStringSubmatch(response)[1])
   262  	})
   263  
   264  	t.Run("Request with illegal namespace", func(t *testing.T) {
   265  		settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm(), argoCDSecret()), "default")
   266  		handler := NewHandler(appclientset.NewSimpleClientset(testApp2()), settingsMgr, "default", []string{"argocd-test"})
   267  		req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&namespace=kube()system", http.NoBody)
   268  		require.NoError(t, err)
   269  
   270  		rr := httptest.NewRecorder()
   271  		handler.ServeHTTP(rr, req)
   272  
   273  		require.Equal(t, http.StatusBadRequest, rr.Result().StatusCode)
   274  	})
   275  }
   276  
   277  func TestHandlerFeatureIsEnabledKeepFullRevisionIsEnabled(t *testing.T) {
   278  	settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm(), argoCDSecret()), "default")
   279  	handler := NewHandler(appclientset.NewSimpleClientset(testApp3()), settingsMgr, "argocd-test", []string{""})
   280  	req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&revision=true&keepFullRevision=true", http.NoBody)
   281  	require.NoError(t, err)
   282  
   283  	rr := httptest.NewRecorder()
   284  	handler.ServeHTTP(rr, req)
   285  
   286  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   287  	assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
   288  
   289  	response := rr.Body.String()
   290  	assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
   291  	assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
   292  	assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
   293  	assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
   294  	assert.NotContains(t, response, "test-app")
   295  	assert.Contains(t, response, "(aa29b85ababababababab)")
   296  }
   297  
   298  func TestHandlerFeatureIsEnabledKeepFullRevisionIsDisabled(t *testing.T) {
   299  	settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm(), argoCDSecret()), "default")
   300  	handler := NewHandler(appclientset.NewSimpleClientset(testApp3()), settingsMgr, "argocd-test", []string{})
   301  	req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&revision=true&keepFullRevision=false", http.NoBody)
   302  	require.NoError(t, err)
   303  
   304  	rr := httptest.NewRecorder()
   305  	handler.ServeHTTP(rr, req)
   306  
   307  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   308  	assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
   309  
   310  	response := rr.Body.String()
   311  	assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
   312  	assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
   313  	assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
   314  	assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
   315  	assert.NotContains(t, response, "test-app")
   316  	assert.Contains(t, response, "(aa29b85)")
   317  }
   318  
   319  func TestHandlerFeatureIsEnabledKeepFullRevisionAndWidthIsEnabled(t *testing.T) {
   320  	settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm(), argoCDSecret()), "default")
   321  	handler := NewHandler(appclientset.NewSimpleClientset(testApp3()), settingsMgr, "argocd-test", []string{""})
   322  	req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&revision=true&keepFullRevision=true&width=500", http.NoBody)
   323  	require.NoError(t, err)
   324  
   325  	rr := httptest.NewRecorder()
   326  	handler.ServeHTTP(rr, req)
   327  
   328  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   329  	assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
   330  
   331  	response := rr.Body.String()
   332  	assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
   333  	assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
   334  	assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
   335  	assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
   336  	assert.Equal(t, "500", svgWidthPattern.FindStringSubmatch(response)[1])
   337  	assert.NotContains(t, response, "test-app")
   338  	assert.Contains(t, response, "(aa29b85ababababababab)")
   339  }
   340  
   341  func createApplicationFeatureProjectIsEnabled(healthStatus health.HealthStatusCode, syncStatus v1alpha1.SyncStatusCode, appName, projectName, namespace string) *v1alpha1.Application {
   342  	return &v1alpha1.Application{
   343  		ObjectMeta: metav1.ObjectMeta{Name: appName, Namespace: namespace},
   344  		Status: v1alpha1.ApplicationStatus{
   345  			Sync:   v1alpha1.SyncStatus{Status: syncStatus},
   346  			Health: v1alpha1.AppHealthStatus{Status: healthStatus},
   347  			OperationState: &v1alpha1.OperationState{
   348  				SyncResult: &v1alpha1.SyncOperationResult{},
   349  			},
   350  		},
   351  		Spec: v1alpha1.ApplicationSpec{
   352  			Project: projectName,
   353  		},
   354  	}
   355  }
   356  
   357  func createApplications(appCombo, projectName []string, namespace string) []*v1alpha1.Application {
   358  	return createApplicationsWithName(appCombo, projectName, namespace, "app")
   359  }
   360  
   361  func createApplicationsWithName(appCombo, projectName []string, namespace string, namePrefix string) []*v1alpha1.Application {
   362  	apps := make([]*v1alpha1.Application, len(appCombo))
   363  	healthStatus := func(healthType string) health.HealthStatusCode {
   364  		switch healthType {
   365  		case "Healthy":
   366  			return health.HealthStatusHealthy
   367  		case "Degraded":
   368  			return health.HealthStatusDegraded
   369  		default:
   370  			return health.HealthStatusUnknown
   371  		}
   372  	}
   373  	syncStatus := func(syncType string) v1alpha1.SyncStatusCode {
   374  		switch syncType {
   375  		case "Synced":
   376  			return v1alpha1.SyncStatusCodeSynced
   377  		case "OutOfSync":
   378  			return v1alpha1.SyncStatusCodeOutOfSync
   379  		default:
   380  			return v1alpha1.SyncStatusCodeUnknown
   381  		}
   382  	}
   383  	for k, v := range appCombo {
   384  		a := strings.Split(v, ":")
   385  		healthApp := healthStatus(a[0])
   386  		syncApp := syncStatus(a[1])
   387  		appName := fmt.Sprintf("%s-%v", namePrefix, k)
   388  		apps[k] = createApplicationFeatureProjectIsEnabled(healthApp, syncApp, appName, projectName[k], namespace)
   389  	}
   390  	return apps
   391  }
   392  
   393  func TestHandlerFeatureIsEnabledRevisionIsEnabled(t *testing.T) {
   394  	settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm(), argoCDSecret()), "default")
   395  	handler := NewHandler(appclientset.NewSimpleClientset(testApp()), settingsMgr, "default", []string{})
   396  	req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&revision=true", http.NoBody)
   397  	require.NoError(t, err)
   398  
   399  	rr := httptest.NewRecorder()
   400  	handler.ServeHTTP(rr, req)
   401  
   402  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   403  	assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
   404  
   405  	response := rr.Body.String()
   406  	assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
   407  	assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
   408  	assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
   409  	assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
   410  	assert.NotContains(t, response, "test-app")
   411  	assert.Contains(t, response, "(aa29b85)")
   412  }
   413  
   414  func TestHandlerRevisionIsEnabledNoOperationState(t *testing.T) {
   415  	app := testApp()
   416  	app.Status.OperationState = nil
   417  
   418  	settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm(), argoCDSecret()), "default")
   419  	handler := NewHandler(appclientset.NewSimpleClientset(app), settingsMgr, "default", []string{})
   420  	req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&revision=true", http.NoBody)
   421  	require.NoError(t, err)
   422  
   423  	rr := httptest.NewRecorder()
   424  	handler.ServeHTTP(rr, req)
   425  
   426  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   427  	assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
   428  
   429  	response := rr.Body.String()
   430  	assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
   431  	assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
   432  	assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
   433  	assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
   434  	assert.NotContains(t, response, "test-app")
   435  	assert.NotContains(t, response, "(aa29b85)")
   436  }
   437  
   438  func TestHandlerRevisionIsEnabledShortCommitSHA(t *testing.T) {
   439  	app := testApp()
   440  	app.Status.OperationState.SyncResult.Revision = "abc"
   441  
   442  	settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm(), argoCDSecret()), "default")
   443  	handler := NewHandler(appclientset.NewSimpleClientset(app), settingsMgr, "default", []string{})
   444  	req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&revision=true", http.NoBody)
   445  	require.NoError(t, err)
   446  
   447  	rr := httptest.NewRecorder()
   448  	handler.ServeHTTP(rr, req)
   449  
   450  	response := rr.Body.String()
   451  	assert.Contains(t, response, "(abc)")
   452  }
   453  
   454  func TestHandlerRevisionIsEnabledMultipleSources(t *testing.T) {
   455  	app := testApp()
   456  	app.Status.OperationState.SyncResult.Revision = ""
   457  	app.Status.OperationState.SyncResult.Revisions = []string{"aa29b85", "cf41g63"}
   458  
   459  	settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewClientset(argoCDCm(), argoCDSecret()), "default")
   460  	handler := NewHandler(appclientset.NewSimpleClientset(app), settingsMgr, "default", []string{})
   461  	req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&revision=true", http.NoBody)
   462  	require.NoError(t, err)
   463  
   464  	rr := httptest.NewRecorder()
   465  	handler.ServeHTTP(rr, req)
   466  
   467  	response := rr.Body.String()
   468  	assert.Contains(t, response, "(aa29b85)")
   469  	assert.NotContains(t, response, "(cf41g63)")
   470  }
   471  
   472  func TestHandlerFeatureIsDisabled(t *testing.T) {
   473  	argoCDCmDisabled := argoCDCm()
   474  	delete(argoCDCmDisabled.Data, "statusbadge.enabled")
   475  
   476  	settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewSimpleClientset(argoCDCmDisabled, argoCDSecret()), "default")
   477  	handler := NewHandler(appclientset.NewSimpleClientset(testApp()), settingsMgr, "default", []string{})
   478  	req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app", http.NoBody)
   479  	require.NoError(t, err)
   480  
   481  	rr := httptest.NewRecorder()
   482  	handler.ServeHTTP(rr, req)
   483  
   484  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   485  	assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
   486  
   487  	response := rr.Body.String()
   488  	assert.Equal(t, toRGBString(Purple), leftRectColorPattern.FindStringSubmatch(response)[1])
   489  	assert.Equal(t, toRGBString(Purple), rightRectColorPattern.FindStringSubmatch(response)[1])
   490  	assert.Equal(t, "Unknown", leftTextPattern.FindStringSubmatch(response)[1])
   491  	assert.Equal(t, "Unknown", rightTextPattern.FindStringSubmatch(response)[1])
   492  	assert.NotContains(t, response, "test-app")
   493  	assert.Equal(t, "\"20\"", svgHeightPattern.FindStringSubmatch(response)[2])
   494  }
   495  
   496  func TestHandlerApplicationNameInBadgeIsEnabled(t *testing.T) {
   497  	settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewSimpleClientset(argoCDCm(), argoCDSecret()), "default")
   498  	handler := NewHandler(appclientset.NewSimpleClientset(testApp()), settingsMgr, "default", []string{})
   499  	req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app&showAppName=true", http.NoBody)
   500  	require.NoError(t, err)
   501  
   502  	rr := httptest.NewRecorder()
   503  	handler.ServeHTTP(rr, req)
   504  
   505  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   506  	assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
   507  
   508  	response := rr.Body.String()
   509  	assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
   510  	assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
   511  	assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
   512  	assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
   513  	assert.NotContains(t, response, "(aa29b85)")
   514  
   515  	assert.Equal(t, "test-app", titleTextPattern.FindStringSubmatch(response)[1])
   516  	assert.Equal(t, fmt.Sprintf("\"%d\"", svgHeightWithAppName), svgHeightPattern.FindStringSubmatch(response)[2])
   517  	assert.Equal(t, fmt.Sprintf("\"%d\"", badgeRowHeight), leftRectYCoodPattern.FindStringSubmatch(response)[2])
   518  	assert.Equal(t, fmt.Sprintf("\"%d\"", badgeRowHeight), rightRectYCoodPattern.FindStringSubmatch(response)[2])
   519  	assert.Equal(t, fmt.Sprintf("\"%d\"", badgeRowHeight), revisionRectYCoodPattern.FindStringSubmatch(response)[2])
   520  	assert.Equal(t, fmt.Sprintf("\"%d\"", logoYCoodWithAppName), logoYCoodPattern.FindStringSubmatch(response)[2])
   521  }
   522  
   523  func TestHandlerApplicationNameInBadgeIsDisabled(t *testing.T) {
   524  	settingsMgr := settings.NewSettingsManager(t.Context(), fake.NewSimpleClientset(argoCDCm(), argoCDSecret()), "default")
   525  	handler := NewHandler(appclientset.NewSimpleClientset(testApp()), settingsMgr, "default", []string{})
   526  	req, err := http.NewRequest(http.MethodGet, "/api/badge?name=test-app", http.NoBody)
   527  	require.NoError(t, err)
   528  
   529  	rr := httptest.NewRecorder()
   530  	handler.ServeHTTP(rr, req)
   531  
   532  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   533  	assert.Equal(t, "*", rr.Header().Get("Access-Control-Allow-Origin"))
   534  
   535  	response := rr.Body.String()
   536  	assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
   537  	assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
   538  	assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
   539  	assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
   540  	assert.Equal(t, "\"20\"", svgHeightPattern.FindStringSubmatch(response)[2])
   541  	assert.Equal(t, "\"0\"", leftRectYCoodPattern.FindStringSubmatch(response)[2])
   542  	assert.Equal(t, "\"0\"", rightRectYCoodPattern.FindStringSubmatch(response)[2])
   543  	assert.Equal(t, "\"0\"", revisionRectYCoodPattern.FindStringSubmatch(response)[2])
   544  	assert.Equal(t, "\"2\"", logoYCoodPattern.FindStringSubmatch(response)[2])
   545  	assert.NotContains(t, response, "test-app")
   546  }