diff --git a/src/Rclone.php b/src/Rclone.php index c663a928..e9c1ccf0 100644 --- a/src/Rclone.php +++ b/src/Rclone.php @@ -8,6 +8,7 @@ use JsonException; use RuntimeException; use Symfony\Component\Process\Process; +use Verseles\Flyclone\Exception\SyntaxErrorException; use Verseles\Flyclone\Providers\LocalProvider; use Verseles\Flyclone\Providers\Provider; @@ -1040,13 +1041,28 @@ public function sync(string $source_path, string $dest_path, array $flags = [], * @param array $flags Additional flags. * @param callable|null $onProgress Optional progress callback. * - * @return bool True if check succeeds. + * @return bool True if check succeeds (files match), false if differences found. + * @throws Exception\SyntaxErrorException If a syntax or usage error occurs. */ public function check(string $source_path, string $dest_path, array $flags = [], ?callable $onProgress = null): bool { - $this->directTwinRun('check', $source_path, $dest_path, $flags, $onProgress); + try { + $this->directTwinRun('check', $source_path, $dest_path, $flags, $onProgress); - return true; + return true; + } catch (SyntaxErrorException $e) { + // Exit code 1 can mean differences found or syntax error. + // We check the output to distinguish. + $message = $e->getMessage(); + + // If it looks like a syntax error ("Error: ...") re-throw + if (str_contains($message, 'Error: ')) { + throw $e; + } + + // Otherwise assume differences found + return false; + } } /** @@ -1342,4 +1358,17 @@ public function setRightSide(Provider $right_side): void { $this->right_side = $right_side; } + + /** + * Set the ProcessManager instance to use. + * Useful for testing or custom process management. + * + * @param ProcessManager $processManager The ProcessManager instance. + */ + public function setProcessManager(ProcessManager $processManager): self + { + $this->processManager = $processManager; + + return $this; + } } diff --git a/tests/Unit/CheckCommandTest.php b/tests/Unit/CheckCommandTest.php new file mode 100644 index 00000000..f3f5e9a7 --- /dev/null +++ b/tests/Unit/CheckCommandTest.php @@ -0,0 +1,72 @@ +createMock(ProcessManager::class); + $rclone->setProcessManager($mockPm); + + $previous = new RuntimeException("Process failed"); + $syntaxError = new SyntaxErrorException($previous, "2 matching files\n1 differences found", 1); + + $mockPm->method('run')->willThrowException($syntaxError); + + $result = $rclone->check('/source', '/dest'); + + $this->assertFalse($result, 'check() should return false when differences are found'); + } + + public function testCheckThrowsOnActualSyntaxError(): void + { + $local = new LocalProvider('test'); + $rclone = new Rclone($local); + + $mockPm = $this->createMock(ProcessManager::class); + $rclone->setProcessManager($mockPm); + + $previous = new RuntimeException("Process failed"); + $syntaxError = new SyntaxErrorException($previous, "Error: unknown flag: --invalid", 1); + + $mockPm->method('run')->willThrowException($syntaxError); + + $this->expectException(SyntaxErrorException::class); + $this->expectExceptionMessage("Error: unknown flag: --invalid"); + + $rclone->check('/source', '/dest'); + } + + public function testCheckReturnsTrueOnSuccess(): void + { + $local = new LocalProvider('test'); + $rclone = new Rclone($local); + + $mockPm = $this->createMock(ProcessManager::class); + $rclone->setProcessManager($mockPm); + + // Mock success process + $process = $this->createMock(Process::class); + $process->method('getOutput')->willReturn(''); + + $mockPm->method('run')->willReturn($process); + + $result = $rclone->check('/source', '/dest'); + + $this->assertTrue($result, 'check() should return true when no differences found'); + } +}