"""Scope B 竞品快赢 · 4 个生成器的 spec/schema 契约测试。 校验每个 spec 的 name/tier/reads/writes/output_schema/scope/input_schema, 对齐不变量 #2(只声明 tier)/ #3(4 生成器一律纯预览 writes=[])。不联网、无 DB。 """ from __future__ import annotations import pytest from ww_agents import ( AgentSpec, BookTeardownResult, ContinuationResult, DeAiResult, PolishResult, continue_spec, de_ai_spec, expand_spec, teardown_spec, ) # (spec, name, tier, reads, output_schema) _COMPETITOR_SPECS = [ (continue_spec, "continue", "writer", ["projects", "outline", "chapters"], ContinuationResult), (expand_spec, "expand", "writer", ["projects"], PolishResult), (de_ai_spec, "de-ai", "analyst", ["projects"], DeAiResult), (teardown_spec, "teardown", "analyst", ["projects"], BookTeardownResult), ] @pytest.mark.parametrize( ("spec", "name", "tier", "reads", "output_schema"), _COMPETITOR_SPECS, ) def test_competitor_spec_declares_expected_contract( spec: AgentSpec, name: str, tier: str, reads: list[str], output_schema: type, ) -> None: assert spec.name == name assert spec.tier == tier assert spec.reads == reads assert spec.writes == [] # 不变量 #3:4 生成器一律纯预览 assert spec.output_schema is output_schema assert spec.scope == "builtin" assert spec.input_schema is None # 注入材料为序列化文本,非结构化入参 @pytest.mark.parametrize("spec", [row[0] for row in _COMPETITOR_SPECS]) def test_competitor_spec_has_nonempty_system_prompt(spec: AgentSpec) -> None: assert spec.system_prompt.strip() # ---- schema 解析/默认值契约 ---- def test_continuation_result_parses_text() -> None: parsed = ContinuationResult.model_validate({"text": "雷光再起,他迈步向前。"}) assert parsed.text == "雷光再起,他迈步向前。" def test_polish_result_parses_text() -> None: parsed = PolishResult.model_validate({"text": "夜色更浓,风穿过断壁。"}) assert parsed.text == "夜色更浓,风穿过断壁。" def test_de_ai_result_parses_text() -> None: parsed = DeAiResult.model_validate({"text": "他咧嘴一笑,没多说什么。"}) assert parsed.text == "他咧嘴一笑,没多说什么。" def test_book_teardown_result_parses_and_lists_default_empty() -> None: empty = BookTeardownResult(structure="开篇立钩→铺垫→爆发") assert empty.themes == [] assert empty.archetypes == [] assert empty.hooks == [] parsed = BookTeardownResult.model_validate( { "themes": ["逆袭", "复仇"], "archetypes": ["废柴主角", "腹黑反派"], "structure": "黄金三章立爽点,逐卷升级", "hooks": ["章末反转", "扮猪吃虎"], } ) assert parsed.themes == ["逆袭", "复仇"] assert parsed.structure == "黄金三章立爽点,逐卷升级" assert parsed.hooks == ["章末反转", "扮猪吃虎"]