From ac40512029eb8d98a3babb4ad7b6d9312557dbae Mon Sep 17 00:00:00 2001 From: Johannes Wachter Date: Sun, 12 Jun 2016 09:45:46 +0200 Subject: [PATCH 1/3] made global config optional --- docs/configuration.rst | 8 +++- src/Bundle/Command/FetchCommand.php | 8 ++++ src/Bundle/Command/PushCommand.php | 8 ++++ .../DependencyInjection/Configuration.php | 6 ++- .../DependencyInjection/NanbandoExtension.php | 7 ++- src/Bundle/Resources/config/services.xml | 4 +- src/Core/Storage/LocalStorage.php | 18 +++++-- .../RemoteStorageNotConfiguredException.php | 11 +++++ tests/Unit/Core/Storage/LocalStorageTest.php | 47 ++++++++++++++++++- 9 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 src/Core/Storage/RemoteStorageNotConfiguredException.php diff --git a/docs/configuration.rst b/docs/configuration.rst index d8f4d41..1a685cf 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -1,7 +1,7 @@ Configuration ============= -The configuration is devided into two parts - global and project configuration. +The configuration is devided into two parts - global (optional) and project configuration. Global Configuration -------------------- @@ -13,7 +13,7 @@ Put this configuration into ``~/.nanbando.yml``. nanbando: storage: - local_directory: "%home%/nanbando/local" + local_directory: "%home%/nanbando" remote_service: filesystem.remote oneup_flysystem: @@ -36,6 +36,10 @@ Put this configuration into ``~/.nanbando.yml``. For nanbando you have to define the local directory, where the backup command can place the backup archives, and the remote filesystem-service which can be configured in the ``oneup_flysystem`` extension. +By default the ``local_directory`` will be set to ``%home%/nanbando`` and the ``remote_service`` will be ``null``. This +leads to local backups will work out of the box but all commands (``fetch``, ``push`) which needs the remote-storage +will be disabled. + Local Configuration ------------------- diff --git a/src/Bundle/Command/FetchCommand.php b/src/Bundle/Command/FetchCommand.php index f29dd0a..3d3f9a5 100644 --- a/src/Bundle/Command/FetchCommand.php +++ b/src/Bundle/Command/FetchCommand.php @@ -75,4 +75,12 @@ protected function execute(InputInterface $input, OutputInterface $output) $storage->fetch($file); } } + + /** + * {@inheritdoc} + */ + public function isEnabled() + { + return $this->getContainer()->has('filesystem.remote'); + } } diff --git a/src/Bundle/Command/PushCommand.php b/src/Bundle/Command/PushCommand.php index fd88fdc..1633ac6 100644 --- a/src/Bundle/Command/PushCommand.php +++ b/src/Bundle/Command/PushCommand.php @@ -37,4 +37,12 @@ protected function execute(InputInterface $input, OutputInterface $output) $storage->push($file); } } + + /** + * {@inheritdoc} + */ + public function isEnabled() + { + return $this->getContainer()->has('filesystem.remote'); + } } diff --git a/src/Bundle/DependencyInjection/Configuration.php b/src/Bundle/DependencyInjection/Configuration.php index 6082f03..fa43cd7 100644 --- a/src/Bundle/DependencyInjection/Configuration.php +++ b/src/Bundle/DependencyInjection/Configuration.php @@ -4,6 +4,7 @@ use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use Webmozart\PathUtil\Path; class Configuration implements ConfigurationInterface { @@ -31,8 +32,11 @@ public function getConfigTreeBuilder() ->end() ->end() ->arrayNode('storage') + ->addDefaultsIfNotSet() ->children() - ->scalarNode('local_directory')->end() + ->scalarNode('local_directory') + ->defaultValue(Path::join([Path::getHomeDirectory(), 'nanbando'])) + ->end() ->scalarNode('remote_service')->end() ->end() ->end() diff --git a/src/Bundle/DependencyInjection/NanbandoExtension.php b/src/Bundle/DependencyInjection/NanbandoExtension.php index f9a3b50..68ae760 100644 --- a/src/Bundle/DependencyInjection/NanbandoExtension.php +++ b/src/Bundle/DependencyInjection/NanbandoExtension.php @@ -24,7 +24,12 @@ public function load(array $configs, ContainerBuilder $container) $container->setParameter('nanbando.temp', $config['temp']); $container->setParameter('nanbando.backup', $config['backup']); $container->setParameter('nanbando.storage.local_directory', $config['storage']['local_directory']); - $container->setParameter('nanbando.storage.remote_service', $config['storage']['remote_service']); + + if (array_key_exists('remote_service', $config['storage']) + && $config['storage']['remote_service'] !== 'filesystem.remote' + ) { + $container->setAlias('filesystem.remote', $config['storage']['remote_service']); + } $container->prependExtensionConfig( 'oneup_flysystem', diff --git a/src/Bundle/Resources/config/services.xml b/src/Bundle/Resources/config/services.xml index 9705267..b9cde47 100644 --- a/src/Bundle/Resources/config/services.xml +++ b/src/Bundle/Resources/config/services.xml @@ -32,9 +32,9 @@ %nanbando.name% - - + + diff --git a/src/Core/Storage/LocalStorage.php b/src/Core/Storage/LocalStorage.php index 199dab2..25e9cce 100644 --- a/src/Core/Storage/LocalStorage.php +++ b/src/Core/Storage/LocalStorage.php @@ -39,16 +39,16 @@ class LocalStorage implements StorageInterface /** * @param string $name * @param TemporaryFileManager $temporaryFileManager + * @param SlugifyInterface $slugify * @param Filesystem $localFilesystem * @param Filesystem $remoteFilesystem - * @param SlugifyInterface $slugify */ public function __construct( $name, TemporaryFileManager $temporaryFileManager, + SlugifyInterface $slugify, Filesystem $localFilesystem, - Filesystem $remoteFilesystem, - SlugifyInterface $slugify + Filesystem $remoteFilesystem = null ) { $this->name = $name; $this->temporaryFileManager = $temporaryFileManager; @@ -121,6 +121,10 @@ public function localListing() */ public function remoteListing() { + if (!$this->remoteFilesystem) { + throw new RemoteStorageNotConfiguredException(); + } + return $this->listing($this->remoteFilesystem); } @@ -142,6 +146,10 @@ public function size(Filesystem $filesystem) */ public function fetch($file) { + if (!$this->remoteFilesystem) { + throw new RemoteStorageNotConfiguredException(); + } + $path = sprintf('%s/%s.zip', $this->name, $file); if (false === ($stream = $this->remoteFilesystem->readStream($path))) { @@ -156,6 +164,10 @@ public function fetch($file) */ public function push($file) { + if (!$this->remoteFilesystem) { + throw new RemoteStorageNotConfiguredException(); + } + $path = sprintf('%s/%s.zip', $this->name, $file); if (false === ($stream = $this->localFilesystem->readStream($path))) { diff --git a/src/Core/Storage/RemoteStorageNotConfiguredException.php b/src/Core/Storage/RemoteStorageNotConfiguredException.php new file mode 100644 index 0000000..ae95396 --- /dev/null +++ b/src/Core/Storage/RemoteStorageNotConfiguredException.php @@ -0,0 +1,11 @@ +storage = new LocalStorage( $this->name, $this->temporaryFileManager->reveal(), + $this->slugify->reveal(), $this->localFilesystem->reveal(), - $this->remoteFilesystem->reveal(), - $this->slugify->reveal() + $this->remoteFilesystem->reveal() ); } @@ -189,4 +190,46 @@ public function testPushNotExists() $this->storage->push($file); } + + public function testPushNoRemote() + { + $this->setExpectedException(RemoteStorageNotConfiguredException::class); + + $storage = new LocalStorage( + $this->name, + $this->temporaryFileManager->reveal(), + $this->slugify->reveal(), + $this->localFilesystem->reveal() + ); + + $storage->push('test'); + } + + public function testFetchNoRemote() + { + $this->setExpectedException(RemoteStorageNotConfiguredException::class); + + $storage = new LocalStorage( + $this->name, + $this->temporaryFileManager->reveal(), + $this->slugify->reveal(), + $this->localFilesystem->reveal() + ); + + $storage->fetch('test'); + } + + public function testRemoteListingNoRemote() + { + $this->setExpectedException(RemoteStorageNotConfiguredException::class); + + $storage = new LocalStorage( + $this->name, + $this->temporaryFileManager->reveal(), + $this->slugify->reveal(), + $this->localFilesystem->reveal() + ); + + $storage->remoteListing(); + } } From 15c73aeea7c037445e7e8b4b68a4f8caca412efc Mon Sep 17 00:00:00 2001 From: Johannes Wachter Date: Sun, 12 Jun 2016 10:22:03 +0200 Subject: [PATCH 2/3] implemented configuration check command --- README.md | 1 + docs/configuration.rst | 7 +- docs/installation.rst | 2 + src/Bundle/Command/CheckCommand.php | 114 ++++++++++ src/Core/Storage/LocalStorage.php | 4 +- .../Unit/Bundle/Command/CheckCommandTest.php | 200 ++++++++++++++++++ 6 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 src/Bundle/Command/CheckCommand.php create mode 100644 tests/Unit/Bundle/Command/CheckCommandTest.php diff --git a/README.md b/README.md index 34e4287..7443b78 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ wget http://nanbando.github.io/core/nanbando.phar.pubkey chmod +x nanbando.phar mv nanbando.phar /usr/local/bin/nanbando mv nanbando.phar.pubkey /usr/local/bin/nanbando.pubkey +nanbando check ``` After first installation you can update the application with a built-in command. diff --git a/docs/configuration.rst b/docs/configuration.rst index 1a685cf..5f96b77 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -3,6 +3,11 @@ Configuration The configuration is devided into two parts - global (optional) and project configuration. +.. warning:: + + After changing configuration please run command ``reconfigure`` to be sure that the configuration will be used for + recreating the symfony container. + Global Configuration -------------------- @@ -37,7 +42,7 @@ For nanbando you have to define the local directory, where the backup command ca remote filesystem-service which can be configured in the ``oneup_flysystem`` extension. By default the ``local_directory`` will be set to ``%home%/nanbando`` and the ``remote_service`` will be ``null``. This -leads to local backups will work out of the box but all commands (``fetch``, ``push`) which needs the remote-storage +leads to local backups will work out of the box but all commands (``fetch``, ``push``) which needs the remote-storage will be disabled. Local Configuration diff --git a/docs/installation.rst b/docs/installation.rst index 81ed055..e64ffa0 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -20,3 +20,5 @@ After first installation you can update the application with a built-in command. .. note:: The executable is signed with a OpenSSL private key. This ensures the origin of the build. + +Check the configuration state of your application by using the command ``nanbando check``. diff --git a/src/Bundle/Command/CheckCommand.php b/src/Bundle/Command/CheckCommand.php new file mode 100644 index 0000000..87dfe62 --- /dev/null +++ b/src/Bundle/Command/CheckCommand.php @@ -0,0 +1,114 @@ +setName('check') + ->setDescription('Checks configuration issues') + ->setHelp( + <<{$this->getName()} command looks for configuration issues + +EOT + ); + } + + /** + * {@inheritdoc} + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = new SymfonyStyle($input, $output); + + $io->title('Configuration Check Report'); + + $io->writeln('Local directory: ' . $this->getContainer()->getParameter('nanbando.storage.local_directory')); + + if (!$this->getContainer()->has('filesystem.remote')) { + $io->warning( + 'No remote storage configuration found. This leads into disabled "fetch" and "push" commands.' . + 'Please follow the documentation for global configuration.' . PHP_EOL . PHP_EOL . + 'http://nanbando.readthedocs.io/en/latest/configuration.html#global-configuration' + ); + } else { + $io->writeln('Remote Storage: YES'); + } + + $backups = $this->getContainer()->getParameter('nanbando.backup'); + if (0 === count($backups)) { + $io->warning( + 'No backup configuration found. Please follow the documentation for local configuration.' . PHP_EOL . PHP_EOL . + 'http://nanbando.readthedocs.io/en/latest/configuration.html#local-configuration' + ); + } + + $this->checkBackups($io, $backups); + + $io->writeln(''); + } + + /** + * Check backup-configuration. + * + * @param SymfonyStyle $io + * @param array $backups + */ + private function checkBackups(SymfonyStyle $io, array $backups) + { + /** @var PluginRegistry $plugins */ + $plugins = $this->getContainer()->get('plugins'); + foreach ($backups as $name => $backup) { + $this->checkBackup($plugins, $io, $name, $backup); + } + } + + /** + * Check single backup-configuration. + * + * @param PluginRegistry $plugins + * @param SymfonyStyle $io + * @param string $name + * @param array $backup + * + * @return bool + */ + private function checkBackup(PluginRegistry $plugins, SymfonyStyle $io, $name, array $backup) + { + $io->section('Backup: ' . $name); + if (!$plugins->has($backup['plugin'])) { + $io->warning(sprintf('Plugin "%s" not found', $backup['plugin'])); + + return false; + } + + $optionsResolver = new OptionsResolver(); + $plugins->getPlugin($backup['plugin'])->configureOptionsResolver($optionsResolver); + + try { + $optionsResolver->resolve($backup['parameter']); + } catch (InvalidArgumentException $e) { + $io->warning(sprintf('Parameter not valid' . PHP_EOL . PHP_EOL . 'Message: "%s"', $e->getMessage())); + + return false; + } + + $io->writeln('OK'); + + return true; + } +} diff --git a/src/Core/Storage/LocalStorage.php b/src/Core/Storage/LocalStorage.php index 25e9cce..d6d2211 100644 --- a/src/Core/Storage/LocalStorage.php +++ b/src/Core/Storage/LocalStorage.php @@ -124,7 +124,7 @@ public function remoteListing() if (!$this->remoteFilesystem) { throw new RemoteStorageNotConfiguredException(); } - + return $this->listing($this->remoteFilesystem); } @@ -167,7 +167,7 @@ public function push($file) if (!$this->remoteFilesystem) { throw new RemoteStorageNotConfiguredException(); } - + $path = sprintf('%s/%s.zip', $this->name, $file); if (false === ($stream = $this->localFilesystem->readStream($path))) { diff --git a/tests/Unit/Bundle/Command/CheckCommandTest.php b/tests/Unit/Bundle/Command/CheckCommandTest.php new file mode 100644 index 0000000..eae67d8 --- /dev/null +++ b/tests/Unit/Bundle/Command/CheckCommandTest.php @@ -0,0 +1,200 @@ +container = $this->prophesize(ContainerInterface::class); + $this->plugins = $this->prophesize(PluginRegistry::class); + } + + private function getCommandTester($remote = false, $backup = []) + { + $this->container->getParameter('nanbando.storage.local_directory')->willReturn('/User/test/nanbando'); + $this->container->getParameter('nanbando.backup')->willReturn($backup); + $this->container->has('filesystem.remote')->willReturn($remote); + + $this->container->get('plugins')->willReturn($this->plugins->reveal()); + + $command = new CheckCommand(); + $command->setContainer($this->container->reveal()); + + $application = new Application(); + $application->add($command); + + $command = $application->find('check'); + + return new CommandTester($command); + } + + public function testExecute() + { + $commandTester = $this->getCommandTester(); + $commandTester->execute([]); + + $this->assertContains('Local directory: /User/test/nanbando', $commandTester->getDisplay()); + $this->assertContains('No remote storage configuration found.', $commandTester->getDisplay()); + $this->assertContains('No backup configuration found.', $commandTester->getDisplay()); + } + + public function testExecuteWithRemote() + { + $commandTester = $this->getCommandTester(true); + $commandTester->execute([]); + + $this->assertContains('Local directory: /User/test/nanbando', $commandTester->getDisplay()); + $this->assertContains('Remote Storage: YES', $commandTester->getDisplay()); + } + + public function testExecutePluginNotFound() + { + $this->plugins->has('my-plugin')->willReturn(false); + + $commandTester = $this->getCommandTester(true, ['test' => ['plugin' => 'my-plugin']]); + $commandTester->execute([]); + + $this->assertContains('Plugin "my-plugin" not found', $commandTester->getDisplay()); + } + + public function testExecutePluginNotFoundMultiple() + { + $plugin = $this->prophesize(PluginInterface::class); + $plugin->configureOptionsResolver(Argument::type(OptionsResolver::class))->shouldBeCalled(); + + $this->plugins->has('my-plugin-1')->willReturn(true); + $this->plugins->getPlugin('my-plugin-1')->willReturn($plugin->reveal()); + $this->plugins->has('my-plugin-2')->willReturn(false); + + $commandTester = $this->getCommandTester( + true, + [ + 'test-1' => ['plugin' => 'my-plugin-1', 'parameter' => []], + 'test-2' => ['plugin' => 'my-plugin-2'], + ] + ); + $commandTester->execute([]); + + + $this->assertRegExp('/test-1[-\s]*OK/', $commandTester->getDisplay()); + $this->assertRegExp('/test-2[-\s]*\[WARNING\] Plugin "my-plugin-2" not found/', $commandTester->getDisplay()); + } + + public function testExecuteParameterNotValid() + { + $plugin = $this->prophesize(PluginInterface::class); + $plugin->configureOptionsResolver(Argument::type(OptionsResolver::class)) + ->will( + function ($args) { + $args[0]->setRequired(['chmod', 'directory']); + } + ); + + $this->plugins->has('my-plugin')->willReturn(true); + $this->plugins->getPlugin('my-plugin')->willReturn($plugin->reveal()); + + $commandTester = $this->getCommandTester( + true, + ['test' => ['plugin' => 'my-plugin', 'parameter' => ['directory' => '/test']]] + ); + $commandTester->execute([]); + + $this->assertContains('Parameter not valid', $commandTester->getDisplay()); + } + + public function testExecuteParameterNotValidMultiple() + { + $plugin = $this->prophesize(PluginInterface::class); + $plugin->configureOptionsResolver(Argument::type(OptionsResolver::class)) + ->will( + function ($args) { + $args[0]->setRequired(['chmod', 'directory']); + } + ); + + $this->plugins->has('my-plugin-1')->willReturn(true); + $this->plugins->has('my-plugin-2')->willReturn(true); + $this->plugins->getPlugin('my-plugin-1')->willReturn($plugin->reveal()); + $this->plugins->getPlugin('my-plugin-2')->willReturn($plugin->reveal()); + + $commandTester = $this->getCommandTester( + true, + [ + 'test-1' => ['plugin' => 'my-plugin-1', 'parameter' => ['directory' => '/test']], + 'test-2' => ['plugin' => 'my-plugin-2', 'parameter' => ['directory' => '/test', 'chmod' => 0777]], + ] + ); + $commandTester->execute([]); + + $this->assertRegExp('/test-1[-\s]*\[WARNING\] Parameter not valid/', $commandTester->getDisplay()); + $this->assertRegExp('/test-2[-\s]*OK/', $commandTester->getDisplay()); + } + + public function testExecuteOK() + { + $plugin = $this->prophesize(PluginInterface::class); + $plugin->configureOptionsResolver(Argument::type(OptionsResolver::class)) + ->will( + function ($args) { + $args[0]->setRequired(['chmod', 'directory']); + } + ); + + $this->plugins->has('my-plugin')->willReturn(true); + $this->plugins->getPlugin('my-plugin')->willReturn($plugin->reveal()); + + $commandTester = $this->getCommandTester( + true, + ['test' => ['plugin' => 'my-plugin', 'parameter' => ['directory' => '/test', 'chmod' => 0777]]] + ); + $commandTester->execute([]); + + $this->assertContains('OK', $commandTester->getDisplay()); + } + + public function testExecuteOKMultiple() + { + $plugin = $this->prophesize(PluginInterface::class); + $plugin->configureOptionsResolver(Argument::type(OptionsResolver::class)) + ->will( + function ($args) { + $args[0]->setRequired(['chmod', 'directory']); + } + ); + + $this->plugins->has('my-plugin')->willReturn(true); + $this->plugins->getPlugin('my-plugin')->willReturn($plugin->reveal()); + + $commandTester = $this->getCommandTester( + true, + [ + 'test-1' => ['plugin' => 'my-plugin', 'parameter' => ['directory' => '/test', 'chmod' => 0777]], + 'test-2' => ['plugin' => 'my-plugin', 'parameter' => ['directory' => '/test', 'chmod' => 0777]], + ] + ); + $commandTester->execute([]); + + $this->assertRegExp('/test-1[-\s]*OK/', $commandTester->getDisplay()); + $this->assertRegExp('/test-2[-\s]*OK/', $commandTester->getDisplay()); + } +} From 44475015e5beed837519af0737ea9e5636993831 Mon Sep 17 00:00:00 2001 From: Johannes Wachter Date: Sun, 12 Jun 2016 17:28:47 +0200 Subject: [PATCH 3/3] improved test coverage for prefix-adapter --- src/Core/Flysystem/PrefixAdapter.php | 2 +- .../Unit/Core/Flysystem/PrefixAdapterTest.php | 75 +++++++++++++++++-- 2 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src/Core/Flysystem/PrefixAdapter.php b/src/Core/Flysystem/PrefixAdapter.php index c164214..df27951 100644 --- a/src/Core/Flysystem/PrefixAdapter.php +++ b/src/Core/Flysystem/PrefixAdapter.php @@ -19,7 +19,7 @@ class PrefixAdapter implements AdapterInterface private $adapter; /** - * @param string $root + * @param string $root * @param AdapterInterface $adapter */ public function __construct($root, AdapterInterface $adapter) diff --git a/tests/Unit/Core/Flysystem/PrefixAdapterTest.php b/tests/Unit/Core/Flysystem/PrefixAdapterTest.php index 9f03c92..ae1bb2a 100644 --- a/tests/Unit/Core/Flysystem/PrefixAdapterTest.php +++ b/tests/Unit/Core/Flysystem/PrefixAdapterTest.php @@ -3,8 +3,9 @@ namespace Nanbando\Tests\Unit\Core\Flysystem; use League\Flysystem\AdapterInterface; +use League\Flysystem\Config; use Nanbando\Core\Flysystem\PrefixAdapter; -use Webmozart\PathUtil\Path; +use Prophecy\Argument; class PrefixAdapterTest extends \PHPUnit_Framework_TestCase { @@ -25,17 +26,77 @@ public function setUp() $this->adapter = new PrefixAdapter(__DIR__, $this->decorated->reveal()); } - public function testCopy() + public function provideDelegateData() { - $this->decorated->copy(Path::join([__DIR__, 'test.json']), Path::join([__DIR__, 'test-new.json'])); + $config = new Config(); - $this->adapter->copy('test.json', 'test-new.json'); + return [ + ['write', ['/test.json', 'test content', $config], ['/test/test.json', 'test content', $config]], + ['writeStream', ['/test.json', 'test content', $config], ['/test/test.json', 'test content', $config]], + ['update', ['/test.json', 'test content', $config], ['/test/test.json', 'test content', $config]], + ['updateStream', ['/test.json', 'test content', $config], ['/test/test.json', 'test content', $config]], + ['rename', ['/test.json', '/test-new.json'], ['/test/test.json', '/test/test-new.json']], + ['copy', ['/test.json', '/test-new.json'], ['/test/test.json', '/test/test-new.json']], + ['delete', ['/test'], ['/test/test']], + ['deleteDir', ['/test'], ['/test/test']], + ['createDir', ['/test', $config], ['/test/test', $config]], + ['createDir', ['/test', $config], ['/test/test', $config]], + ['setVisibility', ['/test.json', true], ['/test/test.json', true]], + ['setVisibility', ['/test.json', false], ['/test/test.json', false]], + ['has', ['/test.json'], ['/test/test.json'], true], + ['has', ['/test.json'], ['/test/test.json'], false], + ['read', ['/test.json'], ['/test/test.json'], 'test content'], + ['getMetadata', ['/test.json'], ['/test/test.json'], 'test metadata'], + ['getSize', ['/test.json'], ['/test/test.json'], 42], + ['getMimetype', ['/test.json'], ['/test/test.json'], 'application/test'], + ['getTimestamp', ['/test.json'], ['/test/test.json'], new \DateTime()], + ['getTimestamp', ['/test.json'], ['/test/test.json'], new \DateTime()], + ['getVisibility', ['/test.json'], ['/test/test.json'], true], + ['getVisibility', ['/test.json'], ['/test/test.json'], false], + ]; } - public function testHas() + /** + * @dataProvider provideDelegateData + */ + public function testDelegate($method, array $parameter, array $delegatedParameter, $result = null, $root = '/test') + { + $adapter = $this->prophesize(AdapterInterface::class); + + $adapter->{$method}(Argument::cetera())->will( + function ($arguments) use ($delegatedParameter, $result) { + PrefixAdapterTest::assertEquals($delegatedParameter, $arguments); + + return $result; + } + ); + + $this->assertEquals( + $result, + call_user_func_array([new PrefixAdapter($root, $adapter->reveal()), $method], $parameter) + ); + } + + public function testListContents() { - $this->decorated->has(Path::join([__DIR__, 'test.json'])); + $adapter = $this->prophesize(AdapterInterface::class); + $prefixAdapter = new PrefixAdapter('/test', $adapter->reveal()); + + $adapter->listContents('/test/dir', false)->willReturn( + [ + ['path' => 'test/dir/test-1.json', 'dirname' => 'test/dir'], + ['path' => 'test/dir/test-2.json', 'dirname' => 'test/dir'], + ] + ); + + $result = $prefixAdapter->listContents('/dir'); - $this->adapter->has('test.json'); + $this->assertEquals( + [ + ['path' => 'dir/test-1.json', 'dirname' => 'dir'], + ['path' => 'dir/test-2.json', 'dirname' => 'dir'], + ], + $result + ); } }