fix: address M3 verifier findings on soak invariants and probe tests

This commit is contained in:
2026-07-22 02:21:33 -04:00
parent fdd36e0e1f
commit 3c9f306ea3
8 changed files with 126 additions and 12 deletions
+42
View File
@@ -292,7 +292,49 @@ class TestBackpressure:
await permit.release()
class FakeClock:
def __init__(self, start=1000.0):
self.t = start
def __call__(self):
return self.t
def advance(self, seconds):
self.t += seconds
class TestCancellation:
async def test_cancel_during_probe_returns_probe(self):
# 探针取消归还(铁律分支;verifier I3): 开路→冷却过后半开探针→
# transport 挂起中取消 → release_probe 必须发生,后续可再探
clock = FakeClock()
inner = InMemoryGate(config=_BREAKER, now=clock) # 门与 client 共用注入钟
gate = RecordingGate(inner)
client, _, _ = _client([_src()], ["hang"], now=clock, breaker=gate)
entry = await inner.try_enter("m1", "setup")
await inner.record_failure(entry, "source_dead", True) # force_open → OPEN
clock.advance(61.0) # 越过 cooldown_s=60 → HALF_OPEN
task = asyncio.create_task(client.recognize_text(b"jpg"))
await asyncio.sleep(0.05)
task.cancel()
with pytest.raises(asyncio.CancelledError):
await task
assert gate.probe_releases >= 1 # 探针已归还,不悬挂
probe = await inner.try_enter("m1", "again")
assert probe.allowed and probe.is_probe # 可再探 = 未悬挂的行为证据
async def test_cancel_during_backoff_sleep_propagates(self):
async def cancelling_sleep(delay):
raise asyncio.CancelledError()
client, limiter, _ = _client(
[_src()], [TransientError("boom", status_code=500)], sleep=cancelling_sleep
)
with pytest.raises(asyncio.CancelledError):
await client.recognize_text(b"jpg")
stats = await limiter.source_stats("m1")
assert stats.inflight == 0 # 退避期取消: permit 已在 finally 释放
async def test_cancel_during_transport_releases_permit(self):
src = _src(max_concurrency=1)
client, limiter, _ = _client([src], ["hang"])