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 章
This commit is contained in:
+113
-2
@@ -1,9 +1,17 @@
|
||||
"""CodeParser 测试(Impact Agent MVP):Java 项目解析 → CodeStructure。"""
|
||||
"""CodeParser 测试(Impact Agent MVP):多语言适配器注册表 + Java 项目解析。"""
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from genesis.impact.code_parser import CodeParseError, CodeParser
|
||||
from genesis.data_models import ControllerInfo, EndpointInfo
|
||||
from genesis.impact.code_parser import (
|
||||
LANGUAGE_PARSERS,
|
||||
BaseLanguageParser,
|
||||
CodeParseError,
|
||||
CodeParser,
|
||||
JavaLanguageParser,
|
||||
register_language_parser,
|
||||
)
|
||||
|
||||
FIXTURE_JAVA = {
|
||||
"trade-order/src/main/java/com/trade/order/controller/OrderController.java": (
|
||||
@@ -124,3 +132,106 @@ def test_parse_real_sample_has_entities_and_endpoints():
|
||||
assert result.entities, "既有系统样本应至少有一个实体类"
|
||||
assert result.endpoints, "既有系统样本应至少有一个端点"
|
||||
assert result.services, "既有系统样本应至少有一个 @Service 类"
|
||||
|
||||
|
||||
# ---------- 多语言适配器注册表(预备性重构) ----------
|
||||
|
||||
class _FakeParser(BaseLanguageParser):
|
||||
"""测试用假语言适配器:验证注册表/分发/多语言合并。"""
|
||||
|
||||
language = "fake"
|
||||
extensions = (".fx",)
|
||||
|
||||
def parse_file(self, text: str, rel: str) -> dict:
|
||||
return {
|
||||
"imports": ["fake.import"],
|
||||
"classes": [{"class_name": "FakeThing", "path": rel}],
|
||||
"controllers": [
|
||||
ControllerInfo(
|
||||
name="FakeThing", class_name="FakeThing", path=rel,
|
||||
base_path="/fake", endpoints=["/fake"], source_uri=rel,
|
||||
)
|
||||
],
|
||||
"services": [],
|
||||
"entities": [],
|
||||
"endpoints": [
|
||||
EndpointInfo(
|
||||
method="GET", path="/fake", controller="FakeThing",
|
||||
description="", source_uri=rel,
|
||||
)
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def test_language_registry_contains_java():
|
||||
assert "java" in LANGUAGE_PARSERS
|
||||
assert issubclass(LANGUAGE_PARSERS["java"], BaseLanguageParser)
|
||||
|
||||
|
||||
def test_java_parser_adapter_metadata():
|
||||
jp = JavaLanguageParser()
|
||||
assert jp.language == "java"
|
||||
assert ".java" in jp.extensions
|
||||
|
||||
|
||||
def test_base_parser_parse_file_contract():
|
||||
with pytest.raises(NotImplementedError):
|
||||
BaseLanguageParser().parse_file("text", "rel")
|
||||
|
||||
|
||||
def test_parse_explicit_language(tmp_path):
|
||||
root = _make_project(tmp_path, FIXTURE_JAVA)
|
||||
result = CodeParser().parse(root, language="java")
|
||||
assert {c.class_name for c in result.controllers} == {"OrderController"}
|
||||
assert result.language == "java"
|
||||
|
||||
|
||||
def test_parse_unsupported_language_raises(tmp_path):
|
||||
root = _make_project(tmp_path, FIXTURE_JAVA)
|
||||
with pytest.raises(CodeParseError):
|
||||
CodeParser().parse(root, language="kotlin")
|
||||
|
||||
|
||||
def test_parse_explicit_language_no_files_raises(tmp_path):
|
||||
(tmp_path / "readme.txt").write_text("hello", encoding="utf-8")
|
||||
with pytest.raises(CodeParseError):
|
||||
CodeParser().parse(tmp_path, language="java")
|
||||
|
||||
|
||||
def test_java_parse_file_interface(tmp_path):
|
||||
jp = JavaLanguageParser()
|
||||
text = FIXTURE_JAVA["trade-order/src/main/java/com/trade/order/controller/OrderController.java"]
|
||||
out = jp.parse_file(text, "OrderController.java")
|
||||
assert len(out["controllers"]) == 1
|
||||
assert out["controllers"][0].class_name == "OrderController"
|
||||
assert {"/api/order/{id}", "/api/order/create"} <= {e.path for e in out["endpoints"]}
|
||||
assert out["classes"] == [{"class_name": "OrderController", "path": "OrderController.java"}]
|
||||
|
||||
|
||||
def test_register_and_dispatch_new_language(tmp_path):
|
||||
register_language_parser("fake", _FakeParser)
|
||||
try:
|
||||
root = _make_project(tmp_path, {
|
||||
"app/main.fx": "FAKE",
|
||||
"trade-order/src/main/java/com/trade/order/controller/OrderController.java": (
|
||||
FIXTURE_JAVA["trade-order/src/main/java/com/trade/order/controller/OrderController.java"]
|
||||
),
|
||||
})
|
||||
result = CodeParser().parse(root)
|
||||
names = {c.class_name for c in result.controllers}
|
||||
assert names == {"OrderController", "FakeThing"}
|
||||
assert result.language == "fake,java" # 多语言按名排序逗号连接
|
||||
assert any(e.path == "/fake" for e in result.endpoints)
|
||||
finally:
|
||||
LANGUAGE_PARSERS.pop("fake", None)
|
||||
|
||||
|
||||
def test_register_language_explicit_only(tmp_path):
|
||||
register_language_parser("fake", _FakeParser)
|
||||
try:
|
||||
root = _make_project(tmp_path, {"a/main.fx": "FAKE"})
|
||||
result = CodeParser().parse(root, language="fake")
|
||||
assert result.language == "fake"
|
||||
assert [c.class_name for c in result.controllers] == ["FakeThing"]
|
||||
finally:
|
||||
LANGUAGE_PARSERS.pop("fake", None)
|
||||
|
||||
Reference in New Issue
Block a user