Compare commits

...

12 Commits

Author SHA1 Message Date
Tito Lins
1e1069a682 fix tests 2025-11-25 17:34:14 -03:00
Tito Lins
27f3ba70c9 fix tests 2025-11-25 17:20:59 -03:00
Tito Lins
debc2d01d2 fix tests 2025-11-25 17:15:48 -03:00
Tito Lins
7a9f88e3c4 fix tests 2025-11-25 17:06:53 -03:00
Tito Lins
2f3c02dfb1 fix tests 2025-11-25 17:04:29 -03:00
Tito Lins
95bc887354 fix tests 2025-11-25 17:01:49 -03:00
Tito Lins
4931472902 fix tests 2025-11-25 16:51:00 -03:00
Tito Lins
86b66dc5bd fix tests 2025-11-25 16:48:51 -03:00
Tito Lins
1968e28a43 fix tests 2025-11-25 16:47:07 -03:00
Tito Lins
34c9e06f6d fix tests 2025-11-25 16:45:09 -03:00
Tito Lins
093298e9e1 fix tests 2025-11-25 16:37:47 -03:00
Tito Lins
7bdce72fbd alerting: validate model.refId on provisioning API 2025-11-25 16:26:21 -03:00
20 changed files with 86 additions and 10 deletions

View File

