package core // 推送历史测试:核心是「任务删了,灰标不该跟着消失」 import ( "encoding/json" "net/http" "os" "path/filepath" "strings" "testing" "eh-downloader-wails/internal/eh" ) func TestPushedStoreMarkAndPersist(t *testing.T) { dir := t.TempDir() p := filepath.Join(dir, "pushed.json") s := NewPushedStore(p) s.Mark("111", "aaa", "T1") s.Mark("222", "bbb", "T2") // 重复推送同一个,只更新时间不重复添加 s.Mark("111", "aaa", "T1 又推了一次") s.Flush() if got := s.Count(); got != 2 { t.Fatalf("应有 2 条,实际 %d", got) } // 重新打开,验证真的落盘了 s2 := NewPushedStore(p) if got := s2.Count(); got != 2 { t.Fatalf("重开后应有 2 条,实际 %d", got) } gids := map[string]string{} for _, e := range s2.Entries() { gids[e.GID] = e.Title } if gids["111"] != "T1 又推了一次" { t.Errorf("重复推送应更新标题,实际 %q", gids["111"]) } if gids["222"] != "T2" { t.Errorf("222 标题不对: %q", gids["222"]) } } func TestPushedStoreMarkBatch(t *testing.T) { dir := t.TempDir() s := NewPushedStore(filepath.Join(dir, "pushed.json")) s.MarkBatch([]PushedEntry{ {GID: "1", Token: "a", Title: "A"}, {GID: "2", Token: "b", Title: "B"}, {GID: "1", Token: "a", Title: "A2"}, // 重复 }) if got := s.Count(); got != 2 { t.Fatalf("批量后应有 2 条,实际 %d", got) } } // 关键场景:任务被「清除已完成」删掉后,推送历史必须还在 func TestPushedSurvivesTaskDeletion(t *testing.T) { dir := t.TempDir() store := NewStore(filepath.Join(dir, "state.json")) pushed := NewPushedStore(filepath.Join(dir, "pushed.json")) // 模拟油猴推送 store.AddTasks([]eh.GalleryRef{{GID: "111", Token: "aaa", Raw: "u", Title: "T1"}}) pushed.Mark("111", "aaa", "T1") // 任务完成 task := store.FindTask("111", "aaa") if task == nil { t.Fatal("任务没了") } task.Status = "done" store.UpsertTask(*task) // 清除已完成 —— 任务从列表消失 n, gids := store.ClearFinishedWithGIDs() if n != 1 { t.Fatalf("应清掉 1 个,实际 %d", n) } if len(gids) != 1 || gids[0] != "111" { t.Fatalf("应返回 gid 111,实际 %v", gids) } if store.CountDone() != 0 { t.Error("任务应已清空") } // 但推送历史还在 —— 灰标不会消失 if pushed.Count() != 1 { t.Fatalf("推送历史应还剩 1 条,实际 %d", pushed.Count()) } entries := pushed.Entries() if entries[0].GID != "111" { t.Errorf("gid 不对: %s", entries[0].GID) } } func TestPushedMarkCompleted(t *testing.T) { dir := t.TempDir() s := NewPushedStore(filepath.Join(dir, "pushed.json")) s.Mark("111", "aaa", "T1") s.MarkCompleted("111") entries := s.Entries() if len(entries) != 1 || !entries[0].Completed { t.Fatalf("应标记为已完成: %+v", entries) } } func TestPushedStoreClear(t *testing.T) { dir := t.TempDir() p := filepath.Join(dir, "pushed.json") s := NewPushedStore(p) s.Mark("111", "aaa", "T1") s.Mark("222", "bbb", "T2") s.Clear() s.Flush() if got := s.Count(); got != 0 { t.Fatalf("清空后应为 0,实际 %d", got) } // 文件应该被写成空数组,而不是残留旧数据 raw, err := os.ReadFile(p) if err != nil { t.Fatal(err) } var st PushedState if err := json.Unmarshal(raw, &st); err != nil { t.Fatalf("文件应还是合法 JSON: %v", err) } if len(st.Entries) != 0 { t.Errorf("文件里应为空,实际 %d 条", len(st.Entries)) } } // 桥口 /pushed 接口 func TestBridgePushedEndpoint(t *testing.T) { dir := t.TempDir() store := NewStore(filepath.Join(dir, "state.json")) pushed := NewPushedStore(filepath.Join(dir, "pushed.json")) base := startTestBridgeWith(t, store, pushed) // 通过 /add 推一个 body := `{"gid":"777","token":"ccc","url":"https://e-hentai.org/g/777/ccc/","title":"T777"}` req, _ := http.NewRequest("POST", base+"/add", strings.NewReader(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-EHDL-Token", BridgeToken) resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } resp.Body.Close() // /pushed 里能查到 code, raw := getWithToken(t, base+"/pushed", BridgeToken) if code != http.StatusOK { t.Fatalf("应 200,实际 %d", code) } var j struct { OK bool `json:"ok"` Count int `json:"count"` Items []struct { GID string `json:"gid"` Title string `json:"title"` } `json:"items"` } if err := json.Unmarshal(raw, &j); err != nil { t.Fatalf("解析失败: %v (%s)", err, raw) } if j.Count != 1 || len(j.Items) != 1 || j.Items[0].GID != "777" { t.Fatalf("/pushed 应返回 777,实际 %s", raw) } if j.Items[0].Title != "T777" { t.Errorf("标题不对: %q", j.Items[0].Title) } // 错 token -> 403 code, _ = getWithToken(t, base+"/pushed", "bad") if code != http.StatusForbidden { t.Errorf("错 token 应 403,实际 %d", code) } }