mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 06:11:58 +00:00
62 lines
3.6 KiB
Python
62 lines
3.6 KiB
Python
"""测试用的分析算子替身(第 1 波各包并行:W1-a 的 app.analysis.run 落地前,入口的测试不等它)。
|
||
|
||
FakeOps 换掉模块上的 app.analysis.run(入口都按模块取 run),按请求交回 app.analysis.testing.fake_result 的假结论,
|
||
并记下每次调用拿到的请求、按名字解析到的输入快照、阈值、运行期依赖、上一层结论——测试据此断言入口交进去的东西。
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
import app.analysis as analysis_ops
|
||
from app.analysis.testing import fake_result
|
||
|
||
#: 每个算子缺省给的判定(fake_result 有现成判定语的那几个)
|
||
VERDICT = {"decompose": "driver", "baseline": "outside_band", "trend": "rising", "differ": "no_evidence",
|
||
"comparable": "comparable"}
|
||
|
||
|
||
class FakeOps:
|
||
"""errors:按调用的先后,每次要抛的异常(None 表示照常交回);用完了都照常交回。statuses / verdicts 同理。"""
|
||
|
||
def __init__(self, monkeypatch, *, errors: list[Any] | None = None, statuses: list[str] | None = None,
|
||
verdicts: list[str] | None = None, peek: list[str] | None = None, **overrides: Any) -> None:
|
||
self.calls: list[dict[str, Any]] = []
|
||
#: 每次调用时看一眼这几个名字能不能按 parents 取到上一层结论(visible)
|
||
self.peek = list(peek or [])
|
||
self.errors = list(errors or [])
|
||
self.statuses = list(statuses or [])
|
||
self.verdicts = list(verdicts or [])
|
||
self.overrides = overrides
|
||
monkeypatch.setattr(analysis_ops, "run", self.run)
|
||
|
||
async def run(self, request: dict[str, Any], resolve: Any, *, settings: Any, context: Any,
|
||
parents: Any = None) -> dict[str, Any]:
|
||
params = request.get("params") or {}
|
||
names = [params[k] for k in ("query", "base_query", "target_query") if params.get(k)]
|
||
snapshots = {name: resolve(name) for name in names}
|
||
parent = None
|
||
if isinstance(params.get("parent"), dict) and parents is not None:
|
||
parent = parents(params["parent"]["of"])
|
||
self.calls.append({"request": request, "snapshots": snapshots, "settings": dict(settings), "context": context,
|
||
"parent": parent, "parents": parents,
|
||
"visible": {n: parents is not None and parents(n) is not None for n in self.peek}})
|
||
error = self.errors.pop(0) if self.errors else None
|
||
if error is not None:
|
||
raise error
|
||
status = self.statuses.pop(0) if self.statuses else "ok"
|
||
verdict = self.verdicts.pop(0) if self.verdicts else VERDICT[request["op"]]
|
||
roles = {"query": "data", "base_query": "base", "target_query": "target"}
|
||
inputs = [{"ref": name, "artifact": snap["artifact"], "role": roles[key], "columns": snap["columns"],
|
||
"rows": "all"}
|
||
for key in ("query", "base_query", "target_query")
|
||
if (name := params.get(key)) and (snap := snapshots.get(name)) is not None]
|
||
if parent is not None:
|
||
from app.core.artifact_store import canonical_json, content_hash
|
||
|
||
inputs.append({"ref": params["parent"]["of"], "artifact": content_hash(canonical_json(parent)),
|
||
"role": "parent"})
|
||
return fake_result(op=request["op"], mode=request.get("mode"), verdict=verdict, status=status,
|
||
reason_code="history_short" if status == "refused" else None, inputs=inputs,
|
||
params=dict(params), settings=dict(settings), hypothesis=request.get("hypothesis"),
|
||
**self.overrides)
|