Files
2026Technology-Competition/tests/test_source_aggregator.py
T
lhl 61ac47c7af refactor(impact): CodeParser 多语言就绪——语言适配器注册表(仅 Java,行为不变)
- BaseLanguageParser 抽象基类 + JavaLanguageParser(迁移全部 Java 正则,输出逐字节一致)
- LANGUAGE_PARSERS 注册表 + register_language_parser 扩展点(新增语言=加适配器类并注册,下游零改动)
- CodeParser 分发器:parse(root, language=None) 自动按扩展名探测 / 显式语言覆盖 / 多语言合并(language 逗号连接)/ 无源码或语言不支持抛 CodeParseError
- source_aggregator 新增 existing_system_language 透传;config.py 探测扩展名补 .py/.ts/.go/.cs
- 删除自动探测下不可达死分支;全量 362 passed / 99.28%(基线 351/99.27%)
- 门禁复跑 PASS:summary 与 MVP 基线一致(16/5/8/3/50/0),概要设计书 13 章
2026-08-23 18:49:34 +08:00

230 lines
7.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import pytest
from pathlib import Path
from genesis.data_models import StructuredSource
from genesis.impact.code_parser import CodeParseError
from genesis.parsers.source_aggregator import SourceParser
from tests.docx_helpers import make_rule_doc, new_document, save_document
from tests.excel_helpers import new_workbook, save_workbook
def _xlsx(tmp_path, name: str = "source.xlsx") -> str:
wb = new_workbook({"機能一覧": [["機能ID", "機能名"], ["A001", "社員登録"]]})
path = tmp_path / name
wb.save(path)
return str(path)
def test_parse_full_assembly(tmp_path):
xlsx = _xlsx(tmp_path)
template = save_document(tmp_path, new_document())
rule = make_rule_doc(tmp_path, [("H1", "1. 機能一覧の書き方")])
instr = make_rule_doc(tmp_path, [("H1", "2. 機能一覧の作成手順")])
result = SourceParser().parse(
requirement_paths=[xlsx],
template_path=template,
write_instruction_paths=[instr],
rule_paths=[rule],
)
assert isinstance(result, StructuredSource)
assert len(result.tables) == 1
assert result.template is not None
assert result.template.file_name == "source.docx"
assert len(result.rule_docs) == 2
assert all(r.category == "write" for r in result.rule_docs)
assert result.image_analyses == []
assert result.existing_system is None
def test_parse_without_template_and_rules(tmp_path):
xlsx = _xlsx(tmp_path)
result = SourceParser().parse(requirement_paths=[xlsx])
assert len(result.tables) == 1
assert result.template is None
assert result.rule_docs == []
def test_parse_missing_requirement_file(tmp_path):
with pytest.raises(FileNotFoundError):
SourceParser().parse(requirement_paths=[tmp_path / "missing.xlsx"])
def test_parse_missing_template_file(tmp_path):
xlsx = _xlsx(tmp_path)
with pytest.raises(FileNotFoundError):
SourceParser().parse(requirement_paths=[xlsx], template_path=tmp_path / "missing.docx")
def test_parse_unknown_extension_in_requirements(tmp_path):
bad = tmp_path / "note.txt"
bad.write_text("hello", encoding="utf-8")
with pytest.raises(ValueError, match="不支持的文件类型"):
SourceParser().parse(requirement_paths=[bad])
def test_parse_unknown_extension_in_rules(tmp_path):
bad = tmp_path / "note.txt"
bad.write_text("hello", encoding="utf-8")
with pytest.raises(ValueError, match="不支持的文件类型"):
SourceParser().parse(rule_paths=[bad])
def test_parse_unknown_extension_in_template(tmp_path):
bad = tmp_path / "note.txt"
bad.write_text("hello", encoding="utf-8")
with pytest.raises(ValueError, match="不支持的文件类型"):
SourceParser().parse(template_path=bad)
def test_parse_missing_rule_file(tmp_path):
with pytest.raises(FileNotFoundError):
SourceParser().parse(rule_paths=[tmp_path / "missing.docx"])
def test_parse_extensionless_requirement(tmp_path):
bad = tmp_path / "note"
bad.write_text("hello", encoding="utf-8")
with pytest.raises(ValueError, match="无扩展名"):
SourceParser().parse(requirement_paths=[bad])
def test_parse_extensionless_rule(tmp_path):
bad = tmp_path / "note"
bad.write_text("hello", encoding="utf-8")
with pytest.raises(ValueError, match="无扩展名"):
SourceParser().parse(rule_paths=[bad])
# ---------- T7: DRY _validate_path helperI8 ----------
def test_validate_path_rejects_bad_extension(tmp_path):
from genesis.parsers.source_aggregator import _validate_path
bad = tmp_path / "note.txt"
bad.write_text("x", encoding="utf-8")
with pytest.raises(ValueError, match="不支持的文件类型"):
_validate_path(bad, (".xlsx",))
def test_validate_path_rejects_missing_file(tmp_path):
from genesis.parsers.source_aggregator import _validate_path
with pytest.raises(FileNotFoundError):
_validate_path(tmp_path / "missing.xlsx", (".xlsx",))
def test_validate_path_returns_resolved_path(tmp_path):
from genesis.parsers.source_aggregator import _validate_path
good = tmp_path / "ok.xlsx"
good.write_bytes(b"x")
result = _validate_path(str(good), (".xlsx",))
assert isinstance(result, Path)
assert result.suffix.lower() == ".xlsx"
# ---------- Impact Agent MVPexisting_system_path2026-08-23 ----------
JAVA_PROJECT = {
"trade-order/OrderController.java": (
"package com.trade.order.controller;\n"
"import org.springframework.web.bind.annotation.*;\n"
"@RestController\n"
"@RequestMapping(\"/api/order\")\n"
"public class OrderController {\n"
" @GetMapping(\"/{id}\")\n"
" public String get(Long id) { return \"ok\"; }\n"
"}\n"
),
"trade-order/OrderServiceImpl.java": (
"package com.trade.order.service;\n"
"import org.springframework.stereotype.Service;\n"
"@Service\n"
"public class OrderServiceImpl {\n"
" public void createOrder() {}\n"
"}\n"
),
"trade-order/OrderDO.java": (
"package com.trade.order.entity;\n"
"import com.baomidou.mybatisplus.annotation.TableName;\n"
"@TableName(\"trade_order\")\n"
"public class OrderDO {\n"
" private Long id;\n"
"}\n"
),
}
def _java_project(tmp_path) -> str:
for rel, content in JAVA_PROJECT.items():
p = tmp_path / "existing" / rel
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text(content, encoding="utf-8")
return str(tmp_path / "existing")
def test_parse_with_existing_system_path(tmp_path):
xlsx = _xlsx(tmp_path)
existing = _java_project(tmp_path)
result = SourceParser().parse(requirement_paths=[xlsx], existing_system_path=existing)
assert result.existing_system is not None
names = {c.class_name for c in result.existing_system.controller_layer}
assert "OrderController" in names
assert {s.class_name for s in result.existing_system.service_layer} == {"OrderServiceImpl"}
assert {e.class_name for e in result.existing_system.entity_layer} == {"OrderDO"}
def test_parse_without_existing_system_path_keeps_none(tmp_path):
xlsx = _xlsx(tmp_path)
result = SourceParser().parse(requirement_paths=[xlsx])
assert result.existing_system is None
def test_parse_existing_system_path_invalid_dir_raises(tmp_path):
xlsx = _xlsx(tmp_path)
with pytest.raises(CodeParseError):
SourceParser().parse(requirement_paths=[xlsx], existing_system_path=tmp_path / "nope")
def test_parse_existing_system_path_without_java_raises(tmp_path):
xlsx = _xlsx(tmp_path)
empty = tmp_path / "empty"
empty.mkdir()
with pytest.raises(CodeParseError):
SourceParser().parse(requirement_paths=[xlsx], existing_system_path=empty)
# ---------- 多语言预备重构:existing_system_language 透传 ----------
def test_parse_existing_system_language_explicit(tmp_path):
xlsx = _xlsx(tmp_path)
existing = _java_project(tmp_path)
result = SourceParser().parse(
requirement_paths=[xlsx],
existing_system_path=existing,
existing_system_language="java",
)
assert result.existing_system is not None
assert {c.class_name for c in result.existing_system.controller_layer} == {"OrderController"}
def test_parse_existing_system_language_unsupported_raises(tmp_path):
xlsx = _xlsx(tmp_path)
existing = _java_project(tmp_path)
with pytest.raises(CodeParseError):
SourceParser().parse(
requirement_paths=[xlsx],
existing_system_path=existing,
existing_system_language="kotlin",
)