tests/test_follow_variants.py
Rama master · réplica de lectura del repositorio original.
"""追更变体判定的隔离测试。
标题取自 2026-08-25 实测的真实来源页:rule34video.com 的
`/models/lazyprocrastinator/`、kemono.cr 的 `fanbox/30917150` 帖子列表,以及 f95zone
`latest_data.php` 里 thread 50685 的记录。证据登记见 `docs/reference-sources.json`。
"""
import unittest
from dataclasses import dataclass
from peach.follow_variants import (
PROVIDER_PRIORITY, VariantVerdict, classify, group_duplicates, normalize_handle,
)
ALIASES = ("LazyProcrastinator", "LazyProcrast", "Lazyprocastinator")
@dataclass(frozen=True)
class _Item:
external_id: str
provider: str
release_key: str
variant_kind: str
published_at: str | None
class ClassifyTests(unittest.TestCase):
def test_combined_resolution_and_frame_rate_are_version_markers(self):
for spec, labels in (("4K60fps", "4K + 60fps"), ("1080p60fps", "1080p + 60fps"),
("4K 60fps", "4K + 60fps")):
for title in (f"Evening Movie ({spec})", f"Evening Movie {spec}"):
with self.subTest(title=title):
verdict = classify(title)
self.assertEqual(verdict.release_key, "evening movie")
self.assertEqual(verdict.variant_label, labels)
self.assertEqual(classify("Evening Movie Part 4K60fpsExtra").release_key,
"evening movie part 4k60fpsextra")
def _key(self, title, **kwargs):
return classify(title, creator_aliases=ALIASES, **kwargs)
def test_alt_marker_groups_with_its_main_release(self):
main = self._key("Fiona - Paizuri")
alt = self._key("Fiona - Paizuri (Nude)")
self.assertEqual(main.release_key, alt.release_key)
self.assertEqual(main.variant_kind, "main")
self.assertIsNone(main.variant_label)
self.assertEqual(alt.variant_kind, "alt")
self.assertEqual(alt.variant_label, "nude")
def test_no_watermark_is_an_alt_not_a_separate_work(self):
verdict = self._key("Bertha Riding (No Watermark)")
self.assertEqual(verdict.variant_kind, "alt")
self.assertEqual(verdict.variant_label, "no watermark")
self.assertEqual(verdict.release_key, "bertha riding")
unwatermarked = self._key("Juno Facial - 4K Unwatermarked")
self.assertEqual(unwatermarked.release_key, "juno facial")
self.assertEqual(unwatermarked.markers, ("4K", "no watermark"))
def test_wip_markers_win_over_alt_markers(self):
verdict = self._key("Mitsuru - School Movie [WIP] (nude)")
self.assertEqual(verdict.variant_kind, "wip")
self.assertEqual(verdict.variant_label, "WIP")
self.assertIn("nude", verdict.markers)
self.assertEqual(verdict.release_key, "mitsuru school movie")
def test_bare_wip_token_is_recognized_without_brackets(self):
self.assertEqual(self._key("Sayuri Cowgirl WIP").variant_kind, "wip")
def test_trailing_digit_is_a_sequence_not_a_version(self):
first = self._key("Sayuri - Cowgirl")
second = self._key("Sayuri - Cowgirl 2")
self.assertNotEqual(first.release_key, second.release_key)
self.assertEqual(second.variant_kind, "main")
def test_explicit_v2_is_an_alt_of_the_same_work(self):
base = self._key("Riona Heartlily - Cowgirl")
second = self._key("Riona Heartlily - Cowgirl v2")
self.assertEqual(base.release_key, second.release_key)
self.assertEqual(second.variant_label, "v2")
def test_creator_handle_is_stripped_even_when_misspelled(self):
tagged = self._key("Femboy Tighnari Footjob [Lazyprocrastinator]")
misspelled = self._key("Femboy Tighnari Footjob (Lazyprocastinator)")
bare = self._key("Femboy Tighnari Footjob")
self.assertEqual(tagged.release_key, bare.release_key)
self.assertEqual(misspelled.release_key, bare.release_key)
def test_slash_separated_handle_group_is_stripped(self):
verdict = classify(
"Lazy Procrastinator Collection [2026-06-28] [LazyProcrastinator/LazyProcrast]",
creator_aliases=ALIASES, semantics="release",
)
self.assertEqual(verdict.release_key, "lazy procrastinator collection")
self.assertIsNone(verdict.version)
def test_dated_collection_batches_are_not_versions(self):
june = classify("Billyhhyb Collection [2026-06-28] [Billyhhyb]",
creator_aliases=ALIASES, semantics="release")
july = classify("Billyhhyb Collection [2026-07-15] [Billyhhyb]",
creator_aliases=ALIASES, semantics="release")
self.assertEqual(june.release_key, july.release_key)
self.assertIsNone(june.version)
self.assertIsNone(july.version)
def test_work_semantics_keeps_date_brackets_in_the_key(self):
june = self._key("Pack [2026-06-28]")
july = self._key("Pack [2026-07-15]")
self.assertNotEqual(june.release_key, july.release_key)
self.assertIsNone(june.version)
def test_release_semantics_group_successive_versions(self):
june = classify("Lazy Procrastinator Collection", creator_aliases=ALIASES,
version="2026-06-28", semantics="release")
august = classify("Lazy Procrastinator Collection", creator_aliases=ALIASES,
version="2026-08-24", semantics="release")
self.assertEqual(june.release_key, august.release_key)
self.assertNotEqual(june.version, august.version)
def test_work_semantics_does_not_mine_versions_from_the_title(self):
verdict = self._key("Mitsuru v3")
self.assertIsNone(verdict.version)
self.assertEqual(verdict.variant_label, "v3")
def test_tag_list_bracket_is_kept_in_the_release_key(self):
episode5 = self._key(
"My Best Friend's Mom Is A Futa - Episode 5 [Anilingus, Light BDSM, Pegging JOI]")
episode6 = self._key("My Best Friend's Mom Is A Futa - Episode 6 [Pegging JOI]")
self.assertNotEqual(episode5.release_key, episode6.release_key)
self.assertIn("anilingus", episode5.release_key)
def test_brackets_do_not_change_the_key(self):
with_parens = self._key("Cecilia - Belle Fingering (Zenless Zone Zero)")
without = self._key("Cecilia - Belle Fingering Zenless Zone Zero")
self.assertEqual(with_parens.release_key, without.release_key)
def test_multi_marker_bracket_splits_on_commas(self):
verdict = self._key("Yorha Commander (nude, 4k)")
self.assertEqual(verdict.variant_kind, "alt")
self.assertEqual(verdict.markers, ("nude", "4K"))
self.assertEqual(verdict.release_key, "yorha commander")
def test_resolution_and_framerate_labels_keep_their_number(self):
self.assertEqual(self._key("Sayuri Handy [1080p]").variant_label, "1080p")
self.assertEqual(self._key("Sayuri Handy [60fps]").variant_label, "60fps")
def test_short_tokens_are_never_fuzzy_matched_as_handles(self):
# riona/rinoa 只差一次换位,短 token 上模糊匹配必然误伤。
self.assertIn("rinoa", self._key("Rinoa Heartilly").release_key)
self.assertIn("riona", self._key("Riona Heartlily").release_key)
def test_unknown_semantics_is_rejected(self):
with self.assertRaises(ValueError):
classify("x", semantics="whatever")
def test_normalize_handle_folds_case_and_punctuation(self):
self.assertEqual(normalize_handle("Lazy_Procrast!"), "lazyprocrast")
def test_resolution_written_without_the_p_is_still_a_version(self):
"""`2B Camp [4K]` 与 `2B Camp [1080]` 是同一部片的两个版本。
`[1080]` 此前留在 release_key 里(`2b camp 1080`),两张卡永远合不到一起,
而 `[4K]` 那张的角标写着「2 个版本」,数的是 `[4K]` + `[WIP]`。
只认已知分辨率档位:任意三四位数可能是年份或作品序号。
"""
self.assertEqual(classify("2B Camp [4K]").release_key,
classify("2B Camp [1080]").release_key)
self.assertEqual(classify("2B Camp [1080]").variant_label, "1080p")
self.assertEqual(classify("Yuffie Photoshoot Part 3 [1080]").release_key,
classify("Yuffie Photoshoot Part 3 [4K]").release_key)
# 不是分辨率的数字照旧留在键里。
self.assertEqual(classify("Sayuri - Cowgirl 2").release_key, "sayuri cowgirl 2")
self.assertEqual(classify("Scene [2023]").release_key, "scene 2023")
self.assertEqual(classify("Part [1200]").release_key, "part 1200")
class GroupDuplicatesTests(unittest.TestCase):
def test_cross_site_duplicates_fold_onto_one_primary(self):
video = _Item("4542713", "rule34video", "fiona paizuri", "main", "2026-08-18")
booru = _Item("998877", "rule34xxx", "fiona paizuri", "main", "2026-08-19")
nude = _Item("4542721", "rule34video", "fiona paizuri", "alt", "2026-08-18")
self.assertEqual(group_duplicates([booru, nude, video]), (video, video, video))
def test_main_wins_over_alt_and_wip_regardless_of_provider(self):
wip = _Item("1", "kemono", "mitsuru school movie", "wip", "2026-07-01")
main = _Item("2", "f95zone", "mitsuru school movie", "main", "2026-08-01")
self.assertEqual(group_duplicates([wip, main]), (main, main))
def test_provider_priority_breaks_ties_between_equal_variants(self):
self.assertLess(PROVIDER_PRIORITY["fanbox"], PROVIDER_PRIORITY["kemono"])
self.assertLess(PROVIDER_PRIORITY["kemono"], PROVIDER_PRIORITY["rule34video"])
low = _Item("1", "rule34video", "k", "main", "2026-08-01")
high = _Item("2", "kemono", "k", "main", "2026-08-01")
self.assertEqual(group_duplicates([low, high])[0], high)
def test_items_without_a_release_key_are_skipped(self):
orphan = _Item("1", "kemono", "", "main", None)
self.assertEqual(group_duplicates([orphan]), (None,))
def test_result_is_positionally_aligned_with_the_input(self):
# 条目带 dict 字段就不可哈希,所以返回值必须按位置对齐,不能拿条目当键。
keyed = _Item("1", "kemono", "k", "main", "2026-08-01")
loose = _Item("2", "kemono", "", "main", "2026-08-01")
self.assertEqual(group_duplicates([loose, keyed]), (None, keyed))
def test_verdict_is_immutable(self):
verdict = VariantVerdict("k", "main", None, None, ())
with self.assertRaises(Exception):
verdict.release_key = "other"
if __name__ == "__main__":
unittest.main()