"""Tests for DWG converter module.""" import subprocess import tempfile from pathlib import Path from unittest.mock import MagicMock, patch from pdf2imos.output.dwg_converter import ( convert_dxf_to_dwg, is_oda_converter_available, ) class TestIsOdaConverterAvailable: """Tests for is_oda_converter_available function.""" def test_returns_bool(self): """Test that function returns a boolean.""" result = is_oda_converter_available() assert isinstance(result, bool) @patch("pdf2imos.output.dwg_converter.shutil.which") def test_returns_true_when_found(self, mock_which): """Test returns True when ODAFileConverter found in PATH.""" mock_which.return_value = "/usr/bin/ODAFileConverter" assert is_oda_converter_available() is True mock_which.assert_called_once_with("ODAFileConverter") @patch("pdf2imos.output.dwg_converter.shutil.which") def test_returns_false_when_not_found(self, mock_which): """Test returns False when ODAFileConverter not in PATH.""" mock_which.return_value = None assert is_oda_converter_available() is False mock_which.assert_called_once_with("ODAFileConverter") class TestConvertDxfToDwg: """Tests for convert_dxf_to_dwg function.""" def test_returns_none_when_converter_not_available(self): """Test returns None when ODAFileConverter not available.""" with patch( "pdf2imos.output.dwg_converter.is_oda_converter_available", return_value=False, ): with tempfile.TemporaryDirectory() as tmpdir: dxf_path = Path(tmpdir) / "test.dxf" dwg_path = Path(tmpdir) / "test.dwg" dxf_path.write_text("dummy dxf content") result = convert_dxf_to_dwg(dxf_path, dwg_path) assert result is None assert not dwg_path.exists() @patch("pdf2imos.output.dwg_converter.subprocess.run") @patch("pdf2imos.output.dwg_converter.is_oda_converter_available") def test_constructs_correct_subprocess_command( self, mock_available, mock_run ): """Test that correct subprocess command is constructed.""" mock_available.return_value = True mock_run.return_value = MagicMock(returncode=0) with tempfile.TemporaryDirectory() as tmpdir: dxf_path = Path(tmpdir) / "test.dxf" dwg_path = Path(tmpdir) / "output" / "test.dwg" dxf_path.write_text("dummy dxf content") with patch( "pdf2imos.output.dwg_converter.shutil.copy2" ) as mock_copy: # Mock copy2 to create the expected output file def copy_side_effect(src, dst): if str(src).endswith(".dxf"): Path(dst).write_text("dummy dxf") elif str(src).endswith(".dwg"): Path(dst).write_text("dummy dwg") mock_copy.side_effect = copy_side_effect # Create a mock temp directory structure with patch("tempfile.TemporaryDirectory") as mock_temp: temp_input = Path(tmpdir) / "temp_input" temp_output = Path(tmpdir) / "temp_output" temp_input.mkdir() temp_output.mkdir() # Create the expected output file (temp_output / "test.dwg").write_text("dummy dwg") mock_temp.return_value.__enter__.side_effect = [ str(temp_input), str(temp_output), ] convert_dxf_to_dwg(dxf_path, dwg_path) # Verify subprocess.run was called with correct command assert mock_run.called call_args = mock_run.call_args cmd = call_args[0][0] assert cmd[0] == "ODAFileConverter" assert cmd[3] == "ACAD2018" assert cmd[4] == "DWG" assert cmd[5] == "0" assert cmd[6] == "1" @patch("pdf2imos.output.dwg_converter.subprocess.run") @patch("pdf2imos.output.dwg_converter.is_oda_converter_available") def test_returns_none_on_subprocess_failure( self, mock_available, mock_run ): """Test returns None when subprocess returns non-zero exit code.""" mock_available.return_value = True mock_run.return_value = MagicMock( returncode=1, stderr="Conversion failed" ) with tempfile.TemporaryDirectory() as tmpdir: dxf_path = Path(tmpdir) / "test.dxf" dwg_path = Path(tmpdir) / "test.dwg" dxf_path.write_text("dummy dxf content") result = convert_dxf_to_dwg(dxf_path, dwg_path) assert result is None @patch("pdf2imos.output.dwg_converter.subprocess.run") @patch("pdf2imos.output.dwg_converter.is_oda_converter_available") def test_returns_none_on_timeout(self, mock_available, mock_run): """Test returns None when subprocess times out.""" mock_available.return_value = True mock_run.side_effect = subprocess.TimeoutExpired("cmd", 30) with tempfile.TemporaryDirectory() as tmpdir: dxf_path = Path(tmpdir) / "test.dxf" dwg_path = Path(tmpdir) / "test.dwg" dxf_path.write_text("dummy dxf content") result = convert_dxf_to_dwg(dxf_path, dwg_path) assert result is None @patch("pdf2imos.output.dwg_converter.subprocess.run") @patch("pdf2imos.output.dwg_converter.is_oda_converter_available") def test_returns_none_when_output_not_created( self, mock_available, mock_run ): """Test returns None if output DWG file not created by converter.""" mock_available.return_value = True mock_run.return_value = MagicMock(returncode=0) with tempfile.TemporaryDirectory() as tmpdir: dxf_path = Path(tmpdir) / "test.dxf" dwg_path = Path(tmpdir) / "test.dwg" dxf_path.write_text("dummy dxf content") with patch("tempfile.TemporaryDirectory") as mock_temp: temp_input = Path(tmpdir) / "temp_input" temp_output = Path(tmpdir) / "temp_output" temp_input.mkdir() temp_output.mkdir() # Don't create the expected output file mock_temp.return_value.__enter__.side_effect = [ str(temp_input), str(temp_output), ] with patch( "pdf2imos.output.dwg_converter.shutil.copy2" ): result = convert_dxf_to_dwg(dxf_path, dwg_path) assert result is None @patch("pdf2imos.output.dwg_converter.subprocess.run") @patch("pdf2imos.output.dwg_converter.is_oda_converter_available") def test_creates_output_directory(self, mock_available, mock_run): """Test that output directory is created if it doesn't exist.""" mock_available.return_value = True mock_run.return_value = MagicMock(returncode=0) with tempfile.TemporaryDirectory() as tmpdir: dxf_path = Path(tmpdir) / "test.dxf" dwg_path = Path(tmpdir) / "nested" / "output" / "test.dwg" dxf_path.write_text("dummy dxf content") with patch("tempfile.TemporaryDirectory") as mock_temp: temp_input = Path(tmpdir) / "temp_input" temp_output = Path(tmpdir) / "temp_output" temp_input.mkdir() temp_output.mkdir() (temp_output / "test.dwg").write_text("dummy dwg") mock_temp.return_value.__enter__.side_effect = [ str(temp_input), str(temp_output), ] with patch( "pdf2imos.output.dwg_converter.shutil.copy2" ) as mock_copy: def copy_side_effect(src, dst): Path(dst).parent.mkdir(parents=True, exist_ok=True) Path(dst).write_text("dummy") mock_copy.side_effect = copy_side_effect convert_dxf_to_dwg(dxf_path, dwg_path) # Verify parent directory was created assert dwg_path.parent.exists() @patch("pdf2imos.output.dwg_converter.subprocess.run") @patch("pdf2imos.output.dwg_converter.is_oda_converter_available") def test_returns_path_on_success(self, mock_available, mock_run): """Test returns Path object on successful conversion.""" mock_available.return_value = True mock_run.return_value = MagicMock(returncode=0) with tempfile.TemporaryDirectory() as tmpdir: dxf_path = Path(tmpdir) / "test.dxf" dwg_path = Path(tmpdir) / "test.dwg" dxf_path.write_text("dummy dxf content") with patch("tempfile.TemporaryDirectory") as mock_temp: temp_input = Path(tmpdir) / "temp_input" temp_output = Path(tmpdir) / "temp_output" temp_input.mkdir() temp_output.mkdir() (temp_output / "test.dwg").write_text("dummy dwg") mock_temp.return_value.__enter__.side_effect = [ str(temp_input), str(temp_output), ] with patch( "pdf2imos.output.dwg_converter.shutil.copy2" ) as mock_copy: def copy_side_effect(src, dst): Path(dst).parent.mkdir(parents=True, exist_ok=True) Path(dst).write_text("dummy") mock_copy.side_effect = copy_side_effect result = convert_dxf_to_dwg(dxf_path, dwg_path) assert result == dwg_path assert isinstance(result, Path)