fix: 完善 UT 覆盖,统一 pytest 测试发现 - Closes #2
CI / test (pull_request) Successful in 9s

- 新建 pytest.ini 统一 test discovery(tests/ + skills/ir_generation_skill/tests/)
- test_step1~3 转换为 pytest 兼容格式,无输出文件时自动 skip
- 新增 tests/test_detect_conflicts.py(18 个纯函数单测)
- 新增 tests/test_config.py(7 个配置模块单测)
- CI 改为 pytest -v 使用 pytest.ini testpaths
- DEV_AGENT.md 新增 PR 提交规范

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-31 00:07:07 +08:00
parent 618364e744
commit 682dedb4b4
11 changed files with 619 additions and 32 deletions
@@ -317,6 +317,93 @@ def run_all_tests():
return total_failures == 0
# ═══════════════════════════════════════════════════════════════════════════════
# pytest discovery support
# ═══════════════════════════════════════════════════════════════════════════════
import pytest # noqa: E402
def _load_fragments_or_skip():
"""Load ir_fragments.json or return None."""
try:
return config.load_json(config.IR_FRAGMENTS_JSON)
except FileNotFoundError:
return None
def test_step2_non_empty_rules():
"""pytest: every fragment must have at least one rule."""
fragments = _load_fragments_or_skip()
if fragments is None:
pytest.skip("ir_fragments.json not found — run step2_ir_extraction.py first")
errors = check_non_empty_rules(fragments)
assert not errors, f"non-empty rule errors: {errors}"
def test_step2_rule_paths():
"""pytest: every rule must have a non-empty path array."""
fragments = _load_fragments_or_skip()
if fragments is None:
pytest.skip("ir_fragments.json not found")
errors = check_rule_paths(fragments)
assert not errors, f"rule path errors: {errors[:5]}"
def test_step2_precondition_fields():
"""pytest: every rule must have precondition with geographic_scope and screen_type."""
fragments = _load_fragments_or_skip()
if fragments is None:
pytest.skip("ir_fragments.json not found")
errors = check_precondition_fields(fragments)
assert not errors, f"precondition errors: {errors[:5]}"
def test_step2_user_interaction_content():
"""pytest: user_interaction actions must have non-empty, non-placeholder content."""
fragments = _load_fragments_or_skip()
if fragments is None:
pytest.skip("ir_fragments.json not found")
errors = check_user_interaction_content(fragments)
assert not errors, f"user_interaction content errors: {errors[:5]}"
def test_step2_sources_have_refs():
"""pytest: every rule should reference at least one source."""
fragments = _load_fragments_or_skip()
if fragments is None:
pytest.skip("ir_fragments.json not found")
errors = check_sources_have_logic_tree_nodes(fragments)
assert not errors, f"source reference errors: {errors[:5]}"
def test_step2_trigger_conditions():
"""pytest: every trigger condition must have signal, operator, value."""
fragments = _load_fragments_or_skip()
if fragments is None:
pytest.skip("ir_fragments.json not found")
errors = check_trigger_conditions(fragments)
assert not errors, f"trigger condition errors: {errors[:5]}"
def test_step2_duplicate_rule_ids():
"""pytest: no duplicate rule_ids across all fragments."""
fragments = _load_fragments_or_skip()
if fragments is None:
pytest.skip("ir_fragments.json not found")
errors = check_duplicate_rule_ids(fragments)
assert not errors, f"duplicate rule_id errors: {errors}"
def test_step2_action_types():
"""pytest: all actions must have valid types."""
fragments = _load_fragments_or_skip()
if fragments is None:
pytest.skip("ir_fragments.json not found")
errors = check_action_types(fragments)
assert not errors, f"action type errors: {errors[:5]}"
if __name__ == "__main__":
success = run_all_tests()
sys.exit(0 if success else 1)