1
// Copyright 2015 The Prometheus Authors
2
// Licensed under the Apache License, Version 2.0 (the "License");
3
// you may not use this file except in compliance with the License.
4
// You may obtain a copy of the License at
6
// http://www.apache.org/licenses/LICENSE-2.0
8
// Unless required by applicable law or agreed to in writing, software
9
// distributed under the License is distributed on an "AS IS" BASIS,
10
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
// See the License for the specific language governing permissions and
12
// limitations under the License.
25
"github.com/prometheus/common/model"
26
"golang.org/x/net/context"
29
func TestConfig(t *testing.T) {
31
if c.transport() != DefaultTransport {
32
t.Fatalf("expected default transport for nil Transport field")
36
func TestClientURL(t *testing.T) {
40
args map[string]string
44
address: "http://localhost:9090",
46
expected: "http://localhost:9090/test",
49
address: "http://localhost",
51
expected: "http://localhost/test",
54
address: "http://localhost:9090",
56
expected: "http://localhost:9090/test",
59
address: "http://localhost:9090/prefix",
61
expected: "http://localhost:9090/prefix/test",
64
address: "https://localhost:9090/",
66
expected: "https://localhost:9090/test",
69
address: "http://localhost:9090",
70
endpoint: "/test/:param",
71
args: map[string]string{
74
expected: "http://localhost:9090/test/content",
77
address: "http://localhost:9090",
78
endpoint: "/test/:param/more/:param",
79
args: map[string]string{
82
expected: "http://localhost:9090/test/content/more/content",
85
address: "http://localhost:9090",
86
endpoint: "/test/:param/more/:foo",
87
args: map[string]string{
91
expected: "http://localhost:9090/test/content/more/bar",
94
address: "http://localhost:9090",
95
endpoint: "/test/:param",
96
args: map[string]string{
97
"nonexistant": "content",
99
expected: "http://localhost:9090/test/:param",
103
for _, test := range tests {
104
ep, err := url.Parse(test.address)
109
hclient := &httpClient{
111
transport: DefaultTransport,
114
u := hclient.url(test.endpoint, test.args)
115
if u.String() != test.expected {
116
t.Errorf("unexpected result: got %s, want %s", u, test.expected)
120
// The apiClient must return exactly the same result as the httpClient.
121
aclient := &apiClient{hclient}
123
u = aclient.url(test.endpoint, test.args)
124
if u.String() != test.expected {
125
t.Errorf("unexpected result: got %s, want %s", u, test.expected)
130
type testClient struct {
133
ch chan apiClientTest
137
type apiClientTest struct {
144
func (c *testClient) url(ep string, args map[string]string) *url.URL {
148
func (c *testClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
150
c.Fatalf("context was not passed down")
153
c.Fatalf("request was not passed down")
161
switch v := test.response.(type) {
165
b, err = json.Marshal(v)
171
resp := &http.Response{
172
StatusCode: test.code,
178
func TestAPIClientDo(t *testing.T) {
179
tests := []apiClientTest{
181
response: &apiResponse{
183
Data: json.RawMessage(`null`),
184
ErrorType: ErrBadData,
191
code: statusAPIError,
195
response: &apiResponse{
197
Data: json.RawMessage(`"test"`),
198
ErrorType: ErrTimeout,
205
code: statusAPIError,
209
response: "bad json",
211
Type: ErrBadResponse,
212
Msg: "bad response code 400",
214
code: http.StatusBadRequest,
217
response: "bad json",
219
Type: ErrBadResponse,
220
Msg: "invalid character 'b' looking for beginning of value",
222
code: statusAPIError,
225
response: &apiResponse{
227
Data: json.RawMessage(`"test"`),
230
Type: ErrBadResponse,
231
Msg: "inconsistent body for response code",
233
code: statusAPIError,
236
response: &apiResponse{
238
Data: json.RawMessage(`"test"`),
239
ErrorType: ErrTimeout,
243
Type: ErrBadResponse,
244
Msg: "inconsistent body for response code",
246
code: statusAPIError,
249
response: &apiResponse{
251
Data: json.RawMessage(`"test"`),
252
ErrorType: ErrTimeout,
256
Type: ErrBadResponse,
257
Msg: "inconsistent body for response code",
265
ch: make(chan apiClientTest, 1),
266
req: &http.Request{},
268
client := &apiClient{tc}
270
for _, test := range tests {
274
_, body, err := client.do(context.Background(), tc.req)
278
t.Errorf("expected error %q but got none", test.err)
281
if test.err.Error() != err.Error() {
282
t.Errorf("unexpected error: want %q, got %q", test.err, err)
287
t.Errorf("unexpeceted error %s", err)
291
want, got := test.expected, string(body)
293
t.Errorf("unexpected body: want %q, got %q", want, got)
298
type apiTestClient struct {
303
type apiTest struct {
304
do func() (interface{}, error)
315
func (c *apiTestClient) url(ep string, args map[string]string) *url.URL {
318
Path: apiPrefix + ep,
323
func (c *apiTestClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
327
if req.URL.Path != test.reqPath {
328
c.Errorf("unexpected request path: want %s, got %s", test.reqPath, req.URL.Path)
330
if req.Method != test.reqMethod {
331
c.Errorf("unexpected request method: want %s, got %s", test.reqMethod, req.Method)
334
b, err := json.Marshal(test.inRes)
339
resp := &http.Response{}
340
if test.inErr != nil {
341
resp.StatusCode = statusAPIError
343
resp.StatusCode = http.StatusOK
346
return resp, b, test.inErr
349
func TestAPIs(t *testing.T) {
351
testTime := time.Now()
353
client := &apiTestClient{T: t}
355
queryApi := &httpQueryAPI{
359
doQuery := func(q string, ts time.Time) func() (interface{}, error) {
360
return func() (interface{}, error) {
361
return queryApi.Query(context.Background(), q, ts)
365
doQueryRange := func(q string, rng Range) func() (interface{}, error) {
366
return func() (interface{}, error) {
367
return queryApi.QueryRange(context.Background(), q, rng)
371
queryTests := []apiTest{
373
do: doQuery("2", testTime),
375
Type: model.ValScalar,
376
Result: &model.Scalar{
378
Timestamp: model.TimeFromUnix(testTime.Unix()),
383
reqPath: "/api/v1/query",
384
reqParam: url.Values{
385
"query": []string{"2"},
386
"time": []string{testTime.Format(time.RFC3339Nano)},
390
Timestamp: model.TimeFromUnix(testTime.Unix()),
394
do: doQuery("2", testTime),
395
inErr: fmt.Errorf("some error"),
398
reqPath: "/api/v1/query",
399
reqParam: url.Values{
400
"query": []string{"2"},
401
"time": []string{testTime.Format(time.RFC3339Nano)},
403
err: fmt.Errorf("some error"),
407
do: doQueryRange("2", Range{
408
Start: testTime.Add(-time.Minute),
412
inErr: fmt.Errorf("some error"),
415
reqPath: "/api/v1/query_range",
416
reqParam: url.Values{
417
"query": []string{"2"},
418
"start": []string{testTime.Add(-time.Minute).Format(time.RFC3339Nano)},
419
"end": []string{testTime.Format(time.RFC3339Nano)},
420
"step": []string{time.Minute.String()},
422
err: fmt.Errorf("some error"),
427
tests = append(tests, queryTests...)
429
for _, test := range tests {
430
client.curTest = test
432
res, err := test.do()
436
t.Errorf("expected error %q but got none", test.err)
439
if err.Error() != test.err.Error() {
440
t.Errorf("unexpected error: want %s, got %s", test.err, err)
445
t.Errorf("unexpected error: %s", err)
449
if !reflect.DeepEqual(res, test.res) {
450
t.Errorf("unexpected result: want %v, got %v", test.res, res)