dndex/server/entities_test.go

409 lines
12 KiB
Go

package server
import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"local/dndex/storage/entity"
"net/http"
"net/http/httptest"
"path"
"strings"
"testing"
)
func TestEntities(t *testing.T) {
rest, authit, clean := testREST(t)
defer clean()
t.Run("create+get1+delete+404", func(t *testing.T) {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "myname"
})
w = testEntitiesMethod(t, authit, rest, http.MethodGet, "/"+id, ``)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "myname"
})
if id2 != id {
t.Fatal(id, id2)
}
w = testEntitiesMethod(t, authit, rest, http.MethodDelete, "/"+id, ``)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
w = testEntitiesMethod(t, authit, rest, http.MethodGet, "/"+id, ``)
if w.Code != http.StatusNotFound {
t.Fatal(w.Code)
}
})
t.Run("get1 404", func(t *testing.T) {
w := testEntitiesMethod(t, authit, rest, http.MethodGet, "/abc123", ``)
if w.Code != http.StatusNotFound {
t.Fatal(w.Code)
}
})
t.Run("create+get0", func(t *testing.T) {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"my friend"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
friend := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "my friend"
})
w = testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname", "attachments": {"abc": {}}, "connections": {"`+friend+`": {"relationship": "ghi"}}}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "myname" && len(one.Attachments) == 1 && len(one.Connections) == 1 && one.Connections[friend].Relationship == "ghi"
})
w = testEntitiesMethod(t, authit, rest, http.MethodGet, "/", ``)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
testEntitiesGetNResponse(t, w.Body, func(one shortEntity) bool {
return one.Name == "myname" && one.ID == id
})
})
t.Run("create+replace+get1", func(t *testing.T) {
check := func(one entity.One) bool {
return one.Name == "myname"
}
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id := testEntitiesGetOneResponse(t, w.Body, check)
w = testEntitiesMethod(t, authit, rest, http.MethodPut, "/"+id, `{"name":"newname"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
w = testEntitiesMethod(t, authit, rest, http.MethodGet, "/"+id, ``)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "newname"
})
if id2 != id {
t.Fatal(id, id2)
}
})
t.Run("create+get.title", func(t *testing.T) {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname", "title": "mytitle"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "myname" && one.Title == "mytitle"
})
w = testEntitiesMethod(t, authit, rest, http.MethodGet, "/"+id+"/title", ``)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
testEntitiesGetOneSubResponse(t, w.Body, func(v interface{}) bool {
return fmt.Sprint(v) == "mytitle"
})
})
t.Run("create+update/replace", func(t *testing.T) {
for _, method := range []string{http.MethodPut, http.MethodPatch} {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname", "title": "mytitle"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "myname" && one.Title == "mytitle"
})
w = testEntitiesMethod(t, authit, rest, method, "/"+id, `{"name": "newname"}`)
if w.Code != http.StatusOK {
t.Fatalf("%v: %s", w.Code, w.Body.Bytes())
}
id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "newname" && one.Title == ""
})
if id != id2 {
t.Fatal(id, id2)
}
}
})
t.Run("create+update/replace.name", func(t *testing.T) {
for _, method := range []string{http.MethodPut, http.MethodPatch} {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname", "title": "mytitle"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "myname" && one.Title == "mytitle"
})
w = testEntitiesMethod(t, authit, rest, method, "/"+id+"/name", `"newname"`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "newname" && one.Title == "mytitle"
})
if id != id2 {
t.Fatal(id, id2)
}
}
})
t.Run("create+update/replace.connection.abc.relationship", func(t *testing.T) {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name": "my friend"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
friend := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "my friend"
})
for _, method := range []string{http.MethodPut, http.MethodPatch} {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"connections": {"`+friend+`": {"relationship": "def"}}}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Connections[friend].Relationship == "def"
})
w = testEntitiesMethod(t, authit, rest, method, "/"+id+"/connections/"+friend+"/relationship", `"new"`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Connections[friend].Relationship == "new"
})
if id != id2 {
t.Fatal(id, id2)
}
}
})
t.Run("create+update/replace.connection.abc", func(t *testing.T) {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name": "my friend"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
friend := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "my friend"
})
for _, method := range []string{http.MethodPut, http.MethodPatch} {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"connections": {"`+friend+`": {"relationship": "def"}}}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Connections[friend].Relationship == "def"
})
w = testEntitiesMethod(t, authit, rest, method, "/"+id+"/connections/"+friend, `{"relationship": "new"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Connections[friend].Relationship == "new"
})
if id != id2 {
t.Fatal(id, id2)
}
}
})
t.Run("create+delete.name", func(t *testing.T) {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname", "title": "mytitle"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "myname" && one.Title == "mytitle"
})
w = testEntitiesMethod(t, authit, rest, http.MethodDelete, "/"+id+"/name", ``)
if w.Code != http.StatusOK {
t.Fatalf("%v: %s", w.Code, w.Body.Bytes())
}
id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "" && one.Title == "mytitle"
})
if id != id2 {
t.Fatal(id, id2)
}
})
t.Run("create w connection.abc+delete.connections.abc", func(t *testing.T) {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name": "my friend"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
friend := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "my friend"
})
w = testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name":"myname", "title": "mytitle", "connections": {"`+friend+`": {"relationship": "good"}}}`)
if w.Code != http.StatusOK {
t.Fatalf("%v: %s", w.Code, w.Body.Bytes())
}
id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
friended, ok := one.Connections[friend]
return one.Name == "myname" && one.Title == "mytitle" && ok && friended.Relationship == "good"
})
w = testEntitiesMethod(t, authit, rest, http.MethodDelete, "/"+id+"/connections/"+friend, ``)
if w.Code != http.StatusOK {
t.Fatalf("%v: %s", w.Code, w.Body.Bytes())
}
id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "myname" && one.Title == "mytitle" && len(one.Connections) == 0
})
if id != id2 {
t.Fatal(id, id2)
}
})
t.Run("create w connection.abc.relationship+delete.connections.abc.relationship", func(t *testing.T) {
w := testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"name": "my friend"}`)
if w.Code != http.StatusOK {
t.Fatal(w.Code)
}
friend := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return one.Name == "my friend"
})
w = testEntitiesMethod(t, authit, rest, http.MethodPost, "/", `{"connections": {"`+friend+`": {"relationship": "good"}}}`)
if w.Code != http.StatusOK {
t.Fatalf("%v: %s", w.Code, w.Body.Bytes())
}
id := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
friended, ok := one.Connections[friend]
return ok && friended.Relationship == "good"
})
w = testEntitiesMethod(t, authit, rest, http.MethodDelete, "/"+id+"/connections/"+friend+"/relationship", ``)
if w.Code != http.StatusOK {
t.Fatalf("%v: %s", w.Code, w.Body.Bytes())
}
id2 := testEntitiesGetOneResponse(t, w.Body, func(one entity.One) bool {
return len(one.Connections) == 1 && one.Connections[friend].Relationship == ""
})
if id != id2 {
t.Fatal(id, id2)
}
})
}
func testEntitiesMethod(t *testing.T, authit func(*http.Request), rest *REST, method, p, body string) *httptest.ResponseRecorder {
r := httptest.NewRequest(method, p, strings.NewReader(body))
if !strings.HasPrefix(r.URL.Path, "/entities") {
r.URL.Path = path.Join("/entities", r.URL.Path)
}
w := httptest.NewRecorder()
authit(r)
rest.scoped(rest.shift(rest.entities))(w, r)
return w
}
func testEntitiesGetNResponse(t *testing.T, body io.Reader, check func(shortEntity) bool) {
var resp map[string][]shortEntity
if err := json.NewDecoder(body).Decode(&resp); err != nil {
t.Fatal(err)
}
if len(resp) != 1 {
t.Fatal("excess found in db")
}
for k := range resp {
contents := resp[k]
if len(contents) < 1 {
t.Fatal(len(contents))
}
for i := range contents {
if check(contents[i]) {
return
}
}
t.Fatal(contents)
}
}
func testEntitiesGetOneResponse(t *testing.T, body io.Reader, check func(entity.One) bool) string {
b, err := ioutil.ReadAll(body)
if err != nil {
t.Fatal(err)
}
var resp map[string]entity.One
if err := json.Unmarshal(b, &resp); err != nil {
t.Fatalf("%v: %s", err, b)
}
if len(resp) != 1 {
t.Fatal(len(resp))
}
for k := range resp {
one := resp[k]
if one.ID != k {
t.Fatal(k, one.ID)
}
if one.Modified == 0 {
t.Fatal(one.Modified)
}
if !check(one) {
t.Fatal(one)
}
return one.ID
}
panic("somehow no keys found")
}
func testEntitiesGetOneSubResponse(t *testing.T, body io.Reader, check func(interface{}) bool) {
b, err := ioutil.ReadAll(body)
if err != nil {
t.Fatal(err)
}
var resp map[string]interface{}
if err := json.Unmarshal(b, &resp); err != nil {
t.Fatalf("%v: %s", err, b)
}
if len(resp) != 1 {
t.Fatal(len(resp))
}
for k := range resp {
one := resp[k]
if !check(one) {
t.Fatal(one)
}
return
}
panic("somehow no keys found")
}