2015-01-20 17:50:13 +00:00
|
|
|
package httpd_test
|
2014-11-19 17:12:46 +00:00
|
|
|
|
|
|
|
import (
|
2015-01-28 08:45:21 +00:00
|
|
|
"encoding/json"
|
2015-05-29 15:53:33 +00:00
|
|
|
"errors"
|
2015-01-29 19:28:13 +00:00
|
|
|
"fmt"
|
2015-05-29 15:53:33 +00:00
|
|
|
"io"
|
2014-11-19 17:12:46 +00:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2015-05-30 20:00:46 +00:00
|
|
|
"reflect"
|
2015-03-28 00:40:21 +00:00
|
|
|
"regexp"
|
2014-11-19 17:12:46 +00:00
|
|
|
"testing"
|
2015-01-29 19:28:13 +00:00
|
|
|
"time"
|
2014-12-30 15:50:15 +00:00
|
|
|
|
2015-09-04 17:47:14 +00:00
|
|
|
"github.com/influxdb/influxdb"
|
2015-03-06 17:29:32 +00:00
|
|
|
"github.com/influxdb/influxdb/client"
|
2015-01-28 16:50:14 +00:00
|
|
|
"github.com/influxdb/influxdb/influxql"
|
2015-05-29 15:53:33 +00:00
|
|
|
"github.com/influxdb/influxdb/meta"
|
2015-09-16 20:33:08 +00:00
|
|
|
"github.com/influxdb/influxdb/models"
|
2015-05-30 14:20:12 +00:00
|
|
|
"github.com/influxdb/influxdb/services/httpd"
|
2015-05-29 15:53:33 +00:00
|
|
|
"github.com/influxdb/influxdb/tsdb"
|
2014-11-19 17:12:46 +00:00
|
|
|
)
|
|
|
|
|
2015-01-29 19:28:13 +00:00
|
|
|
func TestBatchWrite_UnmarshalEpoch(t *testing.T) {
|
2015-01-30 20:36:41 +00:00
|
|
|
now := time.Now().UTC()
|
2015-01-29 19:28:13 +00:00
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
epoch int64
|
|
|
|
precision string
|
|
|
|
expected time.Time
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "nanoseconds",
|
|
|
|
epoch: now.UnixNano(),
|
|
|
|
precision: "n",
|
|
|
|
expected: now,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "microseconds",
|
|
|
|
epoch: now.Round(time.Microsecond).UnixNano() / int64(time.Microsecond),
|
|
|
|
precision: "u",
|
|
|
|
expected: now.Round(time.Microsecond),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "milliseconds",
|
|
|
|
epoch: now.Round(time.Millisecond).UnixNano() / int64(time.Millisecond),
|
|
|
|
precision: "ms",
|
|
|
|
expected: now.Round(time.Millisecond),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "seconds",
|
|
|
|
epoch: now.Round(time.Second).UnixNano() / int64(time.Second),
|
|
|
|
precision: "s",
|
|
|
|
expected: now.Round(time.Second),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "minutes",
|
|
|
|
epoch: now.Round(time.Minute).UnixNano() / int64(time.Minute),
|
|
|
|
precision: "m",
|
|
|
|
expected: now.Round(time.Minute),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "hours",
|
|
|
|
epoch: now.Round(time.Hour).UnixNano() / int64(time.Hour),
|
|
|
|
precision: "h",
|
|
|
|
expected: now.Round(time.Hour),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "max int64",
|
|
|
|
epoch: 9223372036854775807,
|
|
|
|
precision: "n",
|
|
|
|
expected: time.Unix(0, 9223372036854775807),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "100 years from now",
|
|
|
|
epoch: now.Add(time.Hour * 24 * 365 * 100).UnixNano(),
|
|
|
|
precision: "n",
|
|
|
|
expected: now.Add(time.Hour * 24 * 365 * 100),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Logf("testing %q\n", test.name)
|
2015-04-23 17:44:16 +00:00
|
|
|
data := []byte(fmt.Sprintf(`{"time": %d, "precision":"%s"}`, test.epoch, test.precision))
|
2015-01-29 19:28:13 +00:00
|
|
|
t.Logf("json: %s", string(data))
|
2015-03-06 17:29:32 +00:00
|
|
|
var bp client.BatchPoints
|
2015-02-03 23:06:39 +00:00
|
|
|
err := json.Unmarshal(data, &bp)
|
2015-01-29 19:28:13 +00:00
|
|
|
if err != nil {
|
2015-02-04 03:24:56 +00:00
|
|
|
t.Fatalf("unexpected error. expected: %v, actual: %v", nil, err)
|
2015-01-29 19:28:13 +00:00
|
|
|
}
|
2015-04-23 17:44:16 +00:00
|
|
|
if !bp.Time.Equal(test.expected) {
|
|
|
|
t.Fatalf("Unexpected time. expected: %v, actual: %v", test.expected, bp.Time)
|
2015-01-29 19:28:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestBatchWrite_UnmarshalRFC(t *testing.T) {
|
|
|
|
now := time.Now()
|
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
rfc string
|
2015-01-29 19:32:31 +00:00
|
|
|
now time.Time
|
2015-01-29 19:28:13 +00:00
|
|
|
expected time.Time
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "RFC3339Nano",
|
|
|
|
rfc: time.RFC3339Nano,
|
2015-01-29 19:32:31 +00:00
|
|
|
now: now,
|
2015-01-29 19:28:13 +00:00
|
|
|
expected: now,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "RFC3339",
|
|
|
|
rfc: time.RFC3339,
|
2015-01-29 19:32:31 +00:00
|
|
|
now: now.Round(time.Second),
|
2015-01-29 19:28:13 +00:00
|
|
|
expected: now.Round(time.Second),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Logf("testing %q\n", test.name)
|
2015-01-29 19:32:31 +00:00
|
|
|
ts := test.now.Format(test.rfc)
|
2015-04-23 17:44:16 +00:00
|
|
|
data := []byte(fmt.Sprintf(`{"time": %q}`, ts))
|
2015-01-29 19:28:13 +00:00
|
|
|
t.Logf("json: %s", string(data))
|
2015-03-06 17:29:32 +00:00
|
|
|
var bp client.BatchPoints
|
2015-02-03 23:06:39 +00:00
|
|
|
err := json.Unmarshal(data, &bp)
|
2015-01-29 19:28:13 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("unexpected error. exptected: %v, actual: %v", nil, err)
|
|
|
|
}
|
2015-04-23 17:44:16 +00:00
|
|
|
if !bp.Time.Equal(test.expected) {
|
|
|
|
t.Fatalf("Unexpected time. expected: %v, actual: %v", test.expected, bp.Time)
|
2015-01-29 19:28:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// Ensure the handler returns results from a query (including nil results).
|
|
|
|
func TestHandler_Query(t *testing.T) {
|
|
|
|
h := NewHandler(false)
|
2015-06-01 17:54:26 +00:00
|
|
|
h.QueryExecutor.ExecuteQueryFn = func(q *influxql.Query, db string, chunkSize int) (<-chan *influxql.Result, error) {
|
2015-05-29 15:53:33 +00:00
|
|
|
if q.String() != `SELECT * FROM bar` {
|
|
|
|
t.Fatalf("unexpected query: %s", q.String())
|
|
|
|
} else if db != `foo` {
|
|
|
|
t.Fatalf("unexpected db: %s", db)
|
|
|
|
}
|
|
|
|
return NewResultChan(
|
2015-09-21 12:55:38 +00:00
|
|
|
&influxql.Result{StatementID: 1, Series: models.Rows([]*models.Row{{Name: "series0"}})},
|
|
|
|
&influxql.Result{StatementID: 2, Series: models.Rows([]*models.Row{{Name: "series1"}})},
|
2015-05-29 15:53:33 +00:00
|
|
|
nil,
|
|
|
|
), nil
|
|
|
|
}
|
2015-03-18 15:29:45 +00:00
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
w := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(w, MustNewJSONRequest("GET", "/query?db=foo&q=SELECT+*+FROM+bar", nil))
|
|
|
|
if w.Code != http.StatusOK {
|
|
|
|
t.Fatalf("unexpected status: %d", w.Code)
|
|
|
|
} else if w.Body.String() != `{"results":[{"series":[{"name":"series0"}]},{"series":[{"name":"series1"}]}]}` {
|
|
|
|
t.Fatalf("unexpected body: %s", w.Body.String())
|
2015-03-18 15:29:45 +00:00
|
|
|
}
|
2015-05-29 15:53:33 +00:00
|
|
|
}
|
|
|
|
|
2015-07-28 07:53:38 +00:00
|
|
|
// Ensure the handler returns results from a query (including nil results).
|
|
|
|
func TestHandler_QueryRegex(t *testing.T) {
|
|
|
|
h := NewHandler(false)
|
|
|
|
h.QueryExecutor.ExecuteQueryFn = func(q *influxql.Query, db string, chunkSize int) (<-chan *influxql.Result, error) {
|
|
|
|
if q.String() != `SELECT * FROM test WHERE url =~ /http\:\/\/www.akamai\.com/` {
|
|
|
|
t.Fatalf("unexpected query: %s", q.String())
|
|
|
|
} else if db != `test` {
|
|
|
|
t.Fatalf("unexpected db: %s", db)
|
|
|
|
}
|
|
|
|
return NewResultChan(
|
|
|
|
nil,
|
|
|
|
), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(w, MustNewRequest("GET", "/query?db=test&q=SELECT%20%2A%20FROM%20test%20WHERE%20url%20%3D~%20%2Fhttp%5C%3A%5C%2F%5C%2Fwww.akamai%5C.com%2F", nil))
|
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// Ensure the handler merges results from the same statement.
|
|
|
|
func TestHandler_Query_MergeResults(t *testing.T) {
|
|
|
|
h := NewHandler(false)
|
2015-06-01 17:54:26 +00:00
|
|
|
h.QueryExecutor.ExecuteQueryFn = func(q *influxql.Query, db string, chunkSize int) (<-chan *influxql.Result, error) {
|
2015-05-29 15:53:33 +00:00
|
|
|
return NewResultChan(
|
2015-09-21 12:55:38 +00:00
|
|
|
&influxql.Result{StatementID: 1, Series: models.Rows([]*models.Row{{Name: "series0"}})},
|
|
|
|
&influxql.Result{StatementID: 1, Series: models.Rows([]*models.Row{{Name: "series1"}})},
|
2015-05-29 15:53:33 +00:00
|
|
|
), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(w, MustNewJSONRequest("GET", "/query?db=foo&q=SELECT+*+FROM+bar", nil))
|
|
|
|
if w.Code != http.StatusOK {
|
|
|
|
t.Fatalf("unexpected status: %d", w.Code)
|
|
|
|
} else if w.Body.String() != `{"results":[{"series":[{"name":"series0"},{"name":"series1"}]}]}` {
|
|
|
|
t.Fatalf("unexpected body: %s", w.Body.String())
|
2015-03-18 16:26:24 +00:00
|
|
|
}
|
2015-03-18 15:29:45 +00:00
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// Ensure the handler can parse chunked and chunk size query parameters.
|
|
|
|
func TestHandler_Query_Chunked(t *testing.T) {
|
|
|
|
h := NewHandler(false)
|
2015-06-01 17:54:26 +00:00
|
|
|
h.QueryExecutor.ExecuteQueryFn = func(q *influxql.Query, db string, chunkSize int) (<-chan *influxql.Result, error) {
|
2015-05-29 15:53:33 +00:00
|
|
|
if chunkSize != 2 {
|
|
|
|
t.Fatalf("unexpected chunk size: %d", chunkSize)
|
|
|
|
}
|
|
|
|
return NewResultChan(
|
2015-09-21 12:55:38 +00:00
|
|
|
&influxql.Result{StatementID: 1, Series: models.Rows([]*models.Row{{Name: "series0"}})},
|
|
|
|
&influxql.Result{StatementID: 1, Series: models.Rows([]*models.Row{{Name: "series1"}})},
|
2015-05-29 15:53:33 +00:00
|
|
|
), nil
|
|
|
|
}
|
2015-04-07 19:28:38 +00:00
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
w := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(w, MustNewJSONRequest("GET", "/query?db=foo&q=SELECT+*+FROM+bar&chunked=true&chunk_size=2", nil))
|
|
|
|
if w.Code != http.StatusOK {
|
|
|
|
t.Fatalf("unexpected status: %d", w.Code)
|
|
|
|
} else if w.Body.String() != `{"results":[{"series":[{"name":"series0"}]}]}{"results":[{"series":[{"name":"series1"}]}]}` {
|
|
|
|
t.Fatalf("unexpected body: %s", w.Body.String())
|
2015-04-07 19:28:38 +00:00
|
|
|
}
|
2015-05-29 15:53:33 +00:00
|
|
|
}
|
2015-04-07 19:28:38 +00:00
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// Ensure the handler returns a status 400 if the query is not passed in.
|
|
|
|
func TestHandler_Query_ErrQueryRequired(t *testing.T) {
|
|
|
|
h := NewHandler(false)
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(w, MustNewJSONRequest("GET", "/query", nil))
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
|
|
t.Fatalf("unexpected status: %d", w.Code)
|
|
|
|
} else if w.Body.String() != `{"error":"missing required parameter \"q\""}` {
|
|
|
|
t.Fatalf("unexpected body: %s", w.Body.String())
|
2015-04-07 19:28:38 +00:00
|
|
|
}
|
2015-05-29 15:53:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the handler returns a status 400 if the query cannot be parsed.
|
|
|
|
func TestHandler_Query_ErrInvalidQuery(t *testing.T) {
|
|
|
|
h := NewHandler(false)
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(w, MustNewJSONRequest("GET", "/query?q=SELECT", nil))
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
|
|
t.Fatalf("unexpected status: %d", w.Code)
|
|
|
|
} else if w.Body.String() != `{"error":"error parsing query: found EOF, expected identifier, string, number, bool at line 1, char 8"}` {
|
|
|
|
t.Fatalf("unexpected body: %s", w.Body.String())
|
2015-04-07 19:28:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// Ensure the handler returns a status 401 if the user is not authorized.
|
2015-07-10 20:39:33 +00:00
|
|
|
// func TestHandler_Query_ErrUnauthorized(t *testing.T) {
|
|
|
|
// h := NewHandler(false)
|
|
|
|
// h.QueryExecutor.AuthorizeFn = func(u *meta.UserInfo, q *influxql.Query, db string) error {
|
|
|
|
// return errors.New("marker")
|
|
|
|
// }
|
|
|
|
|
|
|
|
// w := httptest.NewRecorder()
|
|
|
|
// h.ServeHTTP(w, MustNewJSONRequest("GET", "/query?u=bar&db=foo&q=SHOW+SERIES+FROM+bar", nil))
|
|
|
|
// if w.Code != http.StatusUnauthorized {
|
|
|
|
// t.Fatalf("unexpected status: %d", w.Code)
|
|
|
|
// }
|
|
|
|
// }
|
2015-05-29 15:53:33 +00:00
|
|
|
|
|
|
|
// Ensure the handler returns a status 500 if an error is returned from the query executor.
|
|
|
|
func TestHandler_Query_ErrExecuteQuery(t *testing.T) {
|
|
|
|
h := NewHandler(false)
|
2015-06-01 17:54:26 +00:00
|
|
|
h.QueryExecutor.ExecuteQueryFn = func(q *influxql.Query, db string, chunkSize int) (<-chan *influxql.Result, error) {
|
2015-05-29 15:53:33 +00:00
|
|
|
return nil, errors.New("marker")
|
|
|
|
}
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(w, MustNewJSONRequest("GET", "/query?db=foo&q=SHOW+SERIES+FROM+bar", nil))
|
|
|
|
if w.Code != http.StatusInternalServerError {
|
|
|
|
t.Fatalf("unexpected status: %d", w.Code)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the handler returns a status 200 if an error is returned in the result.
|
|
|
|
func TestHandler_Query_ErrResult(t *testing.T) {
|
|
|
|
h := NewHandler(false)
|
2015-06-01 17:54:26 +00:00
|
|
|
h.QueryExecutor.ExecuteQueryFn = func(q *influxql.Query, db string, chunkSize int) (<-chan *influxql.Result, error) {
|
2015-05-29 15:53:33 +00:00
|
|
|
return NewResultChan(&influxql.Result{Err: errors.New("measurement not found")}), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
h.ServeHTTP(w, MustNewJSONRequest("GET", "/query?db=foo&q=SHOW+SERIES+from+bin", nil))
|
|
|
|
if w.Code != http.StatusOK {
|
|
|
|
t.Fatalf("unexpected status: %d", w.Code)
|
|
|
|
} else if w.Body.String() != `{"results":[{"error":"measurement not found"}]}` {
|
|
|
|
t.Fatalf("unexpected body: %s", w.Body.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMarshalJSON_NoPretty(t *testing.T) {
|
|
|
|
if b := httpd.MarshalJSON(struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
}{Name: "foo"}, false); string(b) != `{"name":"foo"}` {
|
|
|
|
t.Fatalf("unexpected bytes: %s", b)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMarshalJSON_Pretty(t *testing.T) {
|
|
|
|
if b := httpd.MarshalJSON(struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
}{Name: "foo"}, true); string(b) != "{\n \"name\": \"foo\"\n}" {
|
|
|
|
t.Fatalf("unexpected bytes: %q", string(b))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMarshalJSON_Error(t *testing.T) {
|
|
|
|
if b := httpd.MarshalJSON(&invalidJSON{}, true); string(b) != "json: error calling MarshalJSON for type *httpd_test.invalidJSON: marker" {
|
|
|
|
t.Fatalf("unexpected bytes: %q", string(b))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type invalidJSON struct{}
|
|
|
|
|
|
|
|
func (*invalidJSON) MarshalJSON() ([]byte, error) { return nil, errors.New("marker") }
|
|
|
|
|
2015-05-30 20:00:46 +00:00
|
|
|
func TestNormalizeBatchPoints(t *testing.T) {
|
2015-02-10 22:57:46 +00:00
|
|
|
now := time.Now()
|
2015-05-30 20:00:46 +00:00
|
|
|
tests := []struct {
|
|
|
|
name string
|
|
|
|
bp client.BatchPoints
|
2015-09-16 20:33:08 +00:00
|
|
|
p []models.Point
|
2015-05-30 20:00:46 +00:00
|
|
|
err string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
name: "default",
|
|
|
|
bp: client.BatchPoints{
|
|
|
|
Points: []client.Point{
|
2015-06-02 16:40:52 +00:00
|
|
|
{Measurement: "cpu", Tags: map[string]string{"region": "useast"}, Time: now, Fields: map[string]interface{}{"value": 1.0}},
|
2015-03-24 21:57:03 +00:00
|
|
|
},
|
2015-03-22 16:28:04 +00:00
|
|
|
},
|
2015-09-16 20:33:08 +00:00
|
|
|
p: []models.Point{
|
|
|
|
models.NewPoint("cpu", map[string]string{"region": "useast"}, map[string]interface{}{"value": 1.0}, now),
|
2015-03-22 16:28:04 +00:00
|
|
|
},
|
2015-05-30 20:00:46 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "merge time",
|
|
|
|
bp: client.BatchPoints{
|
|
|
|
Time: now,
|
|
|
|
Points: []client.Point{
|
2015-06-02 16:40:52 +00:00
|
|
|
{Measurement: "cpu", Tags: map[string]string{"region": "useast"}, Fields: map[string]interface{}{"value": 1.0}},
|
2015-05-30 20:00:46 +00:00
|
|
|
},
|
|
|
|
},
|
2015-09-16 20:33:08 +00:00
|
|
|
p: []models.Point{
|
|
|
|
models.NewPoint("cpu", map[string]string{"region": "useast"}, map[string]interface{}{"value": 1.0}, now),
|
2015-05-30 20:00:46 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "merge tags",
|
|
|
|
bp: client.BatchPoints{
|
|
|
|
Tags: map[string]string{"day": "monday"},
|
|
|
|
Points: []client.Point{
|
2015-06-02 16:40:52 +00:00
|
|
|
{Measurement: "cpu", Tags: map[string]string{"region": "useast"}, Time: now, Fields: map[string]interface{}{"value": 1.0}},
|
|
|
|
{Measurement: "memory", Time: now, Fields: map[string]interface{}{"value": 2.0}},
|
2015-05-30 20:00:46 +00:00
|
|
|
},
|
|
|
|
},
|
2015-09-16 20:33:08 +00:00
|
|
|
p: []models.Point{
|
|
|
|
models.NewPoint("cpu", map[string]string{"day": "monday", "region": "useast"}, map[string]interface{}{"value": 1.0}, now),
|
|
|
|
models.NewPoint("memory", map[string]string{"day": "monday"}, map[string]interface{}{"value": 2.0}, now),
|
2015-05-30 20:00:46 +00:00
|
|
|
},
|
|
|
|
},
|
2015-03-26 12:01:27 +00:00
|
|
|
}
|
2015-05-12 18:15:08 +00:00
|
|
|
|
2015-05-30 20:00:46 +00:00
|
|
|
for _, test := range tests {
|
|
|
|
t.Logf("running test %q", test.name)
|
|
|
|
p, e := httpd.NormalizeBatchPoints(test.bp)
|
|
|
|
if test.err == "" && e != nil {
|
|
|
|
t.Errorf("unexpected error %v", e)
|
|
|
|
} else if test.err != "" && e == nil {
|
|
|
|
t.Errorf("expected error %s, got <nil>", test.err)
|
|
|
|
} else if e != nil && test.err != e.Error() {
|
|
|
|
t.Errorf("unexpected error. expected: %s, got %v", test.err, e)
|
2015-03-26 12:01:27 +00:00
|
|
|
}
|
2015-05-30 20:00:46 +00:00
|
|
|
if !reflect.DeepEqual(p, test.p) {
|
|
|
|
t.Logf("expected: %+v", test.p)
|
|
|
|
t.Logf("got: %+v", p)
|
|
|
|
t.Error("failed to normalize.")
|
2015-03-26 12:01:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// NewHandler represents a test wrapper for httpd.Handler.
|
|
|
|
type Handler struct {
|
|
|
|
*httpd.Handler
|
|
|
|
MetaStore HandlerMetaStore
|
|
|
|
QueryExecutor HandlerQueryExecutor
|
2015-07-15 23:08:26 +00:00
|
|
|
TSDBStore HandlerTSDBStore
|
2015-04-03 16:28:01 +00:00
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// NewHandler returns a new instance of Handler.
|
|
|
|
func NewHandler(requireAuthentication bool) *Handler {
|
2015-09-04 17:47:14 +00:00
|
|
|
statMap := influxdb.NewStatistics("httpd", "httpd", nil)
|
2015-05-29 15:53:33 +00:00
|
|
|
h := &Handler{
|
2015-09-04 17:47:14 +00:00
|
|
|
Handler: httpd.NewHandler(requireAuthentication, true, false, statMap),
|
2015-05-29 15:53:33 +00:00
|
|
|
}
|
|
|
|
h.Handler.MetaStore = &h.MetaStore
|
|
|
|
h.Handler.QueryExecutor = &h.QueryExecutor
|
2015-06-11 16:22:37 +00:00
|
|
|
h.Handler.Version = "0.0.0"
|
2015-05-29 15:53:33 +00:00
|
|
|
return h
|
2014-11-19 17:12:46 +00:00
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// HandlerMetaStore is a mock implementation of Handler.MetaStore.
|
|
|
|
type HandlerMetaStore struct {
|
|
|
|
DatabaseFn func(name string) (*meta.DatabaseInfo, error)
|
|
|
|
AuthenticateFn func(username, password string) (ui *meta.UserInfo, err error)
|
|
|
|
UsersFn func() ([]meta.UserInfo, error)
|
2015-01-05 23:47:12 +00:00
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
func (s *HandlerMetaStore) Database(name string) (*meta.DatabaseInfo, error) {
|
|
|
|
return s.DatabaseFn(name)
|
2014-11-19 17:12:46 +00:00
|
|
|
}
|
2015-01-20 17:50:13 +00:00
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
func (s *HandlerMetaStore) Authenticate(username, password string) (ui *meta.UserInfo, err error) {
|
|
|
|
return s.AuthenticateFn(username, password)
|
2015-01-20 17:50:13 +00:00
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
func (s *HandlerMetaStore) Users() ([]meta.UserInfo, error) {
|
|
|
|
return s.UsersFn()
|
2015-01-20 17:50:13 +00:00
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// HandlerQueryExecutor is a mock implementation of Handler.QueryExecutor.
|
|
|
|
type HandlerQueryExecutor struct {
|
2015-07-10 20:39:33 +00:00
|
|
|
AuthorizeFn func(u *meta.UserInfo, q *influxql.Query, db string) error
|
2015-06-01 17:54:26 +00:00
|
|
|
ExecuteQueryFn func(q *influxql.Query, db string, chunkSize int) (<-chan *influxql.Result, error)
|
2015-01-20 17:50:13 +00:00
|
|
|
}
|
|
|
|
|
2015-07-10 20:39:33 +00:00
|
|
|
func (e *HandlerQueryExecutor) Authorize(u *meta.UserInfo, q *influxql.Query, db string) error {
|
|
|
|
return e.AuthorizeFn(u, q, db)
|
|
|
|
}
|
|
|
|
|
2015-06-01 17:54:26 +00:00
|
|
|
func (e *HandlerQueryExecutor) ExecuteQuery(q *influxql.Query, db string, chunkSize int) (<-chan *influxql.Result, error) {
|
|
|
|
return e.ExecuteQueryFn(q, db, chunkSize)
|
2015-02-05 22:54:32 +00:00
|
|
|
}
|
|
|
|
|
2015-07-15 23:08:26 +00:00
|
|
|
// HandlerTSDBStore is a mock implementation of Handler.TSDBStore
|
|
|
|
type HandlerTSDBStore struct {
|
|
|
|
CreateMapperFn func(shardID uint64, query string, chunkSize int) (tsdb.Mapper, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *HandlerTSDBStore) CreateMapper(shardID uint64, query string, chunkSize int) (tsdb.Mapper, error) {
|
|
|
|
return h.CreateMapperFn(shardID, query, chunkSize)
|
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// MustNewRequest returns a new HTTP request. Panic on error.
|
|
|
|
func MustNewRequest(method, urlStr string, body io.Reader) *http.Request {
|
|
|
|
r, err := http.NewRequest(method, urlStr, body)
|
|
|
|
if err != nil {
|
2015-01-20 17:50:13 +00:00
|
|
|
panic(err.Error())
|
|
|
|
}
|
2015-05-29 15:53:33 +00:00
|
|
|
return r
|
2015-01-20 17:50:13 +00:00
|
|
|
}
|
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// MustNewRequest returns a new HTTP request with the content type set. Panic on error.
|
|
|
|
func MustNewJSONRequest(method, urlStr string, body io.Reader) *http.Request {
|
|
|
|
r := MustNewRequest(method, urlStr, body)
|
|
|
|
r.Header.Set("Content-Type", "application/json")
|
|
|
|
return r
|
2015-01-20 17:50:13 +00:00
|
|
|
}
|
2015-01-28 08:45:21 +00:00
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// matchRegex returns true if a s matches pattern.
|
|
|
|
func matchRegex(pattern, s string) bool {
|
|
|
|
return regexp.MustCompile(pattern).MatchString(s)
|
2015-01-28 08:45:21 +00:00
|
|
|
}
|
2015-02-16 20:30:58 +00:00
|
|
|
|
2015-05-29 15:53:33 +00:00
|
|
|
// NewResultChan returns a channel that sends all results and then closes.
|
|
|
|
func NewResultChan(results ...*influxql.Result) <-chan *influxql.Result {
|
|
|
|
ch := make(chan *influxql.Result, len(results))
|
|
|
|
for _, r := range results {
|
|
|
|
ch <- r
|
2015-02-16 20:30:58 +00:00
|
|
|
}
|
2015-05-29 15:53:33 +00:00
|
|
|
close(ch)
|
|
|
|
return ch
|
2015-03-28 00:40:21 +00:00
|
|
|
}
|