"""Tests for /stop — Etapa 0 of the steering plan (one-shot architecture). `stop_turn(channel_id)` kills the in-flight Claude CLI subprocess registered for a channel, without touching sessions/active.json (the session survives, only the turn dies). """ from unittest.mock import MagicMock import pytest from src.claude_session import _live_procs, stop_turn from src.router import route_message @pytest.fixture(autouse=True) def _clear_live_procs(): """Fresh registry per test — fake processes must not leak between tests.""" _live_procs.clear() yield _live_procs.clear() class TestStopTurn: def test_idle_channel_returns_false(self): assert stop_turn("ch-1") is False def test_terminates_registered_process_and_cleans_up_registry(self): fake_proc = MagicMock() fake_proc.poll.return_value = None # still running _live_procs["ch-1"] = fake_proc assert stop_turn("ch-1") is True fake_proc.terminate.assert_called_once() fake_proc.wait.assert_called_once() assert "ch-1" not in _live_procs def test_already_exited_process_returns_false(self): fake_proc = MagicMock() fake_proc.poll.return_value = 0 # exited before /stop ran _live_procs["ch-1"] = fake_proc assert stop_turn("ch-1") is False class TestStopCommand: def test_stop_nothing_running(self): response, is_cmd = route_message("ch-1", "user-1", "/stop") assert response == "Nu rulează nimic pe canalul ăsta." assert is_cmd is True def test_stop_kills_in_flight_turn(self): fake_proc = MagicMock() fake_proc.poll.return_value = None _live_procs["ch-1"] = fake_proc response, is_cmd = route_message("ch-1", "user-1", "/stop") assert response == "⏹ Oprit." assert is_cmd is True fake_proc.terminate.assert_called_once()