Testing recording rules¶
This page shows how to verify Prometheus recording rules with Sonda. You push a metric with known values, wait for the rule to evaluate, and compare the computed result against the expected one.
Recording rules pre-compute PromQL expressions and store the results as new series. A wrong rule fails silently until a dashboard goes blank or an alert never fires.
How the test works¶
The test has three steps:
- Push a metric with a known, constant value.
- Wait for two evaluation intervals. The default Prometheus interval is one minute.
- Query the recording rule output and compare it against the expected value.
A constant input produces a predictable output. For example, sum() over one series at 100.0 returns 100.0.
Test a sum rule¶
The repository includes a ready-to-run example. The scenario pushes the constant 100.0 for http_requests_total. The companion Prometheus config computes a sum per job label.
- Scenario:
examples/recording-rule-test.yaml - Rule config:
examples/recording-rule-prometheus.yml
Step 1: Start VictoriaMetrics¶
Wait for the service to report healthy:
Step 2: Push known values¶
The constant generator emits the same value on every tick. This makes it ideal for deterministic rule testing.
version: 2
kind: runnable
defaults:
rate: 1
duration: 120s
encoder:
type: prometheus_text
sink:
type: http_push
url: "http://localhost:8428/api/v1/import/prometheus"
content_type: "text/plain"
scenarios:
- signal_type: metrics
name: http_requests_total
generator:
type: constant
value: 100.0
labels:
method: GET
status: "200"
job: api
Background execution
The & runs Sonda in the background so you can continue in the same terminal. Sonda stops automatically after the configured duration.
Step 3: Wait for evaluation¶
Recording rules evaluate on a fixed interval. The default is one minute in Prometheus, configurable in vmalert. Wait at least two intervals:
Step 4: Verify the computed value¶
This recording rule computes a sum per job:
groups:
- name: test_rules
rules:
- record: job:http_requests_total:sum
expr: sum(http_requests_total) by (job)
With one series at 100.0, query the result:
curl -s "http://localhost:8428/api/v1/query?query=job:http_requests_total:sum" \
| jq '.data.result'
Expected output:
If the value matches, the recording rule is correct.
Test a rate-based rule¶
For rate() or irate() rules, you need a counter that increases over time. The sawtooth generator increases linearly and then resets, which produces a predictable rate.
version: 2
kind: runnable
defaults:
rate: 1
duration: 300s
encoder:
type: prometheus_text
sink:
type: http_push
url: "http://localhost:8428/api/v1/import/prometheus"
content_type: "text/plain"
scenarios:
- signal_type: metrics
name: http_requests_total
generator:
type: sawtooth
min: 0.0
max: 1000.0
period_secs: 60
labels:
instance: api-01
job: web
The sawtooth rises from 0 to 1000 over 60 seconds, then resets. After enough data, rate(http_requests_total[1m]) returns about 16.67 (1000 / 60 seconds).
# After pushing for at least 2 minutes
curl -s "http://localhost:8428/api/v1/query?query=rate(http_requests_total[1m])" \
| jq '.data.result[0].value[1]'
Wait for enough data
rate() needs at least two data points inside the range window. With a 60-second sawtooth period, wait at least 2 minutes before you query.
Load rules into your stack¶
Tear down¶
Next steps¶
- Alert Testing — test alert rules with the same backend.
- Pipeline Validation — validate a full pipeline change.
- Generators — full reference for
constant,sawtooth, and others. - Sinks — full reference for
http_pushand other destinations.