diff --git a/src/ProcessManager.php b/src/ProcessManager.php index 1ff9f1ff..cd5e828c 100644 --- a/src/ProcessManager.php +++ b/src/ProcessManager.php @@ -32,6 +32,9 @@ class ProcessManager private static string $input = ''; + /** @var ExecutableFinder|null Finder instance for mocking in tests */ + private static ?ExecutableFinder $executableFinder = null; + /** @var array Secrets to redact from error messages */ private array $secrets = []; @@ -81,14 +84,24 @@ public static function setBin(string $bin): void self::$bin = $bin; } + /** + * Set the ExecutableFinder instance for testing. + * + * @param ExecutableFinder|null $finder The finder instance or null to use default. + */ + public static function setExecutableFinder(?ExecutableFinder $finder): void + { + self::$executableFinder = $finder; + } + public static function guessBin(): string { if (isset(self::$bin) && self::$bin !== '') { return self::$bin; } - $finder = new ExecutableFinder(); - $rclonePath = $finder->find('rclone', '/usr/bin/rclone', [ + $finder = self::$executableFinder ?? new ExecutableFinder(); + $rclonePath = $finder->find('rclone', null, [ '/usr/local/bin', '/usr/bin', '/bin', diff --git a/tests/Unit/ConfigurationTest.php b/tests/Unit/ConfigurationTest.php index bcc7055f..6ae0387d 100644 --- a/tests/Unit/ConfigurationTest.php +++ b/tests/Unit/ConfigurationTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; +use Symfony\Component\Process\ExecutableFinder; use Verseles\Flyclone\CommandBuilder; use Verseles\Flyclone\ProcessManager; use Verseles\Flyclone\Providers\LocalProvider; @@ -13,6 +14,21 @@ class ConfigurationTest extends TestCase { + protected function setUp(): void + { + // Mock ExecutableFinder to ensure tests run even without rclone installed + $mockFinder = $this->createMock(ExecutableFinder::class); + $mockFinder->method('find')->willReturn('/mock/path/to/rclone'); + ProcessManager::setExecutableFinder($mockFinder); + ProcessManager::setBin(''); // Force re-guess + } + + protected function tearDown(): void + { + ProcessManager::setExecutableFinder(null); + ProcessManager::setBin(''); + } + #[Test] public function set_and_get_timeout(): void { diff --git a/tests/Unit/ProcessManagerTest.php b/tests/Unit/ProcessManagerTest.php new file mode 100644 index 00000000..e6d65445 --- /dev/null +++ b/tests/Unit/ProcessManagerTest.php @@ -0,0 +1,49 @@ +createMock(ExecutableFinder::class); + $mockFinder->method('find') + ->willReturn('/usr/bin/rclone'); + + ProcessManager::setExecutableFinder($mockFinder); + + $bin = ProcessManager::guessBin(); + + self::assertEquals('/usr/bin/rclone', $bin); + } + + #[Test] + public function guess_bin_throws_exception_when_rclone_not_found(): void + { + $mockFinder = $this->createMock(ExecutableFinder::class); + $mockFinder->method('find') + ->willReturn(null); + + ProcessManager::setExecutableFinder($mockFinder); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Rclone binary not found'); + + ProcessManager::guessBin(); + } +}