"""业务链条「管理员隐藏」目录开关测试。 覆盖(对齐 agents 的 hidden 语义): - store:``hidden`` 随 update 持久化,create 缺省 False; - 普通列表(mine / public / pending)一律排除 hidden —— 含 owner 自己的 「我的链条」与管理员的普通视图(选链弹窗路径,不带 admin_view); - 管理视图(``include_hidden=True`` + admin)不排除,并可按 ``hidden`` 筛选「只看隐藏 / 只看显示中」;非管理员的 ``include_hidden`` 一律忽略; - 单条读取(get_chain)不过滤:owner / 公共链条的已配置使用路径不受影响。 """ from __future__ import annotations import pytest from test_roundtable_chain_review import _SEATS, _make_repo @pytest.mark.asyncio async def test_hidden_flag_roundtrip(): repo, engine = await _make_repo() try: row = await repo.create_chain("u1", {"id": "c1", "title": "链", "seats": _SEATS}) assert row["hidden"] is False updated = await repo.update_chain("c1", "u1", hidden=True) assert updated is not None and updated["hidden"] is True updated = await repo.update_chain("c1", "u1", hidden=False) assert updated is not None and updated["hidden"] is False finally: await engine.dispose() @pytest.mark.asyncio async def test_hidden_chain_vanishes_from_all_normal_listings(): repo, engine = await _make_repo() try: await repo.create_chain("u1", {"id": "c1", "title": "被隐藏的公共链", "seats": _SEATS}) await repo.create_chain("u1", {"id": "c2", "title": "普通链", "seats": _SEATS}) await repo.update_chain("c1", "u1", is_public=True) await repo.update_chain("c2", "u1", is_public=True) await repo.update_chain("c1", "u1", hidden=True) # owner 的「我的链条」:c1 消失(隐藏对本人也生效)。 rows = await repo.list_chains("u1", scope="mine") assert {r["id"] for r in rows} == {"c2"} # 公共列表:任何账号都看不到 c1。 rows = await repo.list_chains("u2", scope="public") assert {r["id"] for r in rows} == {"c2"} # 管理员的普通视图(选链弹窗路径,无 admin_view):同样排除。 rows = await repo.list_chains("admin", scope="all", is_admin=True) assert {r["id"] for r in rows} == {"c2"} # 非管理员传 include_hidden 也绝不越权看到隐藏链条。 rows = await repo.list_chains("u1", scope="mine", is_admin=False, include_hidden=True) assert {r["id"] for r in rows} == {"c2"} # 单条读取不受影响(隐藏不是禁用:业务映射 / 研讨快照继续可用)。 assert await repo.get_chain("c1", "u1") is not None assert await repo.get_chain("c1", "u2") is not None # 公共链条他人可读 finally: await engine.dispose() @pytest.mark.asyncio async def test_admin_view_lists_and_filters_hidden(): repo, engine = await _make_repo() try: await repo.create_chain("u1", {"id": "c1", "title": "被隐藏", "seats": _SEATS}) await repo.create_chain("u1", {"id": "c2", "title": "显示中", "seats": _SEATS}) await repo.update_chain("c1", "u1", hidden=True) # 管理视图:默认(hidden=None)全部可见(含隐藏)。 rows = await repo.list_chains("admin", scope="all", is_admin=True, include_hidden=True) assert {r["id"] for r in rows} == {"c1", "c2"} # 只看隐藏 / 只看显示中。 rows = await repo.list_chains( "admin", scope="all", is_admin=True, include_hidden=True, hidden=True ) assert {r["id"] for r in rows} == {"c1"} rows = await repo.list_chains( "admin", scope="all", is_admin=True, include_hidden=True, hidden=False ) assert {r["id"] for r in rows} == {"c2"} # 管理视图 + 非 admin → include_hidden 失效(普通视图,排除隐藏)。 rows = await repo.list_chains("u1", scope="mine", is_admin=False, include_hidden=True) assert {r["id"] for r in rows} == {"c2"} # 取消隐藏回到普通列表。 await repo.update_chain("c1", "u1", hidden=False) rows = await repo.list_chains("u1", scope="mine") assert {r["id"] for r in rows} == {"c1", "c2"} finally: await engine.dispose()