package main import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "net/url" "path" "strings" "testing" ) func TestServerRoutes(t *testing.T) { server := NewServer(t.TempDir()) if err := server.Routes(); err != nil { t.Fatal(err) } if err := ensureAndWrite(path.Join(server.root, "ui", "files.ctmpl"), []byte(`{{ define "files" }}{{ template "_import" }}HI FROM FILES{{ end }}`)); err != nil { t.Fatal(err) } else if err := ensureAndWrite(path.Join(server.root, "ui", "search.ctmpl"), []byte(`{{ define "search" }}{{ template "_import" }}HI FROM SEARCH{{ end }}`)); err != nil { t.Fatal(err) } else if err := ensureAndWrite(path.Join(server.root, "ui", "templates", "_import.ctmpl"), []byte(`{{ define "_import" }}HI FROM IMPORT{{ end }}`)); err != nil { t.Fatal(err) } if err := ensureAndWrite(server.diskMediaPath("id"), []byte("hi")); err != nil { t.Fatal(err) } ensureAndWrite(server.diskMediaPath("delid"), []byte("hi")) tree := server.tree() leaf, _ := NewLeaf("", "getfid body") if err := tree.Put(NewID("getfid"), leaf); err != nil { t.Fatal(err) } leaf, _ = NewLeaf("putfid title", "initial putfid body") tree.Put(NewID("putfid"), leaf) leaf, _ = NewLeaf("delfid title", "delfid body") tree.Put(NewID("delfid"), leaf) t.Log(tree.GetRoot()) ensureAndWrite(path.Join(server.root, "index.html"), []byte("mom")) cases := map[string]struct { path string method string body string want string }{ "v0: /: get": { path: "/", method: http.MethodGet, want: "/ui/files", }, "v0: search: get": { path: "/api/v0/search?q=getf%20bod", method: http.MethodGet, want: `["getfid"]`, }, "v0: tree: get": { path: "/api/v0/tree", method: http.MethodGet, want: `"Title":"Untitled",`, }, "v0: media: post": { path: "/api/v0/media", method: http.MethodPost, want: `{"data":{"filePath":"/api/v0/media/`, }, "v0: media id: del": { path: "/api/v0/media/delid", method: http.MethodDelete, }, "v0: media id: get": { path: "/api/v0/media/id", method: http.MethodGet, want: "hi", }, "v0: files: post": { path: "/api/v0/files", method: http.MethodPost, }, "v0: files id: get": { path: "/api/v0/files/getfid", method: http.MethodGet, want: "getfid body", }, "v0: files id: put": { path: "/api/v0/files/putfid", method: http.MethodPut, body: "putfid body", }, "v0: files id: del": { path: "/api/v0/files/delfid", method: http.MethodDelete, }, "v0: /: redir": { path: "/", method: http.MethodGet, want: "/ui/files", }, "v0: /ui/: redir": { path: "/ui/", method: http.MethodGet, want: "/ui/files", }, "v0: /ui: redir": { path: "/ui", method: http.MethodGet, want: "/ui/files", }, "v0: /ui/search": { path: "/ui/search", method: http.MethodGet, }, "v0: /ui/search?q=abc": { path: "/ui/search?q=abc", method: http.MethodGet, }, "v0: /ui/files/getfid": { path: "/ui/files/getfid", method: http.MethodGet, }, "v0: /ui/files": { path: "/ui/files", method: http.MethodGet, }, } for name, d := range cases { c := d t.Run(name, func(t *testing.T) { r := httptest.NewRequest(c.method, c.path, strings.NewReader(c.body)) w := httptest.NewRecorder() server.ServeHTTP(w, r) if w.Code == http.StatusNotFound { t.Fatal(w) } if w.Code >= 400 { t.Fatal(w) } if len(c.want) > 0 && !strings.Contains(string(w.Body.Bytes()), c.want) { t.Fatal(w) } t.Logf("%s %s (%+v) => %s", c.method, c.path, w.Header(), w.Body.Bytes()) }) } } func TestServerPutTreeGetFile(t *testing.T) { server := NewServer(t.TempDir()) if err := server.Routes(); err != nil { t.Fatal(err) } server.tree().Put(NewID("my pid"), Leaf{}) var id string t.Run("put to create an id", func(t *testing.T) { r := httptest.NewRequest(http.MethodPut, "/my%20pid/my-put-id", strings.NewReader("body")) r.Header.Set("Title", "my put title") w := httptest.NewRecorder() if err := server.apiV0FilesIDPutHandler(w, r); err != nil { t.Fatal(err) } t.Logf("%s", w.Body.Bytes()) if w.Code != http.StatusOK { t.Fatal(w) } var resp struct { Data struct { FilePath string `json:"filePath"` } `json:"data"` } if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { t.Fatal(err) } if path.Base(resp.Data.FilePath) != "my-put-id" { t.Fatal(resp.Data.FilePath) } }) t.Run("post", func(t *testing.T) { r := httptest.NewRequest(http.MethodPost, "/my%20pid", strings.NewReader("body")) r.Header.Set("Title", "my title") w := httptest.NewRecorder() if err := server.apiV0FilesHandler(w, r); err != nil { t.Fatal(err) } t.Logf("%s", w.Body.Bytes()) if w.Code != http.StatusOK { t.Fatal(w) } var resp struct { Data struct { FilePath string `json:"filePath"` } `json:"data"` } if err := json.NewDecoder(w.Body).Decode(&resp); err != nil { t.Fatal(err) } id = strings.TrimPrefix(resp.Data.FilePath, "/api/v0/files/") }) t.Run("tree", func(t *testing.T) { r := httptest.NewRequest(http.MethodGet, "/", nil) w := httptest.NewRecorder() if err := server.apiV0TreeHandler(w, r); err != nil { t.Fatal(err) } t.Logf("%s", w.Body.Bytes()) if w.Code != http.StatusOK { t.Fatal(w) } if !bytes.Contains(w.Body.Bytes(), []byte(`{"Meta":{"Title":"my title","ReadOnly":false,"Deleted":false},"Content":"`)) { t.Fatal(w) } var branch Branch if err := json.NewDecoder(w.Body).Decode(&branch); err != nil { t.Fatal(err) } t.Logf("TODO: %+v", branch) if branch.Leaf != (Leaf{}) { t.Error(branch.Leaf) } if parent, ok := branch.Branches["my pid"]; !ok { t.Error(ok, branch) } else if parent.Leaf.Meta.Title != "Untitled" { t.Error(parent.Leaf) } else if child, ok := parent.Branches[NewID(id)]; !ok { t.Error(ok, NewID("my pid").Push(id), parent) } else if child.Leaf.Meta.Title != "my title" { t.Error(child.Leaf) } }) t.Run("get", func(t *testing.T) { r := httptest.NewRequest(http.MethodGet, "/"+url.PathEscape(id), nil) t.Logf("%s", r.URL.String()) w := httptest.NewRecorder() if err := server.apiV0FilesIDGetHandler(w, r); err != nil { t.Fatal(err) } t.Logf("%s", w.Body.Bytes()) if w.Code != http.StatusOK { t.Fatal(w) } if !bytes.Contains(w.Body.Bytes(), []byte(`body`)) { t.Fatal(w) } if title := w.Header().Get("Title"); title != "my title" { t.Fatal(title) } }) }