mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 06:51:16 +08:00
fix(debug): sweep expired pending pastes on slash debug paths
This commit is contained in:
@@ -7645,13 +7645,14 @@ class GatewayRunner:
|
|||||||
from hermes_cli.debug import (
|
from hermes_cli.debug import (
|
||||||
_capture_dump, collect_debug_report,
|
_capture_dump, collect_debug_report,
|
||||||
upload_to_pastebin, _schedule_auto_delete,
|
upload_to_pastebin, _schedule_auto_delete,
|
||||||
_GATEWAY_PRIVACY_NOTICE,
|
_GATEWAY_PRIVACY_NOTICE, _best_effort_sweep_expired_pastes,
|
||||||
)
|
)
|
||||||
|
|
||||||
loop = asyncio.get_running_loop()
|
loop = asyncio.get_running_loop()
|
||||||
|
|
||||||
# Run blocking I/O (dump capture, log reads, uploads) in a thread.
|
# Run blocking I/O (dump capture, log reads, uploads) in a thread.
|
||||||
def _collect_and_upload():
|
def _collect_and_upload():
|
||||||
|
_best_effort_sweep_expired_pastes()
|
||||||
dump_text = _capture_dump()
|
dump_text = _capture_dump()
|
||||||
report = collect_debug_report(log_lines=200, dump_text=dump_text)
|
report = collect_debug_report(log_lines=200, dump_text=dump_text)
|
||||||
|
|
||||||
|
|||||||
@@ -148,6 +148,14 @@ def _sweep_expired_pastes(now: Optional[float] = None) -> tuple[int, int]:
|
|||||||
return (deleted, len(remaining))
|
return (deleted, len(remaining))
|
||||||
|
|
||||||
|
|
||||||
|
def _best_effort_sweep_expired_pastes() -> None:
|
||||||
|
"""Attempt pending-paste cleanup without letting /debug fail offline."""
|
||||||
|
try:
|
||||||
|
_sweep_expired_pastes()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Privacy / delete helpers
|
# Privacy / delete helpers
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -518,6 +526,8 @@ def collect_debug_report(
|
|||||||
|
|
||||||
def run_debug_share(args):
|
def run_debug_share(args):
|
||||||
"""Collect debug report + full logs, upload each, print URLs."""
|
"""Collect debug report + full logs, upload each, print URLs."""
|
||||||
|
_best_effort_sweep_expired_pastes()
|
||||||
|
|
||||||
log_lines = getattr(args, "lines", 200)
|
log_lines = getattr(args, "lines", 200)
|
||||||
expiry = getattr(args, "expire", 7)
|
expiry = getattr(args, "expire", 7)
|
||||||
local_only = getattr(args, "local", False)
|
local_only = getattr(args, "local", False)
|
||||||
|
|||||||
60
tests/gateway/test_debug_command.py
Normal file
60
tests/gateway/test_debug_command.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
"""Tests for the gateway /debug command."""
|
||||||
|
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from gateway.config import GatewayConfig, Platform
|
||||||
|
from gateway.platforms.base import MessageEvent
|
||||||
|
from gateway.session import SessionSource
|
||||||
|
|
||||||
|
|
||||||
|
def _make_event(text="/debug", platform=Platform.TELEGRAM,
|
||||||
|
user_id="12345", chat_id="67890"):
|
||||||
|
source = SessionSource(
|
||||||
|
platform=platform,
|
||||||
|
user_id=user_id,
|
||||||
|
chat_id=chat_id,
|
||||||
|
user_name="testuser",
|
||||||
|
)
|
||||||
|
return MessageEvent(text=text, source=source)
|
||||||
|
|
||||||
|
|
||||||
|
def _make_runner():
|
||||||
|
from gateway.run import GatewayRunner
|
||||||
|
|
||||||
|
runner = object.__new__(GatewayRunner)
|
||||||
|
runner.config = GatewayConfig()
|
||||||
|
runner.adapters = {}
|
||||||
|
return runner
|
||||||
|
|
||||||
|
|
||||||
|
class TestHandleDebugCommand:
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_debug_sweeps_expired_pastes_before_upload(self):
|
||||||
|
runner = _make_runner()
|
||||||
|
event = _make_event()
|
||||||
|
|
||||||
|
with patch("hermes_cli.debug._sweep_expired_pastes", return_value=(0, 0)) as mock_sweep, \
|
||||||
|
patch("hermes_cli.debug._capture_dump", return_value="dump"), \
|
||||||
|
patch("hermes_cli.debug.collect_debug_report", return_value="report"), \
|
||||||
|
patch("hermes_cli.debug.upload_to_pastebin", return_value="https://paste.rs/report"), \
|
||||||
|
patch("hermes_cli.debug._schedule_auto_delete"):
|
||||||
|
result = await runner._handle_debug_command(event)
|
||||||
|
|
||||||
|
mock_sweep.assert_called_once()
|
||||||
|
assert "https://paste.rs/report" in result
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_debug_survives_sweep_failure(self):
|
||||||
|
runner = _make_runner()
|
||||||
|
event = _make_event()
|
||||||
|
|
||||||
|
with patch("hermes_cli.debug._sweep_expired_pastes", side_effect=RuntimeError("offline")), \
|
||||||
|
patch("hermes_cli.debug._capture_dump", return_value="dump"), \
|
||||||
|
patch("hermes_cli.debug.collect_debug_report", return_value="report"), \
|
||||||
|
patch("hermes_cli.debug.upload_to_pastebin", return_value="https://paste.rs/report"), \
|
||||||
|
patch("hermes_cli.debug._schedule_auto_delete"):
|
||||||
|
result = await runner._handle_debug_command(event)
|
||||||
|
|
||||||
|
assert "https://paste.rs/report" in result
|
||||||
@@ -283,6 +283,44 @@ class TestCollectDebugReport:
|
|||||||
class TestRunDebugShare:
|
class TestRunDebugShare:
|
||||||
"""Test the run_debug_share CLI handler."""
|
"""Test the run_debug_share CLI handler."""
|
||||||
|
|
||||||
|
def test_share_sweeps_expired_pastes(self, hermes_home, capsys):
|
||||||
|
"""Slash-command path should sweep old pending deletes before uploading."""
|
||||||
|
from hermes_cli.debug import run_debug_share
|
||||||
|
|
||||||
|
args = MagicMock()
|
||||||
|
args.lines = 50
|
||||||
|
args.expire = 7
|
||||||
|
args.local = False
|
||||||
|
|
||||||
|
with patch("hermes_cli.dump.run_dump"), \
|
||||||
|
patch("hermes_cli.debug._sweep_expired_pastes", return_value=(0, 0)) as mock_sweep, \
|
||||||
|
patch("hermes_cli.debug.upload_to_pastebin",
|
||||||
|
return_value="https://paste.rs/test"):
|
||||||
|
run_debug_share(args)
|
||||||
|
|
||||||
|
mock_sweep.assert_called_once()
|
||||||
|
assert "Debug report uploaded" in capsys.readouterr().out
|
||||||
|
|
||||||
|
def test_share_survives_sweep_failure(self, hermes_home, capsys):
|
||||||
|
"""Expired-paste cleanup is best-effort and must not block sharing."""
|
||||||
|
from hermes_cli.debug import run_debug_share
|
||||||
|
|
||||||
|
args = MagicMock()
|
||||||
|
args.lines = 50
|
||||||
|
args.expire = 7
|
||||||
|
args.local = False
|
||||||
|
|
||||||
|
with patch("hermes_cli.dump.run_dump"), \
|
||||||
|
patch(
|
||||||
|
"hermes_cli.debug._sweep_expired_pastes",
|
||||||
|
side_effect=RuntimeError("offline"),
|
||||||
|
), \
|
||||||
|
patch("hermes_cli.debug.upload_to_pastebin",
|
||||||
|
return_value="https://paste.rs/test"):
|
||||||
|
run_debug_share(args)
|
||||||
|
|
||||||
|
assert "https://paste.rs/test" in capsys.readouterr().out
|
||||||
|
|
||||||
def test_local_flag_prints_full_logs(self, hermes_home, capsys):
|
def test_local_flag_prints_full_logs(self, hermes_home, capsys):
|
||||||
"""--local prints the report plus full log contents."""
|
"""--local prints the report plus full log contents."""
|
||||||
from hermes_cli.debug import run_debug_share
|
from hermes_cli.debug import run_debug_share
|
||||||
|
|||||||
Reference in New Issue
Block a user