705 lines
17 KiB
Go
705 lines
17 KiB
Go
package http
|
|
|
|
import (
|
|
"bytes"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func docIDLookup(req *http.Request) string {
|
|
return req.FormValue("docID")
|
|
}
|
|
|
|
func indexNameLookup(req *http.Request) string {
|
|
return req.FormValue("indexName")
|
|
}
|
|
|
|
func TestHandlers(t *testing.T) {
|
|
|
|
basePath := "testbase"
|
|
err := os.MkdirAll(basePath, 0700)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() {
|
|
err := os.RemoveAll(basePath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}()
|
|
|
|
createIndexHandler := NewCreateIndexHandler(basePath)
|
|
createIndexHandler.IndexNameLookup = indexNameLookup
|
|
|
|
getIndexHandler := NewGetIndexHandler()
|
|
getIndexHandler.IndexNameLookup = indexNameLookup
|
|
|
|
deleteIndexHandler := NewDeleteIndexHandler(basePath)
|
|
deleteIndexHandler.IndexNameLookup = indexNameLookup
|
|
|
|
listIndexesHandler := NewListIndexesHandler()
|
|
|
|
docIndexHandler := NewDocIndexHandler("")
|
|
docIndexHandler.IndexNameLookup = indexNameLookup
|
|
docIndexHandler.DocIDLookup = docIDLookup
|
|
|
|
docCountHandler := NewDocCountHandler("")
|
|
docCountHandler.IndexNameLookup = indexNameLookup
|
|
|
|
docGetHandler := NewDocGetHandler("")
|
|
docGetHandler.IndexNameLookup = indexNameLookup
|
|
docGetHandler.DocIDLookup = docIDLookup
|
|
|
|
docDeleteHandler := NewDocDeleteHandler("")
|
|
docDeleteHandler.IndexNameLookup = indexNameLookup
|
|
docDeleteHandler.DocIDLookup = docIDLookup
|
|
|
|
searchHandler := NewSearchHandler("")
|
|
searchHandler.IndexNameLookup = indexNameLookup
|
|
|
|
listFieldsHandler := NewListFieldsHandler("")
|
|
listFieldsHandler.IndexNameLookup = indexNameLookup
|
|
|
|
debugHandler := NewDebugDocumentHandler("")
|
|
debugHandler.IndexNameLookup = indexNameLookup
|
|
debugHandler.DocIDLookup = docIDLookup
|
|
|
|
aliasHandler := NewAliasHandler()
|
|
|
|
tests := []struct {
|
|
Desc string
|
|
Handler http.Handler
|
|
Path string
|
|
Method string
|
|
Params url.Values
|
|
Body []byte
|
|
Status int
|
|
ResponseBody []byte
|
|
ResponseMatch map[string]bool
|
|
}{
|
|
{
|
|
Desc: "create index",
|
|
Handler: createIndexHandler,
|
|
Path: "/create",
|
|
Method: "PUT",
|
|
Params: url.Values{"indexName": []string{"ti1"}},
|
|
Body: []byte("{}"),
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok"}`),
|
|
},
|
|
{
|
|
Desc: "create existing index",
|
|
Handler: createIndexHandler,
|
|
Path: "/create",
|
|
Method: "PUT",
|
|
Params: url.Values{"indexName": []string{"ti1"}},
|
|
Body: []byte("{}"),
|
|
Status: http.StatusInternalServerError,
|
|
ResponseMatch: map[string]bool{
|
|
`path already exists`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "create index missing index",
|
|
Handler: createIndexHandler,
|
|
Path: "/create",
|
|
Method: "PUT",
|
|
Body: []byte("{}"),
|
|
Status: http.StatusBadRequest,
|
|
ResponseBody: []byte(`index name is required`),
|
|
},
|
|
{
|
|
Desc: "create index invalid json",
|
|
Handler: createIndexHandler,
|
|
Path: "/create",
|
|
Method: "PUT",
|
|
Params: url.Values{"indexName": []string{"ti9"}},
|
|
Body: []byte("{"),
|
|
Status: http.StatusBadRequest,
|
|
ResponseMatch: map[string]bool{
|
|
`error parsing index mapping`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "get index",
|
|
Handler: getIndexHandler,
|
|
Path: "/get",
|
|
Method: "GET",
|
|
Params: url.Values{"indexName": []string{"ti1"}},
|
|
Status: http.StatusOK,
|
|
ResponseMatch: map[string]bool{
|
|
`"status":"ok"`: true,
|
|
`"name":"ti1"`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "get index does not exist",
|
|
Handler: getIndexHandler,
|
|
Path: "/get",
|
|
Method: "GET",
|
|
Params: url.Values{"indexName": []string{"dne"}},
|
|
Status: http.StatusNotFound,
|
|
ResponseMatch: map[string]bool{
|
|
`no such index`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "get index missing name",
|
|
Handler: getIndexHandler,
|
|
Path: "/get",
|
|
Method: "GET",
|
|
Status: http.StatusBadRequest,
|
|
ResponseBody: []byte(`index name is required`),
|
|
},
|
|
{
|
|
Desc: "create another index",
|
|
Handler: createIndexHandler,
|
|
Path: "/create",
|
|
Method: "PUT",
|
|
Params: url.Values{"indexName": []string{"ti2"}},
|
|
Body: []byte("{}"),
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok"}`),
|
|
},
|
|
{
|
|
Desc: "list indexes",
|
|
Handler: listIndexesHandler,
|
|
Path: "/list",
|
|
Method: "GET",
|
|
Status: http.StatusOK,
|
|
ResponseMatch: map[string]bool{
|
|
`"status":"ok"`: true,
|
|
`"ti1"`: true,
|
|
`"ti2"`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "delete index",
|
|
Handler: deleteIndexHandler,
|
|
Path: "/delete",
|
|
Method: "DELETE",
|
|
Params: url.Values{"indexName": []string{"ti2"}},
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok"}`),
|
|
},
|
|
{
|
|
Desc: "delete index missing name",
|
|
Handler: deleteIndexHandler,
|
|
Path: "/delete",
|
|
Method: "DELETE",
|
|
Status: http.StatusBadRequest,
|
|
ResponseBody: []byte(`index name is required`),
|
|
},
|
|
{
|
|
Desc: "list indexes after delete",
|
|
Handler: listIndexesHandler,
|
|
Path: "/list",
|
|
Method: "GET",
|
|
Status: http.StatusOK,
|
|
ResponseMatch: map[string]bool{
|
|
`"status":"ok"`: true,
|
|
`"ti1"`: true,
|
|
`"ti2"`: false,
|
|
},
|
|
},
|
|
{
|
|
Desc: "index doc",
|
|
Handler: docIndexHandler,
|
|
Path: "/ti1/a",
|
|
Method: "PUT",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
"docID": []string{"a"},
|
|
},
|
|
Body: []byte(`{"name":"a","body":"test","rating":7,"created":"2014-11-26","former_ratings":[3,4,2]}`),
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok"}`),
|
|
},
|
|
{
|
|
Desc: "index doc invalid index",
|
|
Handler: docIndexHandler,
|
|
Path: "/tix/a",
|
|
Method: "PUT",
|
|
Params: url.Values{
|
|
"indexName": []string{"tix"},
|
|
"docID": []string{"a"},
|
|
},
|
|
Body: []byte(`{"name":"a","body":"test","rating":7,"created":"2014-11-26","former_ratings":[3,4,2]}`),
|
|
Status: http.StatusNotFound,
|
|
ResponseBody: []byte(`no such index 'tix'`),
|
|
},
|
|
{
|
|
Desc: "index doc missing ID",
|
|
Handler: docIndexHandler,
|
|
Path: "/ti1/a",
|
|
Method: "PUT",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
},
|
|
Body: []byte(`{"name":"a","body":"test","rating":7,"created":"2014-11-26","former_ratings":[3,4,2]}`),
|
|
Status: http.StatusBadRequest,
|
|
ResponseBody: []byte(`document id cannot be empty`),
|
|
},
|
|
{
|
|
Desc: "doc count",
|
|
Handler: docCountHandler,
|
|
Path: "/ti1/count",
|
|
Method: "GET",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
},
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok","count":1}`),
|
|
},
|
|
{
|
|
Desc: "doc count invalid index",
|
|
Handler: docCountHandler,
|
|
Path: "/tix/count",
|
|
Method: "GET",
|
|
Params: url.Values{
|
|
"indexName": []string{"tix"},
|
|
},
|
|
Status: http.StatusNotFound,
|
|
ResponseBody: []byte(`no such index 'tix'`),
|
|
},
|
|
{
|
|
Desc: "doc get",
|
|
Handler: docGetHandler,
|
|
Path: "/ti1/a",
|
|
Method: "GET",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
"docID": []string{"a"},
|
|
},
|
|
Status: http.StatusOK,
|
|
ResponseMatch: map[string]bool{
|
|
`"id":"a"`: true,
|
|
`"body":"test"`: true,
|
|
`"name":"a"`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "doc get invalid index",
|
|
Handler: docGetHandler,
|
|
Path: "/tix/a",
|
|
Method: "GET",
|
|
Params: url.Values{
|
|
"indexName": []string{"tix"},
|
|
"docID": []string{"a"},
|
|
},
|
|
Status: http.StatusNotFound,
|
|
ResponseBody: []byte(`no such index 'tix'`),
|
|
},
|
|
{
|
|
Desc: "doc get missing ID",
|
|
Handler: docGetHandler,
|
|
Path: "/ti1/a",
|
|
Method: "GET",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
},
|
|
Status: http.StatusBadRequest,
|
|
ResponseBody: []byte(`document id cannot be empty`),
|
|
},
|
|
{
|
|
Desc: "index another doc",
|
|
Handler: docIndexHandler,
|
|
Path: "/ti1/b",
|
|
Method: "PUT",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
"docID": []string{"b"},
|
|
},
|
|
Body: []byte(`{"name":"b","body":"del"}`),
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok"}`),
|
|
},
|
|
{
|
|
Desc: "doc count again",
|
|
Handler: docCountHandler,
|
|
Path: "/ti1/count",
|
|
Method: "GET",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
},
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok","count":2}`),
|
|
},
|
|
{
|
|
Desc: "delete doc",
|
|
Handler: docDeleteHandler,
|
|
Path: "/ti1/b",
|
|
Method: "DELETE",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
"docID": []string{"b"},
|
|
},
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok"}`),
|
|
},
|
|
{
|
|
Desc: "delete doc invalid index",
|
|
Handler: docDeleteHandler,
|
|
Path: "/tix/b",
|
|
Method: "DELETE",
|
|
Params: url.Values{
|
|
"indexName": []string{"tix"},
|
|
"docID": []string{"b"},
|
|
},
|
|
Status: http.StatusNotFound,
|
|
ResponseBody: []byte(`no such index 'tix'`),
|
|
},
|
|
{
|
|
Desc: "delete doc missing docID",
|
|
Handler: docDeleteHandler,
|
|
Path: "/ti1/b",
|
|
Method: "DELETE",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
},
|
|
Status: http.StatusBadRequest,
|
|
ResponseBody: []byte(`document id cannot be empty`),
|
|
},
|
|
{
|
|
Desc: "doc get",
|
|
Handler: docGetHandler,
|
|
Path: "/ti1/b",
|
|
Method: "GET",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
"docID": []string{"b"},
|
|
},
|
|
Status: http.StatusNotFound,
|
|
ResponseMatch: map[string]bool{
|
|
`no such document`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "search",
|
|
Handler: searchHandler,
|
|
Path: "/ti1/search",
|
|
Method: "POST",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
},
|
|
Body: []byte(`{
|
|
"from": 0,
|
|
"size": 10,
|
|
"query": {
|
|
"fuzziness": 0,
|
|
"prefix_length": 0,
|
|
"field": "body",
|
|
"match": "test"
|
|
}
|
|
}`),
|
|
Status: http.StatusOK,
|
|
ResponseMatch: map[string]bool{
|
|
`"total_hits":1`: true,
|
|
`"id":"a"`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "search index doesnt exist",
|
|
Handler: searchHandler,
|
|
Path: "/tix/search",
|
|
Method: "POST",
|
|
Params: url.Values{
|
|
"indexName": []string{"tix"},
|
|
},
|
|
Body: []byte(`{
|
|
"from": 0,
|
|
"size": 10,
|
|
"query": {
|
|
"fuzziness": 0,
|
|
"prefix_length": 0,
|
|
"field": "body",
|
|
"match": "test"
|
|
}
|
|
}`),
|
|
Status: http.StatusNotFound,
|
|
ResponseBody: []byte(`no such index 'tix'`),
|
|
},
|
|
{
|
|
Desc: "search invalid json",
|
|
Handler: searchHandler,
|
|
Path: "/ti1/search",
|
|
Method: "POST",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
},
|
|
Body: []byte(`{`),
|
|
Status: http.StatusBadRequest,
|
|
ResponseMatch: map[string]bool{
|
|
`error parsing query`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "search query does not validate",
|
|
Handler: searchHandler,
|
|
Path: "/ti1/search",
|
|
Method: "POST",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
},
|
|
Body: []byte(`{
|
|
"from": 0,
|
|
"size": 10,
|
|
"query": {
|
|
"field": "body",
|
|
"terms": []
|
|
}
|
|
}`),
|
|
Status: http.StatusBadRequest,
|
|
ResponseMatch: map[string]bool{
|
|
`error validating query`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "list fields",
|
|
Handler: listFieldsHandler,
|
|
Path: "/ti1/fields",
|
|
Method: "GET",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
},
|
|
Status: http.StatusOK,
|
|
ResponseMatch: map[string]bool{
|
|
`"fields":`: true,
|
|
`"name"`: true,
|
|
`"body"`: true,
|
|
`"_all"`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "list fields invalid index",
|
|
Handler: listFieldsHandler,
|
|
Path: "/tix/fields",
|
|
Method: "GET",
|
|
Params: url.Values{
|
|
"indexName": []string{"tix"},
|
|
},
|
|
Status: http.StatusNotFound,
|
|
ResponseBody: []byte(`no such index 'tix'`),
|
|
},
|
|
{
|
|
Desc: "debug doc",
|
|
Handler: debugHandler,
|
|
Path: "/ti1/a/debug",
|
|
Method: "GET",
|
|
Params: url.Values{
|
|
"indexName": []string{"ti1"},
|
|
"docID": []string{"a"},
|
|
},
|
|
Status: http.StatusOK,
|
|
ResponseMatch: map[string]bool{
|
|
`"key"`: true,
|
|
`"val"`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "debug doc invalid index",
|
|
Handler: debugHandler,
|
|
Path: "/ti1/a/debug",
|
|
Method: "GET",
|
|
Params: url.Values{
|
|
"indexName": []string{"tix"},
|
|
"docID": []string{"a"},
|
|
},
|
|
Status: http.StatusNotFound,
|
|
ResponseBody: []byte(`no such index 'tix'`),
|
|
},
|
|
{
|
|
Desc: "create alias",
|
|
Handler: aliasHandler,
|
|
Path: "/alias",
|
|
Method: "POST",
|
|
Body: []byte(`{
|
|
"alias": "a1",
|
|
"add": ["ti1"]
|
|
}`),
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok"}`),
|
|
},
|
|
{
|
|
Desc: "create alias invalid json",
|
|
Handler: aliasHandler,
|
|
Path: "/alias",
|
|
Method: "POST",
|
|
Body: []byte(`{`),
|
|
Status: http.StatusBadRequest,
|
|
ResponseMatch: map[string]bool{
|
|
`error parsing alias actions`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "create alias empty",
|
|
Handler: aliasHandler,
|
|
Path: "/alias",
|
|
Method: "POST",
|
|
Body: []byte(``),
|
|
Status: http.StatusBadRequest,
|
|
ResponseMatch: map[string]bool{
|
|
`request body must contain alias actions`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "create alias referring to non-existant index",
|
|
Handler: aliasHandler,
|
|
Path: "/alias",
|
|
Method: "POST",
|
|
Body: []byte(`{
|
|
"alias": "a2",
|
|
"add": ["tix"]
|
|
}`),
|
|
Status: http.StatusBadRequest,
|
|
ResponseMatch: map[string]bool{
|
|
`index named 'tix' does not exist`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "create alias removing from new",
|
|
Handler: aliasHandler,
|
|
Path: "/alias",
|
|
Method: "POST",
|
|
Body: []byte(`{
|
|
"alias": "a2",
|
|
"remove": ["ti1"]
|
|
}`),
|
|
Status: http.StatusBadRequest,
|
|
ResponseMatch: map[string]bool{
|
|
`cannot remove indexes from a new alias`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "create alias same name as index",
|
|
Handler: aliasHandler,
|
|
Path: "/alias",
|
|
Method: "POST",
|
|
Body: []byte(`{
|
|
"alias": "ti1",
|
|
"remove": ["ti1"]
|
|
}`),
|
|
Status: http.StatusBadRequest,
|
|
ResponseMatch: map[string]bool{
|
|
`is not an alias`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "search alias",
|
|
Handler: searchHandler,
|
|
Path: "/a1/search",
|
|
Method: "POST",
|
|
Params: url.Values{
|
|
"indexName": []string{"a1"},
|
|
},
|
|
Body: []byte(`{
|
|
"from": 0,
|
|
"size": 10,
|
|
"query": {
|
|
"fuzziness": 0,
|
|
"prefix_length": 0,
|
|
"field": "body",
|
|
"match": "test"
|
|
}
|
|
}`),
|
|
Status: http.StatusOK,
|
|
ResponseMatch: map[string]bool{
|
|
`"total_hits":1`: true,
|
|
`"id":"a"`: true,
|
|
},
|
|
},
|
|
{
|
|
Desc: "create index to add to alias",
|
|
Handler: createIndexHandler,
|
|
Path: "/create",
|
|
Method: "PUT",
|
|
Params: url.Values{"indexName": []string{"ti6"}},
|
|
Body: []byte("{}"),
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok"}`),
|
|
},
|
|
{
|
|
Desc: "update alias add ti6",
|
|
Handler: aliasHandler,
|
|
Path: "/alias",
|
|
Method: "POST",
|
|
Body: []byte(`{
|
|
"alias": "a1",
|
|
"add": ["ti6"]
|
|
}`),
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok"}`),
|
|
},
|
|
{
|
|
Desc: "update alias add doesnt exist",
|
|
Handler: aliasHandler,
|
|
Path: "/alias",
|
|
Method: "POST",
|
|
Body: []byte(`{
|
|
"alias": "a1",
|
|
"add": ["ti99"]
|
|
}`),
|
|
Status: http.StatusBadRequest,
|
|
ResponseBody: []byte(`error updating alias: index named 'ti99' does not exist`),
|
|
},
|
|
{
|
|
Desc: "update alias remove ti6",
|
|
Handler: aliasHandler,
|
|
Path: "/alias",
|
|
Method: "POST",
|
|
Body: []byte(`{
|
|
"alias": "a1",
|
|
"remove": ["ti6"]
|
|
}`),
|
|
Status: http.StatusOK,
|
|
ResponseBody: []byte(`{"status":"ok"}`),
|
|
},
|
|
{
|
|
Desc: "update alias remove doesnt exist",
|
|
Handler: aliasHandler,
|
|
Path: "/alias",
|
|
Method: "POST",
|
|
Body: []byte(`{
|
|
"alias": "a1",
|
|
"remove": ["ti98"]
|
|
}`),
|
|
Status: http.StatusBadRequest,
|
|
ResponseBody: []byte(`error updating alias: index named 'ti98' does not exist`),
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
record := httptest.NewRecorder()
|
|
req := &http.Request{
|
|
Method: test.Method,
|
|
URL: &url.URL{Path: test.Path},
|
|
Form: test.Params,
|
|
Body: ioutil.NopCloser(bytes.NewBuffer(test.Body)),
|
|
}
|
|
test.Handler.ServeHTTP(record, req)
|
|
if got, want := record.Code, test.Status; got != want {
|
|
t.Errorf("%s: response code = %d, want %d", test.Desc, got, want)
|
|
t.Errorf("%s: response body = %s", test.Desc, record.Body)
|
|
}
|
|
|
|
got := bytes.TrimRight(record.Body.Bytes(), "\n")
|
|
if test.ResponseBody != nil {
|
|
if !reflect.DeepEqual(got, test.ResponseBody) {
|
|
t.Errorf("%s: expected: '%s', got: '%s'", test.Desc, test.ResponseBody, got)
|
|
}
|
|
}
|
|
for pattern, shouldMatch := range test.ResponseMatch {
|
|
didMatch := bytes.Contains(got, []byte(pattern))
|
|
if didMatch != shouldMatch {
|
|
t.Errorf("%s: expected match %t for pattern %s, got %t", test.Desc, shouldMatch, pattern, didMatch)
|
|
t.Errorf("%s: response body was: %s", test.Desc, got)
|
|
}
|
|
}
|
|
}
|
|
}
|