fix(gateway): control-socket fallback survives deep TMPDIR; tests bind-location-aware
CI runners put pytest tmp roots past sun_path, which routed every test home through the fallback: two tests assumed in-home binding. Tests now assert against the resolved bind location, and the fallback itself prefers /tmp when tempfile.gettempdir() is too deep to fit sun_path.
This commit is contained in:
@@ -96,8 +96,21 @@ def _default_socket_path(home: Path) -> Path:
|
||||
|
||||
|
||||
def _fallback_socket_path(home: Path) -> Path:
|
||||
"""Short temp-dir path for homes whose direct socket path exceeds sun_path."""
|
||||
return Path(tempfile.gettempdir()) / f"hermes-gw-{_home_hash(home)}.sock"
|
||||
"""Short temp-dir path for homes whose direct socket path exceeds sun_path.
|
||||
|
||||
Prefers ``tempfile.gettempdir()``; when even that yields a too-long path
|
||||
(deep $TMPDIR), falls back to ``/tmp`` on POSIX. If nothing fits, the
|
||||
tempdir candidate is returned anyway — bind will fail non-fatally and
|
||||
consumers use the scan layer.
|
||||
"""
|
||||
name = f"hermes-gw-{_home_hash(home)}.sock"
|
||||
candidates = [Path(tempfile.gettempdir()) / name]
|
||||
if not _IS_WINDOWS:
|
||||
candidates.append(Path("/tmp") / name)
|
||||
for candidate in candidates:
|
||||
if len(str(candidate).encode("utf-8")) <= _MAX_UNIX_PATH:
|
||||
return candidate
|
||||
return candidates[0]
|
||||
|
||||
|
||||
def resolve_server_socket_path(home: Path) -> tuple[Path, Optional[Path]]:
|
||||
|
||||
@@ -44,10 +44,24 @@ def _serve(home: Path, handlers=None):
|
||||
# Path resolution
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_short_home_binds_in_home(home: Path):
|
||||
bind, pointer = resolve_server_socket_path(home)
|
||||
assert bind == home / "gateway.sock"
|
||||
assert pointer is None
|
||||
def test_short_home_binds_in_home(tmp_path: Path):
|
||||
# A home short enough for sun_path binds in-home with no pointer.
|
||||
# tmp_path can exceed the limit on CI runners, so build one in the
|
||||
# system temp root directly.
|
||||
import tempfile
|
||||
|
||||
short_root = Path(tempfile.mkdtemp(prefix="hgw-", dir="/tmp"))
|
||||
try:
|
||||
short_home = short_root / ".hermes"
|
||||
short_home.mkdir()
|
||||
assert len(str(short_home / "gateway.sock").encode()) <= 100
|
||||
bind, pointer = resolve_server_socket_path(short_home)
|
||||
assert bind == short_home / "gateway.sock"
|
||||
assert pointer is None
|
||||
finally:
|
||||
import shutil
|
||||
|
||||
shutil.rmtree(short_root, ignore_errors=True)
|
||||
|
||||
|
||||
def test_long_home_uses_pointer_fallback(tmp_path: Path):
|
||||
@@ -163,7 +177,11 @@ def test_stop_removes_socket_and_pointer(home: Path):
|
||||
|
||||
|
||||
def test_stale_socket_file_is_replaced_on_bind(home: Path):
|
||||
(home / "gateway.sock").touch() # crashed predecessor's leftover
|
||||
# Plant the stale file at wherever the server will actually bind
|
||||
# (in-home OR the temp-dir fallback, depending on path length).
|
||||
bind, _ = resolve_server_socket_path(home)
|
||||
bind.parent.mkdir(parents=True, exist_ok=True)
|
||||
bind.touch() # crashed predecessor's leftover
|
||||
|
||||
async def scenario():
|
||||
server = GatewayControlServer(
|
||||
|
||||
Reference in New Issue
Block a user