157 lines
6.0 KiB
Python
157 lines
6.0 KiB
Python
"""Tests for compact/sidebar-summary menu overrides (简介模式菜单).
|
|
|
|
Overrides are stored per ``menu_type`` (A / B) so two website deployments can
|
|
keep independent left-nav layouts. Saving type B must not wipe type A.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
import pytest_asyncio
|
|
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
|
|
|
|
from deerflow.persistence.base import Base
|
|
from deerflow.persistence.compact_menu_overrides import (
|
|
CompactMenuOverrideRepository,
|
|
MemoryCompactMenuOverrideStore,
|
|
normalize_compact_menu_type,
|
|
)
|
|
from deerflow.persistence.compact_menu_overrides.keys import public_node_id, storage_node_id
|
|
|
|
|
|
@pytest_asyncio.fixture
|
|
async def store(tmp_path):
|
|
engine = create_async_engine(f"sqlite+aiosqlite:///{tmp_path / 'compact-menu.db'}")
|
|
async with engine.begin() as conn:
|
|
import deerflow.persistence.compact_menu_overrides.model # noqa: F401
|
|
|
|
await conn.run_sync(Base.metadata.create_all)
|
|
sf = async_sessionmaker(engine, expire_on_commit=False)
|
|
return CompactMenuOverrideRepository(sf)
|
|
|
|
|
|
def test_normalize_compact_menu_type():
|
|
assert normalize_compact_menu_type(None) == "A"
|
|
assert normalize_compact_menu_type("") == "A"
|
|
assert normalize_compact_menu_type("a") == "A"
|
|
assert normalize_compact_menu_type("default") == "A"
|
|
assert normalize_compact_menu_type("B") == "B"
|
|
assert normalize_compact_menu_type("alt") == "B"
|
|
assert normalize_compact_menu_type("2") == "B"
|
|
|
|
|
|
def test_storage_keys_keep_type_a_unprefixed():
|
|
assert storage_node_id("A", "workspace-chats") == "workspace-chats"
|
|
assert storage_node_id("B", "workspace-chats") == "B::workspace-chats"
|
|
assert public_node_id("B::workspace-chats", "B") == "workspace-chats"
|
|
assert public_node_id("workspace-chats", "A") == "workspace-chats"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_replace_all_roundtrips(store: CompactMenuOverrideRepository):
|
|
out = await store.replace_all(
|
|
[
|
|
{"node_id": "workspace-chats", "placement": "settings", "sort_order": 1, "label": "会话"},
|
|
{"node_id": "workspace-agents", "placement": "sidebar", "sort_order": 0, "label": None},
|
|
{"node_id": "workspace-skills", "placement": "toolbox", "sort_order": 0, "label": "技能工具"},
|
|
],
|
|
updated_by="admin-1",
|
|
)
|
|
assert len(out) == 3
|
|
rows = {r["node_id"]: r for r in await store.list_overrides()}
|
|
assert rows["workspace-chats"]["label"] == "会话"
|
|
assert rows["workspace-chats"]["placement"] == "settings"
|
|
assert rows["workspace-chats"]["menu_type"] == "A"
|
|
assert rows["workspace-agents"]["updated_by"] == "admin-1"
|
|
assert rows["workspace-skills"]["placement"] == "toolbox"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_replace_all_is_full_replacement_within_type(store: CompactMenuOverrideRepository):
|
|
await store.replace_all([{"node_id": "a"}, {"node_id": "b"}], menu_type="A")
|
|
await store.replace_all([{"node_id": "c"}], menu_type="A")
|
|
ids = {r["node_id"] for r in await store.list_overrides("A")}
|
|
assert ids == {"c"}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_types_are_isolated(store: CompactMenuOverrideRepository):
|
|
await store.replace_all(
|
|
[{"node_id": "workspace-chats", "placement": "sidebar", "sort_order": 0, "label": "对话A"}],
|
|
menu_type="A",
|
|
)
|
|
await store.replace_all(
|
|
[{"node_id": "workspace-chats", "placement": "settings", "sort_order": 2, "label": "对话B"}],
|
|
menu_type="B",
|
|
)
|
|
a_rows = await store.list_overrides("A")
|
|
b_rows = await store.list_overrides("B")
|
|
assert len(a_rows) == 1
|
|
assert a_rows[0]["node_id"] == "workspace-chats"
|
|
assert a_rows[0]["label"] == "对话A"
|
|
assert a_rows[0]["placement"] == "sidebar"
|
|
assert len(b_rows) == 1
|
|
assert b_rows[0]["node_id"] == "workspace-chats"
|
|
assert b_rows[0]["label"] == "对话B"
|
|
assert b_rows[0]["placement"] == "settings"
|
|
|
|
await store.replace_all([], menu_type="B")
|
|
assert await store.list_overrides("B") == []
|
|
leftover = await store.list_overrides("A")
|
|
assert len(leftover) == 1
|
|
assert leftover[0]["label"] == "对话A"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_omitted_menu_type_reads_legacy_rows():
|
|
"""Old pages that do not send menu_type still see the original compact menu."""
|
|
mem = MemoryCompactMenuOverrideStore()
|
|
mem._rows["workspace-chats"] = {
|
|
"node_id": "workspace-chats",
|
|
"placement": "settings",
|
|
"sort_order": 1,
|
|
"label": "旧对话",
|
|
}
|
|
rows = await mem.list_overrides(None)
|
|
assert len(rows) == 1
|
|
assert rows[0]["node_id"] == "workspace-chats"
|
|
assert rows[0]["label"] == "旧对话"
|
|
assert rows[0]["menu_type"] == "A"
|
|
assert await mem.list_overrides("B") == []
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sql_blank_menu_type_is_treated_as_original(store: CompactMenuOverrideRepository):
|
|
from sqlalchemy import update
|
|
|
|
from deerflow.persistence.compact_menu_overrides.model import CompactMenuOverrideRow
|
|
|
|
await store.replace_all(
|
|
[{"node_id": "legacy", "placement": "sidebar", "sort_order": 0, "label": "旧菜单"}],
|
|
)
|
|
async with store._sf() as session:
|
|
await session.execute(update(CompactMenuOverrideRow).values(menu_type=""))
|
|
await session.commit()
|
|
|
|
rows = await store.list_overrides(None)
|
|
assert {r["node_id"] for r in rows} == {"legacy"}
|
|
assert rows[0]["label"] == "旧菜单"
|
|
assert await store.list_overrides("B") == []
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_memory_store_parity():
|
|
mem = MemoryCompactMenuOverrideStore()
|
|
await mem.replace_all(
|
|
[{"node_id": "x", "placement": "settings", "sort_order": 3}],
|
|
menu_type="B",
|
|
updated_by="u",
|
|
)
|
|
await mem.replace_all([{"node_id": "y", "sort_order": 0}], menu_type="A")
|
|
b_rows = await mem.list_overrides("B")
|
|
assert b_rows[0]["node_id"] == "x"
|
|
assert b_rows[0]["menu_type"] == "B"
|
|
assert b_rows[0]["placement"] == "settings"
|
|
a_ids = {r["node_id"] for r in await mem.list_overrides("A")}
|
|
assert a_ids == {"y"}
|