Files
basicmachines-co-basic-memory/tests/cli/test_project_commands.py
2025-06-11 16:09:53 -05:00

144 lines
5.0 KiB
Python

"""Tests for the project CLI commands."""
import os
from unittest.mock import patch, MagicMock
from typer.testing import CliRunner
from basic_memory.cli.main import app as cli_app
@patch("basic_memory.cli.commands.project.asyncio.run")
def test_project_list_command(mock_run, cli_env):
"""Test the 'project list' command with mocked API."""
# Mock the API response
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"projects": [{"name": "test", "path": "/path/to/test", "is_default": True}],
"default_project": "test",
"current_project": "test",
}
mock_run.return_value = mock_response
runner = CliRunner()
result = runner.invoke(cli_app, ["project", "list"])
# Just verify it runs without exception
assert result.exit_code == 0
@patch("basic_memory.cli.commands.project.asyncio.run")
def test_project_add_command(mock_run, cli_env):
"""Test the 'project add' command with mocked API."""
# Mock the API response
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"message": "Project 'test-project' added successfully",
"status": "success",
"default": False,
}
mock_run.return_value = mock_response
runner = CliRunner()
result = runner.invoke(cli_app, ["project", "add", "test-project", "/path/to/project"])
# Just verify it runs without exception
assert result.exit_code == 0
@patch("basic_memory.cli.commands.project.asyncio.run")
def test_project_remove_command(mock_run, cli_env):
"""Test the 'project remove' command with mocked API."""
# Mock the API response
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"message": "Project 'test-project' removed successfully",
"status": "success",
"default": False,
}
mock_run.return_value = mock_response
runner = CliRunner()
result = runner.invoke(cli_app, ["project", "remove", "test-project"])
# Just verify it runs without exception
assert result.exit_code == 0
@patch("basic_memory.cli.commands.project.asyncio.run")
@patch("importlib.reload")
def test_project_default_command(mock_reload, mock_run, cli_env):
"""Test the 'project default' command with mocked API."""
# Mock the API response
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"message": "Project 'test-project' set as default successfully",
"status": "success",
"default": True,
}
mock_run.return_value = mock_response
# Mock necessary config methods to have the test-project handled
# Patching call_put directly since it's imported at the module level
# Patch the os.environ for checking
with patch.dict(os.environ, {}, clear=True):
# Patch ConfigManager.set_default_project to prevent validation error
with patch("basic_memory.config.ConfigManager.set_default_project"):
runner = CliRunner()
result = runner.invoke(cli_app, ["project", "default", "test-project"])
# Just verify it runs without exception and environment is set
assert result.exit_code == 0
@patch("basic_memory.cli.commands.project.asyncio.run")
def test_project_sync_command(mock_run, cli_env):
"""Test the 'project sync' command with mocked API."""
# Mock the API response
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.json.return_value = {
"message": "Projects synchronized successfully between configuration and database",
"status": "success",
"default": False,
}
mock_run.return_value = mock_response
runner = CliRunner()
result = runner.invoke(cli_app, ["project", "sync-config"])
# Just verify it runs without exception
assert result.exit_code == 0
@patch("basic_memory.cli.commands.project.asyncio.run")
def test_project_failure_exits_with_error(mock_run, cli_env):
"""Test that CLI commands properly exit with error code on API failures."""
# Mock an exception being raised
mock_run.side_effect = Exception("API server not running")
runner = CliRunner()
# Test various commands for proper error handling
list_result = runner.invoke(cli_app, ["project", "list"])
add_result = runner.invoke(cli_app, ["project", "add", "test-project", "/path/to/project"])
remove_result = runner.invoke(cli_app, ["project", "remove", "test-project"])
default_result = runner.invoke(cli_app, ["project", "default", "test-project"])
# All should exit with code 1 and show error message
assert list_result.exit_code == 1
assert "Error listing projects" in list_result.output
assert add_result.exit_code == 1
assert "Error adding project" in add_result.output
assert remove_result.exit_code == 1
assert "Error removing project" in remove_result.output
assert default_result.exit_code == 1
assert "Error setting default project" in default_result.output