Skip to content
This repository was archived by the owner on Apr 2, 2024. It is now read-only.

Commit e598aba

Browse files
Blagoj Atanasovskiatanasovskib
authored andcommitted
Add unit tests for the query and query_range endpoints
1 parent c772cdc commit e598aba

File tree

4 files changed

+420
-1
lines changed

4 files changed

+420
-1
lines changed

pkg/api/health_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ func (m *mockHealthChecker) HealthCheck() error {
2424
}
2525

2626
func TestHealth(t *testing.T) {
27-
log.Init("debug")
27+
_ = log.Init("debug")
28+
2829
testCases := []struct {
2930
name string
3031
httpStatus int
File renamed without changes.

pkg/api/query_range_test.go

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package api
2+
3+
import (
4+
"bytes"
5+
"context"
6+
"encoding/json"
7+
"fmt"
8+
"github.com/prometheus/client_golang/prometheus"
9+
"github.com/prometheus/prometheus/promql"
10+
"github.com/timescale/timescale-prometheus/pkg/log"
11+
tspromql "github.com/timescale/timescale-prometheus/pkg/promql"
12+
"math"
13+
"net/http"
14+
"net/http/httptest"
15+
"testing"
16+
"time"
17+
)
18+
19+
func TestRangedQuery(t *testing.T) {
20+
_ = log.Init("debug")
21+
testCases := []struct {
22+
name string
23+
timeout string
24+
querier *mockQuerier
25+
metric string
26+
start string
27+
end string
28+
step string
29+
expectCode int
30+
expectError string
31+
canceled bool
32+
}{
33+
{
34+
name: "Start is unparsable",
35+
expectCode: http.StatusBadRequest,
36+
metric: "m",
37+
start: "unparsable",
38+
expectError: "bad_data",
39+
querier: &mockQuerier{},
40+
}, {
41+
name: "End is unparsable",
42+
expectCode: http.StatusBadRequest,
43+
metric: "m",
44+
start: "1.1",
45+
end: "unparsable",
46+
expectError: "bad_data",
47+
querier: &mockQuerier{},
48+
}, {
49+
name: "End is before start",
50+
expectCode: http.StatusBadRequest,
51+
metric: "m",
52+
start: "1.1",
53+
end: "1",
54+
expectError: "bad_data",
55+
querier: &mockQuerier{},
56+
}, {
57+
name: "Step is unparasable",
58+
expectCode: http.StatusBadRequest,
59+
metric: "m",
60+
start: "1",
61+
end: "2",
62+
step: "unparsable",
63+
expectError: "bad_data",
64+
querier: &mockQuerier{},
65+
}, {
66+
name: "Step is non-positive",
67+
expectCode: http.StatusBadRequest,
68+
metric: "m",
69+
start: "1",
70+
end: "2",
71+
step: "0s",
72+
expectError: "bad_data",
73+
querier: &mockQuerier{},
74+
}, {
75+
name: "Resolution is too high",
76+
expectCode: http.StatusBadRequest,
77+
metric: "m",
78+
start: "1",
79+
end: "11002",
80+
step: "1s",
81+
expectError: "bad_data",
82+
querier: &mockQuerier{},
83+
}, {
84+
name: "Timeout is unparsable",
85+
expectCode: http.StatusBadRequest,
86+
metric: "m",
87+
start: "1",
88+
end: "2.2",
89+
timeout: "unparsable",
90+
expectError: "bad_data",
91+
querier: &mockQuerier{},
92+
}, {
93+
name: "No query given",
94+
expectCode: http.StatusBadRequest,
95+
metric: "",
96+
start: "1",
97+
end: "2",
98+
step: "1s",
99+
timeout: "1m",
100+
expectError: "bad_data",
101+
querier: &mockQuerier{},
102+
}, {
103+
name: "Timeout query",
104+
start: "1",
105+
end: "2",
106+
step: "1s",
107+
expectCode: http.StatusServiceUnavailable,
108+
expectError: "timeout",
109+
timeout: "1s",
110+
metric: "m",
111+
querier: &mockQuerier{
112+
timeToSleep: 2 * time.Second,
113+
},
114+
}, {
115+
name: "Cancel query",
116+
start: "1",
117+
end: "2",
118+
step: "1s",
119+
expectCode: http.StatusServiceUnavailable,
120+
expectError: "canceled",
121+
metric: "m",
122+
querier: &mockQuerier{},
123+
canceled: true,
124+
}, {
125+
name: "Select error",
126+
start: "1",
127+
end: "2",
128+
step: "1s",
129+
expectCode: http.StatusUnprocessableEntity,
130+
expectError: "execution",
131+
metric: "m",
132+
querier: &mockQuerier{err: fmt.Errorf("some error")},
133+
timeout: "30s",
134+
}, {
135+
name: "All good",
136+
start: "1",
137+
end: "2",
138+
step: "1s",
139+
expectCode: http.StatusOK,
140+
metric: "m",
141+
querier: &mockQuerier{},
142+
timeout: "30s",
143+
},
144+
}
145+
for _, tc := range testCases {
146+
t.Run(tc.name, func(t *testing.T) {
147+
timeout, _ := parseDuration(tc.timeout)
148+
engine := promql.NewEngine(
149+
promql.EngineOpts{
150+
Logger: log.GetLogger(),
151+
Reg: prometheus.NewRegistry(),
152+
MaxSamples: math.MaxInt32,
153+
Timeout: timeout,
154+
},
155+
)
156+
handler := QueryRange(engine, tspromql.NewQueryable(tc.querier))
157+
queryUrl := constructRangedQuery(tc.metric, tc.start, tc.end, tc.step, tc.timeout)
158+
w := doRangedQuery(t, handler, queryUrl, tc.canceled)
159+
160+
if w.Code != tc.expectCode {
161+
t.Errorf("Unexpected HTTP status code received: got %d wanted %d", w.Code, tc.expectCode)
162+
return
163+
}
164+
if tc.expectError != "" {
165+
var er errResponse
166+
_ = json.NewDecoder(bytes.NewReader(w.Body.Bytes())).Decode(&er)
167+
if tc.expectError != er.ErrorType {
168+
t.Errorf("expected error of type %s, got %s", tc.expectError, er.ErrorType)
169+
return
170+
}
171+
}
172+
})
173+
174+
}
175+
176+
}
177+
178+
func constructRangedQuery(metric, start, end, step, timeout string) string {
179+
return fmt.Sprintf(
180+
"http://localhost:9090/query_range?query=%s&start=%s&end=%s&step=%s&timeout=%s",
181+
metric, start, end, step, timeout,
182+
)
183+
}
184+
185+
func doRangedQuery(t *testing.T, queryHandler http.Handler, url string, canceled bool) *httptest.ResponseRecorder {
186+
ctx, cancelFunc := context.WithCancel(context.Background())
187+
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
188+
if err != nil {
189+
t.Errorf("%v", err)
190+
}
191+
req.Header.Set(
192+
"Content-Type",
193+
"application/x-www-form-urlencoded; param=value",
194+
)
195+
w := httptest.NewRecorder()
196+
if canceled {
197+
cancelFunc()
198+
} else {
199+
defer cancelFunc()
200+
}
201+
queryHandler.ServeHTTP(w, req)
202+
return w
203+
}

0 commit comments

Comments
 (0)