~ubuntu-branches/debian/sid/golang-github-prometheus-client-golang/sid

« back to all changes in this revision

Viewing changes to api/prometheus/api_test.go

  • Committer: Package Import Robot
  • Author(s): Martín Ferrari
  • Date: 2016-08-18 12:06:03 UTC
  • Revision ID: package-import@ubuntu.com-20160818120603-xevgulhsaf9vktsr
Tags: upstream-0.8.0
Import upstream version 0.8.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
5
//
 
6
// http://www.apache.org/licenses/LICENSE-2.0
 
7
//
 
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.
 
13
 
 
14
package prometheus
 
15
 
 
16
import (
 
17
        "encoding/json"
 
18
        "fmt"
 
19
        "net/http"
 
20
        "net/url"
 
21
        "reflect"
 
22
        "testing"
 
23
        "time"
 
24
 
 
25
        "github.com/prometheus/common/model"
 
26
        "golang.org/x/net/context"
 
27
)
 
28
 
 
29
func TestConfig(t *testing.T) {
 
30
        c := Config{}
 
31
        if c.transport() != DefaultTransport {
 
32
                t.Fatalf("expected default transport for nil Transport field")
 
33
        }
 
34
}
 
35
 
 
36
func TestClientURL(t *testing.T) {
 
37
        tests := []struct {
 
38
                address  string
 
39
                endpoint string
 
40
                args     map[string]string
 
41
                expected string
 
42
        }{
 
43
                {
 
44
                        address:  "http://localhost:9090",
 
45
                        endpoint: "/test",
 
46
                        expected: "http://localhost:9090/test",
 
47
                },
 
48
                {
 
49
                        address:  "http://localhost",
 
50
                        endpoint: "/test",
 
51
                        expected: "http://localhost/test",
 
52
                },
 
53
                {
 
54
                        address:  "http://localhost:9090",
 
55
                        endpoint: "test",
 
56
                        expected: "http://localhost:9090/test",
 
57
                },
 
58
                {
 
59
                        address:  "http://localhost:9090/prefix",
 
60
                        endpoint: "/test",
 
61
                        expected: "http://localhost:9090/prefix/test",
 
62
                },
 
63
                {
 
64
                        address:  "https://localhost:9090/",
 
65
                        endpoint: "/test/",
 
66
                        expected: "https://localhost:9090/test",
 
67
                },
 
68
                {
 
69
                        address:  "http://localhost:9090",
 
70
                        endpoint: "/test/:param",
 
71
                        args: map[string]string{
 
72
                                "param": "content",
 
73
                        },
 
74
                        expected: "http://localhost:9090/test/content",
 
75
                },
 
76
                {
 
77
                        address:  "http://localhost:9090",
 
78
                        endpoint: "/test/:param/more/:param",
 
79
                        args: map[string]string{
 
80
                                "param": "content",
 
81
                        },
 
82
                        expected: "http://localhost:9090/test/content/more/content",
 
83
                },
 
84
                {
 
85
                        address:  "http://localhost:9090",
 
86
                        endpoint: "/test/:param/more/:foo",
 
87
                        args: map[string]string{
 
88
                                "param": "content",
 
89
                                "foo":   "bar",
 
90
                        },
 
91
                        expected: "http://localhost:9090/test/content/more/bar",
 
92
                },
 
93
                {
 
94
                        address:  "http://localhost:9090",
 
95
                        endpoint: "/test/:param",
 
96
                        args: map[string]string{
 
97
                                "nonexistant": "content",
 
98
                        },
 
99
                        expected: "http://localhost:9090/test/:param",
 
100
                },
 
101
        }
 
102
 
 
103
        for _, test := range tests {
 
104
                ep, err := url.Parse(test.address)
 
105
                if err != nil {
 
106
                        t.Fatal(err)
 
107
                }
 
108
 
 
109
                hclient := &httpClient{
 
110
                        endpoint:  ep,
 
111
                        transport: DefaultTransport,
 
112
                }
 
113
 
 
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)
 
117
                        continue
 
118
                }
 
119
 
 
120
                // The apiClient must return exactly the same result as the httpClient.
 
121
                aclient := &apiClient{hclient}
 
122
 
 
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)
 
126
                }
 
127
        }
 
128
}
 
129
 
 
130
type testClient struct {
 
131
        *testing.T
 
132
 
 
133
        ch  chan apiClientTest
 
134
        req *http.Request
 
135
}
 
136
 
 
137
type apiClientTest struct {
 
138
        code     int
 
139
        response interface{}
 
140
        expected string
 
141
        err      *Error
 
142
}
 
143
 
 
144
func (c *testClient) url(ep string, args map[string]string) *url.URL {
 
145
        return nil
 
146
}
 
