"""Tests for CLI commands module.""" import tempfile from pathlib import Path from unittest.mock import MagicMock, patch import pytest from typer.testing import CliRunner from codexchange.main import app from codexchange.cli.commands import ( show_supported_languages, convert_cmd, batch_convert_cmd, list_models_cmd, list_languages_cmd, ) runner = CliRunner() class TestListLanguages: """Tests for list-languages command.""" def test_list_languages_cmd(self): """Test list-languages command executes without error.""" result = runner.invoke(app, ["list-languages"]) assert result.exit_code == 0 assert "Supported Languages" in result.output def test_show_supported_languages(self): """Test show_supported_languages function.""" result = runner.invoke(app, ["list-languages"]) assert "JavaScript" in result.output assert "TypeScript" in result.output assert "Python" in result.output assert "Java" in result.output class TestListModels: """Tests for list-models command.""" @patch("codexchange.cli.commands.connect") @patch("codexchange.cli.commands.list_models") def test_list_models_success(self, mock_list, mock_connect): """Test list-models command with successful connection.""" from codexchange.models import ModelInfo mock_list.return_value = [ ModelInfo(name="codellama", size="4GB", modified_at="2024-01-01"), ModelInfo(name="llama2", size="7GB", modified_at="2024-01-02"), ] result = runner.invoke(app, ["list-models"]) assert result.exit_code == 0 @patch("codexchange.cli.commands.connect") def test_list_models_connection_error(self, mock_connect): """Test list-models command with connection error.""" mock_connect.side_effect = Exception("Connection failed") result = runner.invoke(app, ["list-models"]) assert result.exit_code == 1 assert "Error" in result.output class TestConvert: """Tests for convert command.""" @patch("codexchange.cli.commands.connect") def test_convert_file_success(self, mock_connect, sample_python_code): """Test converting a file successfully.""" mock_ollama = MagicMock() mock_ollama.generate.return_value = "console.log('Hello');" mock_connect.return_value = mock_ollama with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: f.write(sample_python_code) input_file = f.name try: result = runner.invoke(app, [ "convert", input_file, "--from", "python", "--to", "javascript", "--model", "codellama" ]) assert result.exit_code == 0 assert "Hello" in result.output or "conversion" in result.output.lower() finally: Path(input_file).unlink() def test_convert_file_not_found(self): """Test convert with non-existent file.""" result = runner.invoke(app, [ "convert", "/nonexistent/file.py", "--from", "python", "--to", "javascript" ]) assert result.exit_code == 1 assert "not found" in result.output.lower() def test_convert_invalid_source_language(self): """Test convert with invalid source language.""" with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: f.write("print('hello')") input_file = f.name try: result = runner.invoke(app, [ "convert", input_file, "--from", "invalid-lang", "--to", "javascript" ]) assert result.exit_code == 1 assert "Unsupported" in result.output finally: Path(input_file).unlink() def test_convert_invalid_target_language(self): """Test convert with invalid target language.""" with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f: f.write("print('hello')") input_file = f.name try: result = runner.invoke(app, [ "convert", input_file, "--from", "python", "--to", "invalid-lang" ]) assert result.exit_code == 1 assert "Unsupported" in result.output finally: Path(input_file).unlink() class TestBatchConvert: """Tests for batch-convert command.""" @patch("codexchange.cli.commands.connect") def test_batch_convert_directory(self, mock_connect, sample_python_code): """Test batch converting a directory.""" mock_ollama = MagicMock() mock_ollama.generate.return_value = "console.log('Hello');" mock_connect.return_value = mock_ollama with tempfile.TemporaryDirectory() as tmpdir: (Path(tmpdir) / "file1.py").write_text(sample_python_code) (Path(tmpdir) / "file2.py").write_text(sample_python_code) result = runner.invoke(app, [ "batch-convert", tmpdir, "--from", "python", "--to", "javascript", "--concurrency", "1" ]) assert result.exit_code == 0 assert "file1.py" in result.output or "Successfully" in result.output def test_batch_convert_directory_not_found(self): """Test batch convert with non-existent directory.""" result = runner.invoke(app, [ "batch-convert", "/nonexistent/dir", "--from", "python", "--to", "javascript" ]) assert result.exit_code == 1 assert "not found" in result.output.lower() def test_batch_convert_no_files_found(self): """Test batch convert with directory having no matching files.""" with tempfile.TemporaryDirectory() as tmpdir: (Path(tmpdir) / "readme.txt").write_text("README") result = runner.invoke(app, [ "batch-convert", tmpdir, "--from", "python", "--to", "javascript" ]) assert result.exit_code == 0 assert "No" in result.output or "found" in result.output.lower() class TestMainApp: """Tests for main app entry point.""" def test_app_help(self): """Test app help command.""" result = runner.invoke(app, ["--help"]) assert result.exit_code == 0 assert "CodeXchange" in result.output assert "convert" in result.output assert "batch-convert" in result.output def test_app_no_args(self): """Test app with no arguments shows help or error.""" result = runner.invoke(app) assert result.exit_code in [0, 2]