Files
svcforge/tests/integration/test_helm_timeout.py
Nguyen Minh Phuc 4544765ec5 test: scope the process-group assertion to this run's own children
test_timeout_kills_the_whole_process_group asserted that `pgrep -f 'sleep 300'`
returns nothing. That is machine-global: it matches a leftover from an earlier run of
the same test, any unrelated 'sleep 300' on the box, and the shell running pgrep,
whose own command line contains the pattern being searched for.

Observed three spurious matches on a dev box, which produced a red run that looked
like a Python 3.14 regression in the helm timeout kill. It was not — the same test
failed identically on 3.12. A test that fails for reasons unrelated to the code is as
useless as one that cannot fail.

The sleep duration is now derived from the pid, so it cannot collide with another run,
and the test asserts up front that its own pattern matches nothing before it starts.

Verified: 3 consecutive passes on each of Python 3.12 and 3.14.
2026-07-19 09:39:38 +00:00

60 lines
2.5 KiB
Python

"""The one test that proves the timeout is real.
`bash -c "sleep N & sleep N"` is a miniature helm: a process that forks a child and waits on
another. Kill the direct child only and the backgrounded `sleep` reparents to init and keeps
running — which, when the process is helm, means a timed-out task retries while the original
helm is still mutating the same release.
This test needs a real process tree, so it lives in integration/. It needs no database: the
`pool` fixture in conftest is not autouse.
"""
from __future__ import annotations
import asyncio
import os
import subprocess
import pytest
from svcforge_core.adapters.helm import _run
# A duration nothing else on the machine will be sleeping for, derived from the pid so two
# concurrent runs cannot collide either.
#
# The obvious version of this test hardcodes `sleep 300` and then asserts
# `pgrep -f "sleep 300"` is empty. That assertion is machine-global: it matches ANY process
# whose command line contains the string, including a leftover from an earlier run of this
# same test, an unrelated `sleep 300` somewhere on the box, and — the subtle one — the shell
# that is running pgrep, whose own command line contains the pattern it is searching for.
# The result is a test that fails for reasons that have nothing to do with the code, which
# is as useless as one that cannot fail at all. Observed: three spurious matches on a
# developer box, and a red run blamed on a Python upgrade that was innocent.
_SLEEP_S = 30000 + (os.getpid() % 1000)
_PATTERN = f"sleep {_SLEEP_S}"
def _survivors() -> list[str]:
"""PIDs still matching this run's unique sleep. Empty means the group really died."""
out = subprocess.run( # noqa: S603
["pgrep", "-f", _PATTERN], # noqa: S607 - resolved via PATH, fixed argv
capture_output=True,
text=True,
check=False,
)
return [line for line in out.stdout.split() if line.strip()]
@pytest.mark.asyncio
async def test_timeout_kills_the_whole_process_group() -> None:
# Guard the guard: if the pattern already matches something, the assertion below would
# be meaningless. Fail loudly rather than report a false negative.
assert not _survivors(), f"{_PATTERN!r} matched before the test started; pick another marker"
argv = ["bash", "-c", f"{_PATTERN} & {_PATTERN}"] # child forks a grandchild
with pytest.raises(TimeoutError):
await _run(argv, timeout_s=1)
await asyncio.sleep(0.5)
assert not _survivors(), "grandchild survived: you killed the child, not the group"