147
 
 
148
func (c *testClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
 
149
        if ctx == nil {
 
150
                c.Fatalf("context was not passed down")
 
151
        }
 
152
        if req != c.req {
 
153
                c.Fatalf("request was not passed down")
 
154
        }
 
155
 
 
156
        test := <-c.ch
 
157
 
 
158
        var b []byte
 
159
        var err error
 
160
 
 
161
        switch v := test.response.(type) {
 
162
        case string:
 
163
                b = []byte(v)
 
164
        default:
 
165
                b, err = json.Marshal(v)
 
166
                if err != nil {
 
167
                        c.Fatal(err)
 
168
                }
 
169
        }
 
170
 
 
171
        resp := &http.Response{
 
172
                StatusCode: test.code,
 
173
        }
 
174
 
 
175
        return resp, b, nil
 
176
}
 
177
 
 
178
func TestAPIClientDo(t *testing.T) {
 
179
        tests := []apiClientTest{
 
180
                {
 
181
                        response: &apiResponse{
 
182
                                Status:    "error",
 
183
                                Data:      json.RawMessage(`null`),
 
184
                                ErrorType: ErrBadData,
 
185
                                Error:     "failed",
 
186
                        },
 
187
                        err: &Error{
 
188
                                Type: ErrBadData,
 
189
                                Msg:  "failed",
 
190
                        },
 
191
                        code:     statusAPIError,
 
192
                        expected: `null`,
 
193
                },
 
194
                {
 
195
                        response: &apiResponse{
 
196
                                Status:    "error",
 
197
                                Data:      json.RawMessage(`"test"`),
 
198
                                ErrorType: ErrTimeout,
 
199
                                Error:     "timed out",
 
200
                        },
 
201
                        err: &Error{
 
202
                                Type: ErrTimeout,
 
203
                                Msg:  "timed out",
 
204
                        },
 
205
                        code:     statusAPIError,
 
206
                        expected: `test`,
 
207
                },
 
208
                {
 
209
                        response: "bad json",
 
210
                        err: &Error{
 
211
                                Type: ErrBadResponse,
 
212
                                Msg:  "bad response code 400",
 
213
                        },
 
214
                        code: http.StatusBadRequest,
 
215
                },
 
216
                {
 
217
                        response: "bad json",
 
218
                        err: &Error{
 
219
                                Type: ErrBadResponse,
 
220
                                Msg:  "invalid character 'b' looking for beginning of value",
 
221
                        },
 
222
                        code: statusAPIError,
 
223
                },
 
224
                {
 
225
                        response: &apiResponse{
 
226
                                Status: "success",
 
227
                                Data:   json.RawMessage(`"test"`),
 
228
                        },
 
229
                        err: &Error{
 
230
                                Type: ErrBadResponse,
 
231
                                Msg:  "inconsistent body for response code",
 
232
                        },
 
233
                        code: statusAPIError,
 
234
                },
 
235
                {
 
236
                        response: &apiResponse{
 
237
                                Status:    "success",
 
238
                                Data:      json.RawMessage(`"test"`),
 
239
                                ErrorType: ErrTimeout,
 
240
                                Error:     "timed out",
 
241
                        },
 
242
                        err: &Error{
 
243
                                Type: ErrBadResponse,
 
244
                                Msg:  "inconsistent body for response code",
 
245
                        },
 
246
                        code: statusAPIError,
 
247
                },
 
248
                {
 
249
                        response: &apiResponse{
 
250
                                Status:    "error",
 
251
                                Data:      json.RawMessage(`"test"`),
 
252
                                ErrorType: ErrTimeout,
 
253
                                Error:     "timed out",
 
254
                        },
 
255
                        err: &Error{
 
256
                                Type: ErrBadResponse,
 
257
                                Msg:  "inconsistent body for response code",
 
258
                        },
 
259
                        code: http.StatusOK,
 
260
                },
 
261
        }
 
262
 
 
263
        tc := &testClient{
 
264
                T:   t,
 
265
                ch:  make(chan apiClientTest, 1),
 
266
                req: &http.Request{},
 
267
        }
 
268
        client := &apiClient{tc}
 
269
 
 
270
        for _, test := range tests {
 
271
 
 
272
                tc.ch <- test
 
273
 
 
274
                _, body, err := client.do(context.Background(), tc.req)
 
275
 
 
276
                if test.err != nil {
 
277
                        if err == nil {
 
278
                                t.Errorf("expected error %q but got none", test.err)
 
279
                                continue
 
280
                        }
 
281
                        if test.err.Error() != err.Error() {
 
282
                                t.Errorf("unexpected error: want %q, got %q", test.err, err)
 
283
                        }
 
284
                        continue
 
285
                }
 
286
                if err != nil {
 
287
                        t.Errorf("unexpeceted error %s", err)
 
288
                        continue
 
289
                }
 
290
 
 
291
                want, got := test.expected, string(body)
 
292
                if want != got {
 
293
                        t.Errorf("unexpected body: want %q, got %q", want, got)
 
294
                }
 
295
        }
 
296
}
 
297
 
 
298
type apiTestClient struct {
 
299
        *testing.T
 
300
        curTest apiTest
 
301
}
 
302
 
 
303
type apiTest struct {
 
304
        do    func() (interface{}, error)
 
305
        inErr error
 
306
        inRes interface{}
 
307
 
 
308
        reqPath   string
 
309
        reqParam  url.Values
 
310
        reqMethod string
 
311
        res       interface{}
 
312
        err       error
 
313
}
 
314
 
 
315
func (c *apiTestClient) url(ep string, args map[string]string) *url.URL {
 
316
        u := &url.URL{
 
317
                Host: "test:9090",
 
318
                Path: apiPrefix + ep,
 
319
        }
 
320
        return u
 
321
}
 
322
 
 
323
func (c *apiTestClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
 
324
 
 
325
        test := c.curTest
 
326
 
 
327
        if req.URL.Path != test.reqPath {
 
328
                c.Errorf("unexpected request path: want %s, got %s", test.reqPath, req.URL.Path)
 
329
        }
 
330
        if req.Method != test.reqMethod {
 
331
                c.Errorf("unexpected request method: want %s, got %s", test.reqMethod, req.Method)
 
332
        }
 
333
 
 
334
        b, err := json.Marshal(test.inRes)
 
335
        if err != nil {
 
336
                c.Fatal(err)
 
337
        }
 
338
 
 
339
        resp := &http.Response{}
 
340
        if test.inErr != nil {
 
341
                resp.StatusCode = statusAPIError
 
342
        } else {
 
343
                resp.StatusCode = http.StatusOK
 
344
        }
 
345
 
 
346
        return resp, b, test.inErr
 
347
}
 
348
 
 
349
func TestAPIs(t *testing.T) {
 
350
 
 
351
        testTime := time.Now()
 
352
 
 
353
        client := &apiTestClient{T: t}
 
354
 
 
355
        queryApi := &httpQueryAPI{
 
356
                client: client,
 
357
        }
 
358
 
 
359
        doQuery := func(q string, ts time.Time) func() (interface{}, error) {
 
360
                return func() (interface{}, error) {
 
361
                        return queryApi.Query(context.Background(), q, ts)
 
362
                }
 
363
        }
 
364
 
 
365
        doQueryRange := func(q string, rng Range) func() (interface{}, error) {
 
366
                return func() (interface{}, error) {
 
367
                        return queryApi.QueryRange(context.Background(), q, rng)
 
368
                }
 
369
        }
 
370
 
 
371
        queryTests := []apiTest{
 
372
                {
 
373
                        do: doQuery("2", testTime),
 
374
                        inRes: &queryResult{
 
375
                                Type: model.ValScalar,
 
376
                                Result: &model.Scalar{
 
377
                                        Value:     2,
 
378
                                        Timestamp: model.TimeFromUnix(testTime.Unix()),
 
379
                                },
 
380
                        },
 
381
 
 
382
                        reqMethod: "GET",
 
383
                        reqPath:   "/api/v1/query",
 
384
                        reqParam: url.Values{
 
385
                                "query": []string{"2"},
 
386
                                "time":  []string{testTime.Format(time.RFC3339Nano)},
 
387
                        },
 
388
                        res: &model.Scalar{
 
389
                                Value:     2,
 
390
                                Timestamp: model.TimeFromUnix(testTime.Unix()),
 
391
                        },
 
392
                },
 
393
                {
 
394
                        do:    doQuery("2", testTime),
 
395
                        inErr: fmt.Errorf("some error"),
 
396
 
 
397
                        reqMethod: "GET",
 
398
                        reqPath:   "/api/v1/query",
 
399
                        reqParam: url.Values{
 
400
                                "query": []string{"2"},
 
401
                                "time":  []string{testTime.Format(time.RFC3339Nano)},
 
402
                        },
 
403
                        err: fmt.Errorf("some error"),
 
404
                },
 
405
 
 
406
                {
 
407
                        do: doQueryRange("2", Range{
 
408
                                Start: testTime.Add(-time.Minute),
 
409
                                End:   testTime,
 
410
                                Step:  time.Minute,
 
411
                        }),
 
412
                        inErr: fmt.Errorf("some error"),
 
413
 
 
414
                        reqMethod: "GET",
 
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()},
 
421
                        },
 
422
                        err: fmt.Errorf("some error"),
 
423
                },
 
424
        }
 
425
 
 
426
        var tests []apiTest
 
427
        tests = append(tests, queryTests...)
 
428
 
 
429
        for _, test := range tests {
 
430
                client.curTest = test
 
431
 
 
432
                res, err := test.do()
 
433
 
 
434
                if test.err != nil {
 
435
                        if err == nil {
 
436
                                t.Errorf("expected error %q but got none", test.err)
 
437
                                continue
 
438
                        }
 
439
                        if err.Error() != test.err.Error() {
 
440
                                t.Errorf("unexpected error: want %s, got %s", test.err, err)
 
441
                        }
 
442
                        continue
 
443
                }
 
444
                if err != nil {
 
445
                        t.Errorf("unexpected error: %s", err)
 
446
                        continue
 
447
                }
 
448
 
 
449
                if !reflect.DeepEqual(res, test.res) {
 
450
                        t.Errorf("unexpected result: want %v, got %v", test.res, res)
 
451
                }
 
452
        }
 
453
}