Files
hermes-agent/tests/gateway/test_delivery_ledger_fd_leak.py
Teknium 3c7069bdcb refactor(gateway): dead-code removal, helper unification, defensive-layer collapse and rationale-preserving comment compaction across 40 modules
authz_mixin, browser_control_broker, delivery, delivery_ledger, display_config, drain_control,
hosted_room_links/peer/policy_checkpoint, hosted_rooms, platform_registry, relay/__init__,
relay/ws_transport, run.py and slash_commands.py (comments), session_context, session_state,
streaming_tts_consumer, turn_lease and small modules.

- HostedRoomPolicyCheckpoint._apply_event -> per-kind handler table
- WebSocketRelayTransport._handle_frame -> frame-handler table
- GatewayAuthorizationMixin: unified adapter setting/flag/extra readers
- dead symbols removed (verified zero references): RoomLinkProbe/select_room_link,
  relay_bot_username, is_restart_loop_tripped, debug_rows, DeadTargetRegistry.all_dead,
  BrowserControlBroker.detach_owner/_prune_tickets, StreamingTTSConsumer.started/_enqueue_done/
  _iter_stream_chunks/_next_stream_chunk, RecoverableHandleCache.status_for, _auth_env,
  _copy_default_catalog, _parse_timestamp_prefix, _present_* helpers, _send_result_error_kind,
  _truthy_env, SessionFieldView/TurnLeaseTokenView dunder shims, and their orphaned tests.
- lost WHY/invariant text from the earlier compaction restored compactly (541 hunks audited)
2026-09-02 13:30:50 -07:00

85 lines
2.7 KiB
Python

"""Regression: the gateway delivery ledger must close every SQLite connection.
Sibling of the cron execution-ledger leak (#69567 / PR #69594). The ledger used
``with _connect() as conn:`` where ``sqlite3.Connection.__exit__`` commits or
rolls back but never closes, leaking the db/-wal/-shm file descriptors on every
call until a long-running gateway exhausts ``RLIMIT_NOFILE``. ``record_obligation``
runs on every outbound final response, so this is the highest-frequency leaker of
the set. These tests fail if the deterministic ``close()`` is ever removed again.
"""
import sqlite3
import pytest
from gateway import delivery_ledger as dl
class _TrackingConnection:
"""Delegates to a real sqlite3.Connection while recording close() calls.
sqlite3.Connection is a static C type: it has no per-instance __dict__ and
its methods can't be monkeypatched, so open/close tracking is done via a
delegating wrapper returned in place of the real connection.
"""
def __init__(self, real, closed_ids):
object.__setattr__(self, "_real", real)
object.__setattr__(self, "_closed_ids", closed_ids)
def close(self):
self._closed_ids.append(id(self._real))
self._real.close()
def __enter__(self):
self._real.__enter__()
return self
def __exit__(self, exc_type, exc, tb):
return self._real.__exit__(exc_type, exc, tb)
def __getattr__(self, name):
return getattr(self._real, name)
def __setattr__(self, name, value):
setattr(self._real, name, value)
def _point_ledger(monkeypatch, tmp_path):
monkeypatch.setattr(dl, "_db_path", lambda: tmp_path / "state.db")
return dl
def _track_connections(monkeypatch):
opened, closed = [], []
real_connect = sqlite3.connect
def tracking_connect(*args, **kwargs):
conn = real_connect(*args, **kwargs)
opened.append(id(conn))
return _TrackingConnection(conn, closed)
monkeypatch.setattr(dl.sqlite3, "connect", tracking_connect)
return opened, closed
def test_ledger_operations_close_every_connection(monkeypatch, tmp_path):
"""Every public ledger operation must close the connection it opened."""
_point_ledger(monkeypatch, tmp_path)
opened, closed = _track_connections(monkeypatch)
oid = dl.compute_obligation_id("sess", "msg", "content")
dl.record_obligation(
obligation_id=oid, session_key="sess", platform="telegram",
chat_id="123", thread_id=None, content="hello",
)
dl.mark_attempting(oid)
dl.mark_delivered(oid)
dl.sweep_recoverable()
assert opened, "expected at least one connection to be opened"
assert len(opened) == len(closed)
assert set(opened) == set(closed)