github.com/argoproj/argo-events@v1.9.1/controllers/sensor/validate_test.go (about) 1 /* 2 Copyright 2018 BlackRock, Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package sensor 18 19 import ( 20 "fmt" 21 "os" 22 "strings" 23 "testing" 24 25 eventbusv1alpha1 "github.com/argoproj/argo-events/pkg/apis/eventbus/v1alpha1" 26 "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1" 27 "github.com/ghodss/yaml" 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 ) 31 32 func TestValidateSensor(t *testing.T) { 33 dir := "../../examples/sensors" 34 dirEntries, err := os.ReadDir(dir) 35 require.NoError(t, err) 36 37 for _, entry := range dirEntries { 38 if entry.IsDir() { 39 continue 40 } 41 t.Run( 42 fmt.Sprintf("test example load: %s/%s", dir, entry.Name()), 43 func(t *testing.T) { 44 content, err := os.ReadFile(fmt.Sprintf("%s/%s", dir, entry.Name())) 45 assert.NoError(t, err) 46 47 var sensor *v1alpha1.Sensor 48 eventBus := &eventbusv1alpha1.EventBus{Spec: eventbusv1alpha1.EventBusSpec{JetStream: &eventbusv1alpha1.JetStreamBus{}}} 49 err = yaml.Unmarshal(content, &sensor) 50 assert.NoError(t, err) 51 52 err = ValidateSensor(sensor, eventBus) 53 assert.NoError(t, err) 54 }) 55 } 56 } 57 58 func TestValidDependencies(t *testing.T) { 59 jetstreamBus := &eventbusv1alpha1.EventBus{Spec: eventbusv1alpha1.EventBusSpec{JetStream: &eventbusv1alpha1.JetStreamBus{}}} 60 stanBus := &eventbusv1alpha1.EventBus{Spec: eventbusv1alpha1.EventBusSpec{NATS: &eventbusv1alpha1.NATSBus{}}} 61 62 t.Run("test duplicate deps fail for STAN", func(t *testing.T) { 63 sObj := sensorObj.DeepCopy() 64 sObj.Spec.Dependencies = append(sObj.Spec.Dependencies, v1alpha1.EventDependency{ 65 Name: "fake-dep2", 66 EventSourceName: "fake-source", 67 EventName: "fake-one", 68 }) 69 err := ValidateSensor(sObj, stanBus) 70 assert.NotNil(t, err) 71 assert.Equal(t, true, strings.Contains(err.Error(), "is referenced for more than one dependency")) 72 }) 73 74 t.Run("test duplicate deps are fine for Jetstream", func(t *testing.T) { 75 sObj := sensorObj.DeepCopy() 76 sObj.Spec.Dependencies = append(sObj.Spec.Dependencies, v1alpha1.EventDependency{ 77 Name: "fake-dep2", 78 EventSourceName: "fake-source", 79 EventName: "fake-one", 80 }) 81 err := ValidateSensor(sObj, jetstreamBus) 82 assert.Nil(t, err) 83 }) 84 85 t.Run("test empty event source name", func(t *testing.T) { 86 sObj := sensorObj.DeepCopy() 87 sObj.Spec.Dependencies = append(sObj.Spec.Dependencies, v1alpha1.EventDependency{ 88 Name: "fake-dep2", 89 EventName: "fake-one", 90 }) 91 err := ValidateSensor(sObj, jetstreamBus) 92 assert.NotNil(t, err) 93 assert.Equal(t, true, strings.Contains(err.Error(), "must define the EventSourceName")) 94 }) 95 96 t.Run("test empty event name", func(t *testing.T) { 97 sObj := sensorObj.DeepCopy() 98 sObj.Spec.Dependencies = append(sObj.Spec.Dependencies, v1alpha1.EventDependency{ 99 Name: "fake-dep2", 100 EventSourceName: "fake-source", 101 }) 102 err := ValidateSensor(sObj, jetstreamBus) 103 assert.NotNil(t, err) 104 assert.Equal(t, true, strings.Contains(err.Error(), "must define the EventName")) 105 }) 106 107 t.Run("test empty event name", func(t *testing.T) { 108 sObj := sensorObj.DeepCopy() 109 sObj.Spec.Dependencies = append(sObj.Spec.Dependencies, v1alpha1.EventDependency{ 110 EventSourceName: "fake-source2", 111 EventName: "fake-one2", 112 }) 113 err := ValidateSensor(sObj, jetstreamBus) 114 assert.NotNil(t, err) 115 assert.Equal(t, true, strings.Contains(err.Error(), "must define a name")) 116 }) 117 } 118 119 func TestValidateLogicalOperator(t *testing.T) { 120 t.Run("test valid", func(t *testing.T) { 121 logOp := v1alpha1.OrLogicalOperator 122 123 err := validateLogicalOperator(logOp) 124 125 assert.NoError(t, err) 126 }) 127 128 t.Run("test not valid", func(t *testing.T) { 129 logOp := v1alpha1.LogicalOperator("fake") 130 131 err := validateLogicalOperator(logOp) 132 133 assert.Error(t, err) 134 }) 135 } 136 137 func TestValidateComparator(t *testing.T) { 138 t.Run("test valid", func(t *testing.T) { 139 comp := v1alpha1.NotEqualTo 140 141 err := validateComparator(comp) 142 143 assert.NoError(t, err) 144 }) 145 146 t.Run("test not valid", func(t *testing.T) { 147 comp := v1alpha1.Comparator("fake") 148 149 err := validateComparator(comp) 150 151 assert.Error(t, err) 152 }) 153 } 154 155 func TestValidateEventFilter(t *testing.T) { 156 t.Run("test empty", func(t *testing.T) { 157 filter := &v1alpha1.EventDependencyFilter{} 158 159 err := validateEventFilter(filter) 160 161 assert.NoError(t, err) 162 }) 163 164 t.Run("test valid, all", func(t *testing.T) { 165 filter := &v1alpha1.EventDependencyFilter{ 166 ExprLogicalOperator: v1alpha1.OrLogicalOperator, 167 Exprs: []v1alpha1.ExprFilter{ 168 { 169 Expr: "fake-expr", 170 Fields: []v1alpha1.PayloadField{ 171 { 172 Path: "fake-path", 173 Name: "fake-name", 174 }, 175 }, 176 }, 177 }, 178 DataLogicalOperator: v1alpha1.OrLogicalOperator, 179 Data: []v1alpha1.DataFilter{ 180 { 181 Path: "fake-path", 182 Type: "fake-type", 183 Value: []string{ 184 "fake-value", 185 }, 186 }, 187 }, 188 Context: &v1alpha1.EventContext{ 189 Type: "type", 190 Subject: "subject", 191 Source: "source", 192 DataContentType: "fake-content-type", 193 }, 194 Time: &v1alpha1.TimeFilter{ 195 Start: "00:00:00", 196 Stop: "06:00:00", 197 }, 198 } 199 200 err := validateEventFilter(filter) 201 202 assert.NoError(t, err) 203 }) 204 205 t.Run("test valid, expr only", func(t *testing.T) { 206 filter := &v1alpha1.EventDependencyFilter{ 207 Exprs: []v1alpha1.ExprFilter{ 208 { 209 Expr: "fake-expr", 210 Fields: []v1alpha1.PayloadField{ 211 { 212 Path: "fake-path", 213 Name: "fake-name", 214 }, 215 }, 216 }, 217 }, 218 } 219 220 err := validateEventFilter(filter) 221 222 assert.NoError(t, err) 223 }) 224 225 t.Run("test valid, data only", func(t *testing.T) { 226 filter := &v1alpha1.EventDependencyFilter{ 227 Data: []v1alpha1.DataFilter{ 228 { 229 Path: "fake-path", 230 Type: "fake-type", 231 Value: []string{ 232 "fake-value", 233 }, 234 }, 235 }, 236 } 237 238 err := validateEventFilter(filter) 239 240 assert.NoError(t, err) 241 }) 242 243 t.Run("test valid, ctx only", func(t *testing.T) { 244 filter := &v1alpha1.EventDependencyFilter{ 245 Context: &v1alpha1.EventContext{ 246 Type: "type", 247 Subject: "subject", 248 Source: "source", 249 DataContentType: "fake-content-type", 250 }, 251 } 252 253 err := validateEventFilter(filter) 254 255 assert.NoError(t, err) 256 }) 257 258 t.Run("test valid, time only", func(t *testing.T) { 259 filter := &v1alpha1.EventDependencyFilter{ 260 Time: &v1alpha1.TimeFilter{ 261 Start: "00:00:00", 262 Stop: "06:00:00", 263 }, 264 } 265 266 err := validateEventFilter(filter) 267 268 assert.NoError(t, err) 269 }) 270 271 t.Run("test not valid, wrong logical operator", func(t *testing.T) { 272 filter := &v1alpha1.EventDependencyFilter{ 273 DataLogicalOperator: "fake", 274 } 275 276 err := validateEventFilter(filter) 277 278 assert.Error(t, err) 279 }) 280 } 281 282 func TestValidateEventExprFilter(t *testing.T) { 283 t.Run("test valid", func(t *testing.T) { 284 exprFilter := &v1alpha1.ExprFilter{ 285 Expr: "fake-expr", 286 Fields: []v1alpha1.PayloadField{ 287 { 288 Path: "fake-path", 289 Name: "fake-name", 290 }, 291 }, 292 } 293 294 err := validateEventExprFilter(exprFilter) 295 296 assert.NoError(t, err) 297 }) 298 299 t.Run("test not valid, no expr", func(t *testing.T) { 300 exprFilter := &v1alpha1.ExprFilter{ 301 Fields: []v1alpha1.PayloadField{ 302 { 303 Path: "fake-path", 304 Name: "fake-name", 305 }, 306 }, 307 } 308 309 err := validateEventExprFilter(exprFilter) 310 311 assert.Error(t, err) 312 }) 313 314 t.Run("test not valid, no field name", func(t *testing.T) { 315 exprFilter := &v1alpha1.ExprFilter{ 316 Expr: "fake-expr", 317 Fields: []v1alpha1.PayloadField{ 318 { 319 Path: "fake-path", 320 }, 321 }, 322 } 323 324 err := validateEventExprFilter(exprFilter) 325 326 assert.Error(t, err) 327 }) 328 } 329 330 func TestValidateEventDataFilter(t *testing.T) { 331 t.Run("test valid", func(t *testing.T) { 332 dataFilter := &v1alpha1.DataFilter{ 333 Path: "body.value", 334 Type: "number", 335 Value: []string{"50.0"}, 336 } 337 338 err := validateEventDataFilter(dataFilter) 339 340 assert.NoError(t, err) 341 }) 342 343 t.Run("test not valid, no path", func(t *testing.T) { 344 dataFilter := &v1alpha1.DataFilter{ 345 Type: "number", 346 Value: []string{"50.0"}, 347 } 348 349 err := validateEventDataFilter(dataFilter) 350 351 assert.Error(t, err) 352 }) 353 354 t.Run("test not valid, empty value", func(t *testing.T) { 355 dataFilter := &v1alpha1.DataFilter{ 356 Path: "body.value", 357 Type: "string", 358 Value: []string{""}, 359 } 360 361 err := validateEventDataFilter(dataFilter) 362 363 assert.Error(t, err) 364 }) 365 366 t.Run("test not valid, wrong comparator", func(t *testing.T) { 367 dataFilter := &v1alpha1.DataFilter{ 368 Comparator: "fake", 369 Path: "body.value", 370 Type: "string", 371 Value: []string{""}, 372 } 373 374 err := validateEventDataFilter(dataFilter) 375 376 assert.Error(t, err) 377 }) 378 } 379 380 func TestValidateEventCtxFilter(t *testing.T) { 381 t.Run("test all fields", func(t *testing.T) { 382 ctxFilter := &v1alpha1.EventContext{ 383 Type: "fake-type", 384 Subject: "fake-subject", 385 Source: "fake-source", 386 DataContentType: "fake-content-type", 387 } 388 389 err := validateEventCtxFilter(ctxFilter) 390 391 assert.NoError(t, err) 392 }) 393 394 t.Run("test single field", func(t *testing.T) { 395 ctxFilter := &v1alpha1.EventContext{ 396 Type: "fake-type", 397 } 398 399 err := validateEventCtxFilter(ctxFilter) 400 401 assert.NoError(t, err) 402 }) 403 404 t.Run("test no fields", func(t *testing.T) { 405 ctxFilter := &v1alpha1.EventContext{} 406 407 err := validateEventCtxFilter(ctxFilter) 408 409 assert.Error(t, err) 410 }) 411 } 412 413 func TestValidateEventTimeFilter(t *testing.T) { 414 t.Run("test start < stop", func(t *testing.T) { 415 timeFilter := &v1alpha1.TimeFilter{ 416 Start: "00:00:00", 417 Stop: "06:00:00", 418 } 419 420 err := validateEventTimeFilter(timeFilter) 421 422 assert.NoError(t, err) 423 }) 424 425 t.Run("test stop < start", func(t *testing.T) { 426 timeFilter := &v1alpha1.TimeFilter{ 427 Start: "06:00:00", 428 Stop: "00:00:00", 429 } 430 431 err := validateEventTimeFilter(timeFilter) 432 433 assert.NoError(t, err) 434 }) 435 436 t.Run("test start = stop", func(t *testing.T) { 437 timeFilter := &v1alpha1.TimeFilter{ 438 Start: "00:00:00", 439 Stop: "00:00:00", 440 } 441 442 err := validateEventTimeFilter(timeFilter) 443 444 assert.Error(t, err) 445 }) 446 } 447 448 func TestValidTriggers(t *testing.T) { 449 t.Run("duplicate trigger names", func(t *testing.T) { 450 triggers := []v1alpha1.Trigger{ 451 { 452 Template: &v1alpha1.TriggerTemplate{ 453 Name: "fake-trigger", 454 K8s: &v1alpha1.StandardK8STrigger{ 455 Operation: "create", 456 Source: &v1alpha1.ArtifactLocation{}, 457 }, 458 }, 459 }, 460 { 461 Template: &v1alpha1.TriggerTemplate{ 462 Name: "fake-trigger", 463 K8s: &v1alpha1.StandardK8STrigger{ 464 Operation: "create", 465 Source: &v1alpha1.ArtifactLocation{}, 466 }, 467 }, 468 }, 469 } 470 err := validateTriggers(triggers) 471 assert.NotNil(t, err) 472 assert.Equal(t, true, strings.Contains(err.Error(), "duplicate trigger name:")) 473 }) 474 475 t.Run("empty trigger template", func(t *testing.T) { 476 triggers := []v1alpha1.Trigger{ 477 { 478 Template: nil, 479 }, 480 } 481 err := validateTriggers(triggers) 482 assert.NotNil(t, err) 483 assert.Equal(t, true, strings.Contains(err.Error(), "trigger template can't be nil")) 484 }) 485 486 t.Run("invalid conditions reset - cron", func(t *testing.T) { 487 triggers := []v1alpha1.Trigger{ 488 { 489 Template: &v1alpha1.TriggerTemplate{ 490 Name: "fake-trigger", 491 Conditions: "A && B", 492 ConditionsReset: []v1alpha1.ConditionsResetCriteria{ 493 { 494 ByTime: &v1alpha1.ConditionsResetByTime{ 495 Cron: "a * * * *", 496 }, 497 }, 498 }, 499 K8s: &v1alpha1.StandardK8STrigger{ 500 Operation: "create", 501 Source: &v1alpha1.ArtifactLocation{}, 502 }, 503 }, 504 }, 505 } 506 err := validateTriggers(triggers) 507 assert.NotNil(t, err) 508 assert.Equal(t, true, strings.Contains(err.Error(), "invalid cron expression")) 509 }) 510 511 t.Run("invalid conditions reset - timezone", func(t *testing.T) { 512 triggers := []v1alpha1.Trigger{ 513 { 514 Template: &v1alpha1.TriggerTemplate{ 515 Name: "fake-trigger", 516 Conditions: "A && B", 517 ConditionsReset: []v1alpha1.ConditionsResetCriteria{ 518 { 519 ByTime: &v1alpha1.ConditionsResetByTime{ 520 Cron: "* * * * *", 521 Timezone: "fake", 522 }, 523 }, 524 }, 525 K8s: &v1alpha1.StandardK8STrigger{ 526 Operation: "create", 527 Source: &v1alpha1.ArtifactLocation{}, 528 }, 529 }, 530 }, 531 } 532 err := validateTriggers(triggers) 533 assert.NotNil(t, err) 534 assert.Equal(t, true, strings.Contains(err.Error(), "invalid timezone")) 535 }) 536 }