package api import ( "encoding/json" "os" "path/filepath" "testing" ) func TestProgressRoundTrip(t *testing.T) { st, sc, h, booksDir := setupAPI(t) atok := adminToken(t, h) do(h, "POST", "/api/users", atok, map[string]string{"username": "p1", "password": testPW, "role": "member"}) mtok := loginAs(t, h, "p1", testPW) lib, root := newLibrary(t, st, h, atok, booksDir, "prog") os.WriteFile(filepath.Join(root, "a.txt"), []byte("aaa"), 0o644) scanNow(t, sc, lib) w := do(h, "GET", "/api/books?q=a", atok, nil) // title="a"(文件名去 .txt) var bs []map[string]any json.Unmarshal(w.Body.Bytes(), &bs) id := itoa(bs[0]["id"]) w = do(h, "PUT", "/api/books/"+id+"/progress", mtok, map[string]any{"locator": map[string]int{"page": 12}, "percent": 0.42}) if w.Code != 204 { t.Fatalf("put %d %s", w.Code, w.Body) } w = do(h, "GET", "/api/progress", mtok, nil) var rows []map[string]any json.Unmarshal(w.Body.Bytes(), &rows) if len(rows) != 1 || rows[0]["percent"].(float64) != 0.42 || rows[0]["title"] != "a" { t.Fatalf("get %s", w.Body) } if rows[0]["locator"].(map[string]any)["page"].(float64) != 12 { t.Fatalf("locator %s", w.Body) } // upsert 覆盖 do(h, "PUT", "/api/books/"+id+"/progress", mtok, map[string]any{"locator": map[string]int{"page": 20}, "percent": 0.8}) w = do(h, "GET", "/api/progress", mtok, nil) json.Unmarshal(w.Body.Bytes(), &rows) if len(rows) != 1 || rows[0]["percent"].(float64) != 0.8 { t.Fatalf("upsert %s", w.Body) } // 别人的进度不可见 w = do(h, "GET", "/api/progress", atok, nil) json.Unmarshal(w.Body.Bytes(), &rows) if len(rows) != 0 { t.Fatalf("leak %s", w.Body) } // 校验 w = do(h, "PUT", "/api/books/"+id+"/progress", mtok, map[string]any{"percent": 1.5}) if w.Code != 400 { t.Fatalf("bad percent want 400 got %d", w.Code) } w = do(h, "PUT", "/api/books/999999/progress", mtok, map[string]any{"percent": 0.5}) if w.Code != 404 { t.Fatalf("bad book want 404 got %d", w.Code) } // 列表 percent 内嵌(Task 10 预告的断言在此兑现) w = do(h, "GET", "/api/books", mtok, nil) json.Unmarshal(w.Body.Bytes(), &bs) if bs[0]["percent"].(float64) != 0.8 { t.Fatalf("list percent %+v", bs[0]) } // 删书,进度按 path 保留,重扫后 0.8 回来 do(h, "DELETE", "/api/books/"+id, atok, nil) os.WriteFile(filepath.Join(root, "a.txt"), []byte("aaa"), 0o644) scanNow(t, sc, lib) w = do(h, "GET", "/api/books?q=a", mtok, nil) json.Unmarshal(w.Body.Bytes(), &bs) if bs[0]["percent"].(float64) != 0.8 { t.Fatalf("progress did not survive delete+rescan: %+v", bs[0]) } }