"""Tests for src/reaper_builder/render.py — render_project.""" import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parents[1])) import pytest from unittest.mock import patch, MagicMock from src.reaper_builder.render import render_project class TestRenderProjectFileNotFound: """Test render_project raises FileNotFoundError when reaper.exe path doesn't exist.""" def test_render_project_raises_file_not_found_for_nonexistent_reaper_exe(self): """FileNotFoundError is raised when reaper.exe does not exist.""" nonexistent = Path("C:/Program Files/NONEXISTENT_REAPER/reaper.exe") if nonexistent.exists(): pytest.skip("Nonexistent path actually exists — cannot test this") with pytest.raises(FileNotFoundError): render_project( rpp_path="input.rpp", output_wav="output.wav", reaper_exe=nonexistent, ) def test_render_project_raises_file_not_found_default_path(self): """FileNotFoundError raised when default reaper.exe doesn't exist and none provided.""" # Patch DEFAULT_REAPER_EXE to a definitely-nonexistent path with patch("src.reaper_builder.render.DEFAULT_REAPER_EXE", Path("/no/such/reaper.exe")): with pytest.raises(FileNotFoundError): render_project( rpp_path="input.rpp", output_wav="output.wav", reaper_exe=None, ) class TestRenderProjectSubprocessError: """Test render_project raises RuntimeError when subprocess returns non-zero exit code.""" def test_render_project_raises_runtime_error_on_nonzero_exit(self): """RuntimeError is raised when subprocess.run returns non-zero returncode.""" from src.reaper_builder.render import DEFAULT_REAPER_EXE # Check if reaper exists — if not, skip if not DEFAULT_REAPER_EXE.exists(): pytest.skip(f"REAPER not installed at {DEFAULT_REAPER_EXE}") # Create a minimal valid .rpp for this test import tempfile with tempfile.NamedTemporaryFile(mode="w", suffix=".rpp", delete=False, encoding="utf-8") as f: rpp_path = f.name f.write('\n') with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f: wav_path = f.name try: # Mock subprocess.run to return non-zero mock_result = MagicMock() mock_result.returncode = 1 mock_result.stdout = "" mock_result.stderr = "Test error" with patch("subprocess.run", return_value=mock_result): with pytest.raises(RuntimeError) as exc_info: render_project(rpp_path=rpp_path, output_wav=wav_path) assert "1" in str(exc_info.value) or "failed" in str(exc_info.value).lower() finally: Path(rpp_path).unlink(missing_ok=True) Path(wav_path).unlink(missing_ok=True) def test_render_project_raises_runtime_error_with_error_message(self): """RuntimeError output includes stdout/stderr from REAPER failure.""" from src.reaper_builder.render import DEFAULT_REAPER_EXE if not DEFAULT_REAPER_EXE.exists(): pytest.skip(f"REAPER not installed at {DEFAULT_REAPER_EXE}") import tempfile with tempfile.NamedTemporaryFile(mode="w", suffix=".rpp", delete=False, encoding="utf-8") as f: rpp_path = f.name f.write('\n') with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as f: wav_path = f.name try: mock_result = MagicMock() mock_result.returncode = 2 mock_result.stdout = "standard output text" mock_result.stderr = "error output text" with patch("subprocess.run", return_value=mock_result): with pytest.raises(RuntimeError) as exc_info: render_project(rpp_path=rpp_path, output_wav=wav_path) # Error message should include the exit code or stderr err_str = str(exc_info.value) assert "2" in err_str or "error output text" in err_str finally: Path(rpp_path).unlink(missing_ok=True) Path(wav_path).unlink(missing_ok=True)