feat: wire delayed prompt TUI actions

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 09:06:36 -04:00
parent dfea5a5630
commit 2e58b191fa
2 changed files with 197 additions and 0 deletions
+149
View File
@@ -1718,3 +1718,152 @@ async def test_sandbox_switch_enabled_when_available():
)
await pilot.pause()
assert app.screen.query_one("#sandbox-switch", Switch).disabled is False
# ---------------------------------------------------------------------------
# delayed prompt: bindings, actions, poll dispatch
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_prompt_later_binding_exists():
bindings = {b.key: b for b in HqtApp.BINDINGS}
assert "p" in bindings
assert bindings["p"].action == "schedule_prompt"
assert "P" in bindings
assert bindings["P"].action == "cancel_scheduled_prompt"
@pytest.mark.asyncio
async def test_schedule_prompt_action_calls_service_and_refreshes():
from datetime import datetime
from unittest.mock import patch
from hqt.db.models import Harness, Project, Session
from hqt.tui.widgets.session_list import SessionList
app = HqtApp()
async with app.run_test(size=(120, 40)) as pilot:
seed_db = app._db_factory()
proj = Project(name="prompt-proj", path="/tmp/prompt-proj")
seed_db.add(proj)
seed_db.flush()
harness = seed_db.query(Harness).first()
sess = Session(
project_id=proj.id,
harness_id=harness.id,
tmux_session_name="hqt-prompt-test",
archived=False,
)
seed_db.add(sess)
seed_db.commit()
proj_id = proj.id
sess_id = sess.id
seed_db.close()
app._selected_project_id = proj_id
await app._refresh_sessions()
await pilot.pause()
app.query_one(SessionList).query_one("#session-list").focus()
await pilot.press("down")
await pilot.pause()
mock_service = MagicMock()
mock_service.schedule_prompt = MagicMock()
mock_service.list_sessions = AsyncMock(return_value=[])
mock_service.sync_window_labels = AsyncMock(return_value=[])
mock_service.dispatch_due_prompts = AsyncMock(return_value=[])
app._session_service = mock_service
notify_calls = []
def capture_notify(message, **kwargs):
notify_calls.append((message, kwargs))
with patch.object(app, "notify", side_effect=capture_notify):
app.action_schedule_prompt()
await pilot.pause()
app.screen.dismiss(("continue", __import__("datetime").timedelta(minutes=30)))
await pilot.pause()
await app.workers.wait_for_complete()
assert mock_service.schedule_prompt.call_count == 1
args = mock_service.schedule_prompt.call_args.args
assert args[0] == sess_id
assert args[1] == "continue"
assert isinstance(args[2], datetime)
mock_service.list_sessions.assert_awaited()
assert any("Prompt scheduled" in call[0] for call in notify_calls)
@pytest.mark.asyncio
async def test_cancel_scheduled_prompt_action_calls_service_and_refreshes():
from hqt.db.models import Harness, Project, Session
from hqt.tui.widgets.session_list import SessionList
app = HqtApp()
async with app.run_test(size=(120, 40)) as pilot:
seed_db = app._db_factory()
proj = Project(name="cancel-proj", path="/tmp/cancel-proj")
seed_db.add(proj)
seed_db.flush()
harness = seed_db.query(Harness).first()
sess = Session(
project_id=proj.id,
harness_id=harness.id,
tmux_session_name="hqt-cancel-test",
archived=False,
)
seed_db.add(sess)
seed_db.commit()
proj_id = proj.id
sess_id = sess.id
seed_db.close()
app._selected_project_id = proj_id
await app._refresh_sessions()
await pilot.pause()
app.query_one(SessionList).query_one("#session-list").focus()
await pilot.press("down")
await pilot.pause()
mock_service = MagicMock()
mock_service.cancel_scheduled_prompt = MagicMock()
mock_service.list_sessions = AsyncMock(return_value=[])
mock_service.sync_window_labels = AsyncMock(return_value=[])
mock_service.dispatch_due_prompts = AsyncMock(return_value=[])
app._session_service = mock_service
await app.run_action("cancel_scheduled_prompt")
await pilot.pause()
await app.workers.wait_for_complete()
mock_service.cancel_scheduled_prompt.assert_called_once_with(sess_id)
mock_service.list_sessions.assert_awaited()
@pytest.mark.asyncio
async def test_poll_sessions_dispatches_due_prompts_and_notifies():
app = HqtApp()
async with app.run_test(size=(120, 40)):
result = MagicMock()
result.ok = True
result.window_name = "hqt-1"
result.error = ""
mock_service = MagicMock()
mock_service.dispatch_due_prompts = AsyncMock(return_value=[result])
mock_service.sync_window_labels = AsyncMock(return_value=[])
app._session_service = mock_service
notify_calls = []
def capture_notify(message, **kwargs):
notify_calls.append((message, kwargs))
from unittest.mock import patch
with patch.object(app, "notify", side_effect=capture_notify):
await app._poll_sessions()
mock_service.dispatch_due_prompts.assert_awaited_once()
assert any("Prompt sent" in call[0] for call in notify_calls)