influxdb/handler_test.go

697 lines
20 KiB
Go

package influxdb_test
import (
"bytes"
"encoding/base64"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"github.com/influxdb/influxdb"
)
func init() {
influxdb.BcryptCost = 4
}
func TestHandler_Databases(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
srvr.CreateDatabase("bar")
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("GET", s.URL+`/db`, "")
if status != http.StatusOK {
t.Fatalf("unexpected status: %d", status)
} else if body != `["bar","foo"]` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateDatabase(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("POST", s.URL+`/db`, `{"name": "foo"}`)
if status != http.StatusCreated {
t.Fatalf("unexpected status: %d", status)
} else if body != `` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateDatabase_BadRequest_NoName(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("POST", s.URL+`/db`, `{"BadRequest": 1}`)
if status != http.StatusBadRequest {
t.Fatalf("unexpected status: %d", status)
} else if body != `database name required` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateDatabase_BadRequest_InvalidJSON(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("POST", s.URL+`/db`, `"BadRequest": 1`)
if status != http.StatusBadRequest {
t.Fatalf("unexpected status: %d", status)
} else if body != `json: cannot unmarshal string into Go value of type struct { Name string "json:\"name\"" }` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateDatabase_Conflict(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("POST", s.URL+`/db`, `{"name": "foo"}`)
if status != http.StatusConflict {
t.Fatalf("unexpected status: %d", status)
} else if body != `database exists` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_DeleteDatabase(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("DELETE", s.URL+`/db/foo`, "")
if status != http.StatusNoContent {
t.Fatalf("unexpected status: %d", status)
} else if body != "" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_DeleteDatabase_NotFound(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("DELETE", s.URL+`/db/foo`, "")
if status != http.StatusNotFound {
t.Fatalf("unexpected status: %d", status)
} else if body != `database not found` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_RetentionPolicies(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
srvr.CreateRetentionPolicy("foo", influxdb.NewRetentionPolicy("bar"))
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("GET", s.URL+`/db/foo/retention_policies`, "")
if status != http.StatusOK {
t.Fatalf("unexpected status: %d", status)
} else if body != `[{"name":"bar","replicaN":1}]` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_RetentionPolicies_DatabaseNotFound(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("GET", s.URL+`/db/foo/retention_policies`, "")
if status != http.StatusNotFound {
t.Fatalf("unexpected status: %d", status)
} else if body != `database not found` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateRetentionPolicy(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
s := NewHTTPServer(srvr)
defer s.Close()
policy := `{"name": "bar", "duration": 1000000, "replicaN": 1, "splitN": 2}`
status, body := MustHTTP("POST", s.URL+`/db/foo/retention_policies`, policy)
if status != http.StatusCreated {
t.Fatalf("unexpected status: %d", status)
} else if body != "" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateRetentionPolicy_DatabaseNotFound(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
policy := `{"name": "bar", "duration": 1000000, "replicaN": 1, "splitN": 2}`
status, body := MustHTTP("POST", s.URL+`/db/foo/retention_policies`, policy)
if status != http.StatusNotFound {
t.Fatalf("unexpected status: %d", status)
} else if body != "database not found" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateRetentionPolicy_Conflict(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
s := NewHTTPServer(srvr)
defer s.Close()
policy := `{"name": "newName", "duration": 1000000, "replicaN": 1, "splitN": 2}`
MustHTTP("POST", s.URL+`/db/foo/retention_policies`, policy)
status, body := MustHTTP("POST", s.URL+`/db/foo/retention_policies`, policy)
if status != http.StatusConflict {
t.Fatalf("unexpected status: %d", status)
} else if body != "retention policy exists" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateRetentionPolicy_BadRequest(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
s := NewHTTPServer(srvr)
defer s.Close()
policy := `{"name": "bar", "duration": "**BAD**", "replicaN": 1, "splitN": 2}`
status, body := MustHTTP("POST", s.URL+`/db/foo/retention_policies`, policy)
if status != http.StatusBadRequest {
t.Fatalf("unexpected status: %d", status)
} else if body != "json: cannot unmarshal string into Go value of type time.Duration" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_UpdateRetentionPolicy(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
srvr.CreateRetentionPolicy("foo", influxdb.NewRetentionPolicy("bar"))
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("PUT", s.URL+`/db/foo/retention_policies/bar`,
`{"name": "newName", "duration": 1000000, "replicaN": 1, "splitN": 2}`)
// Verify updated policy.
p, _ := srvr.RetentionPolicy("foo", "newName")
if status != http.StatusNoContent {
t.Fatalf("unexpected status: %d", status)
} else if body != "" {
t.Fatalf("unexpected body: %s", body)
} else if p.Name != "newName" {
t.Fatalf("unexpected policy name: %s", p.Name)
}
}
func TestHandler_UpdateRetentionPolicy_BadRequest(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
srvr.CreateRetentionPolicy("foo", influxdb.NewRetentionPolicy("bar"))
s := NewHTTPServer(srvr)
defer s.Close()
newPolicy := `{"name": "newName", "duration": "BadRequest", "replicaN": 1, "splitN": 2}`
status, body := MustHTTP("PUT", s.URL+`/db/foo/retention_policies/bar`, newPolicy)
// Verify response.
if status != http.StatusBadRequest {
t.Fatalf("unexpected status: %d", status)
} else if body != "json: cannot unmarshal string into Go value of type time.Duration" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_UpdateRetentionPolicy_DatabaseNotFound(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
newPolicy := `{"name": "newName", "duration": 1000000, "replicaN": 1, "splitN": 2}`
status, body := MustHTTP("PUT", s.URL+`/db/foo/retention_policies/bar`, newPolicy)
if status != http.StatusNotFound {
t.Fatalf("unexpected status: %d", status)
} else if body != "database not found" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_UpdateRetentionPolicy_NotFound(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
s := NewHTTPServer(srvr)
defer s.Close()
newPolicy := `{"name": "newName", "duration": 1000000, "replicaN": 1, "splitN": 2}`
status, body := MustHTTP("PUT", s.URL+`/db/foo/retention_policies/bar`, newPolicy)
if status != http.StatusNotFound {
t.Fatalf("unexpected status: %d", status)
} else if body != "retention policy not found" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_DeleteRetentionPolicy(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
srvr.CreateRetentionPolicy("foo", influxdb.NewRetentionPolicy("bar"))
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("DELETE", s.URL+`/db/foo/retention_policies/bar`, "")
if status != http.StatusNoContent {
t.Fatalf("unexpected status: %d", status)
} else if body != "" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_DeleteRetentionPolicy_DatabaseNotFound(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("DELETE", s.URL+`/db/foo/retention_policies/bar`, "")
if status != http.StatusNotFound {
t.Fatalf("unexpected status: %d", status)
} else if body != "database not found" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_DeleteRetentionPolicy_NotFound(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("DELETE", s.URL+`/db/foo/retention_policies/bar`, "")
if status != http.StatusNotFound {
t.Fatalf("unexpected status: %d", status)
} else if body != "retention policy not found" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_Ping(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, _ := MustHTTP("GET", s.URL+`/ping`, "")
if status != http.StatusOK {
t.Fatalf("unexpected status: %d", status)
}
}
func TestHandler_Users_NoUsers(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("GET", s.URL+`/users`, "")
if status != http.StatusOK {
t.Fatalf("unexpected status: %d", status)
} else if body != "[]" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_Users_OneUser(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateUser("jdoe", "1337", true)
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("GET", s.URL+`/users`, "")
if status != http.StatusOK {
t.Fatalf("unexpected status: %d", status)
} else if body != `[{"name":"jdoe","admin":true}]` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_Users_MultipleUsers(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateUser("jdoe", "1337", false)
srvr.CreateUser("mclark", "1337", true)
srvr.CreateUser("csmith", "1337", false)
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("GET", s.URL+`/users`, "")
if status != http.StatusOK {
t.Fatalf("unexpected status: %d", status)
} else if body != `[{"name":"csmith"},{"name":"jdoe"},{"name":"mclark","admin":true}]` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateUser(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("POST", s.URL+`/users`, `{"name":"jdoe","password":"1337"}`)
if status != http.StatusCreated {
t.Fatalf("unexpected status: %d", status)
} else if body != "" {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateUser_BadRequest(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("POST", s.URL+`/users`, `{"name":0xBAD,"password":"1337"}`)
if status != http.StatusBadRequest {
t.Fatalf("unexpected status: %d", status)
} else if body != `invalid character 'x' after object key:value pair` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateUser_InternalServerError(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("POST", s.URL+`/users`, `{"name":""}`)
if status != http.StatusInternalServerError {
t.Fatalf("unexpected status: %d", status)
} else if body != `username required` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_UpdateUser(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateUser("jdoe", "1337", false)
s := NewHTTPServer(srvr)
defer s.Close()
// Save original password hash.
hash := srvr.User("jdoe").Hash
// Update user password.
status, body := MustHTTP("PUT", s.URL+`/users/jdoe`, `{"password": "7331"}`)
if status != http.StatusNoContent {
t.Fatalf("unexpected status: %d", status)
} else if body != `` {
t.Fatalf("unexpected body: %s", body)
} else if srvr.User("jdoe").Hash == hash {
t.Fatalf("expected password hash to change")
}
}
func TestHandler_UpdateUser_PasswordBadRequest(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateUser("jdoe", "1337", false)
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("PUT", s.URL+`/users/jdoe`, `{"password": 10}`)
if status != http.StatusBadRequest {
t.Fatalf("unexpected status: %d", status)
} else if body != `json: cannot unmarshal number into Go value of type string` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_DeleteUser(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateUser("jdoe", "1337", false)
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("DELETE", s.URL+`/users/jdoe`, "")
if status != http.StatusNoContent {
t.Fatalf("unexpected status: %d", status)
} else if body != `` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_DeleteUser_UserNotFound(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDatabase("foo")
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("DELETE", s.URL+`/users/jdoe`, "")
if status != http.StatusNotFound {
t.Fatalf("unexpected status: %d", status)
} else if body != `user not found` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_DataNodes(t *testing.T) {
srvr := OpenUninitializedServer(NewMessagingClient())
srvr.CreateDataNode(MustParseURL("http://localhost:1000"))
srvr.CreateDataNode(MustParseURL("http://localhost:2000"))
srvr.CreateDataNode(MustParseURL("http://localhost:3000"))
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("GET", s.URL+`/data_nodes`, "")
if status != http.StatusOK {
t.Fatalf("unexpected status: %d", status)
} else if body != `[{"id":1,"url":"http://localhost:1000"},{"id":2,"url":"http://localhost:2000"},{"id":3,"url":"http://localhost:3000"}]` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateDataNode(t *testing.T) {
srvr := OpenUninitializedServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("POST", s.URL+`/data_nodes`, `{"url":"http://localhost:1000"}`)
if status != http.StatusCreated {
t.Fatalf("unexpected status: %d", status)
} else if body != `{"id":1,"url":"http://localhost:1000"}` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateDataNode_BadRequest(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("POST", s.URL+`/data_nodes`, `{"name":`)
if status != http.StatusBadRequest {
t.Fatalf("unexpected status: %d", status)
} else if body != `unexpected EOF` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_CreateDataNode_InternalServerError(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("POST", s.URL+`/data_nodes`, `{"url":""}`)
if status != http.StatusInternalServerError {
t.Fatalf("unexpected status: %d, %s", status, body)
} else if body != `data node url required` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_DeleteDataNode(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateDataNode(MustParseURL("http://localhost:1000"))
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("DELETE", s.URL+`/data_nodes/1`, "")
if status != http.StatusNoContent {
t.Fatalf("unexpected status: %d", status)
} else if body != `` {
t.Fatalf("unexpected body: %s", body)
}
}
func TestHandler_DeleteUser_DataNodeNotFound(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewHTTPServer(srvr)
defer s.Close()
status, body := MustHTTP("DELETE", s.URL+`/data_nodes/10000`, "")
if status != http.StatusNotFound {
t.Fatalf("unexpected status: %d", status)
} else if body != `data node not found` {
t.Fatalf("unexpected body: %s", body)
}
}
// Perform a subset of endpoint testing, with authentication enabled.
func TestHandler_AuthenticatedCreateAdminUser(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewAuthenticatedHTTPServer(srvr)
defer s.Close()
// Attempting to create a non-admin user should fail.
status, _ := MustHTTP("POST", s.URL+`/users`, `{"name": "maeve", "password": "pass", "Admin": false}`)
if status != http.StatusUnauthorized {
t.Fatalf("unexpected status: %d", status)
}
// Creating the first admin user, without supplying authentication
// credentials should be OK.
status, _ = MustHTTP("POST", s.URL+`/users`, `{"name": "orla", "password": "pass", "Admin": true}`)
if status != http.StatusCreated {
t.Fatalf("unexpected status: %d", status)
}
// Creating a second admin user, without supplying authentication
// credentials should fail.
status, _ = MustHTTP("POST", s.URL+`/users`, `{"name": "louise", "password": "pass", "Admin": true}`)
if status != http.StatusUnauthorized {
t.Fatalf("unexpected status: %d", status)
}
}
func TestHandler_AuthenticatedDatabases_Unauthorized(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
s := NewAuthenticatedHTTPServer(srvr)
defer s.Close()
status, _ := MustHTTP("GET", s.URL+`/db`, "")
if status != http.StatusUnauthorized {
t.Fatalf("unexpected status: %d", status)
}
}
func TestHandler_AuthenticatedDatabases_AuthorizedQueryParams(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateUser("lisa", "password", true)
s := NewAuthenticatedHTTPServer(srvr)
defer s.Close()
status, _ := MustHTTP("GET", s.URL+`/db?u=lisa&p=password`, "")
if status != http.StatusOK {
t.Fatalf("unexpected status: %d", status)
}
}
func TestHandler_AuthenticatedDatabases_AuthorizedBasicAuth(t *testing.T) {
srvr := OpenServer(NewMessagingClient())
srvr.CreateUser("lisa", "password", true)
s := NewAuthenticatedHTTPServer(srvr)
defer s.Close()
auth := make(map[string]string)
auth["Authorization"] = "Basic " + base64.StdEncoding.EncodeToString([]byte("lisa:password"))
fmt.Println(auth)
status, _ := MustHTTPWithHeaders("GET", s.URL+`/db`, auth, "")
if status != http.StatusOK {
t.Fatalf("unexpected status: %d", status)
}
}
// Utility functions for this test suite.
func MustHTTP(verb, url, body string) (int, string) {
return MustHTTPWithHeaders(verb, url, nil, body)
}
func MustHTTPWithHeaders(verb, url string, headers map[string]string, body string) (int, string) {
req, err := http.NewRequest(verb, url, bytes.NewBuffer([]byte(body)))
if err != nil {
panic(err)
}
for k, v := range headers {
req.Header.Set(k, v)
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
b, err := ioutil.ReadAll(resp.Body)
return resp.StatusCode, strings.TrimRight(string(b), "\n")
}
// MustParseURL parses a string into a URL. Panic on error.
func MustParseURL(s string) *url.URL {
u, err := url.Parse(s)
if err != nil {
panic(err.Error())
}
return u
}
// Server is a test HTTP server that wraps a handler
type HTTPServer struct {
*httptest.Server
Handler *influxdb.Handler
}
func NewHTTPServer(s *Server) *HTTPServer {
h := influxdb.NewHandler(s.Server)
return &HTTPServer{httptest.NewServer(h), h}
}
func NewAuthenticatedHTTPServer(s *Server) *HTTPServer {
h := influxdb.NewHandler(s.Server)
h.AuthenticationEnabled = true
return &HTTPServer{httptest.NewServer(h), h}
}
func (s *HTTPServer) Close() {
s.Server.Close()
}