chore: import upstream snapshot with attribution
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Has been cancelled
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Has been cancelled
CD: Docs MCP Server / build (linux/amd64) (push) Has been cancelled
CD: Docs MCP Server / build (linux/arm64) (push) Has been cancelled
CD: Docs MCP Server / merge (push) Has been cancelled
CI: cua-driver distro-compat matrix / Resolve release version (push) Has been cancelled
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Has been cancelled
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Has been cancelled
CI: cua-driver distro-compat matrix / Distro compat summary (push) Has been cancelled
CI: Rust Linux unit / Rust Linux unit and compile (push) Has been cancelled
CI: Rust Windows unit / Rust Windows unit and compile (push) Has been cancelled
CI: Nix Linux Rust source / Nix / compositor build (push) Has been cancelled
CI: Nix Linux Rust source / Nix / driver package (push) Has been cancelled
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:03:19 +08:00
commit 91e75e620b
3227 changed files with 1307078 additions and 0 deletions
@@ -0,0 +1 @@
"""Command module tests."""
@@ -0,0 +1,200 @@
"""Tests for auth command module."""
import argparse
from unittest.mock import patch
from cua_cli.commands import auth
class TestRegisterParser:
"""Tests for register_parser function."""
def test_registers_auth_command(self):
"""Test that auth command is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
auth.register_parser(subparsers)
args = parser.parse_args(["auth", "login"])
assert args.auth_command == "login"
def test_login_has_api_key_flag(self):
"""Test that login has --api-key flag."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
auth.register_parser(subparsers)
args = parser.parse_args(["auth", "login", "--api-key", "my-key"])
assert args.api_key == "my-key"
def test_logout_command(self):
"""Test that logout command is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
auth.register_parser(subparsers)
args = parser.parse_args(["auth", "logout"])
assert args.auth_command == "logout"
def test_env_command(self):
"""Test that env command is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
auth.register_parser(subparsers)
args = parser.parse_args(["auth", "env"])
assert args.auth_command == "env"
class TestExecute:
"""Tests for execute function."""
def test_dispatch_to_login(self, args_namespace):
"""Test dispatch to login command."""
args = args_namespace(command="auth", auth_command="login", api_key=False)
with patch.object(auth, "cmd_login", return_value=0) as mock_cmd:
result = auth.execute(args)
mock_cmd.assert_called_once_with(args)
assert result == 0
def test_dispatch_to_logout(self, args_namespace):
"""Test dispatch to logout command."""
args = args_namespace(command="auth", auth_command="logout")
with patch.object(auth, "cmd_logout", return_value=0) as mock_cmd:
auth.execute(args)
mock_cmd.assert_called_once_with(args)
def test_dispatch_to_env(self, args_namespace):
"""Test dispatch to env command."""
args = args_namespace(command="auth", auth_command="env")
with patch.object(auth, "cmd_env", return_value=0) as mock_cmd:
auth.execute(args)
mock_cmd.assert_called_once_with(args)
def test_unknown_command_returns_error(self, args_namespace):
"""Test that unknown command returns error."""
args = args_namespace(command="auth", auth_command=None)
with patch.object(auth, "print_error"):
result = auth.execute(args)
assert result == 1
class TestCmdLogin:
"""Tests for cmd_login function."""
def test_login_with_api_key_direct(self, args_namespace):
"""Test login with --api-key flag and value."""
args = args_namespace(api_key="test-api-key")
with patch.object(auth, "get_api_key", return_value=None):
with patch.object(auth, "save_api_key") as mock_save:
with patch.object(auth, "print_info"):
with patch.object(auth, "print_success"):
result = auth.cmd_login(args)
mock_save.assert_called_once_with("test-api-key")
assert result == 0
def test_login_already_authenticated(self, args_namespace):
"""Test login when already authenticated."""
args = args_namespace(api_key=None)
with patch.object(auth, "get_api_key", return_value="existing-key"):
with patch.object(auth, "print_info") as mock_info:
result = auth.cmd_login(args)
assert result == 0
mock_info.assert_called()
def test_login_browser_flow(self, args_namespace):
"""Test login with browser OAuth flow."""
args = args_namespace(api_key=None)
with patch.object(auth, "get_api_key", return_value=None):
with patch.object(auth, "run_async") as mock_run:
mock_run.return_value = "browser-api-key"
with patch.object(auth, "save_api_key") as mock_save:
with patch.object(auth, "print_success"):
result = auth.cmd_login(args)
mock_save.assert_called_once_with("browser-api-key")
assert result == 0
def test_login_browser_flow_timeout(self, args_namespace):
"""Test login when browser flow times out."""
args = args_namespace(api_key=None)
with patch.object(auth, "get_api_key", return_value=None):
with patch.object(auth, "run_async") as mock_run:
mock_run.side_effect = TimeoutError("Authentication timed out")
with patch.object(auth, "print_error"):
result = auth.cmd_login(args)
assert result == 1
class TestCmdLogout:
"""Tests for cmd_logout function."""
def test_logout_clears_credentials(self, args_namespace):
"""Test that logout clears credentials."""
args = args_namespace()
with patch.object(auth, "clear_credentials") as mock_clear:
with patch.object(auth, "print_success"):
result = auth.cmd_logout(args)
mock_clear.assert_called_once()
assert result == 0
class TestCmdEnv:
"""Tests for cmd_env function."""
def test_env_writes_to_dotenv(self, args_namespace, tmp_path, monkeypatch):
"""Test that env command writes to .env file."""
args = args_namespace(file=str(tmp_path / ".env"))
with patch.object(auth, "get_api_key", return_value="test-api-key"):
with patch.object(auth, "print_success"):
result = auth.cmd_env(args)
assert result == 0
env_file = tmp_path / ".env"
assert env_file.exists()
content = env_file.read_text()
assert "CUA_API_KEY=test-api-key" in content
def test_env_no_credentials_fails(self, args_namespace, tmp_path):
"""Test that env command fails when not logged in."""
args = args_namespace(file=str(tmp_path / ".env"))
with patch.object(auth, "get_api_key", return_value=None):
with patch.object(auth, "print_error"):
result = auth.cmd_env(args)
assert result == 1
def test_env_appends_to_existing_dotenv(self, args_namespace, tmp_path):
"""Test that env command appends to existing .env file."""
env_file = tmp_path / ".env"
env_file.write_text("EXISTING_VAR=value\n")
args = args_namespace(file=str(env_file))
with patch.object(auth, "get_api_key", return_value="test-api-key"):
with patch.object(auth, "print_success"):
result = auth.cmd_env(args)
assert result == 0
content = env_file.read_text()
assert "EXISTING_VAR=value" in content
assert "CUA_API_KEY=test-api-key" in content
@@ -0,0 +1,297 @@
"""Tests for image command module."""
import argparse
from unittest.mock import patch
from cua_cli.commands import image
class TestRegisterParser:
"""Tests for register_parser function."""
def test_registers_image_command(self):
"""Test that image command is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
image.register_parser(subparsers)
args = parser.parse_args(["image", "list"])
assert args.image_command == "list"
def test_registers_img_alias(self):
"""Test that img alias is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
image.register_parser(subparsers)
args = parser.parse_args(["img", "list"])
assert args.image_command == "list"
def test_list_has_json_flag(self):
"""Test that list command has --json flag."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
image.register_parser(subparsers)
args = parser.parse_args(["image", "list", "--json"])
assert args.json is True
def test_list_has_local_flag(self):
"""Test that list command has --local flag."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
image.register_parser(subparsers)
args = parser.parse_args(["image", "list", "--local"])
assert args.local is True
def test_push_command(self):
"""Test that push command is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
image.register_parser(subparsers)
args = parser.parse_args(["image", "push", "my-image"])
assert args.image_command == "push"
assert args.name == "my-image"
def test_pull_command(self):
"""Test that pull command is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
image.register_parser(subparsers)
args = parser.parse_args(["image", "pull", "my-image"])
assert args.image_command == "pull"
assert args.name == "my-image"
def test_delete_command(self):
"""Test that delete command is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
image.register_parser(subparsers)
args = parser.parse_args(["image", "delete", "my-image", "--force"])
assert args.image_command == "delete"
assert args.name == "my-image"
assert args.force is True
class TestExecute:
"""Tests for execute function."""
def test_dispatch_to_list(self, args_namespace):
"""Test dispatch to list command."""
args = args_namespace(
command="image",
image_command="list",
json=False,
local=False,
cloud=False,
)
with patch.object(image, "cmd_list", return_value=0) as mock_cmd:
result = image.execute(args)
mock_cmd.assert_called_once_with(args)
assert result == 0
def test_dispatch_to_push(self, args_namespace):
"""Test dispatch to push command."""
args = args_namespace(command="image", image_command="push", name="my-image")
with patch.object(image, "cmd_push", return_value=0) as mock_cmd:
image.execute(args)
mock_cmd.assert_called_once_with(args)
def test_dispatch_to_pull(self, args_namespace):
"""Test dispatch to pull command."""
args = args_namespace(command="image", image_command="pull", name="my-image")
with patch.object(image, "cmd_pull", return_value=0) as mock_cmd:
image.execute(args)
mock_cmd.assert_called_once_with(args)
def test_dispatch_to_delete(self, args_namespace):
"""Test dispatch to delete command."""
args = args_namespace(command="image", image_command="delete", name="my-image")
with patch.object(image, "cmd_delete", return_value=0) as mock_cmd:
image.execute(args)
mock_cmd.assert_called_once_with(args)
def test_unknown_command_returns_error(self, args_namespace):
"""Test that unknown command returns error."""
args = args_namespace(command="image", image_command=None)
with patch.object(image, "print_error"):
result = image.execute(args)
assert result == 1
class TestCmdList:
"""Tests for cmd_list function."""
def test_list_cloud_images(self, args_namespace, sample_cloud_images, mock_api_key):
"""Test listing cloud images."""
args = args_namespace(json=False, local=False, cloud=True)
with patch.object(image, "run_async") as mock_run:
mock_run.return_value = [
{
"name": "ubuntu-22.04",
"type": "qcow2",
"tag": "latest",
"size": "5.0 GB",
"status": "ready",
"created": "2024-01-10",
"source": "cloud",
}
]
with patch.object(image, "print_table") as mock_print:
result = image.cmd_list(args)
assert result == 0
mock_print.assert_called_once()
def test_list_local_images(self, args_namespace, tmp_path, monkeypatch):
"""Test listing local images delegates to local_image."""
args = args_namespace(json=False, local=True, cloud=False)
with patch("cua_cli.commands.local_image.cmd_local_list", return_value=0) as mock_local:
result = image.cmd_list(args)
assert result == 0
mock_local.assert_called_once_with(args)
def test_list_empty(self, args_namespace, tmp_path, monkeypatch):
"""Test listing when no local images exist."""
args = args_namespace(json=False, local=True, cloud=False)
with patch("cua_cli.commands.local_image.cmd_local_list", return_value=0) as mock_local:
result = image.cmd_list(args)
assert result == 0
mock_local.assert_called_once()
def test_list_json_output(self, args_namespace, mock_api_key):
"""Test JSON output format."""
args = args_namespace(json=True, local=False, cloud=True)
with patch.object(image, "run_async") as mock_run:
mock_run.return_value = [{"name": "test", "source": "cloud"}]
with patch.object(image, "print_json") as mock_print:
result = image.cmd_list(args)
assert result == 0
mock_print.assert_called_once()
class TestCmdPush:
"""Tests for cmd_push function."""
def test_push_success(self, args_namespace, temp_image_file, mock_api_key):
"""Test successful image push."""
args = args_namespace(
name="test-image",
tag="latest",
type="qcow2",
file=str(temp_image_file),
)
with patch.object(image, "run_async") as mock_run:
mock_run.return_value = 0
result = image.cmd_push(args)
assert result == 0
def test_push_file_not_found(self, args_namespace, mock_api_key):
"""Test push with nonexistent file."""
args = args_namespace(
name="test-image",
tag="latest",
type="qcow2",
file="/nonexistent/path",
)
with patch.object(image, "print_error") as mock_error:
result = image.cmd_push(args)
assert result == 1
mock_error.assert_called()
class TestCmdPull:
"""Tests for cmd_pull function."""
def test_pull_success(self, args_namespace, tmp_path, mock_api_key):
"""Test successful image pull."""
args = args_namespace(
name="test-image",
tag="latest",
output=str(tmp_path / "output.qcow2"),
)
with patch.object(image, "run_async") as mock_run:
mock_run.return_value = 0
result = image.cmd_pull(args)
assert result == 0
class TestCmdDelete:
"""Tests for cmd_delete function."""
def test_delete_with_force(self, args_namespace, mock_api_key):
"""Test delete with --force flag."""
args = args_namespace(name="test-image", tag="latest", force=True, local=False)
with patch.object(image, "run_async") as mock_run:
mock_run.return_value = 0
result = image.cmd_delete(args)
assert result == 0
def test_delete_without_force_prompts(self, args_namespace, mock_api_key):
"""Test delete without --force shows prompt."""
args = args_namespace(name="test-image", tag="latest", force=False, local=False)
with patch.object(image, "print_info") as mock_print:
result = image.cmd_delete(args)
assert result == 1
mock_print.assert_called()
def test_delete_local_delegates(self, args_namespace):
"""Test delete with --local delegates to local_image."""
args = args_namespace(name="test-image", tag="latest", force=True, local=True)
with patch("cua_cli.commands.local_image.cmd_local_delete", return_value=0) as mock_local:
result = image.cmd_delete(args)
assert result == 0
mock_local.assert_called_once_with(args)
class TestHelperFunctions:
"""Tests for helper functions."""
def test_format_date_valid(self):
"""Test formatting a valid ISO date."""
result = image._format_date("2024-01-15T10:30:00Z")
assert result == "2024-01-15"
def test_format_date_empty(self):
"""Test formatting empty string."""
result = image._format_date("")
assert result == "-"
def test_format_date_invalid(self):
"""Test formatting invalid date."""
result = image._format_date("not-a-date")
assert result == "not-a-date" # Falls back to first 10 chars
@@ -0,0 +1,232 @@
"""Tests for MCP server command module."""
import argparse
import sys
from unittest.mock import patch
from cua_cli.commands.mcp import (
PERMISSION_GROUPS,
Permission,
execute,
parse_permissions,
register_parser,
)
class TestPermission:
"""Tests for Permission enum."""
def test_all_permissions_have_values(self):
"""Test that all permissions have string values."""
for perm in Permission:
assert isinstance(perm.value, str)
assert ":" in perm.value
def test_sandbox_permissions(self):
"""Test sandbox permission values."""
assert Permission.SANDBOX_LIST.value == "sandbox:list"
assert Permission.SANDBOX_CREATE.value == "sandbox:create"
assert Permission.SANDBOX_DELETE.value == "sandbox:delete"
def test_computer_permissions(self):
"""Test computer permission values."""
assert Permission.COMPUTER_SCREENSHOT.value == "computer:screenshot"
assert Permission.COMPUTER_CLICK.value == "computer:click"
assert Permission.COMPUTER_TYPE.value == "computer:type"
def test_skills_permissions(self):
"""Test skills permission values."""
assert Permission.SKILLS_LIST.value == "skills:list"
assert Permission.SKILLS_READ.value == "skills:read"
assert Permission.SKILLS_RECORD.value == "skills:record"
class TestPermissionGroups:
"""Tests for permission groups."""
def test_sandbox_all_group(self):
"""Test sandbox:all group contains all sandbox permissions."""
group = PERMISSION_GROUPS["sandbox:all"]
assert Permission.SANDBOX_LIST in group
assert Permission.SANDBOX_CREATE in group
assert Permission.SANDBOX_DELETE in group
assert Permission.SANDBOX_START in group
assert Permission.SANDBOX_STOP in group
def test_sandbox_readonly_group(self):
"""Test sandbox:readonly group contains only read permissions."""
group = PERMISSION_GROUPS["sandbox:readonly"]
assert Permission.SANDBOX_LIST in group
assert Permission.SANDBOX_GET in group
assert Permission.SANDBOX_CREATE not in group
assert Permission.SANDBOX_DELETE not in group
def test_computer_all_group(self):
"""Test computer:all group contains all computer permissions."""
group = PERMISSION_GROUPS["computer:all"]
assert Permission.COMPUTER_SCREENSHOT in group
assert Permission.COMPUTER_CLICK in group
assert Permission.COMPUTER_TYPE in group
assert Permission.COMPUTER_SHELL in group
def test_computer_readonly_group(self):
"""Test computer:readonly group contains only screenshot."""
group = PERMISSION_GROUPS["computer:readonly"]
assert Permission.COMPUTER_SCREENSHOT in group
assert Permission.COMPUTER_CLICK not in group
def test_all_group_contains_everything(self):
"""Test that 'all' group contains all permissions."""
group = PERMISSION_GROUPS["all"]
assert len(group) == len(Permission)
class TestParsePermissions:
"""Tests for parse_permissions function."""
def test_empty_string_returns_empty_set(self):
"""Test that empty string returns empty set."""
result = parse_permissions("")
assert result == set()
def test_single_permission(self):
"""Test parsing a single permission."""
result = parse_permissions("sandbox:list")
assert result == {Permission.SANDBOX_LIST}
def test_multiple_permissions(self):
"""Test parsing multiple permissions."""
result = parse_permissions("sandbox:list,sandbox:create")
assert result == {Permission.SANDBOX_LIST, Permission.SANDBOX_CREATE}
def test_permission_with_spaces(self):
"""Test parsing permissions with spaces."""
result = parse_permissions("sandbox:list, sandbox:create")
assert result == {Permission.SANDBOX_LIST, Permission.SANDBOX_CREATE}
def test_group_expansion(self):
"""Test that groups are expanded."""
result = parse_permissions("sandbox:readonly")
assert Permission.SANDBOX_LIST in result
assert Permission.SANDBOX_GET in result
def test_all_group(self):
"""Test that 'all' group includes everything."""
result = parse_permissions("all")
assert len(result) == len(Permission)
def test_mixed_groups_and_permissions(self):
"""Test mixing groups and individual permissions."""
result = parse_permissions("sandbox:readonly,computer:screenshot")
assert Permission.SANDBOX_LIST in result
assert Permission.SANDBOX_GET in result
assert Permission.COMPUTER_SCREENSHOT in result
def test_unknown_permission_ignored(self):
"""Test that unknown permissions are ignored with warning."""
with patch("cua_cli.commands.mcp.logger") as mock_logger:
result = parse_permissions("sandbox:list,unknown:perm")
assert result == {Permission.SANDBOX_LIST}
mock_logger.warning.assert_called()
class TestRegisterParser:
"""Tests for register_parser function."""
def test_registers_serve_mcp_command(self):
"""Test that serve-mcp command is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
register_parser(subparsers)
args = parser.parse_args(["serve-mcp"])
assert hasattr(args, "permissions")
def test_has_permissions_flag(self):
"""Test that --permissions flag is available."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
register_parser(subparsers)
args = parser.parse_args(["serve-mcp", "--permissions", "sandbox:all"])
assert args.permissions == "sandbox:all"
def test_has_sandbox_flag(self):
"""Test that --sandbox flag is available."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
register_parser(subparsers)
args = parser.parse_args(["serve-mcp", "--sandbox", "my-sandbox"])
assert args.sandbox == "my-sandbox"
class TestExecute:
"""Tests for execute function."""
def test_missing_fastmcp_returns_error(self, args_namespace, capsys):
"""Test that missing fastmcp dependency returns error."""
args = args_namespace(permissions="", sandbox="")
# Temporarily remove mcp modules to simulate ImportError
original_modules = {}
for mod in list(sys.modules.keys()):
if mod.startswith("mcp"):
original_modules[mod] = sys.modules.pop(mod)
try:
with patch.dict(
"sys.modules", {"mcp": None, "mcp.server": None, "mcp.server.fastmcp": None}
):
result = execute(args)
finally:
# Restore modules
sys.modules.update(original_modules)
assert result == 1
def test_permissions_from_env_var(self, args_namespace, monkeypatch):
"""Test that permissions are read from environment variable."""
monkeypatch.setenv("CUA_MCP_PERMISSIONS", "sandbox:readonly")
permissions = parse_permissions("sandbox:readonly")
assert Permission.SANDBOX_LIST in permissions
assert Permission.SANDBOX_CREATE not in permissions
def test_permissions_from_args_override_env(self, args_namespace, monkeypatch):
"""Test that command line args override environment variable."""
args = args_namespace(permissions="computer:all", sandbox="")
monkeypatch.setenv("CUA_MCP_PERMISSIONS", "sandbox:readonly")
# The args.permissions should take precedence
assert args.permissions == "computer:all"
class TestMCPToolRegistration:
"""Tests for MCP tool registration based on permissions."""
def test_permission_affects_tool_registration(self):
"""Test that permissions control which tools would be registered."""
# This is a unit test of the permission logic, not the actual registration
# The actual registration requires fastmcp which may not be installed
all_perms = parse_permissions("all")
assert len(all_perms) == len(Permission)
readonly_perms = parse_permissions("sandbox:readonly")
assert Permission.SANDBOX_LIST in readonly_perms
assert Permission.SANDBOX_CREATE not in readonly_perms
# Test that the registration functions exist and are callable
from cua_cli.commands.mcp import (
_register_computer_tools,
_register_sandbox_tools,
_register_skills_tools,
)
assert callable(_register_sandbox_tools)
assert callable(_register_computer_tools)
assert callable(_register_skills_tools)
@@ -0,0 +1,671 @@
"""Tests for sandbox command module."""
import argparse
from unittest.mock import AsyncMock, MagicMock, patch
from cua_cli.commands import sandbox
class TestRegisterParser:
"""Tests for register_parser function."""
def test_registers_sandbox_command(self):
"""Test that sandbox command is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
sandbox.register_parser(subparsers)
# Parse a sandbox command to verify it's registered
args = parser.parse_args(["sandbox", "list"])
assert args.sandbox_command == "list"
def test_registers_sb_alias(self):
"""Test that sb alias is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
sandbox.register_parser(subparsers)
# Parse using alias
args = parser.parse_args(["sb", "list"])
assert args.sandbox_command == "list"
def test_list_command_has_json_flag(self):
"""Test that list command has --json flag."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
sandbox.register_parser(subparsers)
args = parser.parse_args(["sandbox", "list", "--json"])
assert args.json is True
def test_create_command_has_required_args(self):
"""Test that create command has required arguments."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
sandbox.register_parser(subparsers)
args = parser.parse_args(
[
"sandbox",
"create",
"--os",
"linux",
"--size",
"medium",
"--region",
"north-america",
]
)
assert args.os == "linux"
assert args.size == "medium"
assert args.region == "north-america"
class TestExecute:
"""Tests for execute function."""
def test_dispatch_to_list(self, args_namespace):
"""Test dispatch to list command."""
args = args_namespace(
command="sandbox", sandbox_command="list", json=False, show_passwords=False
)
with patch.object(sandbox, "cmd_list", return_value=0) as mock_cmd:
result = sandbox.execute(args)
mock_cmd.assert_called_once_with(args)
assert result == 0
def test_dispatch_to_list_alias(self, args_namespace):
"""Test dispatch to list command via ls alias."""
args = args_namespace(command="sb", sandbox_command="ls", json=False, show_passwords=False)
with patch.object(sandbox, "cmd_list", return_value=0) as mock_cmd:
sandbox.execute(args)
mock_cmd.assert_called_once_with(args)
def test_unknown_command_returns_error(self, args_namespace):
"""Test that unknown command returns error."""
args = args_namespace(command="sandbox", sandbox_command=None)
with patch.object(sandbox, "print_error") as mock_error:
result = sandbox.execute(args)
assert result == 1
mock_error.assert_called()
class TestCmdList:
"""Tests for cmd_list function."""
def test_list_sandboxes_success(self, args_namespace, sample_vm_list, mock_api_key):
"""Test listing sandboxes successfully."""
args = args_namespace(json=False, show_passwords=False)
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.list_vms = AsyncMock(return_value=sample_vm_list)
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_table") as mock_print:
result = sandbox.cmd_list(args)
assert result == 0
mock_print.assert_called_once()
def test_list_sandboxes_empty(self, args_namespace, mock_api_key):
"""Test listing when no sandboxes exist."""
args = args_namespace(json=False, show_passwords=False)
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.list_vms = AsyncMock(return_value=[])
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_info") as mock_print:
result = sandbox.cmd_list(args)
assert result == 0
mock_print.assert_called_with("No sandboxes found.")
def test_list_sandboxes_json_output(self, args_namespace, sample_vm_list, mock_api_key):
"""Test JSON output format."""
args = args_namespace(json=True, show_passwords=False)
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.list_vms = AsyncMock(return_value=sample_vm_list)
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_json") as mock_print:
result = sandbox.cmd_list(args)
assert result == 0
mock_print.assert_called_once_with(sample_vm_list)
class TestCmdCreate:
"""Tests for cmd_create function."""
def test_create_sandbox_success(self, args_namespace, mock_api_key):
"""Test creating a sandbox successfully."""
args = args_namespace(
os="linux",
size="medium",
region="north-america",
json=False,
)
# Mock the API response (status 202 = provisioning)
async def mock_api_request(*args, **kwargs):
return (
202,
{
"status": "provisioning",
"name": "new-sandbox",
"host": "sandbox.example.com",
},
)
with patch.object(sandbox, "_api_request", side_effect=mock_api_request):
with patch.object(sandbox, "print_info"):
result = sandbox.cmd_create(args)
assert result == 0
def test_create_sandbox_error(self, args_namespace, mock_api_key):
"""Test handling create error."""
args = args_namespace(
os="linux",
size="medium",
region="north-america",
json=False,
)
# Mock the API response (status 500 = error)
async def mock_api_request(*args, **kwargs):
return (500, "Quota exceeded")
with patch.object(sandbox, "_api_request", side_effect=mock_api_request):
with patch.object(sandbox, "print_error") as mock_error:
result = sandbox.cmd_create(args)
assert result == 1
mock_error.assert_called()
class TestCmdGet:
"""Tests for cmd_get function."""
def test_get_sandbox_success(self, args_namespace, sample_vm, mock_api_key):
"""Test getting sandbox details."""
args = args_namespace(
name="test-sandbox-1",
json=False,
show_passwords=False,
show_vnc_url=False,
)
vm_dict = {
"name": sample_vm.name,
"status": sample_vm.status,
"os_type": sample_vm.os_type,
"host": "sandbox.example.com",
}
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.list_vms = AsyncMock(return_value=[vm_dict])
mock_provider.get_vm = AsyncMock(return_value={"status": "running"})
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_info"):
result = sandbox.cmd_get(args)
assert result == 0
def test_get_sandbox_not_found(self, args_namespace, mock_api_key):
"""Test getting nonexistent sandbox."""
args = args_namespace(
name="nonexistent",
json=False,
show_passwords=False,
show_vnc_url=False,
)
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.list_vms = AsyncMock(return_value=[])
mock_provider.get_vm = AsyncMock(return_value={"status": "not_found"})
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_error") as mock_error:
result = sandbox.cmd_get(args)
assert result == 1
mock_error.assert_called()
class TestCmdStart:
"""Tests for cmd_start function."""
def test_start_sandbox_success(self, args_namespace, mock_api_key):
"""Test starting a sandbox."""
args = args_namespace(name="test-sandbox")
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.run_vm = AsyncMock(return_value={"status": "starting"})
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_success"):
result = sandbox.cmd_start(args)
assert result == 0
def test_start_sandbox_not_found(self, args_namespace, mock_api_key):
"""Test starting nonexistent sandbox."""
args = args_namespace(name="nonexistent")
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.run_vm = AsyncMock(return_value={"status": "not_found"})
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_error"):
result = sandbox.cmd_start(args)
assert result == 1
class TestCmdStop:
"""Tests for cmd_stop function."""
def test_stop_sandbox_success(self, args_namespace, mock_api_key):
"""Test stopping a sandbox."""
args = args_namespace(name="test-sandbox")
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.stop_vm = AsyncMock(return_value={"status": "stopping"})
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_success"):
result = sandbox.cmd_stop(args)
assert result == 0
class TestCmdRestart:
"""Tests for cmd_restart function."""
def test_restart_sandbox_success(self, args_namespace, mock_api_key):
"""Test restarting a sandbox."""
args = args_namespace(name="test-sandbox")
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.restart_vm = AsyncMock(return_value={"status": "restarting"})
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_success"):
result = sandbox.cmd_restart(args)
assert result == 0
class TestCmdSuspend:
"""Tests for cmd_suspend function."""
def test_suspend_sandbox_success(self, args_namespace, mock_api_key):
"""Test suspending a sandbox."""
args = args_namespace(name="test-sandbox")
# Mock the API response (status 202 = suspending)
async def mock_api_request(*args, **kwargs):
return (202, {"status": "suspending"})
with patch.object(sandbox, "_api_request", side_effect=mock_api_request):
with patch.object(sandbox, "print_success"):
result = sandbox.cmd_suspend(args)
assert result == 0
def test_suspend_unsupported(self, args_namespace, mock_api_key):
"""Test suspend on unsupported sandbox."""
args = args_namespace(name="test-sandbox")
# Mock the API response (status 400 = unsupported)
async def mock_api_request(*args, **kwargs):
return (400, "Suspend not supported for Windows")
with patch.object(sandbox, "_api_request", side_effect=mock_api_request):
with patch.object(sandbox, "print_error"):
result = sandbox.cmd_suspend(args)
assert result == 1
class TestCmdDelete:
"""Tests for cmd_delete function."""
def test_delete_sandbox_success(self, args_namespace, mock_api_key):
"""Test deleting a sandbox."""
args = args_namespace(name="test-sandbox")
# Mock the API response (status 202 = deleting)
async def mock_api_request(*args, **kwargs):
return (202, {"status": "deleting"})
with patch.object(sandbox, "_api_request", side_effect=mock_api_request):
with patch.object(sandbox, "print_success"):
result = sandbox.cmd_delete(args)
assert result == 0
class TestCmdVnc:
"""Tests for cmd_vnc function."""
def test_vnc_opens_browser(self, args_namespace, mock_api_key, mock_webbrowser):
"""Test VNC opens browser with correct URL."""
args = args_namespace(name="test-sandbox")
vm_info = {
"name": "test-sandbox",
"vnc_url": "https://vnc.example.com/test",
}
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.list_vms = AsyncMock(return_value=[vm_info])
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_info"):
result = sandbox.cmd_vnc(args)
assert result == 0
mock_webbrowser.assert_called_once_with("https://vnc.example.com/test")
def test_vnc_constructs_url_from_host(self, args_namespace, mock_api_key, mock_webbrowser):
"""Test VNC constructs URL when vnc_url not provided."""
args = args_namespace(name="test-sandbox")
vm_info = {
"name": "test-sandbox",
"host": "sandbox.example.com",
"password": "secret123",
}
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.list_vms = AsyncMock(return_value=[vm_info])
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_info"):
result = sandbox.cmd_vnc(args)
assert result == 0
mock_webbrowser.assert_called_once()
# Check URL contains host and encoded password
call_url = mock_webbrowser.call_args[0][0]
assert "sandbox.example.com" in call_url
assert "secret123" in call_url
def test_vnc_sandbox_not_found(self, args_namespace, mock_api_key):
"""Test VNC with nonexistent sandbox."""
args = args_namespace(name="nonexistent")
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.list_vms = AsyncMock(return_value=[])
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_error"):
result = sandbox.cmd_vnc(args)
assert result == 1
class TestCmdShell:
"""Tests for cmd_shell function."""
def test_shell_parser_registration(self):
"""Test that shell command is registered with correct arguments."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
sandbox.register_parser(subparsers)
# Test basic shell command
args = parser.parse_args(["sb", "shell", "my-sandbox"])
assert args.name == "my-sandbox"
assert args.sandbox_command == "shell"
def test_shell_with_command(self):
"""Test shell command with a command to execute."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
sandbox.register_parser(subparsers)
args = parser.parse_args(["sb", "shell", "my-sandbox", "ls", "-la"])
assert args.name == "my-sandbox"
assert args.shell_command == ["ls", "-la"]
def test_shell_with_cols_rows(self):
"""Test shell command with terminal size options.
Options must come before name due to argparse REMAINDER behavior.
Usage: cua sb shell --cols 120 --rows 40 mybox [command...]
"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
sandbox.register_parser(subparsers)
args = parser.parse_args(
["sb", "shell", "--cols", "120", "--rows", "40", "my-sandbox", "ls"]
)
assert args.cols == 120
assert args.rows == 40
assert args.name == "my-sandbox"
assert args.shell_command == ["ls"]
def test_shell_sandbox_not_found(self, args_namespace, mock_api_key):
"""Test shell with nonexistent sandbox."""
args = args_namespace(
name="nonexistent",
shell_command=[],
cols=None,
rows=None,
)
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.get_vm = AsyncMock(return_value={"status": "not_found"})
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_error") as mock_error:
with patch("sys.stdin") as mock_stdin:
mock_stdin.isatty.return_value = False
result = sandbox.cmd_shell(args)
assert result == 1
mock_error.assert_called()
def test_shell_no_api_url(self, args_namespace, mock_api_key):
"""Test shell when sandbox has no API URL."""
args = args_namespace(
name="test-sandbox",
shell_command=["echo", "hello"],
cols=None,
rows=None,
)
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.get_vm = AsyncMock(return_value={"status": "stopped", "api_url": None})
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_error") as mock_error:
with patch("sys.stdin") as mock_stdin:
mock_stdin.isatty.return_value = False
result = sandbox.cmd_shell(args)
assert result == 1
mock_error.assert_called()
class TestCmdExec:
"""Tests for cmd_exec function."""
def test_exec_parser_registration(self):
"""Test that exec command is registered with correct arguments."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
sandbox.register_parser(subparsers)
args = parser.parse_args(["sb", "exec", "my-sandbox", "echo", "hello"])
assert args.name == "my-sandbox"
assert args.sandbox_command == "exec"
assert args.exec_command == ["echo", "hello"]
def test_exec_with_json_flag(self):
"""Test exec command with --json flag.
--json must come before name due to argparse REMAINDER behavior.
Usage: cua sb exec --json mybox <command...>
"""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
sandbox.register_parser(subparsers)
args = parser.parse_args(["sb", "exec", "--json", "my-sandbox", "echo", "hello"])
assert args.json is True
assert args.name == "my-sandbox"
assert args.exec_command == ["echo", "hello"]
def test_exec_no_command_error(self, args_namespace, mock_api_key):
"""Test exec with no command returns error."""
args = args_namespace(
name="test-sandbox",
exec_command=[],
json=False,
)
with patch.object(sandbox, "print_error") as mock_error:
result = sandbox.cmd_exec(args)
assert result == 1
mock_error.assert_called_with("No command provided")
def test_exec_sandbox_not_found(self, args_namespace, mock_api_key):
"""Test exec with nonexistent sandbox."""
args = args_namespace(
name="nonexistent",
exec_command=["echo", "hello"],
json=False,
)
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.get_vm = AsyncMock(return_value={"status": "not_found"})
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "print_error") as mock_error:
result = sandbox.cmd_exec(args)
assert result == 1
mock_error.assert_called()
def test_exec_success(self, args_namespace, mock_api_key, capsys):
"""Test successful command execution."""
args = args_namespace(
name="test-sandbox",
exec_command=["echo", "hello"],
json=False,
)
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.get_vm = AsyncMock(
return_value={"status": "running", "api_url": "https://sandbox.example.com:8443"}
)
async def mock_exec(*a, **kw):
return {"success": True, "stdout": "hello\n", "stderr": "", "returncode": 0}
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "_exec_noninteractive", side_effect=mock_exec):
result = sandbox.cmd_exec(args)
assert result == 0
captured = capsys.readouterr()
assert "hello" in captured.out
def test_exec_json_output(self, args_namespace, mock_api_key):
"""Test exec with JSON output."""
args = args_namespace(
name="test-sandbox",
exec_command=["echo", "hello"],
json=True,
)
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.get_vm = AsyncMock(
return_value={"status": "running", "api_url": "https://sandbox.example.com:8443"}
)
async def mock_exec(*a, **kw):
return {"success": True, "stdout": "hello\n", "stderr": "", "returncode": 0}
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "_exec_noninteractive", side_effect=mock_exec):
with patch.object(sandbox, "print_json") as mock_json:
result = sandbox.cmd_exec(args)
assert result == 0
mock_json.assert_called_once()
def test_exec_command_failure(self, args_namespace, mock_api_key, capsys):
"""Test exec when command returns non-zero exit code."""
args = args_namespace(
name="test-sandbox",
exec_command=["false"],
json=False,
)
mock_provider = MagicMock()
mock_provider.__aenter__ = AsyncMock(return_value=mock_provider)
mock_provider.__aexit__ = AsyncMock(return_value=None)
mock_provider.get_vm = AsyncMock(
return_value={"status": "running", "api_url": "https://sandbox.example.com:8443"}
)
async def mock_exec(*a, **kw):
return {"success": True, "stdout": "", "stderr": "error", "returncode": 1}
with patch.object(sandbox, "_get_provider", return_value=mock_provider):
with patch.object(sandbox, "_exec_noninteractive", side_effect=mock_exec):
result = sandbox.cmd_exec(args)
assert result == 1
@@ -0,0 +1,235 @@
"""Tests for skills command module."""
import argparse
from unittest.mock import patch
from cua_cli.commands import skills
class TestRegisterParser:
"""Tests for register_parser function."""
def test_registers_skills_command(self):
"""Test that skills command is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
skills.register_parser(subparsers)
args = parser.parse_args(["skills", "list"])
assert args.skills_command == "list"
def test_record_command_exists(self):
"""Test that record command is registered."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
skills.register_parser(subparsers)
args = parser.parse_args(["skills", "record"])
assert args.skills_command == "record"
def test_record_command_has_sandbox_flag(self):
"""Test that record command has --sandbox flag."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
skills.register_parser(subparsers)
args = parser.parse_args(["skills", "record", "--sandbox", "my-sandbox"])
assert args.sandbox == "my-sandbox"
def test_read_command_has_name_arg(self):
"""Test that read command has name argument."""
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers()
skills.register_parser(subparsers)
args = parser.parse_args(["skills", "read", "my-skill"])
assert args.name == "my-skill"
class TestExecute:
"""Tests for execute function."""
def test_dispatch_to_list(self, args_namespace):
"""Test dispatch to list command."""
args = args_namespace(command="skills", skills_command="list", json=False)
with patch.object(skills, "cmd_list", return_value=0) as mock_cmd:
result = skills.execute(args)
mock_cmd.assert_called_once_with(args)
assert result == 0
def test_dispatch_to_read(self, args_namespace):
"""Test dispatch to read command."""
args = args_namespace(command="skills", skills_command="read", name="my-skill")
with patch.object(skills, "cmd_read", return_value=0) as mock_cmd:
skills.execute(args)
mock_cmd.assert_called_once_with(args)
def test_dispatch_to_delete(self, args_namespace):
"""Test dispatch to delete command."""
args = args_namespace(command="skills", skills_command="delete", name="my-skill")
with patch.object(skills, "cmd_delete", return_value=0) as mock_cmd:
skills.execute(args)
mock_cmd.assert_called_once_with(args)
def test_unknown_command_returns_error(self, args_namespace):
"""Test that unknown command returns error."""
args = args_namespace(command="skills", skills_command=None)
with patch.object(skills, "print_error"):
result = skills.execute(args)
assert result == 1
class TestCmdList:
"""Tests for cmd_list function."""
def test_list_empty_directory(self, args_namespace, temp_skills_dir):
"""Test listing when no skills exist."""
args = args_namespace(json=False)
with patch.object(skills, "print_info") as mock_print:
result = skills.cmd_list(args)
assert result == 0
mock_print.assert_called()
def test_list_with_skills(self, args_namespace, sample_skill, temp_skills_dir):
"""Test listing existing skills."""
args = args_namespace(json=False)
with patch.object(skills, "print_table") as mock_print:
result = skills.cmd_list(args)
assert result == 0
mock_print.assert_called_once()
# Verify the skill is in the output
call_args = mock_print.call_args[0]
skill_data = call_args[0]
assert len(skill_data) == 1
assert skill_data[0]["name"] == "test-skill"
def test_list_json_output(self, args_namespace, sample_skill, temp_skills_dir):
"""Test JSON output format."""
args = args_namespace(json=True)
with patch.object(skills, "print_json") as mock_print:
result = skills.cmd_list(args)
assert result == 0
mock_print.assert_called_once()
class TestCmdRead:
"""Tests for cmd_read function."""
def test_read_existing_skill(self, args_namespace, sample_skill, temp_skills_dir, capsys):
"""Test reading an existing skill."""
args = args_namespace(name="test-skill", format="md")
result = skills.cmd_read(args)
assert result == 0
# Verify content was printed to stdout
captured = capsys.readouterr()
assert "test-skill" in captured.out
def test_read_nonexistent_skill(self, args_namespace, temp_skills_dir):
"""Test reading a nonexistent skill."""
args = args_namespace(name="nonexistent", format="md")
with patch.object(skills, "print_error") as mock_error:
result = skills.cmd_read(args)
assert result == 1
mock_error.assert_called()
class TestCmdDelete:
"""Tests for cmd_delete function."""
def test_delete_existing_skill(self, args_namespace, sample_skill, temp_skills_dir):
"""Test deleting an existing skill."""
args = args_namespace(name="test-skill")
assert sample_skill.exists()
with patch.object(skills, "print_success"):
result = skills.cmd_delete(args)
assert result == 0
assert not sample_skill.exists()
def test_delete_nonexistent_skill(self, args_namespace, temp_skills_dir):
"""Test deleting a nonexistent skill."""
args = args_namespace(name="nonexistent")
with patch.object(skills, "print_error") as mock_error:
result = skills.cmd_delete(args)
assert result == 1
mock_error.assert_called()
class TestCmdClean:
"""Tests for cmd_clean function."""
def test_clean_removes_all_skills(self, args_namespace, sample_skill, temp_skills_dir):
"""Test that clean removes all skills."""
args = args_namespace()
assert sample_skill.exists()
with patch("builtins.input", return_value="y"):
with patch.object(skills, "print_success"):
result = skills.cmd_clean(args)
assert result == 0
# Verify no skills remain
remaining = list(temp_skills_dir.iterdir())
assert len(remaining) == 0
def test_clean_empty_directory(self, args_namespace, temp_skills_dir):
"""Test clean on empty directory."""
args = args_namespace()
with patch.object(skills, "print_info"):
result = skills.cmd_clean(args)
assert result == 0
class TestCmdReplay:
"""Tests for cmd_replay function."""
def test_replay_existing_skill(
self, args_namespace, sample_skill, temp_skills_dir, mock_webbrowser
):
"""Test replaying an existing skill opens the video."""
args = args_namespace(name="test-skill")
with patch.object(skills, "print_info"):
result = skills.cmd_replay(args)
assert result == 0
# Verify webbrowser was called with the video path
mock_webbrowser.assert_called_once()
call_url = mock_webbrowser.call_args[0][0]
assert "test-skill.mp4" in call_url
def test_replay_nonexistent_skill(self, args_namespace, temp_skills_dir):
"""Test replaying a nonexistent skill."""
args = args_namespace(name="nonexistent")
with patch.object(skills, "print_error") as mock_error:
result = skills.cmd_replay(args)
assert result == 1
mock_error.assert_called()