Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
"""Event-loop liveness for webhook GitHub-comment delivery (Pattern A).
|
||||
|
||||
``_deliver_github_comment`` shells out to ``gh`` with a 30s timeout. Before
|
||||
the off-loop fix it called ``subprocess.run`` inline from an ``async def``,
|
||||
freezing the entire gateway event loop — every adapter, timer, and health
|
||||
check — for up to the full timeout while the network call ran.
|
||||
|
||||
The contract under test is behavioral, not structural: while delivery is
|
||||
awaiting the subprocess, OTHER coroutines on the same loop must keep
|
||||
running. A ticker coroutine sampled at the moment delivery returns proves
|
||||
it — on the blocking implementation it manages ~1 tick during a 1-second
|
||||
``gh`` run; off-loop it manages ~20.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import stat
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
from gateway.platforms.webhook import WebhookAdapter
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def fake_gh(tmp_path, monkeypatch):
|
||||
"""A ``gh`` stub that sleeps 1s then succeeds, prepended to PATH."""
|
||||
if sys.platform.startswith("win"):
|
||||
pytest.skip("POSIX shell stub")
|
||||
gh = tmp_path / "gh"
|
||||
gh.write_text("#!/bin/bash\nsleep 1\necho posted\nexit 0\n", encoding="utf-8")
|
||||
gh.chmod(gh.stat().st_mode | stat.S_IXUSR)
|
||||
monkeypatch.setenv("PATH", f"{tmp_path}{os.pathsep}{os.environ['PATH']}")
|
||||
return gh
|
||||
|
||||
|
||||
class TestGithubCommentDeliveryOffLoop:
|
||||
@pytest.mark.asyncio
|
||||
async def test_loop_stays_live_during_gh_subprocess(self, fake_gh):
|
||||
"""Concurrent coroutines keep ticking while ``gh`` runs (~1s)."""
|
||||
adapter = WebhookAdapter.__new__(WebhookAdapter)
|
||||
|
||||
ticks = []
|
||||
stop = False
|
||||
|
||||
async def ticker():
|
||||
while not stop:
|
||||
ticks.append(1)
|
||||
await asyncio.sleep(0.05)
|
||||
|
||||
task = asyncio.create_task(ticker())
|
||||
await asyncio.sleep(0) # let the ticker take its first turn
|
||||
|
||||
result = await adapter._deliver_github_comment(
|
||||
"test body",
|
||||
{"deliver_extra": {"repo": "owner/repo", "pr_number": "1"}},
|
||||
)
|
||||
ticks_during_delivery = len(ticks)
|
||||
|
||||
stop = True
|
||||
try:
|
||||
await asyncio.wait_for(task, timeout=2)
|
||||
except asyncio.TimeoutError:
|
||||
task.cancel()
|
||||
|
||||
assert result.success is True
|
||||
# Blocking implementation: ~1 tick (loop frozen for the 1s gh run).
|
||||
# Off-loop implementation: ~20 ticks. 10 is a comfortable midpoint
|
||||
# that stays robust on slow CI runners.
|
||||
assert ticks_during_delivery >= 10, (
|
||||
f"event loop starved during gh delivery: only "
|
||||
f"{ticks_during_delivery} ticker turns ran in ~1s"
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_delivery_result_faithful_off_loop(self, fake_gh):
|
||||
"""Off-loop offload must not change the SendResult contract."""
|
||||
adapter = WebhookAdapter.__new__(WebhookAdapter)
|
||||
result = await adapter._deliver_github_comment(
|
||||
"body",
|
||||
{"deliver_extra": {"repo": "owner/repo", "pr_number": "7"}},
|
||||
)
|
||||
assert result.success is True
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_invalid_inputs_still_rejected_before_subprocess(self):
|
||||
"""Validation short-circuits stay synchronous and unchanged."""
|
||||
adapter = WebhookAdapter.__new__(WebhookAdapter)
|
||||
bad_repo = await adapter._deliver_github_comment(
|
||||
"body", {"deliver_extra": {"repo": "not a repo!", "pr_number": "1"}}
|
||||
)
|
||||
assert bad_repo.success is False
|
||||
bad_pr = await adapter._deliver_github_comment(
|
||||
"body", {"deliver_extra": {"repo": "owner/repo", "pr_number": "-2"}}
|
||||
)
|
||||
assert bad_pr.success is False
|
||||
Reference in New Issue
Block a user