955 lines
26 KiB
Go
955 lines
26 KiB
Go
package view
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"local/dndex/config"
|
|
"local/dndex/storage"
|
|
"local/dndex/storage/entity"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"os"
|
|
"path"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/buger/jsonparser"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
func TestWho(t *testing.T) {
|
|
os.Args = os.Args[:1]
|
|
if _, ok := os.LookupEnv("DBURI"); !ok {
|
|
f, err := ioutil.TempFile(os.TempDir(), "pattern*")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
f.Close()
|
|
defer os.Remove(f.Name())
|
|
os.Setenv("DBURI", f.Name())
|
|
}
|
|
|
|
t.Logf("config: %+v", config.New())
|
|
|
|
g := storage.NewRateLimitedGraph()
|
|
ones := fillDB(t, g)
|
|
want := ones[len(ones)-1]
|
|
|
|
reset := func() {
|
|
if err := g.Delete(context.TODO(), "col", map[string]string{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ones = fillDB(t, g)
|
|
want = ones[len(ones)-1]
|
|
}
|
|
|
|
handler := jsonHandler(g)
|
|
|
|
t.Log(handler, want)
|
|
|
|
t.Run("get no namespace is 404", func(t *testing.T) {
|
|
reset()
|
|
iwant := want
|
|
r := httptest.NewRequest(http.MethodGet, "/who?id="+iwant.Name, nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
})
|
|
|
|
t.Run("get fake", func(t *testing.T) {
|
|
reset()
|
|
iwant := want
|
|
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col&id=FAKER"+iwant.Name, nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
})
|
|
|
|
t.Run("get real", func(t *testing.T) {
|
|
reset()
|
|
iwant := want
|
|
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col&id="+iwant.Name, nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o := entity.One{}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if fmt.Sprint(o) == fmt.Sprint(iwant) {
|
|
t.Fatal(o, iwant)
|
|
}
|
|
if len(o.Connections) != len(iwant.Connections) {
|
|
t.Fatal(len(o.Connections), len(iwant.Connections))
|
|
}
|
|
if len(o.Attachments) != len(iwant.Attachments) {
|
|
t.Fatal(len(o.Attachments), len(iwant.Attachments))
|
|
}
|
|
for k := range o.Attachments {
|
|
if _, ok := iwant.Attachments[k]; !ok {
|
|
t.Fatal(k, ok)
|
|
}
|
|
if o.Attachments[k] == iwant.Attachments[k] {
|
|
t.Fatal(k, o.Attachments[k], iwant.Attachments[k])
|
|
}
|
|
if !strings.HasSuffix(o.Attachments[k], path.Join(config.New().FilePrefix, "col", iwant.Attachments[k])) {
|
|
t.Fatal(k, o.Attachments[k], iwant.Attachments[k])
|
|
}
|
|
}
|
|
iwant.Attachments = o.Attachments
|
|
iwant.Connections = o.Connections
|
|
iwant.Modified = 0
|
|
o.Modified = 0
|
|
if fmt.Sprint(o) != fmt.Sprint(iwant) {
|
|
t.Fatalf("after resolving connections and modified, iwant and got differ: \nwant %+v\n got %+v", iwant, o)
|
|
}
|
|
b, _ := json.MarshalIndent(o, "", " ")
|
|
t.Logf("POST GET:\n%s", b)
|
|
})
|
|
|
|
t.Run("get real md", func(t *testing.T) {
|
|
reset()
|
|
iwant := want
|
|
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col&md&id="+iwant.Name, nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o := entity.One{}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if fmt.Sprint(o) == fmt.Sprint(iwant) {
|
|
t.Fatal(o, iwant)
|
|
}
|
|
if len(o.Connections) != len(iwant.Connections) {
|
|
t.Fatal(len(o.Connections), len(iwant.Connections))
|
|
}
|
|
iwant.Connections = o.Connections
|
|
iwant.Attachments = o.Attachments
|
|
iwant.Modified = 0
|
|
o.Modified = 0
|
|
if fmt.Sprint(o) != fmt.Sprint(iwant) {
|
|
t.Fatalf("after resolving connections and modified, iwant and got differ: \nwant %+v\n got %+v", iwant, o)
|
|
}
|
|
var paragraphContent string
|
|
if v, err := jsonparser.GetString(w.Body.Bytes(), "md"); err != nil {
|
|
t.Fatal(err)
|
|
} else if err := xml.Unmarshal([]byte(v), ¶graphContent); err != nil {
|
|
t.Fatal(err)
|
|
} else if paragraphContent != iwant.Text {
|
|
t.Fatal(iwant.Text, paragraphContent, v)
|
|
}
|
|
b, _ := json.MarshalIndent(o, "", " ")
|
|
t.Logf("POST GET:\n%s", b)
|
|
})
|
|
|
|
t.Run("get real light", func(t *testing.T) {
|
|
reset()
|
|
iwant := want
|
|
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col&light&id="+iwant.Name, nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o := entity.One{}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if fmt.Sprint(o) == fmt.Sprint(iwant) {
|
|
t.Fatal(o, iwant)
|
|
}
|
|
if len(o.Connections) != len(iwant.Connections) {
|
|
t.Fatal(len(o.Connections), len(iwant.Connections))
|
|
}
|
|
iwant.Connections = o.Connections
|
|
iwant.Attachments = o.Attachments
|
|
iwant.Modified = 0
|
|
o.Modified = 0
|
|
if fmt.Sprint(o) != fmt.Sprint(iwant) {
|
|
t.Fatalf("after resolving connections and modified, iwant and got differ: \nwant %+v\n got %+v", iwant, o)
|
|
}
|
|
for _, connection := range iwant.Connections {
|
|
if len(connection.Connections) != 0 {
|
|
t.Fatal(connection)
|
|
}
|
|
}
|
|
b, _ := json.MarshalIndent(o, "", " ")
|
|
t.Logf("POST GET:\n%s", b)
|
|
})
|
|
|
|
t.Run("get real but case is wrong", func(t *testing.T) {
|
|
reset()
|
|
iwant := want
|
|
iwantName := strings.ToUpper(iwant.Name)
|
|
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col&id="+iwantName, nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o := entity.One{}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if fmt.Sprint(o) == fmt.Sprint(iwant) {
|
|
t.Fatal(o, iwant)
|
|
}
|
|
if len(o.Connections) != len(iwant.Connections) {
|
|
t.Fatal(len(o.Connections), len(iwant.Connections))
|
|
}
|
|
iwant.Connections = o.Connections
|
|
iwant.Attachments = o.Attachments
|
|
iwant.Modified = 0
|
|
o.Modified = 0
|
|
if fmt.Sprint(o) != fmt.Sprint(iwant) {
|
|
t.Fatalf("after resolving connections and modified, iwant and got differ: \nwant %+v\n got %+v", iwant, o)
|
|
}
|
|
b, _ := json.MarshalIndent(o, "", " ")
|
|
t.Logf("POST GET:\n%s", b)
|
|
})
|
|
|
|
t.Run("get sorted type asc/desc", func(t *testing.T) {
|
|
for _, order := range []string{"1", "-1"} {
|
|
reset()
|
|
want := ones[len(ones)-1]
|
|
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col&light&sort=type&order="+order+"&id="+want.Name, nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o := entity.One{}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(o.Connections) < 5 {
|
|
t.Fatal(len(o.Connections))
|
|
}
|
|
if len(o.Connections) != len(want.Connections) {
|
|
t.Fatal(len(want.Connections), len(o.Connections))
|
|
}
|
|
titles := []string{}
|
|
for _, v := range want.Connections {
|
|
if len(v.Type) == 0 {
|
|
t.Fatal(v.Type)
|
|
}
|
|
titles = append(titles, v.Type)
|
|
}
|
|
sort.Strings(titles)
|
|
if order == "-1" {
|
|
for i := 0; i < len(titles)/2; i++ {
|
|
tmp := titles[len(titles)-1-i]
|
|
titles[len(titles)-1-i] = titles[i]
|
|
titles[i] = tmp
|
|
}
|
|
}
|
|
pattern := strings.Join(titles, ".*")
|
|
pattern = strings.Replace(pattern, "-", ".", -1)
|
|
if !regexp.MustCompile(pattern).Match(bytes.Replace(w.Body.Bytes(), []byte("\n"), []byte(" "), -1)) {
|
|
t.Fatal(order, pattern, string(w.Body.Bytes()))
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("put fake", func(t *testing.T) {
|
|
reset()
|
|
iwant := want
|
|
r := httptest.NewRequest(http.MethodPut, "/who?namespace=col&id=FAKER"+iwant.Name, strings.NewReader(`{"title":"this should fail to find someone"}`))
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
})
|
|
|
|
t.Run("put real", func(t *testing.T) {
|
|
reset()
|
|
iwant := want
|
|
r := httptest.NewRequest(http.MethodPut, "/who?namespace=col&id="+iwant.Name, strings.NewReader(`{"title":"this should work"}`))
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o := entity.One{}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(o.Connections) != len(iwant.Connections) {
|
|
t.Fatalf("wrong number of connections returned: want %v, got %v", len(iwant.Connections), len(o.Connections))
|
|
}
|
|
if o.Title != "this should work" {
|
|
t.Fatalf("failed to PUT a new title: %+v", o)
|
|
}
|
|
b, _ := json.MarshalIndent(o, "", " ")
|
|
t.Logf("POST PUT:\n%s", b)
|
|
})
|
|
|
|
t.Run("post exists", func(t *testing.T) {
|
|
reset()
|
|
iwant := want
|
|
iwant.Name = ""
|
|
r := httptest.NewRequest(http.MethodPost, "/who?namespace=col&id="+want.Name, strings.NewReader(`{"title":"this should fail to insert"}`))
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusConflict {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
})
|
|
|
|
t.Run("post real", func(t *testing.T) {
|
|
reset()
|
|
iwant := want
|
|
iwant.Name = ""
|
|
b, err := json.Marshal(iwant)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := httptest.NewRequest(http.MethodPost, "/who?namespace=col&id=NEWBIE"+want.Name, bytes.NewReader(b))
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o := entity.One{}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(o.Connections) != len(iwant.Connections) {
|
|
t.Fatalf("wrong number of connections returned: want %v, got %v", len(iwant.Connections), len(o.Connections))
|
|
}
|
|
if o.Name != "NEWBIE"+want.Name {
|
|
t.Fatalf("failed to POST specified name: %+v", o)
|
|
}
|
|
b, _ = json.MarshalIndent(o, "", " ")
|
|
t.Logf("POST POST:\n%s", b)
|
|
})
|
|
|
|
t.Run("post real with spaces, #, and special chars in name", func(t *testing.T) {
|
|
reset()
|
|
want.Name = "hello world #1 e ę"
|
|
want.Connections = nil
|
|
b, err := json.Marshal(want)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
url := url.URL{
|
|
Scheme: "http",
|
|
Host: "me.me.me",
|
|
Path: "/who",
|
|
RawQuery: (url.Values{"namespace": []string{"col"}, "id": []string{want.Name}}).Encode(),
|
|
}
|
|
r := httptest.NewRequest(http.MethodPost, url.String(), bytes.NewReader(b))
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o := entity.One{}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if o.Name != want.Name {
|
|
t.Fatalf("failed to POST specified name: %+v", o)
|
|
}
|
|
b, _ = json.MarshalIndent(o, "", " ")
|
|
t.Logf("POST POST:\n%q\n%s", url.String(), b)
|
|
})
|
|
|
|
t.Run("delete real", func(t *testing.T) {
|
|
reset()
|
|
r := httptest.NewRequest(http.MethodDelete, "/who?namespace=col&id=NEWBIE"+want.Name, nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
r = httptest.NewRequest(http.MethodGet, "/who?namespace=col&id=NEWBIE"+want.Name, nil)
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
})
|
|
|
|
t.Run("delete real with %20%23 (' #')", func(t *testing.T) {
|
|
reset()
|
|
|
|
want.Name = "hello world #4"
|
|
want.Connections = nil
|
|
b, err := json.Marshal(want)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
url := url.URL{
|
|
Scheme: "http",
|
|
Host: "me.me.me",
|
|
Path: "/who",
|
|
RawQuery: "namespace=col&id=hello%20world%20%234",
|
|
}
|
|
t.Logf("using url %s", url.String())
|
|
r := httptest.NewRequest(http.MethodPost, url.String(), bytes.NewReader(b))
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("(%d) %s", w.Code, w.Body.Bytes())
|
|
}
|
|
|
|
r = httptest.NewRequest(http.MethodDelete, url.String(), nil)
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
|
|
r = httptest.NewRequest(http.MethodGet, url.String(), nil)
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
|
|
t.Logf("can post+del %s", url.String())
|
|
})
|
|
|
|
t.Run("delete real with special chars", func(t *testing.T) {
|
|
reset()
|
|
|
|
want.Name = "hello world #1 e ę"
|
|
want.Connections = nil
|
|
b, err := json.Marshal(want)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
url := url.URL{
|
|
Scheme: "http",
|
|
Host: "me.me.me",
|
|
Path: "/who",
|
|
RawQuery: (url.Values{"namespace": []string{"col"}, "id": []string{want.Name}}).Encode(),
|
|
}
|
|
r := httptest.NewRequest(http.MethodPost, url.String(), bytes.NewReader(b))
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatal(w.Code)
|
|
}
|
|
|
|
r = httptest.NewRequest(http.MethodDelete, url.String(), nil)
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
|
|
r = httptest.NewRequest(http.MethodGet, url.String(), nil)
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusNotFound {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
|
|
t.Logf("can post+del %s", url.String())
|
|
})
|
|
|
|
t.Run("delete fake", func(t *testing.T) {
|
|
reset()
|
|
r := httptest.NewRequest(http.MethodDelete, "/who?namespace=col&id=FAKER"+want.Name, nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
})
|
|
|
|
t.Run("delete regexp should be sanitized", func(t *testing.T) {
|
|
reset()
|
|
r := httptest.NewRequest(http.MethodDelete, "/who?namespace=col&id=.*", nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
|
|
r = httptest.NewRequest(http.MethodTrace, "/who?namespace=col", nil)
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
var v []string
|
|
if err := json.Unmarshal(w.Body.Bytes(), &v); err != nil {
|
|
t.Fatalf("%v: %s", err, w.Body.Bytes())
|
|
}
|
|
if len(v) < 5 {
|
|
t.Fatal(len(v))
|
|
}
|
|
t.Logf("%+v", v)
|
|
})
|
|
|
|
t.Run("patch fake", func(t *testing.T) {
|
|
reset()
|
|
r := httptest.NewRequest(http.MethodPatch, "/who?namespace=col&id=FAKER"+want.Name, nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code == http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
})
|
|
|
|
want = ones[4]
|
|
t.Run("patch real against existing", func(t *testing.T) {
|
|
reset()
|
|
from := ones[4]
|
|
push := ones[10].Peer()
|
|
push.Relationship = "spawn"
|
|
b, err := json.Marshal(push)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := httptest.NewRequest(http.MethodPatch, "/who?namespace=col&id="+from.Name, bytes.NewReader(b))
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
got := entity.One{}
|
|
if err := json.NewDecoder(w.Body).Decode(&got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if fmt.Sprint(got) == fmt.Sprint(from) {
|
|
t.Fatal(got)
|
|
}
|
|
got.Modified = 0
|
|
from.Modified = 0
|
|
if len(got.Connections) != len(from.Connections)+1 {
|
|
t.Fatal(len(got.Connections), len(from.Connections)+1)
|
|
}
|
|
gotPush, ok := got.Connections[push.Name]
|
|
if !ok {
|
|
t.Fatal("cant find pushed connection from remote")
|
|
}
|
|
got.Connections = nil
|
|
from.Connections = nil
|
|
got.Attachments = nil
|
|
from.Attachments = nil
|
|
if fmt.Sprint(got) != fmt.Sprint(from) {
|
|
t.Fatalf("without connections and modified, got != want: want \n %+v, got \n %+v", from, got)
|
|
}
|
|
pushPeer := push.Peer()
|
|
gotPush = gotPush.Peer()
|
|
pushPeer.Modified = 0
|
|
gotPush.Modified = 0
|
|
if fmt.Sprint(gotPush) != fmt.Sprint(pushPeer) {
|
|
t.Fatal("\n", gotPush, "\n", pushPeer)
|
|
}
|
|
t.Logf("%s", w.Body.Bytes())
|
|
})
|
|
|
|
want = ones[2]
|
|
t.Run("patch real", func(t *testing.T) {
|
|
reset()
|
|
iwant := want
|
|
iwant.Relationship = "spawn"
|
|
iwant.Name = "child of " + want.Name
|
|
b, err := json.Marshal(iwant)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := httptest.NewRequest(http.MethodPatch, "/who?namespace=col&id="+want.Name, bytes.NewReader(b))
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
got := entity.One{}
|
|
if err := json.NewDecoder(w.Body).Decode(&got); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if fmt.Sprint(got) == fmt.Sprint(entity.One{}) {
|
|
t.Fatal(got)
|
|
}
|
|
iwant = want
|
|
if len(got.Connections) != len(want.Connections)+1 {
|
|
t.Fatal(len(got.Connections), len(want.Connections)+1)
|
|
}
|
|
got.Connections = want.Connections
|
|
got.Attachments = want.Attachments
|
|
got.Modified = 0
|
|
want.Modified = 0
|
|
if fmt.Sprint(got) != fmt.Sprint(want) {
|
|
t.Fatalf("without connections and modified, got != want: want \n %+v, got \n %+v", want, got)
|
|
}
|
|
t.Logf("%s", w.Body.Bytes())
|
|
})
|
|
|
|
t.Run("trace fake", func(t *testing.T) {
|
|
reset()
|
|
r := httptest.NewRequest(http.MethodTrace, "/who?namespace=notcol", nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
})
|
|
|
|
t.Run("trace real", func(t *testing.T) {
|
|
reset()
|
|
r := httptest.NewRequest(http.MethodTrace, "/who?namespace=col", nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
var v []string
|
|
if err := json.Unmarshal(w.Body.Bytes(), &v); err != nil {
|
|
t.Fatalf("%v: %s", err, w.Body.Bytes())
|
|
}
|
|
if len(v) < 5 {
|
|
t.Fatal(len(v))
|
|
}
|
|
t.Logf("%+v", v)
|
|
})
|
|
|
|
t.Run("get without id == trace real", func(t *testing.T) {
|
|
reset()
|
|
r := httptest.NewRequest(http.MethodGet, "/who?namespace=col", nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
var v []string
|
|
if err := json.Unmarshal(w.Body.Bytes(), &v); err != nil {
|
|
t.Fatalf("%v: %s", err, w.Body.Bytes())
|
|
}
|
|
if len(v) < 5 {
|
|
t.Fatal(len(v))
|
|
}
|
|
t.Logf("%+v", v)
|
|
})
|
|
|
|
t.Run("trace real sorted asc/desc name", func(t *testing.T) {
|
|
reset()
|
|
for _, order := range []string{"1", "-1"} {
|
|
r := httptest.NewRequest(http.MethodTrace, "/who?namespace=col&sort=name&order="+order, nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
var v []string
|
|
if err := json.Unmarshal(w.Body.Bytes(), &v); err != nil {
|
|
t.Fatalf("%v: %s", err, w.Body.Bytes())
|
|
}
|
|
if len(v) < 5 {
|
|
t.Fatal(len(v))
|
|
}
|
|
for i := range v {
|
|
if i == 0 {
|
|
continue
|
|
}
|
|
if (v[i] < v[i-1] && order == "1") || (v[i] > v[i-1] && order == "-1") {
|
|
t.Fatalf("not sorted: %s: %+v", order, v)
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("delete connection 1 of 0 noop but ok", func(t *testing.T) {
|
|
reset()
|
|
want := ones[0]
|
|
if len(want.Connections) > 0 {
|
|
t.Fatal(want)
|
|
}
|
|
r := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/who?namespace=col&id=%s&connection=%s", want.Name, "fake"), nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
|
|
r = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/who?namespace=col&id=%s", want.Name), nil)
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o := entity.One{}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(o.Connections) > 0 {
|
|
t.Fatal(o.Connections)
|
|
}
|
|
})
|
|
|
|
t.Run("delete connection 1 of 1 ok", func(t *testing.T) {
|
|
reset()
|
|
deleted := want.Peers()[0]
|
|
r := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/who?namespace=col&id=%s&connection=%s", want.Name, deleted), nil)
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
|
|
r = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/who?namespace=col&id=%s", want.Name), nil)
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o := entity.One{}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, ok := o.Connections[deleted]; ok {
|
|
t.Fatal(deleted, o.Connections)
|
|
}
|
|
})
|
|
|
|
t.Run("delete connection 1 of 4 ok", func(t *testing.T) {
|
|
reset()
|
|
|
|
want := ones[0]
|
|
|
|
put := entity.One{
|
|
Name: want.Name,
|
|
Connections: map[string]entity.One{
|
|
ones[1].Name: ones[1].Peer(),
|
|
ones[2].Name: ones[2].Peer(),
|
|
ones[3].Name: ones[3].Peer(),
|
|
ones[4].Name: ones[4].Peer(),
|
|
},
|
|
}
|
|
|
|
b, err := json.Marshal(put)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
r := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/who?namespace=col&id=%s", want.Name), bytes.NewReader(b))
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
|
|
want.Connections = put.Connections
|
|
r = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/who?namespace=col&id=%s&light", want.Name), nil)
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o := entity.One{}
|
|
if err := json.NewDecoder(w.Body).Decode(&o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
o.Modified = 0
|
|
want.Modified = 0
|
|
if len(o.Connections) != len(put.Connections) {
|
|
t.Fatal(o.Connections)
|
|
}
|
|
for k := range o.Connections {
|
|
a := want.Connections[k]
|
|
a.Modified = 0
|
|
want.Connections[k] = a
|
|
b := o.Connections[k]
|
|
b.Modified = 0
|
|
o.Connections[k] = b
|
|
}
|
|
o.Attachments = want.Attachments
|
|
if fmt.Sprint(o) != fmt.Sprint(want) {
|
|
t.Fatalf("GET put != expected: want:\n%+v, got \n%+v", want, o)
|
|
}
|
|
|
|
forget := want.Peers()[0]
|
|
r = httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/who?namespace=col&id=%s&connection=%s", want.Name, forget), nil)
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
|
|
r = httptest.NewRequest(http.MethodGet, fmt.Sprintf("/who?namespace=col&id=%s", want.Name), nil)
|
|
w = httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("%d: %s", w.Code, w.Body.Bytes())
|
|
}
|
|
o = entity.One{}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &o); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(o.Connections) != len(put.Connections)-1 {
|
|
t.Fatalf("should've deleted %q but got %+v", forget, o.Connections)
|
|
}
|
|
})
|
|
}
|
|
|
|
func fillDB(t *testing.T, g storage.RateLimitedGraph) []entity.One {
|
|
ones := make([]entity.One, 13)
|
|
for i := range ones {
|
|
ones[i] = randomOne()
|
|
for j := 0; j < i; j++ {
|
|
ones[i].Connections[ones[j].Name] = entity.One{
|
|
Name: ones[j].Name,
|
|
Type: ones[j].Type,
|
|
Relationship: ":D",
|
|
}
|
|
}
|
|
}
|
|
for i := range ones {
|
|
if err := g.Insert(context.TODO(), "col", ones[i]); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if results, err := g.List(context.TODO(), "col", ones[i].Name); err != nil {
|
|
t.Fatal(err)
|
|
} else if len(results) != 1 {
|
|
t.Fatal(len(results))
|
|
} else if len(results[0].Connections) != len(ones[i].Connections) {
|
|
t.Fatal(len(results[0].Connections), len(ones[i].Connections))
|
|
} else if len(results[0].Connections) > 0 {
|
|
for k, v := range results[0].Connections {
|
|
if k == "" || v.Name == "" {
|
|
t.Fatalf("name is gone: %q:%+v", k, v)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ones
|
|
}
|
|
|
|
func randomOne() entity.One {
|
|
return entity.One{
|
|
Name: "name-" + uuid.New().String()[:5],
|
|
Type: "type-" + uuid.New().String()[:5],
|
|
Title: "titl-" + uuid.New().String()[:5],
|
|
Text: "text-" + uuid.New().String()[:5],
|
|
Modified: time.Now().UnixNano(),
|
|
Connections: map[string]entity.One{},
|
|
Attachments: map[string]string{
|
|
uuid.New().String()[:5]: uuid.New().String()[:5],
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestSortOnes(t *testing.T) {
|
|
oneA := entity.One{Name: "A", Title: "c", Modified: 2}
|
|
oneB := entity.One{Name: "B", Title: "b", Modified: 1}
|
|
oneC := entity.One{Name: "C", Title: "a", Modified: 3}
|
|
|
|
cases := map[string]struct {
|
|
ones []entity.One
|
|
sort string
|
|
order string
|
|
test func(entity.One, entity.One) bool
|
|
}{
|
|
"nothing to sort": {},
|
|
"all the same": {
|
|
ones: []entity.One{oneA, oneA, oneA},
|
|
test: func(a, b entity.One) bool { return fmt.Sprint(a) == fmt.Sprint(b) },
|
|
},
|
|
"default: modified desc, but already ordered": {
|
|
ones: []entity.One{oneC, oneB, oneA},
|
|
test: func(a, b entity.One) bool { return a.Modified >= b.Modified },
|
|
},
|
|
"default: modified desc": {
|
|
ones: []entity.One{oneA, oneB, oneC},
|
|
test: func(a, b entity.One) bool { return a.Modified >= b.Modified },
|
|
},
|
|
"default: modified desc, custom entries": {
|
|
ones: []entity.One{
|
|
entity.One{Modified: 2},
|
|
entity.One{Modified: 5},
|
|
entity.One{Modified: 7},
|
|
entity.One{Modified: 3},
|
|
entity.One{Modified: 4},
|
|
entity.One{Modified: 1},
|
|
entity.One{Modified: 6},
|
|
},
|
|
test: func(a, b entity.One) bool { return a.Modified >= b.Modified },
|
|
},
|
|
"default=modified set=desc": {
|
|
ones: []entity.One{oneA, oneB, oneC},
|
|
order: "-1",
|
|
test: func(a, b entity.One) bool { return a.Modified >= b.Modified },
|
|
},
|
|
"set=modified default=desc": {
|
|
ones: []entity.One{oneA, oneB, oneC},
|
|
sort: entity.Modified,
|
|
test: func(a, b entity.One) bool { return a.Modified >= b.Modified },
|
|
},
|
|
"set=modified set=asc": {
|
|
ones: []entity.One{oneA, oneB, oneC},
|
|
sort: entity.Modified,
|
|
order: "1",
|
|
test: func(a, b entity.One) bool { return a.Modified <= b.Modified },
|
|
},
|
|
"set=title set=desc": {
|
|
ones: []entity.One{oneA, oneB, oneC},
|
|
sort: entity.Title,
|
|
order: "-1",
|
|
test: func(a, b entity.One) bool { return a.Title >= b.Title },
|
|
},
|
|
"set=title set=asc": {
|
|
ones: []entity.One{oneA, oneB, oneC},
|
|
sort: entity.Title,
|
|
order: "1",
|
|
test: func(a, b entity.One) bool { return a.Title <= b.Title },
|
|
},
|
|
"set=name set=desc": {
|
|
ones: []entity.One{oneA, oneB, oneC},
|
|
sort: entity.Name,
|
|
order: "-1",
|
|
test: func(a, b entity.One) bool { return a.Name >= b.Name },
|
|
},
|
|
"set=name set=asc": {
|
|
ones: []entity.One{oneA, oneB, oneC},
|
|
sort: entity.Name,
|
|
order: "1",
|
|
test: func(a, b entity.One) bool { return a.Name <= b.Name },
|
|
},
|
|
}
|
|
|
|
for name, d := range cases {
|
|
c := d
|
|
t.Run(name, func(t *testing.T) {
|
|
q := url.Values{}
|
|
q.Set("sort", c.sort)
|
|
q.Set("order", c.order)
|
|
url := url.URL{
|
|
Path: "/",
|
|
RawQuery: q.Encode(),
|
|
}
|
|
r := httptest.NewRequest("GET", url.String(), nil)
|
|
ones := sortOnes(c.ones, r)
|
|
for i := range ones {
|
|
if i == 0 {
|
|
continue
|
|
}
|
|
if ok := c.test(ones[i-1], ones[i]); !ok {
|
|
t.Fatal(ok, ones[i-1], ones[i])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|