Skip to content

Commit 22a4a6e

Browse files
committed
test: add regression coverage for optimization fixes
Ten tests covering the verification 3-tuple contract and sink/source parity, BasePromptAgent placeholder+emit, canonical AgentResult, NVD 2.0 field validation, codeql DB-error formatting, insecure-posture warnings, and the task_manager create-time queue.
1 parent 95167b6 commit 22a4a6e

1 file changed

Lines changed: 197 additions & 0 deletions

File tree

Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
"""针对本轮质量优化所修复问题的回归测试。
2+
3+
覆盖:
4+
- 验证 Agent 统一为三元返回,且 sink/source 行为参数化正确(防 arize 漂移复发)
5+
- codeql 生成类 Agent 共享基类的占位符/事件行为
6+
- cve_fetcher 使用 NVD 2.0 字段校验
7+
- codeql._format_db_error 复用
8+
- API 不安全配置告警
9+
- SSE 历史事件回放
10+
"""
11+
12+
import asyncio
13+
14+
15+
# ---------------------------------------------------------------------------
16+
# 验证 Agent:三元返回 + sink/source 参数化
17+
# ---------------------------------------------------------------------------
18+
def test_verification_agents_share_base_and_params():
19+
from pure_auto_codeql.agents.base_verification_agent import BaseVerificationAgent
20+
from pure_auto_codeql.agents.sink_verification_agent import SinkVerificationAgent
21+
from pure_auto_codeql.agents.source_verification_agent import SourceVerificationAgent
22+
23+
assert issubclass(SinkVerificationAgent, BaseVerificationAgent)
24+
assert issubclass(SourceVerificationAgent, BaseVerificationAgent)
25+
26+
sink = SinkVerificationAgent(analyzer=None, database_path="db", language="Java")
27+
src = SourceVerificationAgent(analyzer=None, database_path="db", language="Java")
28+
29+
assert sink.kind == "sink" and sink.label == "Sink"
30+
assert src.kind == "source" and src.label == "Source"
31+
assert sink._default_agent_name == "Sink Verification Agent"
32+
assert src._default_agent_type == "source_verification"
33+
34+
35+
def test_verification_missing_template_returns_three_tuple():
36+
"""模板加载失败时必须返回 3 元组,否则 pipeline 三元解包会 ValueError。"""
37+
from pure_auto_codeql.agents.sink_verification_agent import SinkVerificationAgent
38+
from pure_auto_codeql.agents.source_verification_agent import SourceVerificationAgent
39+
40+
for cls in (SinkVerificationAgent, SourceVerificationAgent):
41+
agent = cls(analyzer=None, database_path="db", language="java")
42+
# 强制模板加载抛错
43+
agent._load_verification_template = lambda: (_ for _ in ()).throw(RuntimeError("boom"))
44+
result = asyncio.run(agent.verify_analysis_result("{}"))
45+
assert isinstance(result, tuple) and len(result) == 3
46+
is_valid, error_message, verification_query = result # 必须可三元解包
47+
assert is_valid is False
48+
assert "boom" in error_message
49+
assert verification_query is None
50+
51+
52+
def test_verification_requirement_and_extract():
53+
from pure_auto_codeql.agents.sink_verification_agent import SinkVerificationAgent
54+
55+
agent = SinkVerificationAgent(analyzer=None, database_path="db", language="java")
56+
req = agent._build_requirement("ANALYSIS")
57+
assert "Sink 分析结果" in req and "ANALYSIS" in req
58+
59+
extracted = agent._extract_codeql_from_response("```ql\nimport java\nfrom X\n```")
60+
assert extracted == "import java\nfrom X"
61+
62+
63+
# ---------------------------------------------------------------------------
64+
# codeql 生成类 Agent 共享基类
65+
# ---------------------------------------------------------------------------
66+
def test_prompt_agent_fill_placeholders_and_emit():
67+
from pure_auto_codeql.agents.codeql_gen_agents.base import BasePromptAgent
68+
from pure_auto_codeql.agents.codeql_gen_agents.codeql_gen_agent import CodeQLGenAgent
69+
70+
agent = CodeQLGenAgent(analyzer=None)
71+
assert isinstance(agent, BasePromptAgent)
72+
73+
out = agent._fill_placeholders("a=[[A]] b=[[B]] c=[[C]]", {"A": "1", "B": None, "C": "3"})
74+
assert out == "a=1 b= c=3"
75+
76+
# 事件收集:确认 event_type 与统一命名一致
77+
events = []
78+
79+
async def cb(evt):
80+
events.append(evt)
81+
82+
asyncio.run(agent._emit_event(cb, "agent_complete", "done", data={"ok": True}))
83+
assert len(events) == 1
84+
assert events[0]["type"] == "agent_complete"
85+
assert events[0]["agent_name"] == "CodeQL Generation Agent"
86+
assert events[0]["data"] == {"ok": True}
87+
88+
# 无回调时应为 no-op(不抛异常)
89+
asyncio.run(agent._emit_event(None, "agent_start", "x"))
90+
91+
92+
def test_gen_agents_use_canonical_agent_result():
93+
"""生成类 Agent 应使用 llm_service.AgentResult,而非各自的本地重定义。"""
94+
import pure_auto_codeql.agents.codeql_gen_agents.codeql_gen_agent as gen_mod
95+
from pure_auto_codeql.services.llm_service import AgentResult
96+
97+
assert gen_mod.AgentResult is AgentResult
98+
99+
100+
# ---------------------------------------------------------------------------
101+
# cve_fetcher:NVD 2.0 字段
102+
# ---------------------------------------------------------------------------
103+
def test_validate_cve_data_accepts_nvd2_fields():
104+
from pure_auto_codeql.utils.cve_fetcher import validate_cve_data
105+
106+
good = {
107+
"resultsPerPage": 1,
108+
"totalResults": 1,
109+
"vulnerabilities": [
110+
{
111+
"cve": {
112+
"id": "CVE-2021-0000",
113+
"vulnStatus": "Analyzed",
114+
"published": "2021-01-01T00:00:00",
115+
"lastModified": "2021-02-01T00:00:00",
116+
# 故意不含旧的 state / modified 字段,验证不再被要求
117+
}
118+
}
119+
],
120+
}
121+
assert validate_cve_data(good) is True
122+
123+
# 使用旧字段 state/modified 而缺少 NVD 2.0 字段时应校验失败
124+
legacy_only = {
125+
"resultsPerPage": 1,
126+
"totalResults": 1,
127+
"vulnerabilities": [
128+
{
129+
"cve": {
130+
"id": "CVE-2021-0001",
131+
"state": "PUBLISHED",
132+
"published": "2021-01-01T00:00:00",
133+
"modified": "2021-02-01T00:00:00",
134+
}
135+
}
136+
],
137+
}
138+
assert validate_cve_data(legacy_only) is False
139+
140+
141+
# ---------------------------------------------------------------------------
142+
# codeql._format_db_error
143+
# ---------------------------------------------------------------------------
144+
def test_format_db_error_includes_path_and_suggestions():
145+
from pure_auto_codeql.utils.codeql import _format_db_error
146+
147+
msg = _format_db_error("some failure", "/tmp/db")
148+
assert "数据库错误" in msg
149+
assert "some failure" in msg
150+
assert "/tmp/db" in msg
151+
assert "codeql database create" in msg
152+
153+
154+
# ---------------------------------------------------------------------------
155+
# API 不安全配置告警
156+
# ---------------------------------------------------------------------------
157+
def test_insecure_posture_warns_on_public_bind_without_token(caplog):
158+
import logging
159+
160+
from pure_auto_codeql.api import server
161+
162+
class _Cfg:
163+
host = "0.0.0.0"
164+
auth_token = ""
165+
workers = 1
166+
167+
with caplog.at_level(logging.WARNING):
168+
server._warn_on_insecure_posture(_Cfg())
169+
assert any("非回环地址" in r.message or "auth_token" in r.message for r in caplog.records)
170+
171+
172+
def test_insecure_posture_warns_on_multiworker(caplog):
173+
import logging
174+
175+
from pure_auto_codeql.api import server
176+
177+
class _Cfg:
178+
host = "127.0.0.1"
179+
auth_token = "secret"
180+
workers = 4
181+
182+
with caplog.at_level(logging.WARNING):
183+
server._warn_on_insecure_posture(_Cfg())
184+
assert any("workers" in r.message for r in caplog.records)
185+
186+
187+
# ---------------------------------------------------------------------------
188+
# SSE 历史事件回放
189+
# ---------------------------------------------------------------------------
190+
def test_task_manager_creates_event_queue_on_create():
191+
"""create_task 时即应建立事件队列,避免排队中的任务 SSE 连接 404。"""
192+
from pure_auto_codeql.api.task_manager import TaskManager
193+
194+
tm = TaskManager()
195+
task_id = tm.create_task("CVE-TEST")
196+
assert task_id in tm._event_queues
197+
assert task_id in tm._task_events

0 commit comments

Comments
 (0)