We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/basicmachines-co/basic-memory'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
"""Integration tests for project CLI commands."""
import tempfile
from pathlib import Path
from typer.testing import CliRunner
from basic_memory.cli.main import app as cli_app
WIDE_TERMINAL_ENV = {"COLUMNS": "240", "LINES": "60"}
def test_project_list(app, app_config, test_project, config_manager):
"""Test 'bm project list' command shows projects."""
runner = CliRunner()
result = runner.invoke(cli_app, ["project", "list"], env=WIDE_TERMINAL_ENV)
if result.exit_code != 0:
print(f"STDOUT: {result.stdout}")
print(f"STDERR: {result.stderr}")
print(f"Exception: {result.exception}")
assert result.exit_code == 0
assert "test-project" in result.stdout
assert "[X]" in result.stdout # default marker
def test_project_info(app, app_config, test_project, config_manager):
"""Test 'bm project info' command shows project details."""
runner = CliRunner()
result = runner.invoke(cli_app, ["project", "info", "test-project"])
if result.exit_code != 0:
print(f"STDOUT: {result.stdout}")
print(f"STDERR: {result.stderr}")
assert result.exit_code == 0
assert "test-project" in result.stdout
assert "Knowledge Graph" in result.stdout
def test_project_info_json(app, app_config, test_project, config_manager):
"""Test 'bm project info --json' command outputs valid JSON."""
import json
runner = CliRunner()
result = runner.invoke(cli_app, ["project", "info", "test-project", "--json"])
if result.exit_code != 0:
print(f"STDOUT: {result.stdout}")
print(f"STDERR: {result.stderr}")
assert result.exit_code == 0
# Parse JSON to verify it's valid
data = json.loads(result.stdout)
assert data["project_name"] == "test-project"
assert "statistics" in data
assert "system" in data
def test_project_add_and_remove(app, app_config, config_manager):
"""Test adding and removing a project."""
runner = CliRunner()
# Use a separate temporary directory to avoid nested path conflicts
with tempfile.TemporaryDirectory() as temp_dir:
new_project_path = Path(temp_dir) / "new-project"
new_project_path.mkdir()
# Add project
result = runner.invoke(cli_app, ["project", "add", "new-project", str(new_project_path)])
if result.exit_code != 0:
print(f"STDOUT: {result.stdout}")
print(f"STDERR: {result.stderr}")
assert result.exit_code == 0
assert (
"Project 'new-project' added successfully" in result.stdout
or "added" in result.stdout.lower()
)
# Verify it shows up in list
result = runner.invoke(cli_app, ["project", "list"], env=WIDE_TERMINAL_ENV)
assert result.exit_code == 0
assert "new-project" in result.stdout
# Remove project
result = runner.invoke(cli_app, ["project", "remove", "new-project"])
assert result.exit_code == 0
assert "removed" in result.stdout.lower() or "deleted" in result.stdout.lower()
def test_project_set_default(app, app_config, config_manager):
"""Test setting default project."""
runner = CliRunner()
# Use a separate temporary directory to avoid nested path conflicts
with tempfile.TemporaryDirectory() as temp_dir:
new_project_path = Path(temp_dir) / "another-project"
new_project_path.mkdir()
# Add a second project
result = runner.invoke(
cli_app, ["project", "add", "another-project", str(new_project_path)]
)
if result.exit_code != 0:
print(f"STDOUT: {result.stdout}")
print(f"STDERR: {result.stderr}")
assert result.exit_code == 0
# Set as default
result = runner.invoke(cli_app, ["project", "default", "another-project"])
if result.exit_code != 0:
print(f"STDOUT: {result.stdout}")
print(f"STDERR: {result.stderr}")
assert result.exit_code == 0
assert "default" in result.stdout.lower()
# Verify in list
result = runner.invoke(cli_app, ["project", "list"], env=WIDE_TERMINAL_ENV)
assert result.exit_code == 0
# The new project should have the [X] marker now
lines = result.stdout.split("\n")
for line in lines:
if "another-project" in line:
assert "[X]" in line
def test_remove_main_project(app, app_config, config_manager):
"""Test that removing main project then listing projects prevents main from reappearing (issue #397)."""
runner = CliRunner()
# Create separate temp dirs for each project
with (
tempfile.TemporaryDirectory() as main_dir,
tempfile.TemporaryDirectory() as new_default_dir,
):
main_path = Path(main_dir)
new_default_path = Path(new_default_dir)
# Ensure main exists
# Trigger: this test must work on Windows runners where output may contain "runneradmin".
# Why: substring checks against command output can mistake path text for project names.
# Outcome: use config state for setup decisions, then validate behavior via CLI invocation.
if "main" not in config_manager.config.projects:
result = runner.invoke(cli_app, ["project", "add", "main", str(main_path), "--local"])
print(result.stdout)
assert result.exit_code == 0
# Confirm main is present
assert "main" in config_manager.config.projects
# Add a second project
result = runner.invoke(
cli_app, ["project", "add", "new_default", str(new_default_path), "--local"]
)
assert result.exit_code == 0
# Set new_default as default (if needed)
result = runner.invoke(cli_app, ["project", "default", "new_default", "--local"])
assert result.exit_code == 0
# Remove main
result = runner.invoke(cli_app, ["project", "remove", "main", "--local"])
if result.exit_code != 0:
print(f"STDOUT: {result.stdout}")
print(f"STDERR: {result.stderr}")
assert result.exit_code == 0
# Confirm only new_default exists and main does not
result = runner.invoke(cli_app, ["project", "list", "--local"], env=WIDE_TERMINAL_ENV)
assert result.exit_code == 0
config_after_list = config_manager.load_config()
assert "main" not in config_after_list.projects
assert "new_default" in config_after_list.projects