feat(backend): pg schema + store queries incl. path-keyed progress
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"booklib/internal/db"
|
||||
)
|
||||
|
||||
func setup(t *testing.T) *Store {
|
||||
t.Helper()
|
||||
url := os.Getenv("DATABASE_URL")
|
||||
if url == "" {
|
||||
t.Skip("DATABASE_URL not set")
|
||||
}
|
||||
ctx := context.Background()
|
||||
p, err := db.Connect(ctx, url)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Migrate(ctx, p); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s := New(p)
|
||||
for _, q := range []string{
|
||||
"DELETE FROM reading_progress", "DELETE FROM books", "DELETE FROM libraries", "DELETE FROM users"} {
|
||||
if _, err := p.Exec(ctx, q); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func TestBooksDiffLifecycle(t *testing.T) {
|
||||
s := setup(t)
|
||||
ctx := context.Background()
|
||||
libID, err := s.CreateLibrary(ctx, "comics", "/data/books/comics")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
bid, err := s.InsertBook(ctx, libID, "series-a/01.cbz", "01", "cbz", 100, 1000, 24)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
meta, err := s.ListBookMeta(ctx, libID)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if m := meta["series-a/01.cbz"]; m.ID != bid || m.Size != 100 {
|
||||
t.Fatalf("bad meta %+v", m)
|
||||
}
|
||||
if err := s.UpdateBookFile(ctx, bid, 200, 2000, 25); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, _ := s.GetBook(ctx, bid)
|
||||
if b.FileSize != 200 || b.PageCount != 25 {
|
||||
t.Fatalf("update failed: %+v", b)
|
||||
}
|
||||
if err := s.SetBookState(ctx, bid, "error", "boom"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b, _ = s.GetBook(ctx, bid)
|
||||
if b.State != "error" || b.ErrMsg != "boom" {
|
||||
t.Fatal("state not set")
|
||||
}
|
||||
// changed + deleted 的 diff 输入验证
|
||||
meta, _ = s.ListBookMeta(ctx, libID)
|
||||
if _, ok := meta["nope.cbz"]; ok {
|
||||
t.Fatal("unexpected row")
|
||||
}
|
||||
if err := s.DeleteBookByPath(ctx, libID, "series-a/01.cbz"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := s.GetBook(ctx, bid); err == nil {
|
||||
t.Fatal("book still exists")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProgressUpsertAndJoin(t *testing.T) {
|
||||
s := setup(t)
|
||||
ctx := context.Background()
|
||||
uid, _ := s.CreateUser(ctx, "u1", "h", "member")
|
||||
libID, _ := s.CreateLibrary(ctx, "l", "/data/books/l")
|
||||
bid, err := s.InsertBook(ctx, libID, "x/a.cbz", "a", "cbz", 1, 1, 3)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := s.UpsertProgress(ctx, uid, libID, "x/a.cbz", []byte(`{"page":2}`), 0.5); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := s.UpsertProgress(ctx, uid, libID, "x/a.cbz", []byte(`{"page":3}`), 0.9); err != nil {
|
||||
t.Fatal(err) // upsert 不报错
|
||||
}
|
||||
p, err := s.GetProgress(ctx, uid, libID, "x/a.cbz")
|
||||
// jsonb 规范输出: 冒号后有空格
|
||||
if err != nil || p.Percent != 0.9 || string(p.Locator) != `{"page": 3}` {
|
||||
t.Fatalf("bad progress %+v %v", p, err)
|
||||
}
|
||||
list, _ := s.ListProgress(ctx, uid)
|
||||
if len(list) != 1 {
|
||||
t.Fatalf("want 1 got %d", len(list))
|
||||
}
|
||||
// 删书行,进度保留(spec §4)
|
||||
if err := s.DeleteBook(ctx, bid); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := s.GetProgress(ctx, uid, libID, "x/a.cbz"); err != nil {
|
||||
t.Fatal("progress lost with book")
|
||||
}
|
||||
// 列表联查带本人 percent
|
||||
_, _ = s.InsertBook(ctx, libID, "x/a.cbz", "a", "cbz", 1, 1, 3)
|
||||
views, _ := s.ListBooks(ctx, 0, "", "", uid)
|
||||
if len(views) != 1 || views[0].Percent != 0.9 {
|
||||
t.Fatalf("views %+v", views)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user