Files
2026Technology-Competition/tests/test_source_aggregator.py
T
lhl 2e11720a2d feat(impact): Impact Agent MVP —— 变更点定位 + 影响调查书(追加改修场景)
- 门控:用户提供 existing_system 路径 → 进入影响调查;未提供 → 原流程不变
- CodeParser 解析 Java(@RestController/@Service/@Entity/@TableName)+ ExistingSystemExplorer 组装
- ImpactAgent 变更点定位(变更区分×既存対応 确定性比对,无 LLM)→ ImpactReport(JSON 可下载)
- 影响调查结果作为 Writer 生成概要设计书的主上下文({{impact}},无专用影响章)
- source_aggregator 解除 existing_system=None 硬编码
- 既有系统样本 sunOnly/stock-trade-system(无 LICENSE,仅测试输入,保留来源标注)
- 新造股票交易域追加改修样本 要件定義_追加改修_股票.xlsx(对齐 sunOnly 真实类名)
- 全量 351 passed / 99.27% 覆盖;门禁 PASS(16 要素:新规5/変更8/削除3/未受影响50)
2026-08-23 17:59:08 +08:00

202 lines
6.7 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)