From 06fa70568f76d80a732e88ec096a62614e7fefc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9lio?= Date: Sun, 14 Apr 2024 19:31:57 -0300 Subject: [PATCH] some fixes and added method download_to_local (tests included) --- .gitignore | 1 + composer.json | 1 + docker-compose.yml | 1 + Makefile => makefile | 23 +-- src/Rclone.php | 35 +++- tests/Unit/AbstractTwoProvidersTest.php | 203 +++++++++++++----------- tests/Unit/Dockerfile | 3 + 7 files changed, 161 insertions(+), 106 deletions(-) rename Makefile => makefile (50%) diff --git a/.gitignore b/.gitignore index 79b83a9..e98ef96 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .env +ai.txt /vendor composer.phar composer.lock diff --git a/composer.json b/composer.json index d998205..e618a9a 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ "scripts" : { "test" : "phpunit", "test-local" : "phpunit --testsuite offline_no_docker", + "test-offline" : "phpunit --testsuite offline", "security-check" : "security-checker security:check composer.lock", "post-install-cmd" : [ "exit 0 || [ $COMPOSER_DEV_MODE -eq 0 ] || composer run security-check" diff --git a/docker-compose.yml b/docker-compose.yml index a656bed..3f16840 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,7 @@ x-commons: &commons env_file: .env volumes : - ".:/app" + - "./tests/Unit/sandbox:/tmp" networks : internal: diff --git a/Makefile b/makefile similarity index 50% rename from Makefile rename to makefile index 0a79af4..ff9fae0 100644 --- a/Makefile +++ b/makefile @@ -1,22 +1,25 @@ #!/usr/bin/make -.DEFAULT_GOAL := test - -init: ## Start a new develop environment - make cin test: ## Start containers detached CMD="run-script test-local" make composer -test_offline: - CMD="run-script test" make composer +init: ## Start a new develop environment + make cin +test-offline: + docker compose run --rm test_sftp_to_s3 + docker compose run --rm test_s3_to_sftp logs: ## Show the output logs - docker-compose logs + docker compose logs log: ## Open the logs and follow the news - docker-compose logs --follow + docker compose logs --follow restart: ## Restart the app container - docker-compose restart + docker compose restart composer: - docker-compose run --rm composer $(CMD) + composer $(CMD) +composer-docker: + docker compose run --rm composer $(CMD) cin: CMD=install make composer cup: CMD=update make composer +ai: + tog --output=ai.txt --folder-recursive=./src --ignore-folders=src/Exception diff --git a/src/Rclone.php b/src/Rclone.php index cf1437d..2c33e97 100644 --- a/src/Rclone.php +++ b/src/Rclone.php @@ -447,7 +447,7 @@ public function exists ( string $path, string $type ): object $ls = $this->ls($dirname); $found = array_filter($ls, static fn( $i ) => $i->Name === $basename && $i->IsDir === ($type === 'dir')); - return (object) [ 'exists' => count($found) === 1, 'details' => $found[ 0 ] ?? [], 'error' => '' ]; + return (object) [ 'exists' => count($found) === 1, 'details' => reset($found) ?? [], 'error' => '' ]; } catch (\Exception $e) { return (object) [ 'exists' => FALSE, 'error' => $e, ]; } @@ -605,14 +605,39 @@ public function rcat ( string $path, string $input, array $flags = [], callable public function upload_file ( string $local_path, string $remote_path, array $flags = [], callable $onProgress = NULL ): bool { - $left_local = new LocalProvider('local'); - $right_mix = $this->left_side; - - $rclone = new self($left_local, $right_mix); + $rclone = new self(left_side: new LocalProvider('local'), right_side: $this->left_side); return $rclone->moveto($local_path, $remote_path, $flags, $onProgress); } + /** + * Downloads a file from a remote path to local storage. + * + * @param string $remote_path The path of the file on the remote server. + * @param ?string $local_path The local path where the file should be saved. If not provided, a temporary directory will be used. + * @param array $flags Additional flags for the download operation. + * @param ?callable $onProgress A callback function to track download progress. + * + * @return string|false The local path where the file is saved, or false if the download fails. + */ + public function download_to_local(string $remote_path, ?string $local_path = null, array $flags = [], ?callable $onProgress = null): string|false + { + if ($local_path === null) { + $local_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . uniqid('flyclone_download_'); + mkdir($local_path, 0777, true); + } + + $rclone = new self(left_side: $this->left_side, right_side: new LocalProvider('local')); + + $success = $rclone->copy($remote_path, $local_path, $flags, $onProgress); + + if (!$success) { + return false; + } + + return $local_path; + } + public function copy ( string $source_path, string $dest_DIR_path, array $flags = [], callable $onProgress = NULL ): bool { return $this->directTwinRun('copy', $source_path, $dest_DIR_path, $flags, $onProgress); diff --git a/tests/Unit/AbstractTwoProvidersTest.php b/tests/Unit/AbstractTwoProvidersTest.php index b66356c..899d059 100644 --- a/tests/Unit/AbstractTwoProvidersTest.php +++ b/tests/Unit/AbstractTwoProvidersTest.php @@ -4,12 +4,13 @@ namespace Verseles\Flyclone\Test\Unit; -use Verseles\Flyclone\Providers\Provider; -use Verseles\Flyclone\Rclone; use PHPUnit\Framework\Exception; use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\TestCase; use SebastianBergmann\RecursionContext\InvalidArgumentException; +use Verseles\Flyclone\Providers\LocalProvider; +use Verseles\Flyclone\Providers\Provider; +use Verseles\Flyclone\Rclone; abstract class AbstractTwoProvidersTest extends TestCase { @@ -20,160 +21,180 @@ abstract class AbstractTwoProvidersTest extends TestCase protected string $left_working_directory = '/tmp'; protected string $right_working_directory = '/tmp'; - final public function setLeftProviderName ( string $leftProviderName ): void + final public function setLeftProviderName(string $leftProviderName): void { - $this->leftProviderName = $leftProviderName; + $this->leftProviderName = $leftProviderName; } - final public function getLeftProviderName (): string + final public function getLeftProviderName(): string { - return $this->leftProviderName; + return $this->leftProviderName; } - final public function getRightProviderName (): string + final public function getRightProviderName(): string { - return $this->rightProviderName; + return $this->rightProviderName; } - final public function setRightProviderName ( string $rightProviderName ): void + final public function setRightProviderName(string $rightProviderName): void { - $this->rightProviderName = $rightProviderName; + $this->rightProviderName = $rightProviderName; } - final public function getLeftWorkingDirectory (): string + final public function getLeftWorkingDirectory(): string { - return $this->left_working_directory; + return $this->left_working_directory; } - final public function setLeftWorkingDirectory ( string $left_working_directory ): void + final public function setLeftWorkingDirectory(string $left_working_directory): void { - $this->left_working_directory = $left_working_directory; + $this->left_working_directory = $left_working_directory; } - final public function getRightWorkingDirectory (): string + final public function getRightWorkingDirectory(): string { - return $this->right_working_directory; + return $this->right_working_directory; } - final public function setRightWorkingDirectory ( string $right_working_directory ): void + final public function setRightWorkingDirectory(string $right_working_directory): void { - $this->right_working_directory = $right_working_directory; + $this->right_working_directory = $right_working_directory; } - abstract public function instantiate_left_provider (): Provider; + abstract public function instantiate_left_provider(): Provider; - abstract public function instantiate_right_provider (): Provider; + abstract public function instantiate_right_provider(): Provider; /** - * @test - * @depends instantiate_left_provider - * @depends instantiate_right_provider - * @noinspection PhpUnitTestsInspection - * @throws ExpectationFailedException|InvalidArgumentException|Exception - */ - public function instantiate_with_two_providers ( $left_side, $right_side ): Rclone + * @test + * @depends instantiate_left_provider + * @depends instantiate_right_provider + * @noinspection PhpUnitTestsInspection + * @throws ExpectationFailedException|InvalidArgumentException|Exception + */ + public function instantiate_with_two_providers($left_side, $right_side): Rclone { - $two_sides = new Rclone($left_side, $right_side); + $two_sides = new Rclone($left_side, $right_side); - self::assertInstanceOf(Rclone::class, $two_sides); + self::assertInstanceOf(Rclone::class, $two_sides); - return $two_sides; + return $two_sides; } /** - * @test - * @depends instantiate_with_two_providers - * - * @param Rclone $two_sides - * - * @return array - * @throws ExpectationFailedException|InvalidArgumentException - */ - public function touch_a_file_on_left_side ( Rclone $two_sides ): array + * @test + * @depends instantiate_with_two_providers + * + * @param Rclone $two_sides + * + * @return array + * @throws ExpectationFailedException|InvalidArgumentException + */ + public function touch_a_file_on_left_side(Rclone $two_sides): array { - $temp_filepath = $this->getLeftWorkingDirectory() . '/flyclone_' . $this->random_string(); + $temp_filepath = $this->getLeftWorkingDirectory() . '/flyclone_' . $this->random_string(); - $result = $two_sides->touch($temp_filepath); + $result = $two_sides->touch($temp_filepath); - self::assertTrue($result); + self::assertTrue($result); - $file = $two_sides->is_file($temp_filepath); + $file = $two_sides->is_file($temp_filepath); - self::assertTrue($file->exists, 'File not created'); + self::assertTrue($file->exists, 'File not created'); - self::assertEquals(0, $file->details->Size ?? 9999, 'File should be empty by now'); + self::assertIsObject($file->details); + self::assertObjectHasProperty('Size', $file->details); - return [ $two_sides, $temp_filepath ]; + self::assertEquals(0, $file->details->Size ?? 9999, 'File should be empty for now'); + + return [$two_sides, $temp_filepath]; } /** - * @test - * @depends touch_a_file_on_left_side - * @throws ExpectationFailedException|InvalidArgumentException - */ - public function write_to_a_file_on_left_side ( $params ): array + * @test + * @depends touch_a_file_on_left_side + * @throws ExpectationFailedException|InvalidArgumentException + */ + public function write_to_a_file_on_left_side($params): array { - $content = 'But my father lives at https://helio.me :)'; + $content = 'But my father lives at https://helio.me :)'; - /** @var Rclone $two_sides */ - [ $two_sides, $temp_filepath ] = $params; + /** @var Rclone $two_sides */ + [$two_sides, $temp_filepath] = $params; - $two_sides->rcat($temp_filepath, $content); + $two_sides->rcat($temp_filepath, $content); - $file_content = $two_sides->cat($temp_filepath); - self::assertEquals($file_content, $content, 'File content are different'); + $file_content = $two_sides->cat($temp_filepath); + self::assertEquals($file_content, $content, 'File content are different'); - return [ $two_sides, $temp_filepath ]; + return [$two_sides, $temp_filepath, $content]; } /** - * @test - * - * @depends write_to_a_file_on_left_side - * @throws ExpectationFailedException|InvalidArgumentException - */ - public function move_file_to_right_side ( array $params ): array + * @test + * + * @depends write_to_a_file_on_left_side + * @throws ExpectationFailedException|InvalidArgumentException + */ + public function move_file_to_right_side(array $params): array { - /** @var $two_sides Rclone */ - [ $two_sides, $file_on_left_side ] = $params; + /** @var $two_sides Rclone */ + [$two_sides, $file_on_left_side, $content] = $params; - $new_place = $this->getRightWorkingDirectory() . '/' . basename($file_on_left_side); - $two_sides->moveto($file_on_left_side, $new_place); + $new_place = $this->getRightWorkingDirectory() . '/' . basename($file_on_left_side); + $two_sides->moveto($file_on_left_side, $new_place); - $right_side = new Rclone($two_sides->getRightSide()); - $check_new_place = $right_side->is_file($new_place); - self::assertTrue($check_new_place->exists, 'File not moved'); - if (!$two_sides->isRightSideDirAgnostic()) { - self::assertGreaterThan(0, $check_new_place->details->Size, 'File not moved correctly'); - } + $right_side = new Rclone($two_sides->getRightSide()); + $check_new_place = $right_side->is_file($new_place); + self::assertTrue($check_new_place->exists, 'File not moved'); + if (!$two_sides->isRightSideDirAgnostic()) { + self::assertGreaterThan(0, $check_new_place->details->Size, 'File not moved correctly'); + } - return [ $two_sides, $right_side, $new_place ]; + return [$two_sides, $right_side, $new_place, $content]; } /** - * @test - * @depends move_file_to_right_side - * - */ - public function delete_file_on_right_side($params) - : array + * @test + * @depends move_file_to_right_side + */ + public function download_to_local(array $params) { - /** @var Rclone $two_sides */ - /** @var Rclone $right_side */ - [ $two_sides, $right_side, $filepath ] = $params; - - $file = $right_side->is_file($filepath); - self::assertTrue($file->exists, 'File should exist at this point'); + /** @var $right_side Rclone */ + [$two_sides, $right_side, $filepath, $content] = $params; - $right_side->delete($filepath); + $downloaded_path = $right_side->download_to_local($filepath); - $file = $right_side->is_file($filepath); + self::assertFileExists($downloaded_path, 'File not downloaded'); - self::assertFalse($file->exists, 'This file should not exist anymore'); + $local = new Rclone(new LocalProvider('local')); + $local_content = $local->cat($downloaded_path); + self::assertEquals($local_content, $content, 'File content are different'); - return [ $two_sides, $right_side ]; + return [$two_sides, $downloaded_path, $content]; } + /** + * @test + * @depends move_file_to_right_side + * + */ + public function delete_file_on_right_side($params): array + { + /** @var Rclone $two_sides */ + /** @var Rclone $right_side */ + [$two_sides, $right_side, $filepath] = $params; + + $file = $right_side->is_file($filepath); + self::assertTrue($file->exists, 'File should exist at this point'); + + $right_side->delete($filepath); + $file = $right_side->is_file($filepath); + + self::assertFalse($file->exists, 'This file should not exist anymore'); + + return [$two_sides, $right_side]; + } } diff --git a/tests/Unit/Dockerfile b/tests/Unit/Dockerfile index af41163..a7edaa4 100644 --- a/tests/Unit/Dockerfile +++ b/tests/Unit/Dockerfile @@ -3,4 +3,7 @@ COPY --from=rclone/rclone /usr/local/bin/rclone /usr/bin/ LABEL MAINTAINER="Hélio " +# make /tmp free to write and read for everyone +RUN chmod -R 777 /tmp + WORKDIR /app