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

     1  package badge
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"image/color"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
    13  	appclientset "github.com/argoproj/argo-cd/pkg/client/clientset/versioned/fake"
    14  	"github.com/argoproj/argo-cd/util/settings"
    15  
    16  	"github.com/argoproj/gitops-engine/pkg/health"
    17  	"github.com/stretchr/testify/assert"
    18  	corev1 "k8s.io/api/core/v1"
    19  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    20  	"k8s.io/client-go/kubernetes/fake"
    21  )
    22  
    23  var (
    24  	argoCDSecret = corev1.Secret{
    25  		ObjectMeta: v1.ObjectMeta{Name: "argocd-secret", Namespace: "default"},
    26  		Data: map[string][]byte{
    27  			"admin.password":   []byte("test"),
    28  			"server.secretkey": []byte("test"),
    29  		},
    30  	}
    31  	argoCDCm = corev1.ConfigMap{
    32  		ObjectMeta: v1.ObjectMeta{
    33  			Name:      "argocd-cm",
    34  			Namespace: "default",
    35  			Labels: map[string]string{
    36  				"app.kubernetes.io/part-of": "argocd",
    37  			},
    38  		},
    39  		Data: map[string]string{
    40  			"statusbadge.enabled": "true",
    41  		},
    42  	}
    43  	testApp = v1alpha1.Application{
    44  		ObjectMeta: v1.ObjectMeta{Name: "testApp", Namespace: "default"},
    45  		Status: v1alpha1.ApplicationStatus{
    46  			Sync:   v1alpha1.SyncStatus{Status: v1alpha1.SyncStatusCodeSynced},
    47  			Health: v1alpha1.HealthStatus{Status: health.HealthStatusHealthy},
    48  			OperationState: &v1alpha1.OperationState{
    49  				SyncResult: &v1alpha1.SyncOperationResult{
    50  					Revision: "aa29b85",
    51  				},
    52  			},
    53  		},
    54  	}
    55  	testProject = v1alpha1.AppProject{
    56  		ObjectMeta: v1.ObjectMeta{Name: "testProject", Namespace: "default"},
    57  		Spec:       v1alpha1.AppProjectSpec{},
    58  	}
    59  )
    60  
    61  func TestHandlerFeatureIsEnabled(t *testing.T) {
    62  	settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
    63  	handler := NewHandler(appclientset.NewSimpleClientset(&testApp), settingsMgr, "default")
    64  	req, err := http.NewRequest("GET", "/api/badge?name=testApp", nil)
    65  	assert.NoError(t, err)
    66  
    67  	rr := httptest.NewRecorder()
    68  	handler.ServeHTTP(rr, req)
    69  
    70  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
    71  
    72  	response := rr.Body.String()
    73  	assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
    74  	assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
    75  	assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
    76  	assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
    77  	assert.NotContains(t, response, "(aa29b85)")
    78  }
    79  
    80  func TestHandlerFeatureProjectIsEnabled(t *testing.T) {
    81  	projectTests := []struct {
    82  		testApp     []*v1alpha1.Application
    83  		apiEndPoint string
    84  		namespace   string
    85  		health      string
    86  		status      string
    87  		healthColor color.RGBA
    88  		statusColor color.RGBA
    89  	}{
    90  		{createApplications([]string{"Healthy:Synced", "Healthy:Synced"}, []string{"default", "default"}, "test"),
    91  			"/api/badge?project=default", "test", "Healthy", "Synced", Green, Green},
    92  		{createApplications([]string{"Healthy:Synced", "Healthy:OutOfSync"}, []string{"testProject", "testProject"}, "default"),
    93  			"/api/badge?project=testProject", "default", "Healthy", "OutOfSync", Green, Orange},
    94  		{createApplications([]string{"Healthy:Synced", "Degraded:Synced"}, []string{"default", "default"}, "test"),
    95  			"/api/badge?project=default", "test", "Degraded", "Synced", Red, Green},
    96  		{createApplications([]string{"Healthy:Synced", "Degraded:OutOfSync"}, []string{"testProject", "testProject"}, "default"),
    97  			"/api/badge?project=testProject", "default", "Degraded", "OutOfSync", Red, Orange},
    98  		{createApplications([]string{"Healthy:Synced", "Healthy:Synced"}, []string{"testProject", "default"}, "test"),
    99  			"/api/badge?project=default&project=testProject", "test", "Healthy", "Synced", Green, Green},
   100  		{createApplications([]string{"Healthy:OutOfSync", "Healthy:Synced"}, []string{"testProject", "default"}, "default"),
   101  			"/api/badge?project=default&project=testProject", "default", "Healthy", "OutOfSync", Green, Orange},
   102  		{createApplications([]string{"Degraded:Synced", "Healthy:Synced"}, []string{"testProject", "default"}, "test"),
   103  			"/api/badge?project=default&project=testProject", "test", "Degraded", "Synced", Red, Green},
   104  		{createApplications([]string{"Degraded:OutOfSync", "Healthy:OutOfSync"}, []string{"testProject", "default"}, "default"),
   105  			"/api/badge?project=default&project=testProject", "default", "Degraded", "OutOfSync", Red, Orange},
   106  		{createApplications([]string{"Unknown:Unknown", "Unknown:Unknown"}, []string{"testProject", "default"}, "default"),
   107  			"/api/badge?project=", "default", "Unknown", "Unknown", Purple, Purple},
   108  	}
   109  	for _, tt := range projectTests {
   110  		argoCDCm.ObjectMeta.Namespace = tt.namespace
   111  		argoCDSecret.ObjectMeta.Namespace = tt.namespace
   112  		settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), tt.namespace)
   113  		handler := NewHandler(appclientset.NewSimpleClientset(&testProject, tt.testApp[0], tt.testApp[1]), settingsMgr, tt.namespace)
   114  		rr := httptest.NewRecorder()
   115  		req, err := http.NewRequest("GET", tt.apiEndPoint, nil)
   116  		assert.NoError(t, err)
   117  		handler.ServeHTTP(rr, req)
   118  		assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   119  		response := rr.Body.String()
   120  		assert.Equal(t, toRGBString(tt.healthColor), leftRectColorPattern.FindStringSubmatch(response)[1])
   121  		assert.Equal(t, toRGBString(tt.statusColor), rightRectColorPattern.FindStringSubmatch(response)[1])
   122  		assert.Equal(t, tt.health, leftTextPattern.FindStringSubmatch(response)[1])
   123  		assert.Equal(t, tt.status, rightTextPattern.FindStringSubmatch(response)[1])
   124  
   125  	}
   126  }
   127  
   128  func createApplicationFeatureProjectIsEnabled(healthStatus health.HealthStatusCode, syncStatus v1alpha1.SyncStatusCode, appName, projectName, namespace string) *v1alpha1.Application {
   129  	return &v1alpha1.Application{
   130  		ObjectMeta: v1.ObjectMeta{Name: appName, Namespace: namespace},
   131  		Status: v1alpha1.ApplicationStatus{
   132  			Sync:   v1alpha1.SyncStatus{Status: syncStatus},
   133  			Health: v1alpha1.HealthStatus{Status: healthStatus},
   134  			OperationState: &v1alpha1.OperationState{
   135  				SyncResult: &v1alpha1.SyncOperationResult{},
   136  			},
   137  		},
   138  		Spec: v1alpha1.ApplicationSpec{
   139  			Project: projectName,
   140  		},
   141  	}
   142  }
   143  
   144  func createApplications(appCombo, projectName []string, namespace string) []*v1alpha1.Application {
   145  	apps := make([]*v1alpha1.Application, len(appCombo))
   146  	healthStatus := func(healthType string) health.HealthStatusCode {
   147  		switch healthType {
   148  		case "Healthy":
   149  			return health.HealthStatusHealthy
   150  		case "Degraded":
   151  			return health.HealthStatusDegraded
   152  		default:
   153  			return health.HealthStatusUnknown
   154  		}
   155  	}
   156  	syncStatus := func(syncType string) v1alpha1.SyncStatusCode {
   157  		switch syncType {
   158  		case "Synced":
   159  			return v1alpha1.SyncStatusCodeSynced
   160  		case "OutOfSync":
   161  			return v1alpha1.SyncStatusCodeOutOfSync
   162  		default:
   163  			return v1alpha1.SyncStatusCodeUnknown
   164  		}
   165  	}
   166  	for k, v := range appCombo {
   167  		a := strings.Split(v, ":")
   168  		healthApp := healthStatus(a[0])
   169  		syncApp := syncStatus(a[1])
   170  		appName := fmt.Sprintf("App %v", k)
   171  		apps[k] = createApplicationFeatureProjectIsEnabled(healthApp, syncApp, appName, projectName[k], namespace)
   172  	}
   173  	return apps
   174  }
   175  func TestHandlerFeatureIsEnabledRevisionIsEnabled(t *testing.T) {
   176  	settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
   177  	handler := NewHandler(appclientset.NewSimpleClientset(&testApp), settingsMgr, "default")
   178  	req, err := http.NewRequest("GET", "/api/badge?name=testApp&revision=true", nil)
   179  	assert.NoError(t, err)
   180  
   181  	rr := httptest.NewRecorder()
   182  	handler.ServeHTTP(rr, req)
   183  
   184  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   185  
   186  	response := rr.Body.String()
   187  	assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
   188  	assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
   189  	assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
   190  	assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
   191  	assert.Contains(t, response, "(aa29b85)")
   192  }
   193  
   194  func TestHandlerRevisionIsEnabledNoOperationState(t *testing.T) {
   195  	app := testApp.DeepCopy()
   196  	app.Status.OperationState = nil
   197  
   198  	settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
   199  	handler := NewHandler(appclientset.NewSimpleClientset(app), settingsMgr, "default")
   200  	req, err := http.NewRequest("GET", "/api/badge?name=testApp&revision=true", nil)
   201  	assert.NoError(t, err)
   202  
   203  	rr := httptest.NewRecorder()
   204  	handler.ServeHTTP(rr, req)
   205  
   206  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   207  
   208  	response := rr.Body.String()
   209  	assert.Equal(t, toRGBString(Green), leftRectColorPattern.FindStringSubmatch(response)[1])
   210  	assert.Equal(t, toRGBString(Green), rightRectColorPattern.FindStringSubmatch(response)[1])
   211  	assert.Equal(t, "Healthy", leftTextPattern.FindStringSubmatch(response)[1])
   212  	assert.Equal(t, "Synced", rightTextPattern.FindStringSubmatch(response)[1])
   213  	assert.NotContains(t, response, "(aa29b85)")
   214  }
   215  
   216  func TestHandlerRevisionIsEnabledShortCommitSHA(t *testing.T) {
   217  	app := testApp.DeepCopy()
   218  	app.Status.OperationState.SyncResult.Revision = "abc"
   219  
   220  	settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(&argoCDCm, &argoCDSecret), "default")
   221  	handler := NewHandler(appclientset.NewSimpleClientset(app), settingsMgr, "default")
   222  	req, err := http.NewRequest("GET", "/api/badge?name=testApp&revision=true", nil)
   223  	assert.NoError(t, err)
   224  
   225  	rr := httptest.NewRecorder()
   226  	handler.ServeHTTP(rr, req)
   227  
   228  	response := rr.Body.String()
   229  	assert.Contains(t, response, "(abc)")
   230  }
   231  
   232  func TestHandlerFeatureIsDisabled(t *testing.T) {
   233  
   234  	argoCDCmDisabled := argoCDCm.DeepCopy()
   235  	delete(argoCDCmDisabled.Data, "statusbadge.enabled")
   236  
   237  	settingsMgr := settings.NewSettingsManager(context.Background(), fake.NewSimpleClientset(argoCDCmDisabled, &argoCDSecret), "default")
   238  	handler := NewHandler(appclientset.NewSimpleClientset(&testApp), settingsMgr, "default")
   239  	req, err := http.NewRequest("GET", "/api/badge?name=testApp", nil)
   240  	assert.NoError(t, err)
   241  
   242  	rr := httptest.NewRecorder()
   243  	handler.ServeHTTP(rr, req)
   244  
   245  	assert.Equal(t, "private, no-store", rr.Header().Get("Cache-Control"))
   246  
   247  	response := rr.Body.String()
   248  	assert.Equal(t, toRGBString(Purple), leftRectColorPattern.FindStringSubmatch(response)[1])
   249  	assert.Equal(t, toRGBString(Purple), rightRectColorPattern.FindStringSubmatch(response)[1])
   250  	assert.Equal(t, "Unknown", leftTextPattern.FindStringSubmatch(response)[1])
   251  	assert.Equal(t, "Unknown", rightTextPattern.FindStringSubmatch(response)[1])
   252  }