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 }