fix: address M2 verifier findings in soak harness
This commit is contained in:
@@ -131,3 +131,51 @@ def write_report(run_id: str, content: str, out_dir: Path | str = "tests/outputs
|
||||
path = out / f"{run_id}.md"
|
||||
path.write_text(content, encoding="utf-8")
|
||||
return path
|
||||
|
||||
|
||||
# —— 活后端检查与预算帽(2026-07-21 verifier I1/M6/M7 补齐)——
|
||||
|
||||
SCENARIO_CALL_CAPS = { # 设计 §8.1 签字的单场景请求数上限
|
||||
"P1": 500,
|
||||
"P2": 450,
|
||||
"P3": 2500,
|
||||
"P4": 2500,
|
||||
"P5": 1000,
|
||||
"P6": 8000,
|
||||
}
|
||||
|
||||
|
||||
def capped_budget(scenario: str, requested_calls: int) -> int:
|
||||
"""预算帽: 请求数不越过签字上限(超出取上限并由调用方打印告知)。"""
|
||||
cap = SCENARIO_CALL_CAPS[scenario]
|
||||
return min(requested_calls, cap)
|
||||
|
||||
|
||||
async def inv_gate_reenterable(gate, sources: list[str]) -> None:
|
||||
"""不变量 1b: 跑后熔断门可再准入,探针不悬挂。
|
||||
|
||||
run 结束后已无 in-flight 调用,若某源仍处 HALF_OPEN 拒入 = 死探针
|
||||
悬挂(只能等 TTL);OPEN 冷却中属故障源的合法状态,不算击穿。
|
||||
拿到的探针当场归还(release_probe),不留新悬挂。
|
||||
"""
|
||||
for name in sources:
|
||||
decision = await gate.try_enter(name, "scoreboard-probe")
|
||||
if decision.allowed:
|
||||
if decision.is_probe:
|
||||
await gate.release_probe(decision)
|
||||
continue
|
||||
assert str(decision.state) != "half_open", (
|
||||
f"源 {name} 跑后仍 HALF_OPEN 拒入(探针悬挂,retry_after={decision.retry_after_s:.1f}s)"
|
||||
)
|
||||
|
||||
|
||||
def inv_fault_errors_present(rows: list[Row], *, fault_source_names: list[str]) -> None:
|
||||
"""不变量 2c(P5/P6): 配置了故障源则错误必然出现且落在故障源上。
|
||||
|
||||
注入"比例"的精确吻合依赖具体混编配置,自动断言留待 P5 实跑校准
|
||||
(findings §4 条 2);此处先钉存在性: 故障源零错误 = 故障根本没被打到。
|
||||
"""
|
||||
if not fault_source_names:
|
||||
return
|
||||
fault_errors = [r for r in rows if r.get("error") and r["source_name"] in fault_source_names]
|
||||
assert fault_errors, f"故障源 {fault_source_names} 零错误行——故障混编未生效"
|
||||
|
||||
Reference in New Issue
Block a user