Unit testing for rules#4350
Conversation
Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
4156e66 to
6500dc6
Compare
Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
6500dc6 to
5d35364
Compare
…rules Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
2bbd264 to
afd433e
Compare
gouthamve
left a comment
There was a problem hiding this comment.
Looks good for the first version. But ideally, I'd like to remove the custom parsing done by the promql tests altogether and replace it with this YAML format.
But that should be the next PR.
cmd/promtool/unittest.go
Outdated
|
|
||
| // test performs the unit tests. | ||
| func (tg *testGroup) test(mint, maxt time.Time, evalInterval time.Duration, groupOrderMap map[string]int, ruleFiles ...string) []error { | ||
|
|
cmd/promtool/unittest.go
Outdated
| presentInTest := make(map[time.Duration]map[string]struct{}) | ||
| // Map of all the unit tests for given eval_time. | ||
| alertTests := make(map[time.Duration][]alertTestCase) | ||
| for _, art := range tg.AlertRuleTest { |
There was a problem hiding this comment.
alert would be better than art
cmd/promtool/unittest.go
Outdated
| type testGroup struct { | ||
| Interval time.Duration `yaml:"interval"` | ||
| InputSeries []series `yaml:"input_series"` | ||
| AlertRuleTest []alertTestCase `yaml:"alert_rule_test,omitempty"` |
There was a problem hiding this comment.
Plural AlertRuleTests and PromqlExprTests :)
cmd/promtool/unittest.go
Outdated
| } | ||
| return len(alertEvalTimes) | ||
| }() | ||
| alertEvalTimes = append(alertEvalTimes[:pos], append([]time.Duration{art.EvalTime}, alertEvalTimes[pos:]...)...) |
There was a problem hiding this comment.
Can we not sort the alertEvalTimes via: sort.Slice() after getting an unsorted slice? That looks more readable.
cmd/promtool/unittest.go
Outdated
| curr := 0 | ||
|
|
||
| var errs []error | ||
| for ts := mint; maxt.Sub(ts) > 0; ts = ts.Add(evalInterval) { |
cmd/promtool/unittest.go
Outdated
| } | ||
| } | ||
|
|
||
| if _, ok := got[ar.Name()]; ok { |
There was a problem hiding this comment.
You could simply do: got[ar.Name()] = append(got[ar.Name()], alerts...)
cmd/promtool/unittest.go
Outdated
| } | ||
| presentInTest[art.EvalTime][art.Alertname] = struct{}{} | ||
|
|
||
| if _, ok := alertTests[art.EvalTime]; ok { |
There was a problem hiding this comment.
Could just be: alertTests[art.EvalTime] = append(alertTests[art.EvalTime], art)
cmd/promtool/unittest.go
Outdated
|
|
||
| for _, testCase := range alertTests[t] { | ||
| // Checking alerts. | ||
| gotAlerts, ok := got[testCase.Alertname] |
There was a problem hiding this comment.
Just gotAlerts := got[testCase.Alertname] should be fine.
cmd/promtool/unittest.go
Outdated
| } | ||
| } | ||
|
|
||
| for _, testCase := range alertTests[t] { |
There was a problem hiding this comment.
testcase should be fine. No need to camelcase that :)
cmd/promtool/unittest.go
Outdated
| func (la labelsAndAnnotations) Len() int { return len(la) } | ||
| func (la labelsAndAnnotations) Swap(i, j int) { la[i], la[j] = la[j], la[i] } | ||
| func (la labelsAndAnnotations) Less(i, j int) bool { | ||
| l1h, l2h := la[i].Labels.Hash(), la[j].Labels.Hash() |
There was a problem hiding this comment.
labels.Compare is better here.
Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
…rules Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
|
@gouthamve All the reviews fixed. Learn few stuff about Golang :) |
cmd/promtool/main.go
Outdated
| "The rule files to check.", | ||
| ).Required().ExistingFiles() | ||
|
|
||
| ruleTestCmd := testCmd.Command("rules", "Unit tests for rules.") |
There was a problem hiding this comment.
Could you move this to the end? (let all check* commands be next to each other). Further it should be named testRuleCmd for consistency.
cmd/promtool/unittest.go
Outdated
| // mentioned by `groupOrderMap`. The returned slice starts with the groups that need to be in order, | ||
| // followed by rest of the groups in undetermined order. | ||
| func orderedGroups(groupsMap map[string]*rules.Group, groupOrderMap map[string]int) []*rules.Group { | ||
| var groups []*rules.Group |
There was a problem hiding this comment.
Okay, this is too complex. Can we do this instead:
groups := make([]*rules.Group, 0, len(groupsMaps))
for _, g := range groupsMap {
groups = append(groups, g)
}
sort.Slice(groups, func(i, j int) bool {
return groupOrderMap[groups[i].Name()] < groupOrderMap[groups[j].Name()]
})
return groupsThere was a problem hiding this comment.
Didn't think of that, thanks! I have updated.
f85ea9d to
5576d8e
Compare
|
@brian-brazil PTAL when you get free. Also, where would the docs for this go in? |
|
I won't get to look until next week. A new sibling page to Recording/Alerting rules is probably the right place. |
|
Sure, thanks. |
cmd/promtool/unittest.go
Outdated
There was a problem hiding this comment.
If we don't add some extra time, the above loop can miss the last eval if maxt happens to be exactly the last eval. I think adding one evalInterval would make more sense than rounding off.
cmd/promtool/unittest.go
Outdated
There was a problem hiding this comment.
All this preparation is so that we can test alerts as we evaluate the rules. This avoids storing them in memory, as the number of evals might be high.
cmd/promtool/unittest.go
Outdated
cmd/promtool/unittest.go
Outdated
There was a problem hiding this comment.
This is a bit long of a for expression, I'd suggest moving it into an if inside the for
cmd/promtool/unittest.go
Outdated
There was a problem hiding this comment.
Why not break out here, rather than adding a new bool?
There was a problem hiding this comment.
So that I can skip checking for got == exp here in case it was an error. So I am using a bool to keep track of that.
There was a problem hiding this comment.
If you break out of the outer for, you don't need that
There was a problem hiding this comment.
I have made the changes to continue to the outer for.
cmd/promtool/unittest.go
Outdated
There was a problem hiding this comment.
Do you have rights to use this code as apache2?
There was a problem hiding this comment.
I am unaware of licenses, but what I see is, if I remove that comment, the code is general enough that it can be used. Maybe change it a bit you say?
556e729 to
348284d
Compare
|
@brian-brazil PTAL. |
cmd/promtool/unittest.go
Outdated
There was a problem hiding this comment.
We should add proper docs for this
There was a problem hiding this comment.
Yes. I will add docs for this. So I will remove that comment from here as docs will be added for this.
There was a problem hiding this comment.
Any more changes/additions do you feel is required?
There was a problem hiding this comment.
I'm not seeing the docs in the PR
There was a problem hiding this comment.
Oh, I was trying to add docs in https://github.com/prometheus/docs. Just found that there is a docs/ directory in this repo. I will add them.
Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
348284d to
9bf78e7
Compare
3c1b74c to
f9701f7
Compare
|
|
||
| # This uses expanding notation. | ||
| # Expanding notation: | ||
| # a+bxc ~ a a+b a+(2*b) a+(3*b) … a+(c*b) |
There was a problem hiding this comment.
The meaning of the ~ here is not clear, I'd suggest using becomes instead
|
|
||
| ### `<alert>` | ||
|
|
||
| Remember, this alert shoud be firing. |
There was a problem hiding this comment.
This comes across weirdly, I'd remove it.
|
|
||
| ```yaml | ||
| # Labels of the sample in series notation. | ||
| labels: <series_notation> |
There was a problem hiding this comment.
What's series notation?
There was a problem hiding this comment.
I updated it with examples. I meant to say <metric name>{<label name>=<label value>, ...}.
e56d1b9 to
5944b28
Compare
Signed-off-by: Ganesh Vernekar <cs15btech11018@iith.ac.in>
5944b28 to
5215e79
Compare
|
Thanks! |
Issue: #1695
Design Doc: Prometheus - Unit testing for Rules
Usage:
./promtool test rules test1.yml test2.yml test3.yml ...Example for valid test file: Prometheus - Unit Testing for Rules - Example test files