package core // 画廊清单(.ehdl.json)测试 import ( "os" "path/filepath" "testing" "eh-downloader-wails/internal/eh" ) func TestManifestRoundTrip(t *testing.T) { dir := t.TempDir() m := Manifest{ GID: "4239274", Token: "55e069b6b9", Title: "[Noxe] Test", TotalPages: 206, DonePages: []int{3, 1, 2}, FailedPage: []int{10, 9}, } if err := WriteManifest(dir, m); err != nil { t.Fatalf("写清单失败: %v", err) } // 文件应该真的存在 p := filepath.Join(dir, ManifestFileName) if _, err := os.Stat(p); err != nil { t.Fatalf("清单文件没生成: %v", err) } // 不该残留 .tmp if _, err := os.Stat(p + ".tmp"); err == nil { t.Error("临时文件没清理") } got, err := ReadManifest(dir) if err != nil { t.Fatalf("读清单失败: %v", err) } if got == nil { t.Fatal("读回来的清单是 nil") } if got.GID != m.GID || got.Token != m.Token { t.Errorf("gid/token 不对: %s/%s", got.GID, got.Token) } if got.TotalPages != 206 { t.Errorf("总页数不对: %d", got.TotalPages) } if got.Version != ManifestVersion { t.Errorf("版本号不对: %d", got.Version) } // 页码应该被排序过 for i := 1; i < len(got.DonePages); i++ { if got.DonePages[i] < got.DonePages[i-1] { t.Errorf("donePages 没排序: %v", got.DonePages) break } } if len(got.DonePages) != 3 { t.Errorf("donePages 数量不对: %v", got.DonePages) } } func TestManifestNotExist(t *testing.T) { dir := t.TempDir() m, err := ReadManifest(dir) if err != nil { t.Errorf("不存在时不该报错: %v", err) } if m != nil { t.Error("不存在时该返回 nil") } } func TestManifestCorrupted(t *testing.T) { dir := t.TempDir() os.WriteFile(filepath.Join(dir, ManifestFileName), []byte("{坏掉的 json"), 0o644) if _, err := ReadManifest(dir); err == nil { t.Error("坏 json 应该报错") } } func TestScanManifests(t *testing.T) { root := t.TempDir() // 两个正常画廊 d1 := filepath.Join(root, "画廊A") d2 := filepath.Join(root, "画廊B") os.MkdirAll(d1, 0o755) os.MkdirAll(d2, 0o755) WriteManifest(d1, Manifest{GID: "111", Token: "a", TotalPages: 10, DonePages: []int{1, 2}}) WriteManifest(d2, Manifest{GID: "222", Token: "b", TotalPages: 5, DonePages: []int{1}}) // 一个非画廊目录(没清单) os.MkdirAll(filepath.Join(root, "普通文件夹"), 0o755) // 一个隐藏目录(该跳过) hidden := filepath.Join(root, ".hidden") os.MkdirAll(hidden, 0o755) WriteManifest(hidden, Manifest{GID: "999", Token: "z"}) got := ScanManifests(root) if len(got) != 2 { t.Fatalf("应扫到 2 个清单,实际 %d: %+v", len(got), got) } found := map[string]bool{} for _, m := range got { found[m.GID] = true } if !found["111"] || !found["222"] { t.Errorf("扫到的画廊不对: %v", found) } if found["999"] { t.Error("隐藏目录不该被扫到") } } // 用清单补全丢失的任务信息 func TestReconcileTask(t *testing.T) { m := &Manifest{ GID: "111", TotalPages: 206, Title: "从清单恢复的标题", DonePages: []int{1, 2, 3, 7}, } // 模拟 state.json 丢了的任务 task := &Task{GID: "111", Token: "a", Pages: map[string]string{}} if !ReconcileTask(task, m) { t.Error("应该有修改") } if task.Title != "从清单恢复的标题" { t.Errorf("标题没补上: %q", task.Title) } if task.TotalPages != 206 { t.Errorf("总页数没补上: %d", task.TotalPages) } if len(task.Pages) != 4 { t.Errorf("已完成页没补上: %v", task.Pages) } if task.Pages["7"] != "done" { t.Errorf("第 7 页该是 done: %v", task.Pages) } // 已有信息不该被覆盖 task2 := &Task{ GID: "111", Title: "已有标题", TotalPages: 10, Pages: map[string]string{"1": "done", "2": "done", "3": "done", "7": "done"}, } if ReconcileTask(task2, m) { t.Error("清单内容都已存在时不该报改动") } if task2.Title != "已有标题" { t.Errorf("已有标题被覆盖了: %q", task2.Title) } if task2.TotalPages != 10 { t.Errorf("已有总页数被覆盖了: %d", task2.TotalPages) } // 完全空白任务 + 空清单:不该有任何改动 empty := &Task{GID: "999", Pages: map[string]string{}} if ReconcileTask(empty, &Manifest{GID: "999"}) { t.Error("空清单不该产生改动") } } // state.json 丢失的真实场景:完整走一遍 func TestManifestEnablesResumeWithoutState(t *testing.T) { root := t.TempDir() dir := filepath.Join(root, "某画廊") os.MkdirAll(dir, 0o755) // 模拟:之前下过 3 张,清单记录了 WriteManifest(dir, Manifest{ GID: "4239274", Token: "abc", Title: "测试", TotalPages: 10, DonePages: []int{1, 2, 3}, }) for i := 1; i <= 3; i++ { os.WriteFile(filepath.Join(dir, "00"+string(rune('0'+i))+".png"), []byte("data"), 0o644) } // 现在 state.json 完全丢失,只能靠清单 + 文件系统 ms := ScanManifests(root) if len(ms) != 1 { t.Fatalf("应扫到 1 个,实际 %d", len(ms)) } m := ms[0] if m.TotalPages != 10 { t.Errorf("总页数不该: %d", m.TotalPages) } // 靠文件系统知道哪几张有了 have := 0 for i := 1; i <= m.TotalPages; i++ { name := "00" + string(rune('0'+i)) + ".png" if st, err := os.Stat(filepath.Join(dir, name)); err == nil && st.Size() > 0 { have++ } } if have != 3 { t.Errorf("应有 3 张已下载,实际 %d", have) } // 还差 7 张 —— 这是清单带来的价值 if m.TotalPages-have != 7 { t.Errorf("应还差 7 张,实际 %d", m.TotalPages-have) } } func TestFindTaskByGID(t *testing.T) { s, _ := newTestStore(t) s.AddTasks([]eh.GalleryRef{{GID: "111", Token: "tokA"}}) // token 对不上也能按 gid 找到 task := s.FindTaskByGID("111") if task == nil { t.Fatal("应该找得到") } if task.Token != "tokA" { t.Errorf("token 不对: %s", task.Token) } if s.FindTaskByGID("999") != nil { t.Error("不存在的 gid 应返回 nil") } // token 精确匹配仍要正常工作 if s.FindTask("111", "tokA") == nil { t.Error("精确查找应该能找到") } }