"""Tests for ``jarvis memory`` CLI commands.""" from __future__ import annotations import importlib from pathlib import Path from click.testing import CliRunner from openjarvis.cli import cli from openjarvis.core.registry import MemoryRegistry from openjarvis.memory.store import LocalFactStore from openjarvis.tools.storage.sqlite import SQLiteMemory def _register_sqlite(): """Re-register sqlite backend (conftest clears registries).""" if not MemoryRegistry.contains("sqlite"): MemoryRegistry.register_value("sqlite", SQLiteMemory) def test_memory_index_file(tmp_path: Path, monkeypatch): """Index a single text file and check success message.""" _register_sqlite() db_path = str(tmp_path / "mem.db") # Create a text file with enough content doc = tmp_path / "doc.txt" doc.write_text(" ".join(f"word{i}" for i in range(100))) mod = importlib.import_module("openjarvis.cli.memory_cmd") monkeypatch.setattr( mod, "_get_backend", lambda b=None: SQLiteMemory(db_path=db_path), ) result = CliRunner().invoke(cli, ["memory", "index", str(doc)]) assert result.exit_code == 0 assert "Indexed" in result.output or "chunk" in result.output def test_memory_index_nonexistent(tmp_path: Path): """Indexing a nonexistent path should fail.""" _register_sqlite() result = CliRunner().invoke(cli, ["memory", "index", str(tmp_path / "nope")]) assert result.exit_code != 0 def test_memory_search_returns_results(tmp_path: Path, monkeypatch): """Search returns results from pre-populated backend.""" _register_sqlite() db_path = str(tmp_path / "mem.db") backend = SQLiteMemory(db_path=db_path) backend.store( "Python programming language guide", source="guide.md", ) mod = importlib.import_module("openjarvis.cli.memory_cmd") monkeypatch.setattr( mod, "_get_backend", lambda b=None: SQLiteMemory(db_path=db_path), ) result = CliRunner().invoke(cli, ["memory", "search", "Python"]) assert result.exit_code == 0 assert "Python" in result.output backend.close() def test_memory_search_no_results(tmp_path: Path, monkeypatch): """Search with no matches shows appropriate message.""" _register_sqlite() db_path = str(tmp_path / "mem.db") backend = SQLiteMemory(db_path=db_path) backend.store("some unrelated content about cats") mod = importlib.import_module("openjarvis.cli.memory_cmd") monkeypatch.setattr( mod, "_get_backend", lambda b=None: SQLiteMemory(db_path=db_path), ) result = CliRunner().invoke(cli, ["memory", "search", "quantum supercollider"]) assert result.exit_code == 0 assert "No results" in result.output backend.close() def test_memory_stats_shows_count(tmp_path: Path, monkeypatch): """Stats command shows document count.""" _register_sqlite() db_path = str(tmp_path / "mem.db") backend = SQLiteMemory(db_path=db_path) backend.store("doc one") backend.store("doc two") mod = importlib.import_module("openjarvis.cli.memory_cmd") monkeypatch.setattr( mod, "_get_backend", lambda b=None: SQLiteMemory(db_path=db_path), ) result = CliRunner().invoke(cli, ["memory", "stats"]) assert result.exit_code == 0 assert "2" in result.output backend.close() def _patch_fact_store(monkeypatch, tmp_path: Path) -> LocalFactStore: """Point ``jarvis memory list/clear`` at a temp fact store.""" mod = importlib.import_module("openjarvis.cli.memory_cmd") store = LocalFactStore(tmp_path / "facts.jsonl") monkeypatch.setattr(mod, "_get_fact_store", lambda: store) return store def test_memory_list_empty(tmp_path: Path, monkeypatch): _patch_fact_store(monkeypatch, tmp_path) result = CliRunner().invoke(cli, ["memory", "list"]) assert result.exit_code == 0 assert "No memory facts" in result.output def test_memory_list_shows_facts(tmp_path: Path, monkeypatch): store = _patch_fact_store(monkeypatch, tmp_path) store.add("User prefers dark mode") store.add("User lives in Berlin") result = CliRunner().invoke(cli, ["memory", "list"]) assert result.exit_code == 0 assert "dark mode" in result.output assert "Berlin" in result.output def test_memory_clear_with_confirmation(tmp_path: Path, monkeypatch): store = _patch_fact_store(monkeypatch, tmp_path) store.add("fact one") store.add("fact two") result = CliRunner().invoke(cli, ["memory", "clear"], input="y\n") assert result.exit_code == 0 assert "Cleared 2" in result.output assert store.count() == 0 def test_memory_clear_aborted(tmp_path: Path, monkeypatch): store = _patch_fact_store(monkeypatch, tmp_path) store.add("keep me") result = CliRunner().invoke(cli, ["memory", "clear"], input="n\n") assert result.exit_code == 0 assert "Aborted" in result.output assert store.count() == 1 def test_memory_clear_yes_flag(tmp_path: Path, monkeypatch): store = _patch_fact_store(monkeypatch, tmp_path) store.add("fact") result = CliRunner().invoke(cli, ["memory", "clear", "--yes"]) assert result.exit_code == 0 assert "Cleared 1" in result.output assert store.count() == 0 def test_memory_clear_empty(tmp_path: Path, monkeypatch): _patch_fact_store(monkeypatch, tmp_path) result = CliRunner().invoke(cli, ["memory", "clear"]) assert result.exit_code == 0 assert "No memory facts to clear" in result.output