package view import ( "bytes" "context" "encoding/json" "fmt" "io/ioutil" "local/dndex/config" "local/dndex/storage" "local/dndex/storage/entity" "net/http" "net/http/httptest" "os" "strings" "testing" "time" "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.NewGraph() 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) { 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) { 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) { 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)) } 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("put fake", func(t *testing.T) { 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) { 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) { 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) { 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("delete real", func(t *testing.T) { 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 fake", func(t *testing.T) { 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("patch fake", func(t *testing.T) { 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[2] t.Run("patch real", func(t *testing.T) { 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.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) { 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) { 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) }) } func fillDB(t *testing.T, g storage.Graph) []entity.One { ones := make([]entity.One, 5) for i := range ones { ones[i] = randomOne() if i > 0 { ones[i].Connections[ones[i-1].Name] = entity.One{ Name: ones[i-1].Name, 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{}, } }