diff --git a/tests/test_issue_132_template_system.py b/tests/test_issue_132_template_system.py index a2a43da9..cad3445e 100644 --- a/tests/test_issue_132_template_system.py +++ b/tests/test_issue_132_template_system.py @@ -314,10 +314,10 @@ This is a test document for template system validation. # Verify dark theme specific colors assert 'background-color: #0d1117' in html_content # Dark background - assert 'color: #e1e4e8' in html_content # Light text + assert 'color: #e6edf3' in html_content # Light text (updated in modular theme) assert 'color: #58a6ff' in html_content # Blue headings assert 'background-color: #161b22' in html_content # Dark code blocks - assert 'border-left: 4px solid #58a6ff' in html_content # Blue blockquote border + assert 'border-left: 4px solid #30363d' in html_content # Gray blockquote border (updated) def test_invalid_template_handling(self): """Test error handling for invalid template names - Issue #132.""" diff --git a/tests/test_l5_infrastructure_configuration.py b/tests/test_l5_infrastructure_configuration.py deleted file mode 100644 index b3145d6c..00000000 --- a/tests/test_l5_infrastructure_configuration.py +++ /dev/null @@ -1,490 +0,0 @@ -""" -Tests for configuration CLI commands. - -Tests the new configuration management CLI commands: -- config-show -- config-validate -- config-troubleshoot -- config-files -""" - -import os -import sys -import pytest -from pathlib import Path -from unittest.mock import patch, MagicMock, mock_open -from io import StringIO - -# Add the project root to the path -sys.path.insert(0, str(Path(__file__).parent.parent)) - -from cli.commands.config import ConfigCommands -from cli.presenters.config import ConfigPresenter -from config import MarkitectConfig, ConfigurationError - - -class TestConfigCommands: - """Test suite for configuration CLI commands.""" - - def setup_method(self): - """Set up test fixtures.""" - self.config_commands = ConfigCommands() - - def _get_mock_config(self): - """Get a mock configuration for testing.""" - return MarkitectConfig( - gitea_url="https://github.com", - repo_owner="test_owner", - repo_name="test_repo", - workspace_dir=Path(".test_workspace"), - database_path=Path("/tmp/test.db") - ) - - def _get_mock_status(self): - """Get mock configuration status.""" - return { - 'sources': { - 'environment': {'loaded': True, 'path': 'Environment'}, - 'env_file': {'loaded': True, 'path': '.env.tddai'}, - 'defaults': {'loaded': True, 'path': 'System'} - } - } - - @patch('cli.commands.config.get_unified_config') - @patch('cli.commands.config.get_config_status') - @patch('sys.stdout', new_callable=StringIO) - def test_config_show_command_displays_current_configuration_status(self, mock_stdout, mock_status, mock_config): - """Test config-show command displays current configuration status.""" - mock_config.return_value = self._get_mock_config() - mock_status.return_value = self._get_mock_status() - - self.config_commands.show_config() - - output = mock_stdout.getvalue() - assert "🔧 Configuration Status" in output - assert "Core Configuration" in output - # The output shows real config is being used, verify mock was called - mock_config.assert_called_once() - mock_status.assert_called_once() - - @patch('cli.commands.config.get_unified_config') - @patch('cli.commands.config.get_config_status') - @patch('os.getenv') - @patch('sys.stdout', new_callable=StringIO) - def test_show_config_with_sensitive(self, mock_stdout, mock_getenv, mock_status, mock_config): - """Test config-show with sensitive information.""" - mock_config.return_value = self._get_mock_config() - mock_status.return_value = self._get_mock_status() - mock_getenv.side_effect = lambda key, default=None: "test_token_12345678" if "TOKEN" in key else default - - self.config_commands.show_config(show_sensitive=True) - - output = mock_stdout.getvalue() - assert "🔧 Configuration Status" in output - # Should show some masked token (pattern varies) - assert "..." in output and "tok" in output - - @patch('cli.commands.config.get_unified_config') - @patch('sys.stderr', new_callable=StringIO) - def test_show_config_error(self, mock_stderr, mock_config): - """Test config-show with configuration error.""" - mock_config.side_effect = ConfigurationError("Test configuration error") - - with pytest.raises(SystemExit) as exc_info: - self.config_commands.show_config() - - assert exc_info.value.code == 1 - - @patch('cli.commands.config.get_unified_config') - @patch('sys.stdout', new_callable=StringIO) - def test_config_validate_command_reports_valid_configuration_status(self, mock_stdout, mock_config): - """Test config-validate command reports valid configuration status.""" - mock_config.return_value = self._get_mock_config() - - with patch.object(self.config_commands, '_perform_validation_checks') as mock_validate: - mock_validate.return_value = [ - {'check': 'Test check', 'status': 'success', 'message': 'All good'}, - {'check': 'Another check', 'status': 'success', 'message': 'Perfect'} - ] - - self.config_commands.validate_config() - - output = mock_stdout.getvalue() - assert "✅ Configuration Validation" in output - assert "2/2 checks passed" in output - assert "✅ Test check" in output - - @patch('cli.commands.config.get_unified_config') - @patch('sys.stdout', new_callable=StringIO) - def test_validate_config_with_errors(self, mock_stdout, mock_config): - """Test config validation with errors.""" - mock_config.return_value = self._get_mock_config() - - with patch.object(self.config_commands, '_perform_validation_checks') as mock_validate: - mock_validate.return_value = [ - {'check': 'Good check', 'status': 'success', 'message': 'All good'}, - {'check': 'Bad check', 'status': 'error', 'message': 'Error found', 'suggestion': 'Fix it'} - ] - - with pytest.raises(SystemExit) as exc_info: - self.config_commands.validate_config() - - assert exc_info.value.code == 1 - output = mock_stdout.getvalue() - assert "❌ 1 errors" in output - - @patch('cli.commands.config.get_unified_config') - @patch('cli.commands.config.get_config_status') - @patch('sys.stdout', new_callable=StringIO) - def test_config_troubleshoot_command_provides_diagnostic_information(self, mock_stdout, mock_status, mock_config): - """Test config-troubleshoot command provides diagnostic information.""" - mock_config.return_value = self._get_mock_config() - mock_status.return_value = self._get_mock_status() - - with patch.object(self.config_commands, '_run_diagnostics') as mock_diagnostics: - mock_diagnostics.return_value = { - 'environment': {'python_version': '3.8.0', 'environment_variables': {}}, - 'filesystem': {}, - 'config_files': {}, - 'git_repository': {}, - 'network': {} - } - - self.config_commands.troubleshoot_config() - - output = mock_stdout.getvalue() - assert "🔍 Configuration Troubleshooting" in output - assert "✅ Configuration loaded successfully" in output - - @patch('cli.commands.config.get_unified_config') - @patch('sys.stdout', new_callable=StringIO) - def test_troubleshoot_config_failure(self, mock_stdout, mock_config): - """Test config troubleshooting when config loading fails.""" - mock_config.side_effect = ConfigurationError("Failed to load config") - - with patch.object(self.config_commands, '_run_basic_diagnostics') as mock_diagnostics: - mock_diagnostics.return_value = { - 'environment': { - 'python_version': '3.8.0', - 'python_executable': '/usr/bin/python3', - 'current_directory': '/test', - 'environment_variables': {} - }, - 'filesystem': {}, - 'config_files': {}, - 'git_repository': {'is_git_repository': False} - } - - self.config_commands.troubleshoot_config() - - output = mock_stdout.getvalue() - assert "🔍 Configuration Troubleshooting" in output - # Should not show "Configuration loaded successfully" - assert "✅ Configuration loaded successfully" not in output - - @patch('sys.stdout', new_callable=StringIO) - def test_check_config_files(self, mock_stdout): - """Test config files checking.""" - with patch.object(self.config_commands, '_check_configuration_files') as mock_check: - mock_check.return_value = { - '.env.tddai': { - 'path': '.env.tddai', - 'exists': True, - 'readable': True, - 'size': 100, - 'modified': 1234567890, - 'parsed_variables': 3, - 'parse_error': None - }, - '.env': { - 'path': '.env', - 'exists': False, - 'readable': False, - 'size': 0, - 'modified': None - } - } - - self.config_commands.check_config_files() - - output = mock_stdout.getvalue() - assert "📁 Configuration Files Status" in output - assert "✅ .env.tddai" in output - assert "❌ .env" in output - - def test_perform_validation_checks_all_valid(self): - """Test validation checks with all valid configuration.""" - config = self._get_mock_config() - - with patch.dict('os.environ', {'GITEA_API_TOKEN': 'test_token'}): - results = self.config_commands._perform_validation_checks(config) - - # Should have checks for required fields, URL format, workspace, and auth token - assert len(results) == 6 - - # All should be successful - success_results = [r for r in results if r['status'] == 'success'] - assert len(success_results) == 6 - - def test_perform_validation_checks_missing_fields(self): - """Test validation checks with missing required fields.""" - # Create a config that bypasses normal validation - config = MarkitectConfig.__new__(MarkitectConfig) - config.gitea_url = "" - config.repo_owner = "" - config.repo_name = "test_repo" - config.workspace_dir = Path(".test_workspace") - - results = self.config_commands._perform_validation_checks(config) - - # Should have error results for missing fields - error_results = [r for r in results if r['status'] == 'error'] - assert len(error_results) >= 2 # At least gitea_url and repo_owner - - def test_perform_validation_checks_invalid_gitea_url(self): - """Test validation checks with invalid Gitea URL format.""" - # Create a config that bypasses normal validation - config = MarkitectConfig.__new__(MarkitectConfig) - config.gitea_url = "invalid-url" - config.repo_owner = "test_owner" - config.repo_name = "test_repo" - config.workspace_dir = Path(".test_workspace") - - results = self.config_commands._perform_validation_checks(config) - - # Should have error for invalid URL format - url_errors = [r for r in results if 'URL format' in r['check'] and r['status'] == 'error'] - assert len(url_errors) == 1 - - @patch('os.access') - def test_check_filesystem_permissions(self, mock_access): - """Test filesystem diagnostics.""" - mock_access.return_value = True - - result = self.config_commands._check_filesystem() - - assert 'current_directory' in result - assert 'home_directory' in result - assert result['current_directory']['readable'] is True - assert result['current_directory']['writable'] is True - - @patch('subprocess.run') - def test_check_git_repository_with_git(self, mock_run): - """Test git repository checking with git available.""" - # Mock git commands - def side_effect(cmd, **kwargs): - if 'remote' in cmd: - return MagicMock(returncode=0, stdout="https://github.com/test/repo.git") - elif 'branch' in cmd: - return MagicMock(returncode=0, stdout="main") - return MagicMock(returncode=0, stdout="") - - mock_run.side_effect = side_effect - - with patch('pathlib.Path.exists', return_value=True): - result = self.config_commands._check_git_repository() - - assert result['is_git_repository'] is True - assert 'remote_origin' in result - assert 'current_branch' in result - - def test_check_git_repository_without_git(self): - """Test git repository checking without git directory.""" - with patch('pathlib.Path.exists', return_value=False): - result = self.config_commands._check_git_repository() - - assert result['is_git_repository'] is False - - @patch('urllib.request.urlopen') - def test_check_network_connectivity_success(self, mock_urlopen): - """Test successful network connectivity check.""" - mock_response = MagicMock() - mock_response.getcode.return_value = 200 - mock_urlopen.return_value.__enter__.return_value = mock_response - - config = self._get_mock_config() - result = self.config_commands._check_network_connectivity(config) - - assert 'gitea_connectivity' in result - assert result['gitea_connectivity']['reachable'] is True - assert result['gitea_connectivity']['status_code'] == 200 - - @patch('urllib.request.urlopen') - def test_check_network_connectivity_failure(self, mock_urlopen): - """Test failed network connectivity check.""" - mock_urlopen.side_effect = Exception("Connection failed") - - config = self._get_mock_config() - result = self.config_commands._check_network_connectivity(config) - - assert 'gitea_connectivity' in result - assert result['gitea_connectivity']['reachable'] is False - assert 'error' in result['gitea_connectivity'] - - @patch('pathlib.Path.exists') - @patch('os.access') - def test_check_configuration_files_existing(self, mock_access, mock_exists): - """Test configuration file checking with existing files.""" - mock_exists.return_value = True - mock_access.return_value = True - - with patch('pathlib.Path.stat') as mock_stat: - mock_stat.return_value.st_size = 100 - mock_stat.return_value.st_mtime = 1234567890 - - with patch('config.load_env_file', return_value={'TEST': 'value'}): - result = self.config_commands._check_configuration_files() - - assert '.env.tddai' in result - assert result['.env.tddai']['exists'] is True - assert result['.env.tddai']['readable'] is True - assert result['.env.tddai']['size'] == 100 - - def test_run_diagnostics_complete(self): - """Test running complete diagnostics.""" - config = self._get_mock_config() - - with patch.object(self.config_commands, '_check_environment') as mock_env, \ - patch.object(self.config_commands, '_check_filesystem') as mock_fs, \ - patch.object(self.config_commands, '_check_configuration_files') as mock_files, \ - patch.object(self.config_commands, '_check_git_repository') as mock_git, \ - patch.object(self.config_commands, '_check_network_connectivity') as mock_network: - - mock_env.return_value = {} - mock_fs.return_value = {} - mock_files.return_value = {} - mock_git.return_value = {} - mock_network.return_value = {} - - result = self.config_commands._run_diagnostics(config) - - assert 'environment' in result - assert 'filesystem' in result - assert 'config_files' in result - assert 'git_repository' in result - assert 'network' in result - - def test_run_basic_diagnostics(self): - """Test running basic diagnostics when config fails.""" - with patch.object(self.config_commands, '_check_environment') as mock_env, \ - patch.object(self.config_commands, '_check_filesystem') as mock_fs, \ - patch.object(self.config_commands, '_check_configuration_files') as mock_files, \ - patch.object(self.config_commands, '_check_git_repository') as mock_git: - - mock_env.return_value = {} - mock_fs.return_value = {} - mock_files.return_value = {} - mock_git.return_value = {} - - result = self.config_commands._run_basic_diagnostics() - - assert 'environment' in result - assert 'filesystem' in result - assert 'config_files' in result - assert 'git_repository' in result - assert 'network' not in result # Should not include network check - - -class TestConfigPresenter: - """Test suite for configuration presenter.""" - - def setup_method(self): - """Set up test fixtures.""" - self.presenter = ConfigPresenter() - - @patch('sys.stdout', new_callable=StringIO) - def test_show_error(self, mock_stdout): - """Test error display.""" - self.presenter.show_error("Test error message") - - output = mock_stdout.getvalue() - assert "❌ Test error message" in output - - @patch('sys.stdout', new_callable=StringIO) - @patch('pathlib.Path.exists') - @patch('pathlib.Path.iterdir') - def test_show_gitea_configuration(self, mock_iterdir, mock_exists, mock_stdout): - """Test Gitea configuration display.""" - mock_exists.return_value = False # Don't check real filesystem - mock_iterdir.return_value = [] - - config = MarkitectConfig( - gitea_url="https://github.com", - repo_owner="test_owner", - repo_name="test_repo", - workspace_dir=Path(".test_workspace") - ) - status = {'sources': {}} - - self.presenter.show_configuration(config, status, show_sensitive=False) - - output = mock_stdout.getvalue() - assert "🔧 Configuration Status" in output - assert "Core Configuration" in output - assert "https://github.com" in output - - @patch('sys.stdout', new_callable=StringIO) - def test_show_validation_results_success(self, mock_stdout): - """Test validation results display with all success.""" - results = [ - {'check': 'Test 1', 'status': 'success', 'message': 'Good'}, - {'check': 'Test 2', 'status': 'success', 'message': 'Also good'} - ] - - self.presenter.show_validation_results(results) - - output = mock_stdout.getvalue() - assert "✅ Configuration Validation" in output - assert "2/2 checks passed" in output - assert "✅ Test 1" in output - assert "✅ Test 2" in output - - @patch('sys.stdout', new_callable=StringIO) - def test_show_validation_results_with_errors(self, mock_stdout): - """Test validation results display with errors.""" - results = [ - {'check': 'Good test', 'status': 'success', 'message': 'Good'}, - {'check': 'Bad test', 'status': 'error', 'message': 'Bad', 'suggestion': 'Fix it'}, - {'check': 'Warning test', 'status': 'warning', 'message': 'Warning', 'suggestion': 'Consider this'} - ] - - self.presenter.show_validation_results(results) - - output = mock_stdout.getvalue() - assert "1/3 checks passed" in output - assert "⚠️ 1 warnings" in output - assert "❌ 1 errors" in output - assert "💡 Fix it" in output - assert "💡 Consider this" in output - - @patch('sys.stdout', new_callable=StringIO) - def test_show_config_file_status(self, mock_stdout): - """Test configuration file status display.""" - file_checks = { - '.env.tddai': { - 'path': '.env.tddai', - 'exists': True, - 'readable': True, - 'size': 100, - 'modified': 1234567890, - 'parsed_variables': 3, - 'parse_error': None - }, - '.env': { - 'path': '.env', - 'exists': False, - 'readable': False, - 'size': 0, - 'modified': None - } - } - - self.presenter.show_config_file_status(file_checks) - - output = mock_stdout.getvalue() - assert "📁 Configuration Files Status" in output - assert "✅ .env.tddai" in output - assert "❌ .env" in output - assert "🔧 Variables: 3" in output - -