diff --git a/scripts/odysseus-memory b/scripts/odysseus-memory index f46f2c0..1a4f8a0 100755 --- a/scripts/odysseus-memory +++ b/scripts/odysseus-memory @@ -47,8 +47,12 @@ def _manager() -> MemoryManager: return _mgr +def _memory_entries(entries): + return [e for e in entries or [] if isinstance(e, dict)] + + def cmd_list(args): - entries = _manager().load_all() + entries = _memory_entries(_manager().load_all()) if args.category: entries = [e for e in entries if (e.get("category") or "fact") == args.category] if args.source: @@ -62,14 +66,14 @@ def cmd_list(args): def cmd_search(args): q = args.query.lower() - entries = _manager().load_all() + entries = _memory_entries(_manager().load_all()) matches = [e for e in entries if q in (e.get("text") or "").lower()] matches = sorted(matches, key=lambda e: e.get("timestamp", 0), reverse=True) emit(matches[: args.limit], args) def cmd_show(args): - for e in _manager().load_all(): + for e in _memory_entries(_manager().load_all()): if e.get("id") == args.id: emit(e, args) return @@ -93,7 +97,7 @@ def cmd_add(args): def cmd_delete(args): - entries = _manager().load_all() + entries = _memory_entries(_manager().load_all()) target = next((e for e in entries if e.get("id") == args.id), None) if not target: fail(f"no memory with id {args.id!r}") @@ -104,7 +108,7 @@ def cmd_delete(args): def cmd_categories(args): counts: dict[str, int] = {} - for e in _manager().load_all(): + for e in _memory_entries(_manager().load_all()): cat = e.get("category") or "fact" counts[cat] = counts.get(cat, 0) + 1 rows = sorted(counts.items(), key=lambda kv: -kv[1]) diff --git a/tests/test_memory_cli_rows.py b/tests/test_memory_cli_rows.py new file mode 100644 index 0000000..fe63d24 --- /dev/null +++ b/tests/test_memory_cli_rows.py @@ -0,0 +1,31 @@ +import importlib.machinery +import importlib.util +import sys +import types +from pathlib import Path +from unittest.mock import MagicMock + + +ROOT = Path(__file__).resolve().parents[1] + + +def _load_cli(monkeypatch): + svc = types.ModuleType("services.memory.memory") + svc.MemoryManager = MagicMock() + monkeypatch.setitem(sys.modules, "services.memory.memory", svc) + path = ROOT / "scripts" / "odysseus-memory" + loader = importlib.machinery.SourceFileLoader("odysseus_memory_cli", str(path)) + spec = importlib.util.spec_from_loader(loader.name, loader) + module = importlib.util.module_from_spec(spec) + loader.exec_module(module) + return module + + +def test_memory_entries_skips_invalid_rows(monkeypatch): + cli = _load_cli(monkeypatch) + + assert cli._memory_entries([ + {"id": "m1", "text": "ok"}, + "bad-row", + None, + ]) == [{"id": "m1", "text": "ok"}]