"""NodeExecutor 接口抽象测试(T1:整体项目部分拆解·先行实现)。""" import pytest from router_system.executors import (ModelNodeExecutor, RuleNodeExecutor, build_node_executor) from router_system.experts import build_expert_pool from router_system.knowledge import KnowledgeBase from router_system.memory import TaskNode, WorkingMemory from router_system.router import build_router @pytest.mark.asyncio async def test_rule_node_executor_deterministic(): ex = RuleNodeExecutor(kb=KnowledgeBase()) node = TaskNode(id="t1", kind="solve", domain="math", query="求解方程 x^2-5x+6=0") mem = WorkingMemory() r1 = await ex.execute(node, "math", "medium", mem) r2 = await ex.execute(node, "math", "medium", mem) assert r1.text == r2.text # 确定性 assert "求解" in r1.text assert r1.model_used.startswith("rule:") @pytest.mark.asyncio async def test_model_node_executor_uses_expert_pool(): experts = build_expert_pool({}, ["code", "math", "legal", "medical", "finance", "life", "education", "general"]) ex = ModelNodeExecutor(experts) node = TaskNode(id="t1", kind="implement", domain="code", query="写一个函数") mem = WorkingMemory() resp = await ex.execute(node, "code", "medium", mem) assert resp.text # mock 专家输出 assert "mock" in resp.text @pytest.mark.asyncio async def test_model_node_executor_fallback_to_general(): # 未知领域 → general 专家兜底 experts = {"general": build_expert_pool({}, ["general"])["general"]} ex = ModelNodeExecutor(experts) node = TaskNode(id="t1", kind="explain", domain="unknown", query="测试") resp = await ex.execute(node, "unknown", "easy", WorkingMemory()) assert resp.text def test_factory_rule(): ex = build_node_executor("rule", kb=KnowledgeBase()) assert isinstance(ex, RuleNodeExecutor) def test_factory_model(): experts = build_expert_pool({}, ["code"]) ex = build_node_executor("api", experts=experts) assert isinstance(ex, ModelNodeExecutor) def test_factory_invalid(): with pytest.raises(ValueError): build_node_executor("bad-backend") @pytest.mark.asyncio async def test_router_uses_node_executor(): r = build_router() assert r.node_executor.name == "rule" res = await r.route("求解方程 x^2 - 5x + 6 = 0") assert res.domain == "math" assert res.response