package eh import "testing" // ---- 输入解析 ---- func TestParseGalleryInput(t *testing.T) { tests := []struct { name string in string want []GalleryRef }{ { name: "标准完整链接", in: "https://e-hentai.org/g/4239272/55e069b6b9", want: []GalleryRef{{GID: "4239272", Token: "55e069b6b9"}}, }, { name: "多行多链接", in: "https://e-hentai.org/g/1111111/aaaaaaaaaa\n" + "https://e-hentai.org/g/2222222/bbbbbbbbbb", want: []GalleryRef{ {GID: "1111111", Token: "aaaaaaaaaa"}, {GID: "2222222", Token: "bbbbbbbbbb"}, }, }, { name: "竖线分隔一行多个", in: "https://e-hentai.org/g/1111111/aaaaaaaaaa|https://e-hentai.org/g/2222222/bbbbbbbbbb", want: []GalleryRef{ {GID: "1111111", Token: "aaaaaaaaaa"}, {GID: "2222222", Token: "bbbbbbbbbb"}, }, }, { name: "去重", in: "https://e-hentai.org/g/1111111/aaaaaaaaaa\nhttps://e-hentai.org/g/1111111/aaaaaaaaaa", want: []GalleryRef{{GID: "1111111", Token: "aaaaaaaaaa"}}, }, { name: "注释行忽略", in: "# 这是注释\nhttps://e-hentai.org/g/1111111/aaaaaaaaaa", want: []GalleryRef{{GID: "1111111", Token: "aaaaaaaaaa"}}, }, { name: "裸 gid 不匹配完整链接", in: "4239274", want: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ParseGalleryInput(tt.in) if len(got) != len(tt.want) { t.Fatalf("数量不对: got %d want %d (%+v)", len(got), len(tt.want), got) } for i := range got { if got[i].GID != tt.want[i].GID || got[i].Token != tt.want[i].Token { t.Errorf("第 %d 项不对: got %s/%s want %s/%s", i, got[i].GID, got[i].Token, tt.want[i].GID, tt.want[i].Token) } } }) } } func TestExtractGIDs(t *testing.T) { got := ExtractGIDs("看这个 4239274 还有 4239274 重复 和 1234567") if len(got) != 2 { t.Fatalf("应去重成 2 个,实际 %d 个: %v", len(got), got) } if got[0] != "4239274" || got[1] != "1234567" { t.Errorf("结果不对: %v", got) } } // ---- hath 直链正则(核心) ---- func TestReHath(t *testing.T) { // 从 README 里抄的真实样本 samples := []string{ `https://oclzqqk.gyktyewfonyf.hath.network:39656/h/4e9af9f...-44168-707-754-wbp/keystamp=1791448200-8b38ea8f6d;fileindex=270107377;xres=800/NH.webp`, ``, `"https://foo.bar.hath.network/h/a/b/c.png"`, } for _, s := range samples { m := reHath.FindString(s) if m == "" { t.Errorf("没匹配到: %s", s) continue } if m[len(m)-1] == '"' || m[len(m)-1] == '>' { t.Errorf("匹配多了尾部字符: %q", m) } } // 不该匹配的 for _, s := range []string{ `https://e-hentai.org/s/abc/123-1`, `https://example.com/h/foo`, } { if m := reHath.FindString(s); m != "" { t.Errorf("不该匹配却匹配了: %q", m) } } } func TestApplyXres(t *testing.T) { const base = "https://a.hath.network:39656/h/x/keystamp=1;fileindex=2;xres=800/NH.webp" cases := []struct { xres string want string }{ {"auto", base}, {"", base}, {"org", "https://a.hath.network:39656/h/x/keystamp=1;fileindex=2;xres=org/NH.webp"}, {"1920", "https://a.hath.network:39656/h/x/keystamp=1;fileindex=2;xres=1920/NH.webp"}, } for _, c := range cases { cl := NewClient(0, "", "off", c.xres) if got := cl.applyXres(base); got != c.want { t.Errorf("xres=%s: got %s\n want %s", c.xres, got, c.want) } } } // ---- 缩略图链接解析(画廊页分页的关键) ---- func TestReThumbLink(t *testing.T) { // 模拟 EH 画廊页里的一行 html := `
` ms := reThumbLink.FindAllStringSubmatch(html, -1) if len(ms) != 2 { t.Fatalf("应匹配 2 个,实际 %d", len(ms)) } if ms[0][1] != "/s/bb06b5db77/4239250-1" { t.Errorf("第 1 个路径不对: %q", ms[0][1]) } if ms[0][2] != "1" || ms[0][3] != "001.jpg" { t.Errorf("第 1 个页码/文件名不对: page=%s name=%q", ms[0][2], ms[0][3]) } if ms[1][1] != "https://e-hentai.org/s/cc11ee22ff/4239250-2" { t.Errorf("第 2 个路径不对: %q", ms[1][1]) } } func TestReTotal(t *testing.T) { for _, s := range []string{ "Showing 1 - 20 of 206 images", "Showing 21 - 40 of 206 images", } { m := reTotal.FindStringSubmatch(s) if m == nil { t.Fatalf("没匹配: %s", s) } if m[1] != "206" { t.Errorf("总数不对: %s", m[1]) } } } // ---- meta 解析 ---- func TestParseMeta(t *testing.T) { html := `
artist:
group:
parody:
language:
other:
uploader` meta := parseMeta(html, "[Noxe] Title (Circle)") if meta.Category != "doujinshi" { t.Errorf("分类不对: %q", meta.Category) } if meta.Artist != "Noxe" { t.Errorf("作者不对: %q", meta.Artist) } if meta.Group != "CriticalHAI" { t.Errorf("社团不对: %q", meta.Group) } if meta.Series != "wuthering waves" { t.Errorf("系列不对: %q", meta.Series) } if meta.Lang != "chinese" { t.Errorf("语言不对: %q", meta.Lang) } // more 按钮要过滤掉 if len(meta.Tags) != 2 || meta.Tags[0] != "qingxiao" || meta.Tags[1] != "rover" { t.Errorf("标签不对(more 未过滤): %v", meta.Tags) } if meta.Uploader != "velius11" { t.Errorf("上传者不对: %q", meta.Uploader) } } // 标题约定兜底:[作者] 标题 (社团) func TestParseMetaTitleFallback(t *testing.T) { html := `
` meta := parseMeta(html, "[Noxe] 0820 Qingxiao [AI Generated] (CriticalHAI)") if meta.Artist != "Noxe" { t.Errorf("作者兜底失败: %q", meta.Artist) } if meta.Group != "CriticalHAI" { t.Errorf("社团兜底失败: %q", meta.Group) } } // ---- HTML 实体 ---- func TestDecodeEntities(t *testing.T) { cases := map[string]string{ "a & b": "a & b", "<tag>": "", ""x"": `"x"`, "AB": "AB", "A": "A", "plain": "plain", "😀": "\U0001F600", "no entities!": "no entities!", } for in, want := range cases { if got := DecodeEntities(in); got != want { t.Errorf("DecodeEntities(%q) = %q, want %q", in, got, want) } } }