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  }