"""SIGTERM should stop the server subprocess promptly (<< 30s platform window).""" from __future__ import annotations import json import os import signal import subprocess import sys import time import unittest import urllib.request from pathlib import Path ROOT = Path(__file__).resolve().parents[1] class TestSigterm(unittest.TestCase): def test_sigterm_exits_quickly(self) -> None: port = "19090" env = os.environ.copy() env["STRATEGY_ID"] = "sigterm-test" env["PORT"] = port env["HOST"] = "127.0.0.1" proc = subprocess.Popen( [sys.executable, "-m", "src.server"], cwd=str(ROOT), env=env, stdout=subprocess.DEVNULL, stderr=subprocess.PIPE, ) try: # wait until health is up url = f"http://127.0.0.1:{port}/health" deadline = time.time() + 5 while time.time() < deadline: try: with urllib.request.urlopen(url, timeout=0.5) as resp: data = json.loads(resp.read().decode("utf-8")) if resp.status == 200 and data.get("status") == "ok": break except Exception: time.sleep(0.1) else: if proc.poll() is not None: self.fail(f"server exited before health check: {proc.stderr.read().decode(errors='replace')}") self.fail("server did not become healthy in time") t0 = time.time() proc.send_signal(signal.SIGTERM) try: proc.wait(timeout=5) except subprocess.TimeoutExpired: proc.kill() self.fail("server did not exit within 5s after SIGTERM (platform allows ≤30s)") elapsed = time.time() - t0 self.assertLess(elapsed, 5.0, f"exit took {elapsed:.2f}s") self.assertEqual(proc.returncode, 0, "SIGTERM must exit cleanly, not kill the process") finally: if proc.poll() is None: proc.kill() proc.wait(timeout=2) if proc.stderr: proc.stderr.close() if __name__ == "__main__": unittest.main()