Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions agent/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ dev = [
"pytest",
"pygments==2.20.0",
"pytest-cov==7.1.0",
"pytest-timeout==2.4.0", # per-test wall-clock cap: a single hung test (network/subprocess/Bedrock without its own timeout) must fail LOUDLY with a traceback, not silently burn the whole build-verify budget (ABCA-684/686: one hang stalled the baseline build past its 3600s ceiling)
"vulture==2.16", # dead-code detection (#282): unused functions/classes ruff F can't see
]

Expand Down Expand Up @@ -122,6 +123,18 @@ ignore = [
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["src"]
# pytest-timeout: hard wall-clock cap PER TEST. A unit test that blocks (an
# unmocked network/subprocess/Bedrock call with no timeout of its own) otherwise
# hangs the whole `mise run build` until the 3600s build-verify ceiling kills it,
# turning one flaky test into an un-diagnosable 60-min stall (ABCA-684/686). With
# this, the offending test fails with a dumped stack in 120s and the suite moves on.
# method="signal" (SIGALRM) actually ABORTS the hung test — pytest runs
# single-threaded in the main thread here, so the alarm interrupts even a blocked
# C-level/socket call. method="thread" only PRINTS the stack and cannot interrupt a
# syscall-blocked test (proven live on ABCA-688: the thread method dumped the stack
# but the build kept hanging ~55 min until the 3600s ceiling). signal is the real fix.
timeout = 120
timeout_method = "signal"

[tool.coverage.run]
branch = true
Expand Down
15 changes: 13 additions & 2 deletions agent/tests/test_aws_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,15 @@ def _slow_build(_arn: str) -> Any:
return MagicMock(name="scoped")

def _worker() -> None:
start.wait()
# Bounded barrier wait. A bare Barrier(20).wait() blocks FOREVER if
# even one of the 20 threads never arrives (e.g. a worker reaped under
# container memory pressure, or thread creation throttled) — every
# survivor then hangs here and the main thread hangs in join() below,
# stalling the whole `mise run build` until the 3600s ceiling. This is
# the ECS-only flaky hang chased across ABCA-684/686/688 (pytest-timeout
# only fixed the SYMPTOM; this Barrier is the ROOT cause). A timeout
# makes the barrier raise BrokenBarrierError so the test fails fast.
start.wait(timeout=30)
session = get_session()
with lock:
results.append(session)
Expand All @@ -140,8 +148,11 @@ def _worker() -> None:
threads = [threading.Thread(target=_worker) for _ in range(20)]
for t in threads:
t.start()
# Bounded joins for the same reason — a worker that died before
# appending must not hang the suite; a leftover live thread trips the
# assertion below (results != 20) rather than blocking indefinitely.
for t in threads:
t.join()
t.join(timeout=60)

mk_build.assert_called_once()
assert len(results) == 20
Expand Down
14 changes: 14 additions & 0 deletions agent/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading