github.com/argoproj/argo-cd@v1.8.7/test/e2e/fixture/app/expectation.go (about) 1 package app 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 8 "github.com/argoproj/gitops-engine/pkg/health" 9 . "github.com/argoproj/gitops-engine/pkg/sync/common" 10 v1 "k8s.io/api/core/v1" 11 apierr "k8s.io/apimachinery/pkg/api/errors" 12 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 "k8s.io/apimachinery/pkg/fields" 14 15 . "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1" 16 "github.com/argoproj/argo-cd/test/e2e/fixture" 17 ) 18 19 type state = string 20 21 const ( 22 failed = "failed" 23 pending = "pending" 24 succeeded = "succeeded" 25 ) 26 27 type Expectation func(c *Consequences) (state state, message string) 28 29 func OperationPhaseIs(expected OperationPhase) Expectation { 30 return func(c *Consequences) (state, string) { 31 operationState := c.app().Status.OperationState 32 actual := OperationRunning 33 if operationState != nil { 34 actual = operationState.Phase 35 } 36 return simple(actual == expected, fmt.Sprintf("operation phase should be %s, is %s", expected, actual)) 37 } 38 } 39 40 func OperationMessageContains(text string) Expectation { 41 return func(c *Consequences) (state, string) { 42 operationState := c.app().Status.OperationState 43 actual := "" 44 if operationState != nil { 45 actual = operationState.Message 46 } 47 return simple(strings.Contains(actual, text), fmt.Sprintf("operation message should contains '%s', got: '%s'", text, actual)) 48 } 49 } 50 51 func simple(success bool, message string) (state, string) { 52 if success { 53 return succeeded, message 54 } else { 55 return pending, message 56 } 57 } 58 59 func SyncStatusIs(expected SyncStatusCode) Expectation { 60 return func(c *Consequences) (state, string) { 61 actual := c.app().Status.Sync.Status 62 return simple(actual == expected, fmt.Sprintf("sync status to be %s, is %s", expected, actual)) 63 } 64 } 65 66 func Condition(conditionType ApplicationConditionType, conditionMessage string) Expectation { 67 return func(c *Consequences) (state, string) { 68 got := c.app().Status.Conditions 69 message := fmt.Sprintf("condition {%s %s} in %v", conditionType, conditionMessage, got) 70 for _, condition := range got { 71 if conditionType == condition.Type && strings.Contains(condition.Message, conditionMessage) { 72 return succeeded, message 73 } 74 } 75 return pending, message 76 } 77 } 78 79 func NoConditions() Expectation { 80 return func(c *Consequences) (state, string) { 81 message := "no conditions" 82 if len(c.app().Status.Conditions) == 0 { 83 return succeeded, message 84 } 85 return pending, message 86 } 87 } 88 89 func HealthIs(expected health.HealthStatusCode) Expectation { 90 return func(c *Consequences) (state, string) { 91 actual := c.app().Status.Health.Status 92 return simple(actual == expected, fmt.Sprintf("health to should %s, is %s", expected, actual)) 93 } 94 } 95 96 func ResourceSyncStatusIs(kind, resource string, expected SyncStatusCode) Expectation { 97 return func(c *Consequences) (state, string) { 98 actual := c.resource(kind, resource, "").Status 99 return simple(actual == expected, fmt.Sprintf("resource '%s/%s' sync status should be %s, is %s", kind, resource, expected, actual)) 100 } 101 } 102 func ResourceSyncStatusWithNamespaceIs(kind, resource, namespace string, expected SyncStatusCode) Expectation { 103 return func(c *Consequences) (state, string) { 104 actual := c.resource(kind, resource, namespace).Status 105 return simple(actual == expected, fmt.Sprintf("resource '%s/%s' sync status should be %s, is %s", kind, resource, expected, actual)) 106 } 107 } 108 func ResourceHealthIs(kind, resource string, expected health.HealthStatusCode) Expectation { 109 return func(c *Consequences) (state, string) { 110 actual := c.resource(kind, resource, "").Health.Status 111 return simple(actual == expected, fmt.Sprintf("resource '%s/%s' health should be %s, is %s", kind, resource, expected, actual)) 112 } 113 } 114 func ResourceHealthWithNamespaceIs(kind, resource, namespace string, expected health.HealthStatusCode) Expectation { 115 return func(c *Consequences) (state, string) { 116 actual := c.resource(kind, resource, namespace).Health.Status 117 return simple(actual == expected, fmt.Sprintf("resource '%s/%s' health should be %s, is %s", kind, resource, expected, actual)) 118 } 119 } 120 func ResourceResultNumbering(num int) Expectation { 121 return func(c *Consequences) (state, string) { 122 actualNum := len(c.app().Status.OperationState.SyncResult.Resources) 123 if actualNum < num { 124 return pending, fmt.Sprintf("not enough results yet, want %d, got %d", num, actualNum) 125 } else if actualNum == num { 126 return succeeded, fmt.Sprintf("right number of results, want %d, got %d", num, actualNum) 127 } else { 128 return failed, fmt.Sprintf("too many results, want %d, got %d", num, actualNum) 129 } 130 } 131 } 132 133 func ResourceResultIs(result ResourceResult) Expectation { 134 return func(c *Consequences) (state, string) { 135 results := c.app().Status.OperationState.SyncResult.Resources 136 for _, res := range results { 137 if *res == result { 138 return succeeded, fmt.Sprintf("found resource result %v", result) 139 } 140 } 141 return pending, fmt.Sprintf("waiting for resource result %v in %v", result, results) 142 } 143 } 144 145 func DoesNotExist() Expectation { 146 return func(c *Consequences) (state, string) { 147 _, err := c.get() 148 if err != nil { 149 if apierr.IsNotFound(err) { 150 return succeeded, "app does not exist" 151 } 152 return failed, err.Error() 153 } 154 return pending, "app should not exist" 155 } 156 } 157 158 func Pod(predicate func(p v1.Pod) bool) Expectation { 159 return func(c *Consequences) (state, string) { 160 pods, err := pods() 161 if err != nil { 162 return failed, err.Error() 163 } 164 for _, pod := range pods.Items { 165 if predicate(pod) { 166 return succeeded, fmt.Sprintf("pod predicate matched pod named '%s'", pod.GetName()) 167 } 168 } 169 return pending, "pod predicate does not match pods" 170 } 171 } 172 173 func NotPod(predicate func(p v1.Pod) bool) Expectation { 174 return func(c *Consequences) (state, string) { 175 pods, err := pods() 176 if err != nil { 177 return failed, err.Error() 178 } 179 for _, pod := range pods.Items { 180 if predicate(pod) { 181 return pending, fmt.Sprintf("pod predicate matched pod named '%s'", pod.GetName()) 182 } 183 } 184 return succeeded, "pod predicate did not match any pod" 185 } 186 } 187 188 func pods() (*v1.PodList, error) { 189 fixture.KubeClientset.CoreV1() 190 pods, err := fixture.KubeClientset.CoreV1().Pods(fixture.DeploymentNamespace()).List(context.Background(), metav1.ListOptions{}) 191 return pods, err 192 } 193 194 func Event(reason string, message string) Expectation { 195 return func(c *Consequences) (state, string) { 196 list, err := fixture.KubeClientset.CoreV1().Events(fixture.ArgoCDNamespace).List(context.Background(), metav1.ListOptions{ 197 FieldSelector: fields.SelectorFromSet(map[string]string{ 198 "involvedObject.name": c.context.name, 199 "involvedObject.namespace": fixture.ArgoCDNamespace, 200 }).String(), 201 }) 202 if err != nil { 203 return failed, err.Error() 204 } 205 206 for i := range list.Items { 207 event := list.Items[i] 208 if event.Reason == reason && strings.Contains(event.Message, message) { 209 return succeeded, fmt.Sprintf("found event with reason=%s; message=%s", reason, message) 210 } 211 } 212 return failed, fmt.Sprintf("unable to find event with reason=%s; message=%s", reason, message) 213 } 214 } 215 216 // asserts that the last command was successful 217 func Success(message string) Expectation { 218 return func(c *Consequences) (state, string) { 219 if c.actions.lastError != nil { 220 return failed, "error" 221 } 222 if !strings.Contains(c.actions.lastOutput, message) { 223 return failed, fmt.Sprintf("output did not contain '%s'", message) 224 } 225 return succeeded, fmt.Sprintf("no error and output contained '%s'", message) 226 } 227 } 228 229 // asserts that the last command was an error with substring match 230 func Error(message, err string) Expectation { 231 return func(c *Consequences) (state, string) { 232 if c.actions.lastError == nil { 233 return failed, "no error" 234 } 235 if !strings.Contains(c.actions.lastOutput, message) { 236 return failed, fmt.Sprintf("output does not contain '%s'", message) 237 } 238 if !strings.Contains(c.actions.lastError.Error(), err) { 239 return failed, fmt.Sprintf("error does not contain '%s'", message) 240 } 241 return succeeded, fmt.Sprintf("error '%s'", message) 242 } 243 }