"""人物资料页的外部入口:地址怎么拼、摆在哪个位置、什么时候不出。""" from __future__ import annotations import json import tempfile import unittest from pathlib import Path from urllib.parse import quote from peach import entry_links def ref(provider: str, external_id: str, kind: str = "performer") -> dict: return {"provider": provider, "external_kind": kind, "external_id": external_id} def alias(written: str, source: str = "r18:performer") -> dict: return {"alias": written, "source": source} class EntryLinkAddressTests(unittest.TestCase): def test_each_site_lands_in_the_slot_it_belongs_to(self): rows = entry_links.build(entry_links.defaults(), "河合あすな", [ref("javdb", "QDvG"), ref("minnano-av", "155084")]) self.assertEqual([row["site"] for row in rows], ["minnano-av", "javdb", "missav"]) # minnano-av 是一份资料页,排进上面那排链接;看片那两枚自己一行。 self.assertEqual([row["slot"] for row in rows], ["pill", "mark", "mark"]) self.assertEqual(rows[0]["url"], "https://www.minnano-av.com/actress155084.html") self.assertEqual(rows[1]["url"], "https://javdb.com/actors/QDvG") # 药丸上写站点自己的名字,取自 minnano-av 官网的 title。 self.assertEqual([row["label"] for row in rows], ["みんなのAV", "JavDB", "MISSAV"]) # MISSAV 没有可用的图形标识,那一枚由前端按它站上的排版规则排字。 self.assertEqual([row["mark"] for row in rows], ["brand-minnano", "mark-javdb", ""]) def test_two_pages_on_the_same_site_each_get_an_entry(self): """javdb 上同一位女优有两个演员页,两边挂的作品不同,都要能点进去。""" rows = entry_links.build(entry_links.defaults(), "释爱丽丝", [ref("javdb", "d45k9"), ref("javdb", "ZX5z7")], [alias("釈アリス", "avdb-actor-mapping@8e2d")]) pages = [row for row in rows if row["site"] == "javdb"] self.assertEqual([row["label"] for row in pages], ["JavDB", "JavDB ②"]) self.assertEqual([row["ordinal"] for row in pages], ["", "②"]) self.assertEqual([row["url"] for row in pages], ["https://javdb.com/actors/d45k9", "https://javdb.com/actors/ZX5z7"]) def test_the_page_credited_on_her_works_comes_before_a_name_backfilled_one(self): """#271:旧艺名那页(白石アイリ)常年是空的,排第一会让人以为链接错了。""" backfilled = ref("javdb", "MmbDR") | {"metadata_json": json.dumps({"source": "backfill_performer_entry_ids"})} credited = ref("javdb", "bkxd") | {"metadata_json": json.dumps({"source": "javdb", "provider_id": "NNPJ-466"})} rows = entry_links.build(entry_links.defaults(), "森日向子", [backfilled, credited]) self.assertEqual([row["url"] for row in rows if row["site"] == "javdb"], ["https://javdb.com/actors/bkxd", "https://javdb.com/actors/MmbDR"]) def test_a_name_matched_page_of_the_same_record_as_the_credited_one_is_dropped(self): """#271:署名那页与按名字对上的那页都是有碼记录时,她在这一类只有署名那页。 白石アイリ 那页是旧艺名的空页,点开就是查无此人;订阅新作同样不能去拉它。 """ def page(external_id, **metadata): return ref("javdb", external_id) | {"metadata_json": json.dumps(metadata)} refs = [page("MmbDR", source="backfill_performer_entry_ids", uncensored=False), page("bkxd", source="javdb", provider_id="NNPJ-466", uncensored=False)] rows = entry_links.build(entry_links.defaults(), "森日向子", refs) self.assertEqual([(row["label"], row["url"]) for row in rows if row["site"] == "javdb"], [("JavDB", "https://javdb.com/actors/bkxd")]) self.assertEqual(entry_links.javdb_actor_pages("森日向子", refs), ["https://javdb.com/actors/bkxd"]) def test_name_matched_pages_stay_when_the_record_differs_or_cannot_be_told(self): def page(external_id, **metadata): return ref("javdb", external_id) | {"metadata_json": json.dumps(metadata)} backfill = {"source": "backfill_performer_entry_ids"} cases = { # 署名的是有碼那条,按名字对上的是同一人的無碼那条:两页都是她的。 "另一类记录": ([page("E26vd", **backfill, uncensored=True), page("pRMq", source="javdb", provider_id="MIDV-001", uncensored=False)], ["pRMq", "E26vd"]), # 没记下记录类型,分不出是不是同一类。 "类型未知": ([page("MmbDR", **backfill), page("bkxd", source="javdb", provider_id="NNPJ-466", uncensored=False)], ["bkxd", "MmbDR"]), # 同一类只有按名字对上的几页,没有署名可比,交给用户点开核对。 "没有署名": ([page("GydD", **backfill, uncensored=False), page("ZzOA", **backfill, uncensored=False)], ["GydD", "ZzOA"]), } for name, (refs, expected) in cases.items(): with self.subTest(name): self.assertEqual(entry_links.provider_ids(refs)["javdb"], expected) def test_a_site_without_an_id_is_absent_instead_of_a_search_address(self): rows = entry_links.build(entry_links.defaults(), "深田えいみ", [ref("r18dev", "1041521")]) self.assertEqual([row["site"] for row in rows], ["missav"]) self.assertNotIn("search", rows[0]["url"]) def test_a_performer_outside_the_jav_directories_gets_no_watch_row(self): """`145cm色白お嬢様` 这类 FC2 个人摄创作者,账本 kind 也是 performer。 JavDB 与 MISSAV 只收商业 AV 女优;本机 Stash 的 id 与 r18 的名字映射每个人都有, 它们不是「别处收录过她」的证据,所以不能拿来开这一行。 """ rows = entry_links.build(entry_links.defaults(), "145cm色白お嬢様", [ref("stash", "912"), ref("r18", "145cm", kind="performer_name")]) self.assertEqual(rows, []) def test_an_agency_roster_id_does_not_open_the_watch_row(self): """K-MIB 那类事务所站收的是所属艺人,不是 JAV 的作品目录。 名册上有她,说明这家公司签了她;她拍的是不是商业 AV、JavDB 与 MISSAV 上有没有 她的页面,这条引用一个字都没说。 """ rows = entry_links.build(entry_links.defaults(), "神木ほのか", [ref("kmib", "218")]) self.assertEqual(rows, []) def test_missav_uses_the_japanese_stage_name(self): """规范名是中文译名时,MISSAV 那条按别名里的日文艺名拼。 中文译名与纯平假名读音不作为混合文字艺名的入口。 """ rows = entry_links.build(entry_links.defaults(), "释爱丽丝", [ref("r18dev", "1096544")], [ alias("Alice Shaku", "avdb-actor-mapping@8e2d"), alias("しゃくありす", "r18:performer"), alias("釈アリス", "avdb-actor-mapping@8e2d"), ]) self.assertEqual( rows[0]["url"], "https://missav.ws/dm3/cn/actresses/%E9%87%88%E3%82%A2%E3%83%AA%E3%82%B9") def test_current_japanese_name_wins_over_a_former_name_alias(self): rows = entry_links.build(entry_links.defaults(), "逢沢みゆ", [ref("javdb", "zK98J")], [ alias("あいざわみゆ", "r18dev:performer-profile"), alias("坂本マリア", "auto:performer-alias@2048"), ]) self.assertEqual(next(row["url"] for row in rows if row["site"] == "missav"), "https://missav.ws/dm3/cn/actresses/" "%E9%80%A2%E6%B3%BD%E3%81%BF%E3%82%86") def test_plain_hiragana_canonical_reading_uses_a_stage_name_alias(self): self.assertEqual(entry_links.japanese_name("あいざわみゆ", [alias("逢沢みゆ")]), "逢沢みゆ") def test_glyph_equivalent_alias_preserves_the_japanese_spelling(self): self.assertEqual(entry_links.japanese_name("蓝川みれい", [ alias("藍川みれい", "avdb-actor-mapping"), alias("坂本マリア"), ]), "藍川みれい") def test_plain_hiragana_alias_is_not_a_han_stage_name(self): for name, reading in (("三宅美香", "みやけみか"), ("音海里奈", "おとみりな")): with self.subTest(name=name): self.assertEqual(entry_links.japanese_name(name, [alias(reading)]), name) def test_a_published_hiragana_stage_name_is_distinct_from_a_profile_reading(self): aliases = [alias("なぎさ", "javdb-actor-page")] self.assertEqual(entry_links.japanese_name("渚向日葵", aliases), "なぎさ") self.assertEqual(entry_links.japanese_name("渚向日葵", aliases, {"source": "javdb-actor-page", "jp": "なぎさ", "zh_cn": "渚向日葵"}), "なぎさ") def test_glyph_equivalent_chinese_alias_does_not_hide_a_published_kana_stage_name(self): for name, jp, chinese in (("逢泽美优", "逢沢みゆ", "逢澤美優"), ("优梨舞奈", "優梨まいな", "優梨舞奈"), ("新井爱丽", "新井エリー", "新井愛麗")): with self.subTest(name=name): aliases = [alias(chinese, "review:performer-localization"), alias(jp, "review:performer-localization")] self.assertEqual(entry_links.japanese_name(name, aliases, {"source": "review:performer-localization", "jp": jp, "zh_cn": name}), jp) def test_equivalent_han_name_preserves_original_glyphs_and_iteration_mark(self): for name, jp in (("北条麻妃", "北條麻妃"), ("乃乃果花", "乃々果花"), ("梨梨衣", "梨々衣")): with self.subTest(name=name): self.assertEqual(entry_links.japanese_name(name, [alias(jp)]), jp) def test_missav_preserves_source_stage_name_glyphs(self): from urllib.parse import unquote, urlsplit for name, jp in (("七泽美亚", "七沢みあ"), ("逢泽美优", "逢沢みゆ"), ("北条麻妃", "北條麻妃"), ("乃乃果花", "乃々果花"), ("优梨舞奈", "優梨まいな")): with self.subTest(name=name): directory = entry_links.directory_index({"source": entry_links.DIRECTORY_URL, "entries": [{"name": name, "url": "https://missav.ws/dm3/cn/actresses/" + quote(jp)}]}) rows = entry_links.build(entry_links.defaults(), name, [ref("javdb", "sample")], [alias(jp)], directory=directory) address = next(row["url"] for row in rows if row["site"] == "missav") self.assertEqual(unquote(urlsplit(address).path), f"/dm3/cn/actresses/{jp}") def test_paired_current_han_name_wins_over_an_unreviewed_former_name(self): localized = {"source": "avdb-actor-mapping", "jp": "水谷心音", "zh_cn": "水谷心音"} rows = entry_links.build(entry_links.defaults(), "水谷心音", [ref("javdb", "sample")], [alias("藤崎りお"), alias("みずたにここね")], localized) self.assertEqual(next(row["url"] for row in rows if row["site"] == "missav"), "https://missav.ws/dm3/cn/actresses/" "%E6%B0%B4%E8%B0%B7%E5%BF%83%E9%9F%B3") def test_localization_conflicts_do_not_replace_the_current_identity(self): self.assertEqual(entry_links.japanese_name("霞理沙", [alias("かすみりさ")], {"source": "avdb-actor-mapping", "jp": "松冈仁美", "zh_cn": "霞理沙"}), "霞理沙") self.assertEqual(entry_links.japanese_name("泽村玲子", [ alias("澤村レイコ", "avdb-actor-mapping")], {"source": "avdb-actor-mapping", "jp": "泽村玲子", "zh_cn": "泽村玲子"}), "澤村レイコ") self.assertEqual(entry_links.japanese_name("鹫尾芽衣", [ alias("筧ジュン", "avdb-actor-mapping"), alias("鷲尾めい", "avdb-actor-mapping")], {"source": "avdb-actor-mapping", "jp": "鷲尾めい", "zh_cn": "鹫尾芽衣"}), "筧ジュン") def test_paired_han_name_requires_the_same_current_chinese_name(self): self.assertEqual(entry_links.japanese_name("宇野刊菜", [alias("あべかんな")], {"source": "avdb-actor-mapping", "jp": "宇野栞菜", "zh_cn": "宇野刊菜"}), "宇野栞菜") self.assertEqual(entry_links.japanese_name("宇野刊菜", [alias("あべかんな")], {"source": "avdb-actor-mapping", "jp": "宇野栞菜", "zh_cn": "别的写法"}), "宇野刊菜") def test_a_reading_in_plain_hiragana_loses_to_the_written_stage_name(self): rows = entry_links.build(entry_links.defaults(), "天川空", [ref("r18dev", "1091104")], [ alias("あまかわそら", "r18:performer"), alias("天川そら", "avdb-actor-mapping@8e2d"), ]) self.assertTrue(rows[0]["url"].endswith("/%E5%A4%A9%E5%B7%9D%E3%81%9D%E3%82%89")) def test_a_stage_name_source_wins_among_writings_of_the_same_shape(self): rows = entry_links.build(entry_links.defaults(), "佐佐木纱希", [ref("r18dev", "1075533")], [ alias("佐々木サキ", "stash:performer"), alias("佐々木さき", "avdb-actor-mapping@8e2d"), ]) self.assertTrue(rows[0]["url"].endswith( "/%E4%BD%90%E3%80%85%E6%9C%A8%E3%81%95%E3%81%8D")) def test_the_canonical_name_stands_in_when_no_alias_reads_as_japanese(self): rows = entry_links.build(entry_links.defaults(), "楪 カレン", [ref("r18dev", "1058712")], [alias("Karen Yuzuriha", "r18:performer")]) self.assertEqual( rows[0]["url"], "https://missav.ws/dm3/cn/actresses/%E6%A5%AA%20%E3%82%AB%E3%83%AC%E3%83%B3") def test_ids_from_another_entity_kind_do_not_count(self): # 事务所那 55 条 minnano-av 引用记的是 `production`,不是这个人在站上的号; # 它既拼不出 minnano 的资料页,也不足以说明这个人是 JAV 女优。 rows = entry_links.build(entry_links.defaults(), "七沢みあ", [ref("minnano-av", "110", kind="production")]) self.assertEqual(rows, []) def test_a_mirror_domain_replaces_only_the_host(self): settings = entry_links.defaults() settings["javdb"]["host"] = "javdb521.com" rows = entry_links.build(settings, "七沢みあ", [ref("javdb", "NPD3")]) self.assertEqual(rows[0]["url"], "https://javdb521.com/actors/NPD3") def test_the_feed_address_stays_on_javdb_whatever_the_mirror(self): """订阅地址按主机计限流预算,跟着镜像走就绕开了 `javdb.com` 那份预算。""" self.assertEqual(entry_links.javdb_actor_pages("七沢みあ", [ref("javdb", "NPD3")]), ["https://javdb.com/actors/NPD3"]) self.assertEqual(entry_links.javdb_actor_pages( "145cm色白お嬢様", [ref("stash", "12")]), []) def test_a_broken_domain_falls_back_to_the_default_site(self): """手改坏的域名拼出来是个不存在的主机名,那比退回默认站更难看出问题。""" settings = entry_links.defaults() settings["javdb"]["host"] = "javdb.com/actors" rows = entry_links.build(settings, "七沢みあ", [ref("javdb", "NPD3")]) self.assertEqual(rows[0]["url"], "https://javdb.com/actors/NPD3") def test_an_empty_domain_means_the_default_one(self): """配置页那个框默认就是空的:留空是「没换过」,不是「没有地址」。""" rows = entry_links.build({"javdb": {"host": ""}}, "七沢みあ", [ref("javdb", "NPD3")]) self.assertEqual(rows[0]["url"], "https://javdb.com/actors/NPD3") class EntryLinkSettingsFileTests(unittest.TestCase): def setUp(self): temporary = tempfile.TemporaryDirectory() self.addCleanup(temporary.cleanup) self.root = Path(temporary.name).resolve() def body(self, **changed: dict) -> dict: """配置页提交的形状:能换域名的那几站各一行,值就是框里写的那串。""" sites = {site.key: {"host": ""} for site in entry_links.SITES if site.mirrored} sites.update(changed) return {"sites": sites} def test_an_absent_file_reads_as_no_domain_changed(self): """配置页只管能换域名的那两站;みんなのAV 只此一家,不在这份设置里。""" saved = entry_links.read(self.root) self.assertEqual(sorted(saved), ["javdb", "missav"]) self.assertTrue(all(row["host"] == "" for row in saved.values())) def test_saving_round_trips_through_the_snapshot(self): entry_links.save(self.root, self.body(javdb={"host": "javdb521.com"})) rows = {row["key"]: row for row in entry_links.snapshot(self.root)["sites"]} self.assertEqual(sorted(rows), ["javdb", "missav"]) self.assertEqual(rows["javdb"]["host"], "javdb521.com") self.assertEqual(rows["javdb"]["default_host"], "javdb.com") self.assertEqual(rows["javdb"]["label"], "JavDB") # 没动过的那一站留空:框里显示的是占位符,不是一串要人去认的默认值。 self.assertEqual(rows["missav"]["host"], "") self.assertEqual(rows["missav"]["default_host"], "missav.ws") def test_directory_file_supplies_the_published_missav_path(self): path = self.root / entry_links.DIRECTORY_FILENAME first = "https://missav.ws/dm102/cn/actresses/" + quote("七沢みあ") path.write_text(json.dumps({"source": entry_links.DIRECTORY_URL, "entries": [{"name": "七泽美亚", "url": first}]}), encoding="utf-8") rows = entry_links.entry_links(self.root, "七泽美亚", [ref("javdb", "NPD3")], [alias("七沢みあ")]) self.assertEqual(next(row["url"] for row in rows if row["site"] == "missav"), first) second = first.replace("dm102", "dm1002") path.write_text(json.dumps({"source": entry_links.DIRECTORY_URL, "entries": [{"name": "七泽美亚", "url": second}]}), encoding="utf-8") rows = entry_links.entry_links(self.root, "七泽美亚", [ref("javdb", "NPD3")], [alias("七沢みあ")]) self.assertEqual(next(row["url"] for row in rows if row["site"] == "missav"), second) def test_a_pasted_address_is_trimmed_down_to_its_domain(self): """镜像地址多半是整条复制过来的,前缀与末尾的斜杠不值得弹一条错误。""" entry_links.save(self.root, self.body(missav={"host": "https://missav.ai/"})) self.assertEqual(entry_links.read(self.root)["missav"]["host"], "missav.ai") def test_an_address_with_a_path_is_refused(self): with self.assertRaises(ValueError) as caught: entry_links.save(self.root, self.body( javdb={"host": "javdb.com/actors/{javdb_id}"})) self.assertIn("只写域名本身", str(caught.exception)) def test_clearing_the_box_goes_back_to_the_default_domain(self): entry_links.save(self.root, self.body(missav={"host": "missav.ai"})) entry_links.save(self.root, self.body(missav={"host": " "})) self.assertEqual(entry_links.read(self.root)["missav"]["host"], "") def test_a_hand_broken_file_falls_back_instead_of_emptying_the_row(self): (self.root / entry_links.FILENAME).write_text( json.dumps({"sites": {"javdb": {"host": "not a domain"}}}), encoding="utf-8") self.assertEqual(entry_links.read(self.root)["javdb"]["host"], "") class MissavDirectoryTests(unittest.TestCase): def directory(self, rows, *, complete=False): return entry_links.directory_index({"source": entry_links.DIRECTORY_URL, "entries": rows, "complete": complete}) def test_failed_source_page_has_no_template_fallback(self): address = "https://missav.ws/dm31/cn/actresses/Rina" for complete in (False, True): with self.subTest(complete=complete): directory = self.directory([{"name": "Rina", "url": address, "verified": False}], complete=complete) rows = entry_links.build(entry_links.defaults(), "RINA", [ref("javdb", "actor")], directory=directory) self.assertNotIn("missav", [row["site"] for row in rows]) def test_conflicting_page_evidence_requires_a_verified_source(self): failed = {"name": "Rina", "url": "https://missav.ws/dm31/cn/actresses/Rina", "verified": False} alternate = {"name": "Rina", "url": "https://missav.ws/dm83/cn/actresses/Rina"} self.assertEqual(self.directory([failed, alternate]).path("Rina", "RINA"), "") restored = self.directory([{**failed, "verified": True}]) self.assertEqual(restored.path("Rina", "RINA"), "/dm31/cn/actresses/Rina") def test_site_glyphs_and_compound_stage_names_use_the_published_href(self): for canonical, jp, display, slug in ( ("北条麻妃", "北條麻妃", "北条麻妃", "北条麻妃"), ("森泽佳奈", "飯岡かなこ", "森泽佳奈 (饭冈佳奈子)", "森沢かな(飯岡かなこ)")): with self.subTest(canonical=canonical): address = "https://missav.ws/dm193/cn/actresses/" + quote(slug) directory = self.directory([{"name": display, "url": address}]) rows = entry_links.build(entry_links.defaults(), canonical, [ref("javdb", "actor")], [alias(jp)], directory=directory) self.assertEqual(next(row["url"] for row in rows if row["site"] == "missav"), address) def test_a_han_current_name_recorded_by_directories_beats_a_former_name_alias(self): """#271:r18 与 avwikidb 都把「森日向子」记成她的名字,可信来源的旧艺名不能顶替。""" aliases = [alias("白石アイリ", "av_name@namewikis-20260924"), alias("朝比奈えみり", "auto:performer-alias@1")] recorded = [ref("javdb", "bkxd"), ref("r18", "森日向子", "performer_name"), ref("avwikidb", "1") | {"metadata_json": json.dumps({"name": "森日向子", "kana": "もり ひなこ"})}] for refs in (recorded[:2], [recorded[0], recorded[2]]): with self.subTest(refs=[row["provider"] for row in refs]): self.assertEqual(entry_links.japanese_name("森日向子", aliases, refs=refs), "森日向子") self.assertEqual(entry_links.japanese_name("森日向子", aliases, refs=recorded[:1]), "白石アイリ") # 记下的是日文字形时用资料里的写法,中文规范名只用来对上她。 self.assertEqual(entry_links.japanese_name("三上悠亚", [alias("ヒュージョン亀頭", "wiki")], refs=[ref("r18", "三上悠亜", "performer_name")]), "三上悠亜") address = "https://missav.ws/dm127/cn/actresses/" + quote("森日向子") directory = self.directory([{"name": "森日向子", "url": address}, {"name": "白石アイリ", "url": "https://missav.ws/dm31/cn/actresses/X"}], complete=True) rows = entry_links.build(entry_links.defaults(), "森日向子", recorded, aliases, directory=directory) self.assertEqual(next(row["url"] for row in rows if row["site"] == "missav"), address) def test_a_recorded_name_without_a_directory_page_keeps_the_alias_page(self): """资料记下的是她改名前的写法、名录里只有新艺名的页时,照旧链到别名挑出的那页。""" address = "https://missav.ws/dm62/cn/actresses/" + quote("とみの伊織") directory = self.directory([{"name": "とみの伊織", "url": address}], complete=True) rows = entry_links.build(entry_links.defaults(), "富野伊织", [ref("javdb", "a"), ref("r18", "富野伊織", "performer_name")], [alias("とみの伊織", "wiki")], directory=directory) self.assertEqual(next(row["url"] for row in rows if row["site"] == "missav"), address) def test_ambiguous_directory_names_have_no_missav_entry(self): directory = self.directory([ {"name": "同名艺人", "url": "https://missav.ws/dm1/cn/actresses/A"}, {"name": "同名艺人", "url": "https://missav.ws/dm2/cn/actresses/B"}, ]) rows = entry_links.build(entry_links.defaults(), "同名艺人", [ref("javdb", "actor")], directory=directory) self.assertEqual([row["site"] for row in rows], ["javdb"]) def test_native_name_whitespace_preserves_the_published_url(self): for written, published in (("ローレン 花恋", "ローレン花恋"), ("日向\u3000なつ", "日向なつ")): with self.subTest(name=written): address = "https://missav.ws/dm193/cn/actresses/" + quote(published) directory = self.directory([{"name": published, "url": address}]) rows = entry_links.build(entry_links.defaults(), written, [ref("javdb", "actor")], directory=directory) self.assertEqual(next(row["url"] for row in rows if row["site"] == "missav"), address) def test_latin_name_whitespace_keeps_names_distinct(self): directory = self.directory([{"name": "Rina", "url": "https://missav.ws/dm1/cn/actresses/Rina"}], complete=True) rows = entry_links.build(entry_links.defaults(), "Rin A", [ref("javdb", "actor")], directory=directory) self.assertEqual([row["site"] for row in rows], ["javdb"]) def test_distinct_published_slugs_remain_ambiguous_with_native_whitespace(self): directory = self.directory([ {"name": "日向なつ", "url": "https://missav.ws/dm1/cn/actresses/" + quote("日向なつ")}, {"name": "日向 なつ", "url": "https://missav.ws/dm2/cn/actresses/" + quote("日向 なつ")}, ]) self.assertEqual(directory.path("日向なつ", "日向なつ"), "") def test_prefix_variants_of_the_same_published_slug_share_one_identity(self): directory = self.directory([ {"name": "A", "url": "https://missav.ws/dm1/cn/actresses/A"}, {"name": "A", "url": "https://missav.ws/dm2/cn/actresses/A"}, ]) self.assertEqual(directory.path("A", "A"), "/dm1/cn/actresses/A") def test_an_incomplete_directory_does_not_assert_an_unlisted_name_is_absent(self): directory = self.directory([]) self.assertIsNone(directory.path("七沢みあ", "七泽美亚")) rows = entry_links.build(entry_links.defaults(), "七泽美亚", [ref("javdb", "actor")], [alias("七沢みあ")], directory=directory) self.assertTrue(any(row["site"] == "missav" for row in rows)) def test_a_complete_directory_has_no_entry_for_an_unmatched_name(self): rows = entry_links.build(entry_links.defaults(), "同名艺人", [ref("javdb", "actor")], directory=self.directory([], complete=True)) self.assertEqual([row["site"] for row in rows], ["javdb"]) def test_directory_entries_are_scoped_to_the_public_actress_origin(self): invalid = ("http://missav.ws/cn/actresses/A", "https://missav.ws.evil.test/cn/actresses/A", "https://name@missav.ws/cn/actresses/A", "https://missav.ws/cn/videos/A", "https://missav.ws/cn/actresses/A/extra") directory = self.directory([{"name": "A", "url": value} for value in invalid]) self.assertEqual(directory.paths, {}) def test_a_mirror_preserves_the_directory_path(self): settings = entry_links.defaults() settings["missav"]["host"] = "missav.ai" directory = self.directory([{"name": "A", "url": "https://missav.ws/dm102/cn/actresses/A"}]) rows = entry_links.build(settings, "A", [ref("javdb", "actor")], directory=directory) self.assertEqual(next(row["url"] for row in rows if row["site"] == "missav"), "https://missav.ai/dm102/cn/actresses/A") if __name__ == "__main__": unittest.main()