Add MappingService (work_record_uuid ↔ external id), project/map CLI with outward push-status, optional TaskSpec work_record fields, boundary-sync policy, and packaging/capability framing cleanup. Mark WP-0005 finished.
223 lines
No EOL
8.4 KiB
Python
223 lines
No EOL
8.4 KiB
Python
"""
|
|
Test suite for CLI commands functionality.
|
|
|
|
These tests ensure the CLI commands work correctly.
|
|
"""
|
|
|
|
import pytest
|
|
import json
|
|
import tempfile
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from click.testing import CliRunner
|
|
from unittest.mock import Mock, patch, MagicMock
|
|
|
|
from issue_core.cli.main import cli
|
|
from issue_core.cli.utils import load_backend_configs, save_backend_configs
|
|
|
|
|
|
class TestCLICommands:
|
|
"""Test CLI command functionality."""
|
|
|
|
def setup_method(self):
|
|
"""Set up test environment."""
|
|
self.runner = CliRunner()
|
|
|
|
def test_cli_help(self):
|
|
"""Test main CLI help displays correctly."""
|
|
result = self.runner.invoke(cli, ['--help'])
|
|
assert result.exit_code == 0
|
|
assert 'External issue-tracker connector' in result.output
|
|
assert 'project' in result.output
|
|
assert 'map' in result.output
|
|
|
|
def test_backend_list_command(self):
|
|
"""Test backend list command."""
|
|
result = self.runner.invoke(cli, ['backend', 'list'])
|
|
assert result.exit_code == 0
|
|
# Should show either configured backends or "No backends configured"
|
|
|
|
@patch('issue_core.cli.backend_commands.load_backend_configs')
|
|
@patch('issue_core.cli.backend_commands.save_backend_configs')
|
|
@patch('issue_core.cli.backend_commands.test_backend_connection')
|
|
def test_backend_add_gitea_with_env_token(self, mock_test_conn, mock_save, mock_load):
|
|
"""Test adding Gitea backend with environment token."""
|
|
# Mock empty initial config
|
|
mock_load.return_value = {}
|
|
mock_test_conn.return_value = True
|
|
|
|
# Test with environment variable
|
|
with patch('os.getenv', return_value='test-token'):
|
|
result = self.runner.invoke(cli, [
|
|
'backend', 'add', 'test-gitea', 'gitea'
|
|
], input='https://git.example.com\ntestorg\ntestrepo\n')
|
|
|
|
assert result.exit_code == 0
|
|
assert 'Using API token from GITEA_API_TOKEN environment variable' in result.output
|
|
assert 'Backend \'test-gitea\' added successfully' in result.output
|
|
|
|
# Verify save_backend_configs was called with correct data
|
|
mock_save.assert_called()
|
|
saved_config = mock_save.call_args[0][0]
|
|
assert 'test-gitea' in saved_config
|
|
assert saved_config['test-gitea']['type'] == 'gitea'
|
|
assert saved_config['test-gitea']['token'] == 'test-token'
|
|
|
|
@patch('issue_core.cli.backend_commands.load_backend_configs')
|
|
@patch('issue_core.cli.backend_commands.save_backend_configs')
|
|
@patch('issue_core.cli.backend_commands.test_backend_connection')
|
|
def test_backend_add_local(self, mock_test_conn, mock_save, mock_load):
|
|
"""Test adding local backend."""
|
|
mock_load.return_value = {}
|
|
mock_test_conn.return_value = True
|
|
|
|
result = self.runner.invoke(cli, [
|
|
'backend', 'add', 'test-local', 'local'
|
|
], input='/tmp/test.db\n')
|
|
|
|
assert result.exit_code == 0
|
|
assert 'Backend \'test-local\' added successfully' in result.output
|
|
|
|
@patch('issue_core.cli.commands.get_backend')
|
|
def test_show_command(self, mock_get_backend):
|
|
"""Test issue show command."""
|
|
# Mock backend and issue
|
|
mock_backend = Mock()
|
|
mock_issue = Mock()
|
|
mock_issue.number = 123
|
|
mock_issue.title = "Test Issue"
|
|
mock_issue.description = "Test description"
|
|
mock_issue.state.value = "open"
|
|
mock_issue.created_at = datetime(2023, 1, 1, 12, 0, 0)
|
|
mock_issue.updated_at = datetime(2023, 1, 1, 12, 0, 0)
|
|
mock_issue.closed_at = None
|
|
mock_issue.assignees = []
|
|
mock_issue.labels = []
|
|
|
|
mock_backend.get_issue_by_number.return_value = mock_issue
|
|
mock_get_backend.return_value = mock_backend
|
|
|
|
result = self.runner.invoke(cli, ['show', '123'])
|
|
|
|
assert result.exit_code == 0
|
|
assert '#123: Test Issue' in result.output
|
|
assert 'Test description' in result.output
|
|
assert 'State: open' in result.output
|
|
|
|
@patch('issue_core.cli.utils.get_backend')
|
|
def test_show_command_issue_not_found(self, mock_get_backend):
|
|
"""Test issue show command when issue doesn't exist."""
|
|
mock_backend = Mock()
|
|
mock_backend.get_issue.side_effect = Exception("Issue not found")
|
|
mock_get_backend.return_value = mock_backend
|
|
|
|
result = self.runner.invoke(cli, ['show', '999'])
|
|
|
|
assert result.exit_code == 1
|
|
assert 'Error' in result.output
|
|
|
|
def test_version_option(self):
|
|
"""Test --version option."""
|
|
result = self.runner.invoke(cli, ['--version'])
|
|
assert result.exit_code == 0
|
|
|
|
@patch('issue_core.cli.commands.get_backend')
|
|
def test_list_command_basic(self, mock_get_backend):
|
|
"""Test basic list command functionality."""
|
|
from issue_core.core.models import Issue, IssueState
|
|
from datetime import timezone
|
|
|
|
mock_backend = Mock()
|
|
now = datetime(2023, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
|
|
mock_backend.list_issues.return_value = [
|
|
Issue(
|
|
id="1",
|
|
number=1,
|
|
title="First Issue",
|
|
description="",
|
|
state=IssueState.OPEN,
|
|
created_at=now,
|
|
updated_at=now,
|
|
),
|
|
Issue(
|
|
id="2",
|
|
number=2,
|
|
title="Second Issue",
|
|
description="",
|
|
state=IssueState.CLOSED,
|
|
created_at=now,
|
|
updated_at=now,
|
|
),
|
|
]
|
|
mock_get_backend.return_value = mock_backend
|
|
|
|
result = self.runner.invoke(cli, ['list'])
|
|
assert result.exit_code == 0, result.output
|
|
assert "First Issue" in result.output
|
|
|
|
|
|
class TestBackendConfiguration:
|
|
"""Test backend configuration functionality."""
|
|
|
|
def test_config_directory_creation(self):
|
|
"""Test configuration directory is created properly."""
|
|
from issue_core.cli.utils import get_config_dir
|
|
|
|
config_dir = get_config_dir()
|
|
assert config_dir.exists()
|
|
assert config_dir.is_dir()
|
|
|
|
def test_backend_config_persistence(self):
|
|
"""Test backend configurations are saved and loaded correctly."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
config_file = Path(temp_dir) / 'test_backends.json'
|
|
|
|
test_config = {
|
|
'test-backend': {
|
|
'type': 'local',
|
|
'db_path': '/tmp/test.db'
|
|
},
|
|
'default': 'test-backend'
|
|
}
|
|
|
|
# Test saving
|
|
with patch('issue_core.cli.utils.get_backend_config_path', return_value=config_file):
|
|
save_backend_configs(test_config)
|
|
|
|
# Test loading
|
|
loaded_config = load_backend_configs()
|
|
|
|
assert loaded_config == test_config
|
|
|
|
def test_empty_config_handling(self):
|
|
"""Test handling of empty or missing configuration files."""
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
non_existent_file = Path(temp_dir) / 'nonexistent.json'
|
|
|
|
with patch('issue_core.cli.utils.get_backend_config_path', return_value=non_existent_file):
|
|
config = load_backend_configs()
|
|
|
|
assert config == {}
|
|
|
|
|
|
class TestEnvironmentTokenDetection:
|
|
"""Test automatic environment token detection."""
|
|
|
|
@patch('os.getenv')
|
|
def test_gitea_token_detection(self, mock_getenv):
|
|
"""Test GITEA_API_TOKEN environment variable detection."""
|
|
mock_getenv.return_value = 'test-env-token'
|
|
|
|
from issue_core.cli.backend_commands import add_backend
|
|
|
|
runner = CliRunner()
|
|
|
|
with patch('issue_core.cli.backend_commands.load_backend_configs', return_value={}):
|
|
with patch('issue_core.cli.backend_commands.save_backend_configs'):
|
|
with patch('issue_core.cli.backend_commands.test_backend_connection', return_value=True):
|
|
result = runner.invoke(add_backend, [
|
|
'test-gitea', 'gitea'
|
|
], input='https://git.example.com\ntestorg\ntestrepo\n')
|
|
|
|
assert result.exit_code == 0
|
|
assert 'Using API token from GITEA_API_TOKEN environment variable' in result.output |