# -*- coding: utf-8 -*- """博主备注 —— 作品栏里那一位到底是谁。 按 creator_hash 分组、显示 creator_name,两样都认不出人:一个是哈希,一个是平台昵称。 备注是人自己起的名字。 """ import httpx import pytest import pytest_asyncio from api.main import app from api.monitor import db as monitor_db from api.monitor.models import MODE_CREATOR, MonitorNote, MonitorTask CREATOR_HASH = "hash-a" NICKNAME = "张三" @pytest_asyncio.fixture async def client(tmp_path): monitor_db.set_sqlite_path(tmp_path / "monitor.db") await monitor_db.init_db() transport = httpx.ASGITransport(app=app) async with httpx.AsyncClient(transport=transport, base_url="http://test") as http_client: yield http_client await monitor_db.dispose_engine() async def _seed(platform: str = "xhs", task_name: str = "任务") -> int: """一个任务 + 一条作品,博主固定用 CREATOR_HASH / NICKNAME。""" async with monitor_db.get_session() as session: task = MonitorTask( name=task_name, platform=platform, mode=MODE_CREATOR, enabled=True, interval_minutes=60, max_notes_count=20, enable_comments=False, max_comments_count=50, run_timeout_seconds=3600, notify_enabled=False, created_at=0, updated_at=0, ) session.add(task) await session.flush() session.add( MonitorNote( task_id=task.id, note_id=f"{platform}-n1", title="作品", note_url="", cover="", creator_hash=CREATOR_HASH, creator_name=NICKNAME, source_kind="", published_at=None, first_seen_run_id=1, first_seen_at=0, last_seen_run_id=1, last_seen_at=0, ) ) return task.id async def _notes(client, platform: str = "xhs"): return (await client.get("/api/monitor/notes", params={"platform": platform})).json()["notes"] async def _set_alias(client, alias: str, platform: str = "xhs", creator_hash=CREATOR_HASH): return await client.put( f"/api/monitor/creators/{creator_hash}", params={"platform": platform}, json={"alias": alias}, ) class TestCreatorAlias: @pytest.mark.asyncio async def test_notes_start_without_an_alias(self, client): await _seed() assert (await _notes(client))[0]["creator_alias"] == "" @pytest.mark.asyncio async def test_an_alias_comes_back_with_the_notes(self, client): await _seed() response = await _set_alias(client, "竞品A") assert response.status_code == 200 note = (await _notes(client))[0] assert note["creator_alias"] == "竞品A" # 备注是**叠加**在昵称之上的,不是替换 —— 昵称仍然是有用的对照。 assert note["creator_name"] == NICKNAME @pytest.mark.asyncio async def test_an_alias_is_shared_across_tasks(self, client): """同一个博主出现在两个任务里,备注只该填一次。 creator_hash 对同一个 uid 是稳定的,所以键取 (platform, creator_hash) 而不是 按任务存 —— 否则每加一个任务都要重新认一遍人。 """ await _seed(task_name="任务甲") await _seed(task_name="任务乙") await _set_alias(client, "竞品A") for note in await _notes(client): assert note["creator_alias"] == "竞品A" @pytest.mark.asyncio async def test_the_alias_does_not_leak_to_another_platform(self, client): """同一个哈希在另一个平台上是另一个(或同一个)人 —— 别串味。""" await _seed(platform="xhs") await _seed(platform="dy") await _set_alias(client, "小红书那边的", platform="xhs") assert (await _notes(client, "xhs"))[0]["creator_alias"] == "小红书那边的" assert (await _notes(client, "dy"))[0]["creator_alias"] == "" @pytest.mark.asyncio async def test_an_empty_alias_clears_it(self, client): await _seed() await _set_alias(client, "竞品A") await _set_alias(client, "") assert (await _notes(client))[0]["creator_alias"] == "" @pytest.mark.asyncio async def test_an_alias_is_trimmed(self, client): await _seed() await _set_alias(client, " 竞品A ") assert (await _notes(client))[0]["creator_alias"] == "竞品A"