test: add comprehensive coverage for cli, chat, renderer, dirs, install, paths
56 new tests covering previously untested modules: - test_cli.py: memory write/read/append/list + plugin enable/disable + daemon stubs (via CliRunner) - test_chat_history.py: ConversationHistory build_for_api, add_*/clear, _trim_to_budget - test_chat_renderer.py: render_text_response return values, void render_* functions - test_config_dirs.py: bootstrap idempotency, directory/template/vault/db creation - test_plugin_install.py: list_bundled_plugins, read_manifest, install_bundled_plugin - test_utils_paths.py: ensure_dir (nested, idempotent), safe_chmod Total: 171 → 227 passing tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from pyra.cli import main
|
||||
|
||||
|
||||
def test_memory_write_creates_file(tmp_pyra_home):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["memory", "write", "user/note.md", "Hello world"])
|
||||
assert result.exit_code == 0
|
||||
assert (tmp_pyra_home / "memory" / "user" / "note.md").exists()
|
||||
assert "Hello world" in (tmp_pyra_home / "memory" / "user" / "note.md").read_text()
|
||||
|
||||
|
||||
def test_memory_write_updates_db(tmp_pyra_home):
|
||||
runner = CliRunner()
|
||||
runner.invoke(main, ["memory", "write", "user/note.md", "DB test content"])
|
||||
from pyra.memory import database
|
||||
rows = database.list_all()
|
||||
assert any(r["path"] == "user/note.md" for r in rows)
|
||||
|
||||
|
||||
def test_memory_append_adds_content(tmp_pyra_home):
|
||||
runner = CliRunner()
|
||||
runner.invoke(main, ["memory", "write", "user/note.md", "First line"])
|
||||
runner.invoke(main, ["memory", "append", "user/note.md", "Second line"])
|
||||
content = (tmp_pyra_home / "memory" / "user" / "note.md").read_text()
|
||||
assert "First line" in content
|
||||
assert "Second line" in content
|
||||
|
||||
|
||||
def test_memory_read_existing(tmp_pyra_home):
|
||||
from pyra.memory.writer import write_memory
|
||||
write_memory("user/note.md", "Readable content")
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["memory", "read", "user/note.md"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_memory_read_missing_exits_cleanly(tmp_pyra_home):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["memory", "read", "user/does_not_exist.md"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_memory_read_blocked_path_exits_cleanly(tmp_pyra_home):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["memory", "read", "../../../../vault/secrets/api_keys.json"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_memory_list_empty(tmp_pyra_home):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["memory", "list"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_memory_list_populated(tmp_pyra_home):
|
||||
from pyra.memory.writer import write_memory
|
||||
write_memory("user/profile.md", "# Profile")
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["memory", "list"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_plugin_list_no_config(tmp_pyra_home):
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["plugin", "list"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def _make_config():
|
||||
from pyra.config.schema import PyraConfig, ProviderConfig
|
||||
return PyraConfig(ai=ProviderConfig(provider_id="lmstudio", model="gemma"))
|
||||
|
||||
|
||||
def test_plugin_enable_not_installed(tmp_pyra_home):
|
||||
from pyra.config.manager import save_config
|
||||
save_config(_make_config())
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(main, ["plugin", "enable", "nonexistent"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
def test_plugin_enable_and_disable(tmp_pyra_home):
|
||||
from pyra.config.manager import load_config, save_config
|
||||
|
||||
save_config(_make_config())
|
||||
|
||||
plugin_dir = tmp_pyra_home / "plugins" / "myplugin"
|
||||
plugin_dir.mkdir(parents=True, exist_ok=True)
|
||||
(plugin_dir / "manifest.json").write_text('{"name": "myplugin", "version": "1.0"}')
|
||||
|
||||
runner = CliRunner()
|
||||
|
||||
runner.invoke(main, ["plugin", "enable", "myplugin"])
|
||||
cfg = load_config()
|
||||
assert "myplugin" in cfg.plugins.enabled
|
||||
|
||||
runner.invoke(main, ["plugin", "disable", "myplugin"])
|
||||
cfg = load_config()
|
||||
assert "myplugin" not in cfg.plugins.enabled
|
||||
|
||||
|
||||
def test_daemon_commands_exit_cleanly(tmp_pyra_home):
|
||||
runner = CliRunner()
|
||||
for cmd in ["start", "stop", "status", "restart"]:
|
||||
result = runner.invoke(main, ["daemon", cmd])
|
||||
assert result.exit_code == 0, f"daemon {cmd} exited with {result.exit_code}"
|
||||
Reference in New Issue
Block a user