Skip to content

Commit 35bb2c7

Browse files
committed
test(ruler): add integration test for select merging query reduction
1 parent f0f0825 commit 35bb2c7

1 file changed

Lines changed: 73 additions & 0 deletions

File tree

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package ruler
2+
3+
import (
4+
"context"
5+
"sync/atomic"
6+
"testing"
7+
"time"
8+
9+
"github.com/prometheus/prometheus/model/labels"
10+
"github.com/prometheus/prometheus/promql"
11+
"github.com/prometheus/prometheus/rules"
12+
"github.com/stretchr/testify/assert"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
func TestSelectMerger_Integration(t *testing.T) {
17+
// 3 rules with overlapping selectors on http_requests_total.
18+
rls := []rules.Rule{
19+
mustParseRule(t, `rate(http_requests_total{job="api",deploy_color="blue"}[5m])`),
20+
mustParseRule(t, `rate(http_requests_total{job="api",deploy_color="green"}[5m])`),
21+
mustParseRule(t, `rate(http_requests_total{job="api",deploy_color=~".*"}[5m])`),
22+
}
23+
24+
// Step 1: Plan merged selects.
25+
plan := planMergedSelects(rls, 2)
26+
require.Len(t, plan, 1, "expected 1 merged entry for http_requests_total")
27+
assert.Equal(t, "http_requests_total", plan[0].metricName)
28+
29+
// Mock data: series for blue, green, and red.
30+
allSeries := promql.Vector{
31+
{Metric: labels.FromStrings("__name__", "http_requests_total", "job", "api", "deploy_color", "blue"), T: 1000, F: 10},
32+
{Metric: labels.FromStrings("__name__", "http_requests_total", "job", "api", "deploy_color", "green"), T: 1000, F: 20},
33+
{Metric: labels.FromStrings("__name__", "http_requests_total", "job", "api", "deploy_color", "red"), T: 1000, F: 30},
34+
}
35+
36+
// Mock QueryFunc that counts calls and returns allSeries.
37+
var callCount atomic.Int32
38+
mockQueryFunc := func(ctx context.Context, qs string, t time.Time) (promql.Vector, error) {
39+
callCount.Add(1)
40+
return allSeries, nil
41+
}
42+
43+
// Step 2: Execute prefetch — should call QueryFunc exactly once.
44+
ts := time.Now()
45+
cache := executePrefetch(context.Background(), plan, mockQueryFunc, ts)
46+
assert.Equal(t, int32(1), callCount.Load(), "executePrefetch should call QueryFunc exactly once")
47+
48+
// Step 3: Create cached query func wrapping the mock.
49+
cached := cachedQueryFunc(mockQueryFunc, cache)
50+
51+
// Step 4: Query with each rule's expression — should all be served from cache.
52+
callsBefore := callCount.Load()
53+
54+
// Blue rule: should return only blue series.
55+
vecBlue, err := cached(context.Background(), `http_requests_total{job="api",deploy_color="blue"}`, ts)
56+
require.NoError(t, err)
57+
assert.Len(t, vecBlue, 1)
58+
assert.Equal(t, "blue", vecBlue[0].Metric.Get("deploy_color"))
59+
60+
// Green rule: should return only green series.
61+
vecGreen, err := cached(context.Background(), `http_requests_total{job="api",deploy_color="green"}`, ts)
62+
require.NoError(t, err)
63+
assert.Len(t, vecGreen, 1)
64+
assert.Equal(t, "green", vecGreen[0].Metric.Get("deploy_color"))
65+
66+
// Regex .* rule: should return all series.
67+
vecAll, err := cached(context.Background(), `http_requests_total{job="api",deploy_color=~".*"}`, ts)
68+
require.NoError(t, err)
69+
assert.Len(t, vecAll, 3)
70+
71+
// Step 5: Verify no additional calls to the underlying QueryFunc.
72+
assert.Equal(t, callsBefore, callCount.Load(), "cachedQueryFunc should not call underlying QueryFunc")
73+
}

0 commit comments

Comments
 (0)