""" Unit tests for installer/deploy.py helpers. Covers check_xcode_tools and build_prole_app_core error paths without requiring a real macOS Xcode build environment. """ from __future__ import annotations import subprocess from pathlib import Path from unittest.mock import MagicMock, patch import pytest from installer.deploy import check_xcode_tools, build_prole_app_core # --------------------------------------------------------------------------- # check_xcode_tools # --------------------------------------------------------------------------- def test_check_xcode_tools_not_darwin(): with patch("installer.deploy.platform.system", return_value="Linux"): assert check_xcode_tools() is False def test_check_xcode_tools_not_darwin_windows(): with patch("installer.deploy.platform.system", return_value="Windows"): assert check_xcode_tools() is False def test_check_xcode_tools_darwin_success(): mock_result = MagicMock() mock_result.returncode = 0 with patch("installer.deploy.platform.system", return_value="Darwin"), \ patch("installer.deploy.subprocess.run", return_value=mock_result): assert check_xcode_tools() is True def test_check_xcode_tools_darwin_failure(): mock_result = MagicMock() mock_result.returncode = 1 with patch("installer.deploy.platform.system", return_value="Darwin"), \ patch("installer.deploy.subprocess.run", return_value=mock_result): assert check_xcode_tools() is False def test_check_xcode_tools_darwin_exception(): with patch("installer.deploy.platform.system", return_value="Darwin"), \ patch("installer.deploy.subprocess.run", side_effect=FileNotFoundError("xcrun not found")): assert check_xcode_tools() is False def test_check_xcode_tools_darwin_timeout(): with patch("installer.deploy.platform.system", return_value="Darwin"), \ patch("installer.deploy.subprocess.run", side_effect=subprocess.TimeoutExpired("xcrun", 10)): assert check_xcode_tools() is False # --------------------------------------------------------------------------- # build_prole_app_core — error paths # --------------------------------------------------------------------------- def test_build_prole_app_core_not_darwin(tmp_path): with patch("installer.deploy.platform.system", return_value="Linux"): with pytest.raises(Exception, match="macOS"): build_prole_app_core(tmp_path) def test_build_prole_app_core_no_xcode(tmp_path): with patch("installer.deploy.platform.system", return_value="Darwin"), \ patch("installer.deploy.check_xcode_tools", return_value=False): with pytest.raises(Exception, match="[Xx]code"): build_prole_app_core(tmp_path) def test_build_prole_app_core_no_build_script(tmp_path): # xcode ok but prole-app/build.sh missing prole_app = tmp_path / "prole-app" prole_app.mkdir() with patch("installer.deploy.platform.system", return_value="Darwin"), \ patch("installer.deploy.check_xcode_tools", return_value=True): with pytest.raises(Exception, match="[Bb]uild script"): build_prole_app_core(tmp_path) def test_build_prole_app_core_build_fails(tmp_path): prole_app = tmp_path / "prole-app" prole_app.mkdir() build_sh = prole_app / "build.sh" build_sh.write_text("#!/bin/bash\nexit 1\n") mock_result = MagicMock() mock_result.returncode = 1 mock_result.stderr = "build error" mock_result.stdout = "" with patch("installer.deploy.platform.system", return_value="Darwin"), \ patch("installer.deploy.check_xcode_tools", return_value=True), \ patch("installer.deploy.subprocess.run", return_value=mock_result): with pytest.raises(Exception, match="[Bb]uild failed"): build_prole_app_core(tmp_path) def test_build_prole_app_core_app_missing_after_build(tmp_path): prole_app = tmp_path / "prole-app" prole_app.mkdir() build_sh = prole_app / "build.sh" build_sh.write_text("#!/bin/bash\nexit 0\n") mock_result = MagicMock() mock_result.returncode = 0 mock_result.stderr = "" mock_result.stdout = "Build OK" with patch("installer.deploy.platform.system", return_value="Darwin"), \ patch("installer.deploy.check_xcode_tools", return_value=True), \ patch("installer.deploy.subprocess.run", return_value=mock_result): # dist/Prole.app is not created → should raise with pytest.raises(Exception, match="[Pp]role.app"): build_prole_app_core(tmp_path) def test_build_prole_app_core_success(tmp_path): prole_app = tmp_path / "prole-app" (prole_app / "dist").mkdir(parents=True) app_path = prole_app / "dist" / "Prole.app" app_path.mkdir() build_sh = prole_app / "build.sh" build_sh.write_text("#!/bin/bash\nexit 0\n") mock_result = MagicMock() mock_result.returncode = 0 with patch("installer.deploy.platform.system", return_value="Darwin"), \ patch("installer.deploy.check_xcode_tools", return_value=True), \ patch("installer.deploy.subprocess.run", return_value=mock_result): result = build_prole_app_core(tmp_path) assert result == app_path