| package meter |
|
|
| import ( |
| "fmt" |
| "regexp" |
| "testing" |
| "time" |
|
|
| "github.com/oklog/ulid/v2" |
| "github.com/samber/lo" |
| "github.com/stretchr/testify/assert" |
| "github.com/stretchr/testify/require" |
|
|
| "github.com/openmeterio/openmeter/pkg/models" |
| ) |
|
|
| func TestMeterValidation(t *testing.T) { |
| tests := []struct { |
| description string |
| meter Meter |
| error error |
| }{ |
| { |
| description: "valid meter", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationSum, |
| EventType: "event-type-test", |
| ValueProperty: lo.ToPtr("$.my_property"), |
| GroupBy: map[string]string{"test_group": "$.test_group"}, |
| }, |
| error: nil, |
| }, |
| { |
| description: "valid without group by", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationSum, |
| EventType: "event-type-test", |
| ValueProperty: lo.ToPtr("$.my_property"), |
| }, |
| error: nil, |
| }, |
| { |
| description: "count is valid without value property", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationCount, |
| EventType: "event-type-test", |
| GroupBy: map[string]string{"test_group": "$.test_group"}, |
| }, |
| error: nil, |
| }, |
| { |
| description: "count is invalid with value property", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationCount, |
| EventType: "event-type-test", |
| ValueProperty: lo.ToPtr("$.my_property"), |
| GroupBy: map[string]string{"test_group": "$.test_group"}, |
| }, |
| error: fmt.Errorf("meter value property is not allowed when the aggregation is count"), |
| }, |
| { |
| description: "key is empty", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Aggregation: MeterAggregationCount, |
| EventType: "event-type-test", |
| GroupBy: map[string]string{"test_group": "$.test_group"}, |
| }, |
| error: fmt.Errorf("meter key is required"), |
| }, |
| { |
| description: "aggregation is empty", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| EventType: "event-type-test", |
| ValueProperty: lo.ToPtr("$.my_property"), |
| GroupBy: map[string]string{"test_group": "$.test_group"}, |
| }, |
| error: fmt.Errorf("meter aggregation is required"), |
| }, |
| { |
| description: "window size is empty", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationCount, |
| EventType: "event-type-test", |
| GroupBy: map[string]string{"test_group": "$.test_group"}, |
| }, |
| error: nil, |
| }, |
| { |
| description: "event type is empty", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationSum, |
| ValueProperty: lo.ToPtr("$.my_property"), |
| GroupBy: map[string]string{"test_group": "$.test_group"}, |
| }, |
| error: fmt.Errorf("meter event type is required"), |
| }, |
| { |
| description: "missing value property", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationSum, |
| EventType: "event-type-test", |
| GroupBy: map[string]string{"test_group": "$.test_group"}, |
| }, |
| error: fmt.Errorf("meter value property is required"), |
| }, |
| { |
| description: "invalid value property", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationSum, |
| EventType: "event-type-test", |
| ValueProperty: lo.ToPtr("invalid"), |
| GroupBy: map[string]string{"test_group": "$.test_group"}, |
| }, |
| error: fmt.Errorf("meter value property must start with $"), |
| }, |
| { |
| description: "invalid group by key", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationSum, |
| EventType: "event-type-test", |
| ValueProperty: lo.ToPtr("$.my_property"), |
| GroupBy: map[string]string{"in-valid": "$.test_group"}, |
| }, |
| error: fmt.Errorf("meter group by key in-valid is invalid, only alphanumeric and underscore characters are allowed"), |
| }, |
| { |
| description: "invalid group by key", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationSum, |
| EventType: "event-type-test", |
| ValueProperty: lo.ToPtr("$.my_property"), |
| GroupBy: map[string]string{"": "$.test_group"}, |
| }, |
| error: fmt.Errorf("meter group by key cannot be empty"), |
| }, |
| { |
| description: "invalid group by property", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationSum, |
| EventType: "event-type-test", |
| ValueProperty: lo.ToPtr("$.my_property"), |
| GroupBy: map[string]string{"test_group": "invalid"}, |
| }, |
| error: fmt.Errorf("meter group by value must start with $ for key test_group"), |
| }, |
| { |
| description: "value property cannot be in the group by", |
| meter: Meter{ |
| ManagedResource: models.ManagedResource{ |
| ID: ulid.Make().String(), |
| NamespacedModel: models.NamespacedModel{ |
| Namespace: ulid.Make().String(), |
| }, |
| ManagedModel: models.ManagedModel{ |
| CreatedAt: time.Now(), |
| UpdatedAt: time.Now(), |
| }, |
| Name: "Test meter", |
| }, |
| Key: "slug-test", |
| Aggregation: MeterAggregationUniqueCount, |
| EventType: "event-type-test", |
| ValueProperty: lo.ToPtr("$.my_property"), |
| GroupBy: map[string]string{"test_group": "$.my_property"}, |
| }, |
| error: fmt.Errorf("meter group by value test_group cannot be the same as value property"), |
| }, |
| } |
|
|
| for _, tt := range tests { |
| tt := tt |
| t.Run(tt.description, func(t *testing.T) { |
| err := tt.meter.Validate() |
|
|
| if tt.error == nil && err != nil { |
| t.Error(err) |
| } |
|
|
| if tt.error != nil && err == nil { |
| t.Errorf("expected error %v, got nil", tt.error) |
| } |
| }) |
| } |
| } |
|
|
| func Test_EventTypeFilter(t *testing.T) { |
| var reservedEventTypePatterns []*EventTypePattern |
|
|
| patterns := []string{ |
| `^om\.`, |
| `^_\.`, |
| } |
|
|
| for _, pattern := range patterns { |
| re, err := regexp.Compile(pattern) |
| require.NoErrorf(t, err, "invalid regexp pattern '%s'", pattern) |
|
|
| reservedEventTypePatterns = append(reservedEventTypePatterns, re) |
| } |
|
|
| validator := NewEventTypeValidator(reservedEventTypePatterns) |
|
|
| tests := []struct { |
| name string |
| eventType string |
|
|
| expectedMatch bool |
| }{ |
| { |
| name: "Random", |
| eventType: "event-type-1", |
| expectedMatch: false, |
| }, |
| { |
| name: "Openmeter", |
| eventType: "om.event-type-1", |
| expectedMatch: true, |
| }, |
| { |
| name: "System", |
| eventType: "_.event-type-1", |
| expectedMatch: true, |
| }, |
| } |
|
|
| for _, test := range tests { |
| t.Run(test.name, func(t *testing.T) { |
| err := validator(test.eventType) |
| assert.Equal(t, test.expectedMatch, err != nil) |
| }) |
| } |
| } |
|
|