Files
Paul Hernandez 1ad3a350ad fix(mcp): close out the #952 manual verification findings (#981)
Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 17:59:35 -05:00

96 lines
3.5 KiB
Python

"""Tests for `bm man install` (#952 / #610: make `man bm` work)."""
import subprocess
from typer.testing import CliRunner
from basic_memory.cli.app import app
# Importing the module registers the man command group on the top-level app.
import basic_memory.cli.commands.man as man_command # noqa: F401
runner = CliRunner()
def _flattened(output: str) -> str:
# rich wraps console output at the terminal width, which differs between
# local shells and CI — collapse all whitespace so phrase assertions can't
# be split by a line break.
return " ".join(output.split())
def test_man_install_writes_pages_to_target(tmp_path):
"""Install copies every bundled page into <root>/man1 as valid groff."""
result = runner.invoke(app, ["man", "install", "--dir", str(tmp_path)])
assert result.exit_code == 0, result.output
bm_page = tmp_path / "man1" / "bm.1"
alias_page = tmp_path / "man1" / "basic-memory.1"
assert bm_page.exists()
assert alias_page.exists()
# groff sanity: a real title header and the alias .so include
assert bm_page.read_text().startswith(".TH BM 1")
assert alias_page.read_text().strip() == ".so man1/bm.1"
assert "Try:" in _flattened(result.output)
def test_man_install_warns_when_root_not_on_manpath(tmp_path, monkeypatch):
"""A root provably absent from manpath output gets the MANPATH hint."""
def fake_run(*args, **kwargs):
return subprocess.CompletedProcess(
args=["manpath"], returncode=0, stdout="/usr/share/man:/opt/man", stderr=""
)
monkeypatch.setattr(man_command.subprocess, "run", fake_run)
result = runner.invoke(app, ["man", "install", "--dir", str(tmp_path)])
assert result.exit_code == 0, result.output
assert "not on your manpath" in _flattened(result.output)
assert "MANPATH" in _flattened(result.output)
def test_man_install_stays_quiet_when_manpath_unavailable(tmp_path, monkeypatch):
"""No manpath binary → no false-alarm warning, install still succeeds."""
def fake_run(*args, **kwargs):
raise FileNotFoundError("manpath")
monkeypatch.setattr(man_command.subprocess, "run", fake_run)
result = runner.invoke(app, ["man", "install", "--dir", str(tmp_path)])
assert result.exit_code == 0, result.output
assert "not on your manpath" not in _flattened(result.output)
def test_man_install_treats_manpath_failure_as_unknown(tmp_path, monkeypatch):
"""manpath exiting non-zero → unknown, no warning."""
def fake_run(*args, **kwargs):
return subprocess.CompletedProcess(args=["manpath"], returncode=1, stdout="", stderr="boom")
monkeypatch.setattr(man_command.subprocess, "run", fake_run)
result = runner.invoke(app, ["man", "install", "--dir", str(tmp_path)])
assert result.exit_code == 0, result.output
assert "not on your manpath" not in _flattened(result.output)
def test_man_install_skips_app_initialization(tmp_path, monkeypatch):
"""man install must not touch the database (PR #971 review).
Installing offline docs only copies packaged files; a locked or broken
local database must not block it, so `man` is in skip_init_commands.
"""
import basic_memory.services.initialization as init_module
def explode(*args, **kwargs):
raise AssertionError("ensure_initialization must not run for `bm man`")
monkeypatch.setattr(init_module, "ensure_initialization", explode)
result = runner.invoke(app, ["man", "install", "--dir", str(tmp_path)])
assert result.exit_code == 0, result.output
assert (tmp_path / "man1" / "bm.1").exists()