personal memory agent
at main 153 lines 4.8 kB view raw
1# SPDX-License-Identifier: AGPL-3.0-only 2# Copyright (c) 2026 sol pbc 3 4"""Tests for think.help_cli.""" 5 6import io 7import json 8import subprocess 9import sys 10from unittest.mock import MagicMock, patch 11 12import pytest 13 14from think.help_cli import main 15 16 17@pytest.fixture(autouse=True) 18def _set_journal_path(monkeypatch): 19 monkeypatch.setenv("_SOLSTONE_JOURNAL_OVERRIDE", "tests/fixtures/journal") 20 21 22def _make_popen(stdout_lines, *, returncode=0): 23 """Build a mock Popen whose stdout yields *stdout_lines*.""" 24 proc = MagicMock() 25 proc.stdin = MagicMock() 26 proc.stdout = io.StringIO("\n".join(stdout_lines) + "\n") 27 proc.stderr = MagicMock() 28 proc.stderr.read.return_value = "" 29 proc.returncode = returncode 30 proc.wait.return_value = returncode 31 return proc 32 33 34def test_help_no_question_shows_static_help(monkeypatch): 35 monkeypatch.setattr(sys, "argv", ["sol help"]) 36 37 with patch("sol.print_help") as mock_print_help: 38 main() 39 40 mock_print_help.assert_called_once() 41 42 43def test_help_parses_question(monkeypatch): 44 monkeypatch.setattr(sys, "argv", ["sol help", "how", "do", "I", "search"]) 45 mock_proc = _make_popen( 46 ['{"event":"finish","result":"Use sol call journal search"}'], 47 ) 48 49 with patch("think.help_cli.subprocess.Popen", return_value=mock_proc) as mock_cls: 50 main() 51 52 call_args = mock_cls.call_args 53 assert call_args[0][0] == ["sol", "agents"] 54 assert call_args[1]["stdin"] == subprocess.PIPE 55 assert call_args[1]["stdout"] == subprocess.PIPE 56 assert call_args[1]["text"] is True 57 58 written = mock_proc.stdin.write.call_args[0][0] 59 payload = json.loads(written.strip()) 60 assert payload["prompt"] == "how do I search" 61 mock_proc.stdin.close.assert_called_once() 62 63 64def test_help_ndjson_config(monkeypatch): 65 monkeypatch.setattr(sys, "argv", ["sol help", "show", "todo", "commands"]) 66 mock_proc = _make_popen(['{"event":"finish","result":"ok"}']) 67 68 with patch("think.help_cli.subprocess.Popen", return_value=mock_proc): 69 main() 70 71 written = mock_proc.stdin.write.call_args[0][0] 72 payload = json.loads(written.strip()) 73 assert payload == {"name": "unified", "prompt": "show todo commands"} 74 75 76def test_help_parses_finish_event(monkeypatch, capsys): 77 monkeypatch.setattr(sys, "argv", ["sol help", "how", "to", "search"]) 78 mock_proc = _make_popen( 79 [ 80 '{"event":"start","ts":1}', 81 '{"event":"thinking","ts":2,"summary":"..."}', 82 '{"event":"finish","ts":3,"result":"Use `sol call journal search`."}', 83 ] 84 ) 85 86 with patch("think.help_cli.subprocess.Popen", return_value=mock_proc): 87 main() 88 89 captured = capsys.readouterr() 90 assert "Use `sol call journal search`." in captured.out 91 92 93def test_help_uses_last_finish_event(monkeypatch, capsys): 94 monkeypatch.setattr(sys, "argv", ["sol help", "search"]) 95 mock_proc = _make_popen( 96 [ 97 '{"event":"finish","ts":1,"result":"old result"}', 98 '{"event":"finish","ts":2,"result":"new result"}', 99 ] 100 ) 101 102 with patch("think.help_cli.subprocess.Popen", return_value=mock_proc): 103 main() 104 105 captured = capsys.readouterr() 106 assert "new result" in captured.out 107 assert "old result" not in captured.out 108 109 110def test_help_handles_error_event(monkeypatch, capsys): 111 monkeypatch.setattr(sys, "argv", ["sol help", "bad", "request"]) 112 mock_proc = _make_popen( 113 ['{"event":"error","error":"provider unavailable"}'], 114 returncode=1, 115 ) 116 117 with patch("think.help_cli.subprocess.Popen", return_value=mock_proc): 118 with pytest.raises(SystemExit) as exc_info: 119 main() 120 121 assert exc_info.value.code == 1 122 captured = capsys.readouterr() 123 assert "provider unavailable" in captured.err 124 125 126def test_help_handles_empty_finish_result(monkeypatch, capsys): 127 monkeypatch.setattr(sys, "argv", ["sol help", "empty"]) 128 mock_proc = _make_popen(['{"event":"finish","result":""}']) 129 130 with patch("think.help_cli.subprocess.Popen", return_value=mock_proc): 131 with pytest.raises(SystemExit) as exc_info: 132 main() 133 134 assert exc_info.value.code == 1 135 captured = capsys.readouterr() 136 assert "empty result" in captured.err.lower() 137 138 139def test_help_handles_timeout(monkeypatch, capsys): 140 monkeypatch.setattr(sys, "argv", ["sol help", "slow", "question"]) 141 mock_proc = _make_popen([]) 142 mock_proc.wait.side_effect = subprocess.TimeoutExpired( 143 cmd=["sol", "agents"], timeout=120 144 ) 145 146 with patch("think.help_cli.subprocess.Popen", return_value=mock_proc): 147 with pytest.raises(SystemExit) as exc_info: 148 main() 149 150 assert exc_info.value.code == 1 151 mock_proc.kill.assert_called_once() 152 captured = capsys.readouterr() 153 assert "timed out" in captured.err.lower()