@@ -80,7 +80,7 @@ func validRule() apimodels.PostableExtendedRuleNode {
To: 0,
},
DatasourceUID: "DATASOURCE_TEST",
Model: nil,
Model: []byte(`{"refId":"A"}`),
},
},
UID: uid,
@@ -140,9 +140,11 @@ func TestValidateCondition(t *testing.T) {
data: []apimodels.AlertQuery{
{
RefID: "B",
Model: []byte(`{"refId":"B"}`),
},
{
RefID: "C",
Model: []byte(`{"refId":"C"}`),
},
},
errorMsg: "condition A does not exist, must be one of [B,C]",
@@ -153,9 +155,11 @@ func TestValidateCondition(t *testing.T) {
data: []apimodels.AlertQuery{
{
RefID: "A",
Model: []byte(`{"refId":"A"}`),
},
{
RefID: "A",
Model: []byte(`{"refId":"A"}`),
},
},
errorMsg: "refID 'A' is already used by query/expression at index 0",
@@ -166,22 +170,37 @@ func TestValidateCondition(t *testing.T) {
data: []apimodels.AlertQuery{
{
RefID: "",
Model: []byte(`{"refId":""}`),
},
{
RefID: "A",
Model: []byte(`{"refId":"A"}`),
},
},
errorMsg: "refID is not specified for data query/expression at index 0",
},
{
name: "error when mismatch between refId and model.refId",
condition: "A",
data: []apimodels.AlertQuery{
{
RefID: "A",
Model: []byte(`{"refId":"B"}`),
},
},
errorMsg: "mismatch between query and model refIDs at index 0",
},
{
name: "valid case",
condition: "B",
data: []apimodels.AlertQuery{
{
RefID: "A",
Model: []byte(`{"refId":"A"}`),
},
{
RefID: "B",
Model: []byte(`{"refId":"B"}`),
},
},
},
@@ -351,7 +370,7 @@ func TestValidateRuleNode_NoUID(t *testing.T) {
orgId := rand.Int63()
folder := randFolder()
name := util.GenerateShortUID()
var cfg = config(t)
cfg := config(t)
limits := makeLimits(cfg)
interval := cfg.BaseInterval * time.Duration(rand.Int63n(10)+1)
@@ -753,7 +772,7 @@ func TestValidateRuleNode_UID(t *testing.T) {
orgId := rand.Int63()
folder := randFolder()
name := util.GenerateShortUID()
var cfg = config(t)
cfg := config(t)
limits := makeLimits(cfg)
interval := cfg.BaseInterval * time.Duration(rand.Int63n(10)+1)

View File

@@ -1,6 +1,7 @@
package validation
import (
"encoding/json"
"errors"
"fmt"
"sort"
@@ -229,9 +230,19 @@ func ValidateCondition(condition string, queries []apimodels.AlertQuery, canPatc
refIDs := make(map[string]int, len(queries))
for idx, query := range queries {
if query.RefID == "" {
var m struct {
RefID string `json:"refId"`
}
if err := json.Unmarshal(query.Model, &m); err != nil {
return fmt.Errorf("%w: failed to unmarshal query model at index %d with %s", ngmodels.ErrAlertRuleFailedValidation, idx, err)
}
if query.RefID == "" || m.RefID == "" {
return fmt.Errorf("%w: refID is not specified for data query/expression at index %d", ngmodels.ErrAlertRuleFailedValidation, idx)
}
if query.RefID != m.RefID {
return fmt.Errorf("%w: mismatch between query and model refIDs at index %d", ngmodels.ErrAlertRuleFailedValidation, idx)
}
if usedIdx, ok := refIDs[query.RefID]; ok {
return fmt.Errorf("%w: refID '%s' is already used by query/expression at index %d", ngmodels.ErrAlertRuleFailedValidation, query.RefID, usedIdx)
}

View File

@@ -652,18 +652,20 @@ func GenerateAlertQuery() AlertQuery {
func (g *AlertRuleGenerator) GenerateQuery() AlertQuery {
f := rand.Intn(10) + 5
t := rand.Intn(f)
refId := util.GenerateShortUID()
return AlertQuery{
DatasourceUID: util.GenerateShortUID(),
Model: json.RawMessage(fmt.Sprintf(`{
"%s": "%s",
"%s":"%d"
}`, util.GenerateShortUID(), util.GenerateShortUID(), util.GenerateShortUID(), rand.Int())),
"%s":"%d",
"refId":"%s"
}`, util.GenerateShortUID(), util.GenerateShortUID(), util.GenerateShortUID(), rand.Int(), refId)),
RelativeTimeRange: RelativeTimeRange{
From: Duration(time.Duration(f) * time.Minute),
To: Duration(time.Duration(t) * time.Minute),
},
RefID: util.GenerateShortUID(),
RefID: refId,
QueryType: util.GenerateShortUID(),
}
}

View File

@@ -451,6 +451,7 @@ func createTestAlertRule(title, folderUID string) apimodels.PostableExtendedRule
},
DatasourceUID: "-100",
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),

View File

@@ -1238,6 +1238,7 @@ func TestIntegrationFullpath(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -1291,6 +1292,7 @@ func TestIntegrationFullpath(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),

View File

@@ -833,6 +833,7 @@ func TestIntegrationAlertRuleEditorSettings(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId":"A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -1102,6 +1103,7 @@ func TestIntegrationRulerRulesFilterByDashboard(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId":"A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -1122,6 +1124,7 @@ func TestIntegrationRulerRulesFilterByDashboard(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId":"A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -1163,6 +1166,7 @@ func TestIntegrationRulerRulesFilterByDashboard(t *testing.T) {
},
"datasourceUid": "__expr__",
"model": {
"refId":"A",
"expression": "2 + 3 \u003e 1",
"intervalMs": 1000,
"maxDataPoints": 43200,
@@ -1206,6 +1210,7 @@ func TestIntegrationRulerRulesFilterByDashboard(t *testing.T) {
},
"datasourceUid": "__expr__",
"model": {
"refId":"A",
"expression": "2 + 3 \u003e 1",
"intervalMs": 1000,
"maxDataPoints": 43200,
@@ -1261,6 +1266,7 @@ func TestIntegrationRulerRulesFilterByDashboard(t *testing.T) {
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "2 + 3 \u003e 1",
"intervalMs": 1000,
"maxDataPoints": 43200,
@@ -1576,7 +1582,7 @@ func TestIntegrationRuleCreate(t *testing.T) {
To: apimodels.Duration(15 * time.Minute),
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{"type": "math","expression": "1"}`),
Model: json.RawMessage(`{"refId": "A","type": "math","expression": "1"}`),
},
},
},
@@ -1610,7 +1616,7 @@ func TestIntegrationRuleCreate(t *testing.T) {
To: apimodels.Duration(15 * time.Minute),
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{"expression":"1","intervalMs":1000,"maxDataPoints":43200,"type":"math"}`),
Model: json.RawMessage(`{"refId":"A",expression":"1","intervalMs":1000,"maxDataPoints":43200,"type":"math"}`),
},
},
UpdatedBy: &apimodels.UserInfo{
@@ -2059,6 +2065,7 @@ func TestIntegrationAlertAndGroupsQuery(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -2198,6 +2205,7 @@ func TestIntegrationRulerAccess(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -2264,6 +2272,7 @@ func TestIntegrationEval(t *testing.T) {
},
"datasourceUid": "__expr__",
"model": {
"refId":"A",
"type":"math",
"expression":"1 < 2"
}
@@ -2326,6 +2335,7 @@ func TestIntegrationEval(t *testing.T) {
},
"datasourceUid": "__expr__",
"model": {
"refId":"A",
"type":"math",
"expression":"1 > 2"
}
@@ -2388,6 +2398,7 @@ func TestIntegrationEval(t *testing.T) {
},
"datasourceUid": "unknown",
"model": {
"refId":"A",
}
}
],
@@ -2422,6 +2433,7 @@ func TestIntegrationEval(t *testing.T) {
},
"datasourceUid": "__expr__",
"model": {
"refId":"A",
"type":"math",
"expression":"1 > 2"
}
@@ -2585,6 +2597,7 @@ func TestIntegrationQuota(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId":"A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -2620,6 +2633,7 @@ func TestIntegrationQuota(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId":"A",
"type": "math",
"expression": "2 + 4 > 1"
}`),
@@ -2678,6 +2692,7 @@ func TestIntegrationQuota(t *testing.T) {
},
"datasourceUid":"__expr__",
"model":{
"refId": "A",
"expression":"2 + 4 \u003E 1",
"intervalMs":1000,
"maxDataPoints":43200,
@@ -2795,6 +2810,7 @@ func TestIntegrationDeleteFolderWithRules(t *testing.T) {
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "2 + 3 > 1",
"intervalMs": 1000,
"maxDataPoints": 43200,
@@ -2964,6 +2980,7 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -2994,6 +3011,7 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -3024,6 +3042,7 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -3055,6 +3074,7 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -3085,6 +3105,7 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) {
},
DatasourceUID: "unknown",
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),
@@ -3126,6 +3147,7 @@ func TestIntegrationAlertRuleCRUD(t *testing.T) {
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),

View File

@@ -19,6 +19,7 @@
"refId": "A",
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 > 0",
"type": "math"
}

View File

@@ -20,6 +20,7 @@
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 \u003e 0",
"intervalMs": 1000,
"maxDataPoints": 43200,
@@ -52,6 +53,7 @@
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 == 0",
"intervalMs": 1000,
"maxDataPoints": 43200,

View File

@@ -25,6 +25,7 @@
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 > 0",
"intervalMs": 1000,
"maxDataPoints": 43200,
@@ -73,6 +74,7 @@
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 == 0",
"intervalMs": 1000,
"maxDataPoints": 43200,

View File

@@ -18,6 +18,7 @@
"refId": "A",
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 > 0",
"type": "math"
}
@@ -44,6 +45,7 @@
"refId": "A",
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 == 0",
"type": "math"
}

View File

@@ -20,6 +20,7 @@
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0/0",
"intervalMs": 1000,
"maxDataPoints": 43200,

View File

@@ -25,6 +25,7 @@
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0/0",
"intervalMs": 1000,
"maxDataPoints": 43200,

View File

@@ -18,6 +18,7 @@
"refId": "A",
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0/0",
"type": "math"
}

View File

@@ -20,6 +20,7 @@
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 \u003e 0",
"intervalMs": 1000,
"maxDataPoints": 43200,
@@ -51,6 +52,7 @@
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 == 0",
"intervalMs": 1000,
"maxDataPoints": 43200,

View File

@@ -25,6 +25,7 @@
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 > 0",
"intervalMs": 1000,
"maxDataPoints": 43200,
@@ -72,6 +73,7 @@
},
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 == 0",
"intervalMs": 1000,
"maxDataPoints": 43200,

View File

@@ -18,6 +18,7 @@
"refId": "A",
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 > 0",
"type": "math"
}
@@ -43,6 +44,7 @@
"refId": "A",
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 == 0",
"type": "math"
}

View File

@@ -132,6 +132,7 @@ func alertRuleGen(mutators ...ruleMutator) func() apimodels.PostableExtendedRule
},
DatasourceUID: expr.DatasourceUID,
Model: json.RawMessage(`{
"refId": "A",
"type": "math",
"expression": "2 + 3 > 1"
}`),

View File

@@ -18,6 +18,7 @@
"refId": "A",
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 > 0",
"type": "math"
}

View File

@@ -18,6 +18,7 @@
"refId": "A",
"datasourceUid": "__expr__",
"model": {
"refId": "A",
"expression": "0 > 0",
"type": "math"
}

View File

@@ -288,7 +288,7 @@ func TestIntegrationFolderDeletionBlockedByAlertRules(t *testing.T) {
From: apimodels.Duration(600 * time.Second),
To: 0,
},
Model: json.RawMessage(`{"type":"math","expression":"2 + 3 > 1"}`),
Model: json.RawMessage(`{"refId":"A","type":"math","expression":"2 + 3 > 1"}`),
},
},
},