package prom import ( "testing" "time" prommodel "github.com/prometheus/common/model" "github.com/stretchr/testify/require" "go.yaml.in/yaml/v3" "github.com/grafana/grafana/pkg/util" ) func TestPrometheusRuleGroup_Validate(t *testing.T) { tests := []struct { name string group PrometheusRuleGroup expectError bool errorMsg string }{ { name: "valid group with no unsupported fields", group: PrometheusRuleGroup{ Name: "test_group", Interval: prommodel.Duration(60), Labels: map[string]string{ "label-1": "value-1", }, QueryOffset: util.Pointer(prommodel.Duration(time.Duration(1) * time.Second)), Rules: []PrometheusRule{ { Alert: "test_alert", Expr: "up == 0", KeepFiringFor: util.Pointer(prommodel.Duration(10)), }, }, }, expectError: false, }, { name: "invalid group with negative query_offset", group: PrometheusRuleGroup{ Name: "test_group", Interval: prommodel.Duration(60), QueryOffset: util.Pointer(prommodel.Duration(-1)), }, expectError: true, errorMsg: "query_offset must be >= 0", }, { name: "invalid group with limit", group: PrometheusRuleGroup{ Name: "test_group", Interval: prommodel.Duration(60), Limit: 10, }, expectError: true, errorMsg: "limit is not supported", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := tt.group.Validate() if tt.expectError { require.Error(t, err) require.Contains(t, err.Error(), tt.errorMsg) } else { require.NoError(t, err) } }) } } func TestPrometheusRulesFileYAML(t *testing.T) { interval := prommodel.Duration(5 * time.Minute) alertFor := prommodel.Duration(10 * time.Minute) keepFiring := prommodel.Duration(15 * time.Minute) tests := []struct { name string input PrometheusRulesFile expectedYAML string }{ { name: "simple alert rule and a recording rule", input: PrometheusRulesFile{ Groups: []PrometheusRuleGroup{ { Name: "test_group", Interval: interval, Rules: []PrometheusRule{ { Alert: "alert-1", Expr: "vector(0) > 90", For: &alertFor, KeepFiringFor: &keepFiring, Labels: map[string]string{ "team": "alerting", }, Annotations: map[string]string{ "summary": "some summary", "description": "some description", }, }, { Record: "vector(1)", }, }, }, }, }, expectedYAML: ` groups: - name: test_group interval: 5m rules: - alert: alert-1 expr: vector(0) > 90 for: 10m keep_firing_for: 15m labels: team: alerting annotations: description: some description summary: some summary - record: vector(1) `, }, { name: "empty rules file", input: PrometheusRulesFile{ Groups: []PrometheusRuleGroup{}, }, expectedYAML: `groups: []`, }, { name: "empty group", input: PrometheusRulesFile{ Groups: []PrometheusRuleGroup{ { Name: "empty_group", Interval: interval, Rules: []PrometheusRule{}, }, }, }, expectedYAML: ` groups: - name: empty_group interval: 5m rules: []`, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { yamlData, err := yaml.Marshal(tt.input) require.NoError(t, err, "Failed to marshal to YAML") require.YAMLEq(t, tt.expectedYAML, string(yamlData)) var parsed PrometheusRulesFile err = yaml.Unmarshal(yamlData, &parsed) require.NoError(t, err, "Failed to unmarshal from YAML") require.Equal(t, tt.input, parsed) }) } }