Files
AIPass/src/aipass/memory/tests/test_verify.py
T
56ba4e6815 feat(memory): seedgo compliance: 92% → 96%, fixes + 70 bypasses (#121)
* feat(seedgo): deep nesting bypasses, dead code cleanup, json structure compliance

Co-Authored-By: @seedgo <seedgo@aipass>

* feat(memory): seedgo certification: introspection fixes, subprocess bypasses, silent catch cleanup

Co-Authored-By: @memory <memory@aipass>

* feat(api): seedgo certification: 94%→97%, 31/33 standards at 100%

Co-Authored-By: @api <api@aipass>

* feat(seedgo): deep nesting 100%, limit 3→4, checker refactors, @ validation, bypass cleanup

Co-Authored-By: @seedgo <seedgo@aipass>

* feat: seedgo cert sprint — 10 branches dispatched, drone introspection rebuilt, system-wide compliance push

Session 49-50 cert sprint results:
- drone: introspection rebuilt (proper auto-discovery), silent_catch 92%→100%, overall 97%
- api: 94%→97%, json_handler fixed, PR #116
- backup: 93%→94%, json_handler load_template→inline
- memory: 88%→91%, introspection 79%→100%, 10 bypasses for subprocess files
- skills: 97%, json_structure→100%, introspection→100%
- spawn: 97%→99%, 32/34 standards at 100%
- ai_mail: 95%→97%, 12 unused functions removed, 32/34 at 100%
- seedgo: checker improvements (deep_nesting threshold 3→4, various fixes)
- drone: removed from _MODULE_REGISTRY (DPLAN-0053 consensus)
- commons: introspection bypasses (22 entries), python3→drone refs fixed
- trigger/cli/prax/daemon/flow/backup: various cert fixes

New DPLANs: 0053 (drone audit), 0054 (bypass tracker), 0055 (persistent git branches)
New FPLAN: 0134 (persistent citizen git branches — drone build)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(seedgo): audit display dynamic rendering, 176 unit tests, test coverage 6→93%

Co-Authored-By: @seedgo <seedgo@aipass>

* feat(memory): seedgo compliance: 92% → 96%, fixes + 70 bypasses

Co-Authored-By: @memory <memory@aipass>

* feat: night shift — compliance push, drone persistent branches + module routing, dead code cleanup

Autonomous night shift (DPLAN-0057). System avg 93% → 96%, all 14 branches 95%+.

Drone: persistent citizen/{name} branches (FPLAN-0134), module routing fix (FPLAN-0136),
19 logger.info→console.print across 6 modules, @ enforcement hints.

Compliance: backup 94→95%, daemon 94→95%, flow 93→96%, prax 94→96%, trigger 93→96%.
Prax: 27 dead functions removed, monitoring cleanup. Flow: dead code removal, bypass.json.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat(memory): unit tests: 194 tests, 7 test files, 43% module coverage

Co-Authored-By: @memory <memory@aipass>

---------

Co-authored-by: @seedgo <seedgo@aipass>
Co-authored-by: @memory <memory@aipass>
Co-authored-by: @api <api@aipass>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 16:43:36 -07:00

564 lines
21 KiB
Python

# =================== META ====================
# Name: test_verify.py
# Description: Tests for verify module (plan vectorization check)
# Version: 1.0.0
# Created: 2026-03-24
# Modified: 2026-03-24
# =============================================
"""Tests for the verify module: _get_memory_python, _check_plan_subprocess,
is_plan_vectorized, handle_command, _discover_handlers.
Covers: from aipass.memory.apps.modules.verify import handle_command
"""
import json
import subprocess
import sys
from pathlib import Path
from unittest.mock import MagicMock
import pytest
# ---------------------------------------------------------------------------
# Helper: mock CLI modules that verify.py imports at module level
# ---------------------------------------------------------------------------
def _mock_cli_modules(monkeypatch):
"""Inject mocks for aipass.cli.apps.modules (console, error)."""
mock_console = MagicMock()
mock_error = MagicMock()
cli_modules = MagicMock()
cli_modules.console = mock_console
cli_modules.error = mock_error
monkeypatch.setitem(sys.modules, "aipass.cli", MagicMock())
monkeypatch.setitem(sys.modules, "aipass.cli.apps", MagicMock())
monkeypatch.setitem(sys.modules, "aipass.cli.apps.modules", cli_modules)
return mock_console, mock_error
def _import_verify():
"""Import verify module inside the test (after mocks are in place)."""
from aipass.memory.apps.modules import verify
return verify
# ===========================================================================
# 1. _get_memory_python()
# ===========================================================================
class TestGetMemoryPython:
"""Tests for _get_memory_python() resolution logic."""
def test_env_override_takes_precedence(self, monkeypatch):
"""AIPASS_MEMORY_PYTHON env var overrides all other paths."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
monkeypatch.setenv("AIPASS_MEMORY_PYTHON", "/custom/bin/python3")
result = verify._get_memory_python()
assert result == "/custom/bin/python3"
def test_venv_python_used_when_exists(self, monkeypatch, tmp_path):
"""When the memory .venv python exists, it should be returned."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
# Remove env override so it doesn't interfere
monkeypatch.delenv("AIPASS_MEMORY_PYTHON", raising=False)
# Create a fake venv python
fake_python = tmp_path / ".venv" / "bin" / "python"
fake_python.parent.mkdir(parents=True)
fake_python.touch()
# Patch the module-level constant
monkeypatch.setattr(verify, "_MEMORY_VENV_PYTHON", fake_python)
result = verify._get_memory_python()
assert result == str(fake_python)
def test_fallback_to_sys_executable(self, monkeypatch):
"""When no env var and no venv, fall back to sys.executable."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
monkeypatch.delenv("AIPASS_MEMORY_PYTHON", raising=False)
# Point to a path that does not exist
monkeypatch.setattr(
verify, "_MEMORY_VENV_PYTHON", Path("/nonexistent/.venv/bin/python")
)
result = verify._get_memory_python()
assert result == sys.executable
def test_env_override_empty_string_is_falsy(self, monkeypatch):
"""An empty AIPASS_MEMORY_PYTHON env var should be treated as unset."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
monkeypatch.setenv("AIPASS_MEMORY_PYTHON", "")
# Empty string is falsy, so it should NOT be returned
monkeypatch.setattr(
verify, "_MEMORY_VENV_PYTHON", Path("/nonexistent/.venv/bin/python")
)
result = verify._get_memory_python()
# Falls through to venv check (missing) then sys.executable
assert result == sys.executable
# ===========================================================================
# 2. _check_plan_subprocess()
# ===========================================================================
class TestCheckPlanSubprocess:
"""Tests for _check_plan_subprocess() subprocess orchestration."""
def test_success_returns_parsed_json(self, monkeypatch):
"""Successful subprocess returns parsed JSON dict."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
expected = {"success": True, "found": True, "count": 5, "source_files": ["a.md"]}
fake_result = MagicMock()
fake_result.returncode = 0
fake_result.stdout = json.dumps(expected)
monkeypatch.setattr(verify, "_get_memory_python", lambda: "/usr/bin/python3")
monkeypatch.setattr(subprocess, "run", lambda *a, **kw: fake_result)
result = verify._check_plan_subprocess("FPLAN-0126")
assert result["success"] is True
assert result["found"] is True
assert result["count"] == 5
assert "a.md" in result["source_files"]
def test_nonzero_returncode_returns_error(self, monkeypatch):
"""Non-zero return code produces error dict with stderr message."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
fake_result = MagicMock()
fake_result.returncode = 1
fake_result.stderr = "collection not found"
monkeypatch.setattr(verify, "_get_memory_python", lambda: "/usr/bin/python3")
monkeypatch.setattr(subprocess, "run", lambda *a, **kw: fake_result)
result = verify._check_plan_subprocess("FPLAN-9999")
assert result["success"] is False
assert "collection not found" in result["error"]
def test_nonzero_returncode_empty_stderr(self, monkeypatch):
"""Non-zero return with empty stderr gives generic failure message."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
fake_result = MagicMock()
fake_result.returncode = 1
fake_result.stderr = ""
monkeypatch.setattr(verify, "_get_memory_python", lambda: "/usr/bin/python3")
monkeypatch.setattr(subprocess, "run", lambda *a, **kw: fake_result)
result = verify._check_plan_subprocess("FPLAN-0001")
assert result["success"] is False
assert result["error"] == "Subprocess failed"
def test_timeout_returns_error(self, monkeypatch):
"""Subprocess timeout produces a clear error message."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
def raise_timeout(*args, **kwargs):
raise subprocess.TimeoutExpired(cmd="python", timeout=60)
monkeypatch.setattr(verify, "_get_memory_python", lambda: "/usr/bin/python3")
monkeypatch.setattr(subprocess, "run", raise_timeout)
result = verify._check_plan_subprocess("FPLAN-0126")
assert result["success"] is False
assert "timed out" in result["error"]
def test_invalid_json_returns_error(self, monkeypatch):
"""Invalid JSON stdout produces a JSON decode error."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
fake_result = MagicMock()
fake_result.returncode = 0
fake_result.stdout = "not valid json {{"
monkeypatch.setattr(verify, "_get_memory_python", lambda: "/usr/bin/python3")
monkeypatch.setattr(subprocess, "run", lambda *a, **kw: fake_result)
result = verify._check_plan_subprocess("FPLAN-0126")
assert result["success"] is False
assert "Invalid JSON" in result["error"]
def test_generic_exception_returns_error(self, monkeypatch):
"""Any other exception is caught and returned as error."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
def raise_oserror(*args, **kwargs):
raise OSError("No such file or directory")
monkeypatch.setattr(verify, "_get_memory_python", lambda: "/usr/bin/python3")
monkeypatch.setattr(subprocess, "run", raise_oserror)
result = verify._check_plan_subprocess("FPLAN-0126")
assert result["success"] is False
assert "No such file or directory" in result["error"]
def test_subprocess_receives_correct_input(self, monkeypatch):
"""Verify the input JSON sent to subprocess contains correct fields."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
captured_kwargs = {}
def capture_run(*args, **kwargs):
captured_kwargs.update(kwargs)
result = MagicMock()
result.returncode = 0
result.stdout = json.dumps({"success": True, "found": False, "count": 0})
return result
monkeypatch.setattr(verify, "_get_memory_python", lambda: "/usr/bin/python3")
monkeypatch.setattr(subprocess, "run", capture_run)
verify._check_plan_subprocess("HPLAN-0042")
input_data = json.loads(captured_kwargs["input"])
assert input_data["operation"] == "check_plan"
assert input_data["plan_label"] == "HPLAN-0042"
# ===========================================================================
# 3. is_plan_vectorized()
# ===========================================================================
class TestIsPlanVectorized:
"""Tests for the public is_plan_vectorized() API."""
def test_delegates_to_check_plan_subprocess(self, monkeypatch):
"""is_plan_vectorized is a thin wrapper around _check_plan_subprocess."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
expected = {"success": True, "found": True, "count": 3, "source_files": []}
monkeypatch.setattr(verify, "_check_plan_subprocess", lambda label: expected)
result = verify.is_plan_vectorized("FPLAN-0126")
assert result is expected
assert result["found"] is True
assert result["count"] == 3
def test_returns_failure_dict_on_error(self, monkeypatch):
"""When subprocess fails, the error dict propagates through."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
error_result = {"success": False, "error": "something broke"}
monkeypatch.setattr(verify, "_check_plan_subprocess", lambda label: error_result)
result = verify.is_plan_vectorized("FPLAN-0000")
assert result["success"] is False
assert result["error"] == "something broke"
# ===========================================================================
# 4. handle_command()
# ===========================================================================
class TestHandleCommand:
"""Tests for handle_command() routing."""
def test_verify_no_args_calls_introspection(self, monkeypatch):
"""'verify' with no args triggers print_introspection."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
called = {"introspection": False}
def fake_introspection():
called["introspection"] = True
monkeypatch.setattr(verify, "print_introspection", fake_introspection)
result = verify.handle_command("verify", [])
assert result is True
assert called["introspection"] is True
def test_verify_help_flag(self, monkeypatch):
"""'verify --help' triggers print_help."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
called = {"help": False}
def fake_help():
called["help"] = True
monkeypatch.setattr(verify, "print_help", fake_help)
result = verify.handle_command("verify", ["--help"])
assert result is True
assert called["help"] is True
def test_verify_h_flag(self, monkeypatch):
"""'verify -h' triggers print_help."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
called = {"help": False}
monkeypatch.setattr(verify, "print_help", lambda: called.update(help=True))
result = verify.handle_command("verify", ["-h"])
assert result is True
assert called["help"] is True
def test_verify_help_word(self, monkeypatch):
"""'verify help' triggers print_help."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
called = {"help": False}
monkeypatch.setattr(verify, "print_help", lambda: called.update(help=True))
result = verify.handle_command("verify", ["help"])
assert result is True
assert called["help"] is True
def test_verify_with_plan_label(self, monkeypatch):
"""'verify FPLAN-0126' calls _verify_plan with the label."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
captured_label = {}
def fake_verify_plan(label):
captured_label["label"] = label
monkeypatch.setattr(verify, "_verify_plan", fake_verify_plan)
result = verify.handle_command("verify", ["FPLAN-0126"])
assert result is True
assert captured_label["label"] == "FPLAN-0126"
def test_unknown_command_returns_false(self, monkeypatch):
"""Unknown command returns False (not handled)."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
result = verify.handle_command("bogus", ["arg1"])
assert result is False
def test_toplevel_help_flag(self, monkeypatch):
"""Top-level '--help' command triggers print_help."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
called = {"help": False}
monkeypatch.setattr(verify, "print_help", lambda: called.update(help=True))
result = verify.handle_command("--help", [])
assert result is True
assert called["help"] is True
def test_toplevel_h_flag(self, monkeypatch):
"""Top-level '-h' command triggers print_help."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
called = {"help": False}
monkeypatch.setattr(verify, "print_help", lambda: called.update(help=True))
result = verify.handle_command("-h", [])
assert result is True
assert called["help"] is True
def test_toplevel_help_word(self, monkeypatch):
"""Top-level 'help' command triggers print_help."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
called = {"help": False}
monkeypatch.setattr(verify, "print_help", lambda: called.update(help=True))
result = verify.handle_command("help", [])
assert result is True
assert called["help"] is True
# ===========================================================================
# 5. _discover_handlers()
# ===========================================================================
def _setup_fake_module_tree(tmp_path):
"""Build tmp_path/apps/modules/verify.py and apps/handlers/ so that
patching verify.__file__ makes _discover_handlers() scan our fake tree.
_discover_handlers does:
Path(__file__).resolve().parent.parent / "handlers"
so __file__ must sit at <root>/apps/modules/verify.py
and handlers at <root>/apps/handlers/
"""
modules_dir = tmp_path / "apps" / "modules"
modules_dir.mkdir(parents=True)
fake_file = modules_dir / "verify.py"
fake_file.touch()
handlers_dir = tmp_path / "apps" / "handlers"
handlers_dir.mkdir(parents=True)
return str(fake_file), handlers_dir
class TestDiscoverHandlers:
"""Tests for _discover_handlers() directory scanning."""
def test_discovers_handler_directories_with_py_files(self, monkeypatch, tmp_path):
"""Finds handler dirs containing .py files (excluding __init__.py)."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
fake_file, handlers_dir = _setup_fake_module_tree(tmp_path)
storage_dir = handlers_dir / "storage"
storage_dir.mkdir()
(storage_dir / "__init__.py").write_text("", encoding="utf-8")
(storage_dir / "chroma_subprocess.py").write_text("", encoding="utf-8")
(storage_dir / "vector_store.py").write_text("", encoding="utf-8")
search_dir = handlers_dir / "search"
search_dir.mkdir()
(search_dir / "__init__.py").write_text("", encoding="utf-8")
(search_dir / "query_engine.py").write_text("", encoding="utf-8")
monkeypatch.setattr(verify, "__file__", fake_file)
result = verify._discover_handlers()
assert "storage" in result
assert "search" in result
assert "chroma_subprocess.py" in result["storage"]
assert "vector_store.py" in result["storage"]
assert "query_engine.py" in result["search"]
# __init__.py should be excluded
assert "__init__.py" not in result["storage"]
def test_skips_dunder_directories(self, monkeypatch, tmp_path):
"""Directories starting with __ (like __pycache__) are skipped."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
fake_file, handlers_dir = _setup_fake_module_tree(tmp_path)
pycache = handlers_dir / "__pycache__"
pycache.mkdir()
(pycache / "verify.cpython-312.pyc").write_text("", encoding="utf-8")
real_dir = handlers_dir / "json"
real_dir.mkdir()
(real_dir / "json_handler.py").write_text("", encoding="utf-8")
monkeypatch.setattr(verify, "__file__", fake_file)
result = verify._discover_handlers()
assert "__pycache__" not in result
assert "json" in result
def test_empty_handlers_dir_returns_empty(self, monkeypatch, tmp_path):
"""An empty handlers directory returns an empty dict."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
fake_file, _handlers_dir = _setup_fake_module_tree(tmp_path)
# handlers_dir exists but is empty (no subdirs)
monkeypatch.setattr(verify, "__file__", fake_file)
result = verify._discover_handlers()
assert result == {}
def test_missing_handlers_dir_returns_empty(self, monkeypatch, tmp_path):
"""A nonexistent handlers directory returns an empty dict."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
# Point __file__ at a tree with no handlers/ at all
modules_dir = tmp_path / "no_handlers" / "apps" / "modules"
modules_dir.mkdir(parents=True)
fake_file = modules_dir / "verify.py"
fake_file.touch()
monkeypatch.setattr(verify, "__file__", str(fake_file))
result = verify._discover_handlers()
assert result == {}
def test_dir_with_only_init_is_excluded(self, monkeypatch, tmp_path):
"""A handler dir with only __init__.py (no real modules) is excluded."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
fake_file, handlers_dir = _setup_fake_module_tree(tmp_path)
empty_handler = handlers_dir / "empty_handler"
empty_handler.mkdir()
(empty_handler / "__init__.py").write_text("", encoding="utf-8")
monkeypatch.setattr(verify, "__file__", fake_file)
result = verify._discover_handlers()
assert "empty_handler" not in result
assert result == {}
def test_non_py_files_are_ignored(self, monkeypatch, tmp_path):
"""Non-.py files (README, .json, etc.) are not included."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
fake_file, handlers_dir = _setup_fake_module_tree(tmp_path)
config_dir = handlers_dir / "config"
config_dir.mkdir()
(config_dir / "settings.json").write_text("{}", encoding="utf-8")
(config_dir / "README.md").write_text("docs", encoding="utf-8")
(config_dir / "config_handler.py").write_text("", encoding="utf-8")
monkeypatch.setattr(verify, "__file__", fake_file)
result = verify._discover_handlers()
assert "config" in result
assert result["config"] == ["config_handler.py"]
# Confirm non-py files are absent
all_files = [f for files in result.values() for f in files]
assert "settings.json" not in all_files
assert "README.md" not in all_files
def test_results_are_sorted(self, monkeypatch, tmp_path):
"""Handler dirs and files within them are returned in sorted order."""
_mock_cli_modules(monkeypatch)
verify = _import_verify()
fake_file, handlers_dir = _setup_fake_module_tree(tmp_path)
# Create dirs in reverse alphabetical order
for name in ["zebra", "alpha", "middle"]:
d = handlers_dir / name
d.mkdir()
(d / "handler.py").write_text("", encoding="utf-8")
# Add multiple files to alpha in non-sorted order
(handlers_dir / "alpha" / "z_module.py").write_text("", encoding="utf-8")
(handlers_dir / "alpha" / "a_module.py").write_text("", encoding="utf-8")
monkeypatch.setattr(verify, "__file__", fake_file)
result = verify._discover_handlers()
dir_names = list(result.keys())
assert dir_names == ["alpha", "middle", "zebra"]
assert result["alpha"] == ["a_module.py", "handler.py", "z_module.py"]