#!/usr/bin/env python3 """ Tests for code.format skill Generated by meta.skill and enhanced with comprehensive test coverage """ import pytest import sys import os import tempfile import shutil from pathlib import Path from unittest.mock import Mock, patch, MagicMock # Add parent directory to path sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../.."))) from skills.code_format import code_format class TestCodeFormat: """Tests for CodeFormat skill""" def setup_method(self): """Setup test fixtures""" self.skill = code_format.CodeFormat() self.temp_dir = tempfile.mkdtemp() def teardown_method(self): """Cleanup test fixtures""" if os.path.exists(self.temp_dir): shutil.rmtree(self.temp_dir) def test_initialization(self): """Test skill initializes correctly""" assert self.skill is not None assert self.skill.base_dir is not None assert isinstance(self.skill.SUPPORTED_EXTENSIONS, set) assert len(self.skill.SUPPORTED_EXTENSIONS) > 0 assert isinstance(self.skill.CONFIG_FILES, list) assert len(self.skill.CONFIG_FILES) > 0 def test_supported_extensions(self): """Test that all expected file types are supported""" extensions = self.skill.SUPPORTED_EXTENSIONS # Check JavaScript extensions assert '.js' in extensions assert '.jsx' in extensions assert '.mjs' in extensions assert '.cjs' in extensions # Check TypeScript extensions assert '.ts' in extensions assert '.tsx' in extensions # Check style extensions assert '.css' in extensions assert '.scss' in extensions assert '.less' in extensions # Check other formats assert '.json' in extensions assert '.yaml' in extensions assert '.md' in extensions assert '.html' in extensions def test_check_prettier_installed_with_npx(self): """Test Prettier detection via npx""" with patch('subprocess.run') as mock_run: mock_run.return_value = Mock(returncode=0, stdout="3.0.0\n") is_installed, cmd = self.skill._check_prettier_installed() assert is_installed is True assert cmd == 'npx prettier' def test_check_prettier_installed_global(self): """Test Prettier detection via global installation""" with patch('subprocess.run') as mock_run: # First call (npx) fails mock_run.side_effect = [ FileNotFoundError(), Mock(returncode=0, stdout="3.0.0\n") ] with patch('shutil.which', return_value='/usr/local/bin/prettier'): is_installed, cmd = self.skill._check_prettier_installed() assert is_installed is True assert cmd == 'prettier' def test_check_prettier_not_installed(self): """Test when Prettier is not installed""" with patch('subprocess.run', side_effect=FileNotFoundError()): with patch('shutil.which', return_value=None): is_installed, cmd = self.skill._check_prettier_installed() assert is_installed is False assert cmd is None def test_find_config_file_custom(self): """Test finding custom config file""" # Create a custom config file config_path = Path(self.temp_dir) / '.prettierrc.custom' config_path.write_text('{"semi": true}') found_config = self.skill._find_config_file( Path(self.temp_dir), custom_config=str(config_path) ) assert found_config == config_path def test_find_config_file_auto_detect(self): """Test auto-detecting config file""" # Create a .prettierrc file config_path = Path(self.temp_dir) / '.prettierrc' config_path.write_text('{"semi": true}') found_config = self.skill._find_config_file(Path(self.temp_dir)) assert found_config == config_path def test_find_config_file_none(self): """Test when no config file exists""" found_config = self.skill._find_config_file(Path(self.temp_dir)) assert found_config is None def test_discover_files_single_file(self): """Test discovering a single file""" test_file = Path(self.temp_dir) / 'test.js' test_file.write_text('console.log("test");') files = self.skill._discover_files(test_file) assert len(files) == 1 assert files[0] == test_file def test_discover_files_directory(self): """Test discovering files in a directory""" # Create test files (Path(self.temp_dir) / 'test1.js').write_text('console.log("test1");') (Path(self.temp_dir) / 'test2.ts').write_text('console.log("test2");') (Path(self.temp_dir) / 'test.txt').write_text('not supported') files = self.skill._discover_files(Path(self.temp_dir)) assert len(files) == 2 assert any(f.name == 'test1.js' for f in files) assert any(f.name == 'test2.ts' for f in files) def test_discover_files_with_patterns(self): """Test discovering files with glob patterns""" # Create test files (Path(self.temp_dir) / 'test1.js').write_text('console.log("test1");') (Path(self.temp_dir) / 'test2.ts').write_text('console.log("test2");') (Path(self.temp_dir) / 'test3.css').write_text('body { margin: 0; }') files = self.skill._discover_files(Path(self.temp_dir), patterns=['*.js']) assert len(files) == 1 assert files[0].name == 'test1.js' def test_discover_files_ignores_node_modules(self): """Test that node_modules is ignored""" # Create node_modules directory with files node_modules = Path(self.temp_dir) / 'node_modules' node_modules.mkdir() (node_modules / 'test.js').write_text('console.log("test");') # Create regular file (Path(self.temp_dir) / 'app.js').write_text('console.log("app");') files = self.skill._discover_files(Path(self.temp_dir)) assert len(files) == 1 assert files[0].name == 'app.js' def test_format_file_success(self): """Test formatting a file successfully""" test_file = Path(self.temp_dir) / 'test.js' test_file.write_text('console.log("test");') with patch('subprocess.run') as mock_run: mock_run.return_value = Mock(returncode=0, stdout='', stderr='') result = self.skill._format_file(test_file, 'prettier', check_only=False) assert result['ok'] is True assert result['status'] == 'formatted' assert result['file'] == str(test_file) def test_format_file_check_mode_needs_formatting(self): """Test check mode when file needs formatting""" test_file = Path(self.temp_dir) / 'test.js' test_file.write_text('console.log("test");') with patch('subprocess.run') as mock_run: mock_run.return_value = Mock( returncode=1, stdout='', stderr='Code style issues found' ) result = self.skill._format_file(test_file, 'prettier', check_only=True) assert result['ok'] is True assert result['status'] == 'needs_formatting' def test_format_file_error(self): """Test formatting with error""" test_file = Path(self.temp_dir) / 'test.js' test_file.write_text('invalid syntax {{{') with patch('subprocess.run') as mock_run: mock_run.return_value = Mock( returncode=1, stdout='', stderr='Syntax error' ) result = self.skill._format_file(test_file, 'prettier', check_only=False) assert result['ok'] is False assert result['status'] == 'error' assert 'error' in result def test_execute_prettier_not_installed(self): """Test execute when Prettier is not installed""" with patch.object(self.skill, '_check_prettier_installed', return_value=(False, None)): result = self.skill.execute(path=self.temp_dir) assert result['ok'] is False assert result['status'] == 'failed' assert 'not installed' in result['error'].lower() def test_execute_invalid_path(self): """Test execute with invalid path""" with patch.object(self.skill, '_check_prettier_installed', return_value=(True, 'prettier')): result = self.skill.execute(path='/nonexistent/path') assert result['ok'] is False assert result['status'] == 'failed' assert 'does not exist' in result['error'].lower() def test_execute_no_files(self): """Test execute when no files are found""" with patch.object(self.skill, '_check_prettier_installed', return_value=(True, 'prettier')): with patch.object(self.skill, '_discover_files', return_value=[]): result = self.skill.execute(path=self.temp_dir) assert result['ok'] is True assert result['status'] == 'success' assert result['formatted_count'] == 0 assert 'No files found' in result['message'] def test_execute_successful_formatting(self): """Test successful formatting execution""" # Create test files test_file = Path(self.temp_dir) / 'test.js' test_file.write_text('console.log("test");') with patch.object(self.skill, '_check_prettier_installed', return_value=(True, 'prettier')): with patch.object(self.skill, '_format_file') as mock_format: mock_format.return_value = { 'ok': True, 'status': 'formatted', 'file': str(test_file) } result = self.skill.execute(path=str(test_file)) assert result['ok'] is True assert result['status'] == 'success' assert result['formatted_count'] == 1 assert result['error_count'] == 0 def test_execute_check_mode(self): """Test execute in check mode""" test_file = Path(self.temp_dir) / 'test.js' test_file.write_text('console.log("test");') with patch.object(self.skill, '_check_prettier_installed', return_value=(True, 'prettier')): with patch.object(self.skill, '_format_file') as mock_format: mock_format.return_value = { 'ok': True, 'status': 'needs_formatting', 'file': str(test_file) } result = self.skill.execute(path=str(test_file), check_only=True) assert result['ok'] is True assert result['status'] == 'success' assert result['needs_formatting_count'] == 1 assert 'need formatting' in result['message'].lower() def test_execute_with_patterns(self): """Test execute with file patterns""" # Create test files (Path(self.temp_dir) / 'test.js').write_text('console.log("test");') (Path(self.temp_dir) / 'test.ts').write_text('console.log("test");') with patch.object(self.skill, '_check_prettier_installed', return_value=(True, 'prettier')): with patch.object(self.skill, '_discover_files') as mock_discover: mock_discover.return_value = [Path(self.temp_dir) / 'test.js'] result = self.skill.execute( path=self.temp_dir, file_patterns='*.js' ) # Verify patterns were parsed correctly mock_discover.assert_called_once() call_args = mock_discover.call_args assert call_args[0][1] == ['*.js'] def test_execute_with_errors(self): """Test execute when some files have errors""" test_file = Path(self.temp_dir) / 'test.js' test_file.write_text('invalid') with patch.object(self.skill, '_check_prettier_installed', return_value=(True, 'prettier')): with patch.object(self.skill, '_format_file') as mock_format: mock_format.return_value = { 'ok': False, 'status': 'error', 'file': str(test_file), 'error': 'Syntax error' } result = self.skill.execute(path=str(test_file)) assert result['ok'] is True # Overall success even with file errors assert result['status'] == 'success' assert result['error_count'] == 1 assert len(result['files_with_errors']) == 1 def test_execute_exception_handling(self): """Test execute handles exceptions gracefully""" with patch.object(self.skill, '_check_prettier_installed', side_effect=Exception('Test error')): result = self.skill.execute(path=self.temp_dir) assert result['ok'] is False assert result['status'] == 'failed' assert 'error' in result def test_cli_help(capsys): """Test CLI help message""" sys.argv = ["code_format.py", "--help"] with pytest.raises(SystemExit) as exc_info: code_format.main() assert exc_info.value.code == 0 captured = capsys.readouterr() assert "Format code using Prettier" in captured.out def test_cli_missing_path(capsys): """Test CLI with missing required path argument""" sys.argv = ["code_format.py"] with pytest.raises(SystemExit) as exc_info: code_format.main() assert exc_info.value.code != 0 def test_cli_execution(): """Test CLI execution with mocked skill""" sys.argv = ["code_format.py", "--path", "/tmp", "--check"] with patch.object(code_format.CodeFormat, 'execute') as mock_execute: mock_execute.return_value = { 'ok': True, 'status': 'success', 'message': 'Test' } with pytest.raises(SystemExit) as exc_info: code_format.main() assert exc_info.value.code == 0 mock_execute.assert_called_once() if __name__ == "__main__": pytest.main([__file__, "-v"])