Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions src/Rclone.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -1040,13 +1041,28 @@
* @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

Check failure on line 1047 in src/Rclone.php

View workflow job for this annotation

GitHub Actions / Static Analysis

PHPDoc tag `@throws` with type Exception\SyntaxErrorException is not subtype of Throwable
{
$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;
}
}

/**
Expand Down Expand Up @@ -1342,4 +1358,17 @@
{
$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;
}
}
72 changes: 72 additions & 0 deletions tests/Unit/CheckCommandTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
<?php

declare(strict_types=1);

namespace Verseles\Flyclone\Test\Unit;

use PHPUnit\Framework\TestCase;
use RuntimeException;
use Symfony\Component\Process\Process;
use Verseles\Flyclone\Exception\SyntaxErrorException;
use Verseles\Flyclone\ProcessManager;
use Verseles\Flyclone\Providers\LocalProvider;
use Verseles\Flyclone\Rclone;

class CheckCommandTest extends TestCase
{
public function testCheckReturnsFalseOnDifference(): 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, "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');
}
}
Loading