mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
2e5813d31e
Refactor CLI commands to use typed ProjectClient instead of raw HTTP calls, and add workspace metadata to cloud project listings so users can distinguish personal vs organization projects. Key changes: - 🔧 CLI commands now use ProjectClient typed API clients instead of call_get/call_post with manual URL construction - 🏢 Cloud project listings include workspace_name, workspace_type, and workspace_tenant_id for each cloud-sourced project - Pass config.default_workspace when fetching cloud projects via _fetch_cloud_projects() and CLI list_projects - Add --workspace flag to `bm project list` for explicit workspace override - Add "Workspace" column to CLI project list table - Add `bm tool list-projects` and `bm tool list-workspaces` JSON commands - Comprehensive tests for workspace passthrough, merge behavior, and CLI routing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: phernandez <paul@basicmachines.co>
181 lines
6.3 KiB
Python
181 lines
6.3 KiB
Python
"""Tests for workspace CLI commands."""
|
|
|
|
import tempfile
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from typer.testing import CliRunner
|
|
|
|
import basic_memory.config
|
|
from basic_memory.cli.app import app
|
|
from basic_memory.config import BasicMemoryConfig, ConfigManager
|
|
from basic_memory.schemas.cloud import WorkspaceInfo
|
|
|
|
# Importing the cloud package registers workspace_app on cloud_app.
|
|
import basic_memory.cli.commands.cloud as cloud_cmd # noqa: F401
|
|
import basic_memory.cli.commands.cloud.workspace as workspace_cmd # noqa: F401
|
|
|
|
|
|
@pytest.fixture
|
|
def runner():
|
|
return CliRunner()
|
|
|
|
|
|
def test_workspace_list_prints_available_workspaces(runner, monkeypatch):
|
|
async def fake_get_available_workspaces(context=None):
|
|
return [
|
|
WorkspaceInfo(
|
|
tenant_id="11111111-1111-1111-1111-111111111111",
|
|
workspace_type="personal",
|
|
name="Personal",
|
|
role="owner",
|
|
),
|
|
WorkspaceInfo(
|
|
tenant_id="22222222-2222-2222-2222-222222222222",
|
|
workspace_type="organization",
|
|
name="Team",
|
|
role="editor",
|
|
),
|
|
]
|
|
|
|
monkeypatch.setattr(workspace_cmd, "get_available_workspaces", fake_get_available_workspaces)
|
|
|
|
result = runner.invoke(app, ["cloud", "workspace", "list"])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Available Workspaces" in result.stdout
|
|
assert "Personal" in result.stdout
|
|
assert "Team" in result.stdout
|
|
# Tenant ID may be truncated by Rich table rendering
|
|
assert "11111111" in result.stdout
|
|
|
|
|
|
def test_workspace_list_requires_oauth_login_message(runner, monkeypatch):
|
|
async def fail_get_available_workspaces(context=None): # pragma: no cover
|
|
raise RuntimeError("Workspace discovery requires OAuth login. Run 'bm cloud login' first.")
|
|
|
|
monkeypatch.setattr(workspace_cmd, "get_available_workspaces", fail_get_available_workspaces)
|
|
|
|
result = runner.invoke(app, ["cloud", "workspace", "list"])
|
|
|
|
assert result.exit_code == 1
|
|
assert "Workspace discovery requires OAuth login" in result.stdout
|
|
assert "bm cloud login" in result.stdout
|
|
|
|
|
|
class TestWorkspaceSetDefault:
|
|
"""Tests for 'bm cloud workspace set-default' command."""
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _setup_config(self, monkeypatch):
|
|
"""Set up a temp config for each test."""
|
|
self.temp_dir = tempfile.mkdtemp()
|
|
temp_path = Path(self.temp_dir)
|
|
config_dir = temp_path / ".basic-memory"
|
|
config_dir.mkdir(parents=True, exist_ok=True)
|
|
monkeypatch.setenv("HOME", str(temp_path))
|
|
monkeypatch.setenv("BASIC_MEMORY_CONFIG_DIR", str(config_dir))
|
|
basic_memory.config._CONFIG_CACHE = None
|
|
|
|
config_manager = ConfigManager()
|
|
test_config = BasicMemoryConfig(
|
|
projects={"main": {"path": str(temp_path / "main")}},
|
|
)
|
|
config_manager.save_config(test_config)
|
|
|
|
def test_set_default_workspace_by_name(self, runner, monkeypatch):
|
|
async def fake_get_available_workspaces(context=None):
|
|
return [
|
|
WorkspaceInfo(
|
|
tenant_id="11111111-1111-1111-1111-111111111111",
|
|
workspace_type="personal",
|
|
name="Personal",
|
|
role="owner",
|
|
),
|
|
]
|
|
|
|
monkeypatch.setattr(
|
|
workspace_cmd, "get_available_workspaces", fake_get_available_workspaces
|
|
)
|
|
|
|
result = runner.invoke(app, ["cloud", "workspace", "set-default", "Personal"])
|
|
|
|
assert result.exit_code == 0
|
|
assert "Default workspace set" in result.stdout
|
|
assert "Personal" in result.stdout
|
|
|
|
# Verify config was updated
|
|
basic_memory.config._CONFIG_CACHE = None
|
|
config = ConfigManager().config
|
|
assert config.default_workspace == "11111111-1111-1111-1111-111111111111"
|
|
|
|
def test_set_default_workspace_by_tenant_id(self, runner, monkeypatch):
|
|
async def fake_get_available_workspaces(context=None):
|
|
return [
|
|
WorkspaceInfo(
|
|
tenant_id="22222222-2222-2222-2222-222222222222",
|
|
workspace_type="organization",
|
|
name="Team",
|
|
role="editor",
|
|
),
|
|
]
|
|
|
|
monkeypatch.setattr(
|
|
workspace_cmd, "get_available_workspaces", fake_get_available_workspaces
|
|
)
|
|
|
|
result = runner.invoke(
|
|
app, ["cloud", "workspace", "set-default", "22222222-2222-2222-2222-222222222222"]
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
assert "Default workspace set" in result.stdout
|
|
|
|
def test_set_default_workspace_not_found(self, runner, monkeypatch):
|
|
async def fake_get_available_workspaces(context=None):
|
|
return [
|
|
WorkspaceInfo(
|
|
tenant_id="11111111-1111-1111-1111-111111111111",
|
|
workspace_type="personal",
|
|
name="Personal",
|
|
role="owner",
|
|
),
|
|
]
|
|
|
|
monkeypatch.setattr(
|
|
workspace_cmd, "get_available_workspaces", fake_get_available_workspaces
|
|
)
|
|
|
|
result = runner.invoke(app, ["cloud", "workspace", "set-default", "Nonexistent"])
|
|
|
|
assert result.exit_code == 1
|
|
assert "not found" in result.stdout
|
|
|
|
def test_set_default_workspace_no_workspaces(self, runner, monkeypatch):
|
|
async def fake_get_available_workspaces(context=None):
|
|
return []
|
|
|
|
monkeypatch.setattr(
|
|
workspace_cmd, "get_available_workspaces", fake_get_available_workspaces
|
|
)
|
|
|
|
result = runner.invoke(app, ["cloud", "workspace", "set-default", "Personal"])
|
|
|
|
assert result.exit_code == 1
|
|
assert "No accessible workspaces" in result.stdout
|
|
|
|
def test_set_default_workspace_oauth_error(self, runner, monkeypatch):
|
|
async def fail_get_available_workspaces(context=None): # pragma: no cover
|
|
raise RuntimeError(
|
|
"Workspace discovery requires OAuth login. Run 'bm cloud login' first."
|
|
)
|
|
|
|
monkeypatch.setattr(
|
|
workspace_cmd, "get_available_workspaces", fail_get_available_workspaces
|
|
)
|
|
|
|
result = runner.invoke(app, ["cloud", "workspace", "set-default", "Personal"])
|
|
|
|
assert result.exit_code == 1
|
|
assert "OAuth login" in result.stdout
|