120 lines
3.4 KiB
Go
120 lines
3.4 KiB
Go
package api
|
||
|
||
import (
|
||
"archive/zip"
|
||
"bytes"
|
||
"io"
|
||
"net/http/httptest"
|
||
"os"
|
||
"path/filepath"
|
||
"testing"
|
||
"testing/fstest"
|
||
|
||
"rawmark/internal/auth"
|
||
"rawmark/internal/events"
|
||
"rawmark/internal/search"
|
||
"rawmark/internal/store"
|
||
"rawmark/internal/vault"
|
||
)
|
||
|
||
func TestPortableNotesExcludeApplicationData(t *testing.T) {
|
||
for _, layout := range []string{"default", "same-root", "nested-app", "symlink-data"} {
|
||
t.Run(layout, func(t *testing.T) {
|
||
root := t.TempDir()
|
||
data := root
|
||
switch layout {
|
||
case "default":
|
||
root = filepath.Join(data, "vault")
|
||
case "nested-app", "symlink-data":
|
||
data = filepath.Join(root, "application")
|
||
}
|
||
if err := os.MkdirAll(data, 0700); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if layout == "symlink-data" {
|
||
link := filepath.Join(t.TempDir(), "data")
|
||
if err := os.Symlink(data, link); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
data = link
|
||
}
|
||
v, err := vault.Open(root)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
st, err := store.Open(filepath.Join(data, "rawmark.db"))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
defer st.Close()
|
||
am := auth.New(st)
|
||
if err = am.Setup("disposable-export-password"); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
idx, err := search.Open(filepath.Join(data, "index.db"), v)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
defer idx.Close()
|
||
server := New(v, am, st, events.NewHub(), idx)
|
||
server.DataDir = data
|
||
srv := httptest.NewServer(server.Handler(fstest.MapFS{}))
|
||
defer srv.Close()
|
||
f := &fixture{t: t, url: srv.URL, root: root, store: st, admin: newClient(), server: server}
|
||
res := f.login(f.admin, "disposable-export-password")
|
||
assertOK(t, res)
|
||
res.Body.Close()
|
||
f.write("note.md", "# portable note\n")
|
||
f.write("assets/a.png", "image")
|
||
f.write("personal/fonts/keep.dat", "user resource")
|
||
for _, name := range []string{"temp", "backups", "logs", "fonts", "themes", "plugins"} {
|
||
dir := filepath.Join(data, name)
|
||
if err = os.MkdirAll(dir, 0700); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
if err = os.WriteFile(filepath.Join(dir, "application.dat"), []byte("private"), 0600); err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
}
|
||
// As in the real process, export temporary files live inside the application data directory.
|
||
t.Setenv("TMPDIR", filepath.Join(data, "temp"))
|
||
t.Setenv("TMP", filepath.Join(data, "temp"))
|
||
t.Setenv("TEMP", filepath.Join(data, "temp"))
|
||
res = f.do(f.admin, "POST", "/api/v1/maintenance/notes/export", "", nil)
|
||
defer res.Body.Close()
|
||
assertOK(t, res)
|
||
body, err := io.ReadAll(res.Body)
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
z, err := zip.NewReader(bytes.NewReader(body), int64(len(body)))
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
want := map[string]string{"note.md": "# portable note\n", "assets/a.png": "image", "personal/fonts/keep.dat": "user resource"}
|
||
for _, file := range z.File {
|
||
if file.FileInfo().IsDir() {
|
||
continue
|
||
}
|
||
expected, ok := want[file.Name]
|
||
if !ok {
|
||
t.Fatalf("笔记导出包含应用数据或临时文件:%s", file.Name)
|
||
}
|
||
r, err := file.Open()
|
||
if err != nil {
|
||
t.Fatal(err)
|
||
}
|
||
content, err := io.ReadAll(r)
|
||
r.Close()
|
||
if err != nil || string(content) != expected {
|
||
t.Fatalf("笔记或附件内容改变:%s,%v", file.Name, err)
|
||
}
|
||
delete(want, file.Name)
|
||
}
|
||
if len(want) != 0 {
|
||
t.Fatalf("遗漏用户文件:%v", want)
|
||
}
|
||
})
|
||
}
|
||
}
|