diff --git a/src/ReadinessChecker/Filesystem.php b/src/ReadinessChecker/Filesystem.php index 2edc02f181d019d1a8939d51f15022895cbed492..de0914ff2a350c1b728669daa3c9d0d751ea05a4 100644 --- a/src/ReadinessChecker/Filesystem.php +++ b/src/ReadinessChecker/Filesystem.php @@ -38,7 +38,7 @@ abstract class Filesystem implements ReadinessCheckerInterface { * {@inheritdoc} */ public function run() { - if (!$this->exists($this->getRootPath() . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, ['core', 'core.api.php']))) { + if (!file_exists($this->getRootPath() . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, ['core', 'core.api.php']))) { return [$this->t('The web root could not be located.')]; } @@ -96,17 +96,4 @@ abstract class Filesystem implements ReadinessCheckerInterface { return $root_statistics && $vendor_statistics && $root_statistics['dev'] === $vendor_statistics['dev']; } - /** - * Checks whether a file or directory exists. - * - * @param string $file - * The file path to test. - * - * @return bool - * TRUE if the file exists, otherwise FALSE. - */ - protected function exists($file) { - return file_exists($file); - } - } diff --git a/src/ReadinessChecker/Vendor.php b/src/ReadinessChecker/Vendor.php index 08aaa736e7a1194ec5c64f243e00e5fad8ab5bd5..3529b72c0e7ee039d4aa639f255f1c14f4c52f0b 100644 --- a/src/ReadinessChecker/Vendor.php +++ b/src/ReadinessChecker/Vendor.php @@ -11,7 +11,7 @@ class Vendor extends Filesystem { * {@inheritdoc} */ protected function doCheck() { - if (!$this->exists($this->getVendorPath() . DIRECTORY_SEPARATOR . 'autoload.php')) { + if (!file_exists($this->getVendorPath() . DIRECTORY_SEPARATOR . 'autoload.php')) { return [$this->t('The vendor folder could not be located.')]; } return []; diff --git a/tests/src/Kernel/ReadinessChecker/ReadOnlyFilesystemTest.php b/tests/src/Kernel/ReadinessChecker/ReadOnlyFilesystemTest.php new file mode 100644 index 0000000000000000000000000000000000000000..acff7f82a2c7f712cec0a956f3c95143e68b41bb --- /dev/null +++ b/tests/src/Kernel/ReadinessChecker/ReadOnlyFilesystemTest.php @@ -0,0 +1,216 @@ +<?php + +namespace Drupal\Tests\automatic_updates\Kernel\ReadinessChecker; + +use Drupal\automatic_updates\ReadinessChecker\ReadOnlyFilesystem; +use Drupal\Core\File\Exception\FileException; +use Drupal\Core\File\FileSystemInterface; +use Drupal\KernelTests\KernelTestBase; +use org\bovigo\vfs\vfsStream; +use Psr\Log\LoggerInterface; + +/** + * @coversDefaultClass \Drupal\automatic_updates\ReadinessChecker\ReadOnlyFilesystem + * + * @group automatic_updates + */ +class ReadOnlyFilesystemTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + public static $modules = [ + 'automatic_updates', + 'system', + ]; + + /** + * Tests the readiness check where the root directory does not exist. + * + * @covers ::run + * + * @dataProvider providerNoWebRoot + */ + public function testNoWebRoot($files) { + vfsStream::setup('root'); + vfsStream::create($files); + $readOnly = new ReadOnlyFilesystem( + vfsStream::url('root'), + $this->prophesize(LoggerInterface::class)->reveal(), + $this->prophesize(FileSystemInterface::class)->reveal() + ); + $this->assertEquals(['The web root could not be located.'], $readOnly->run()); + } + + /** + * Data provider for testNoWebRoot(). + */ + public function providerNoWebRoot() { + return [ + 'no core.api.php' => [ + [ + 'core' => [ + 'core.txt' => 'test', + ], + ], + ], + 'core.api.php in wrong location' => [ + [ + 'core.api.php' => 'test', + ], + ], + + ]; + } + + /** + * Tests the readiness check on writable file system on same logic disk. + * + * @covers ::run + */ + public function testSameLogicDiskWritable() { + $readOnly = new ReadOnlyFilesystem( + self::getVfsRoot(), + $this->container->get('logger.channel.automatic_updates'), + $this->container->get('file_system') + ); + $this->assertEquals([], $readOnly->run()); + } + + /** + * Tests root and vendor directories are writable on different logical disks. + * + * @covers ::run + */ + public function testDifferentLogicDiskWritable() { + $readOnly = new TestReadOnlyFilesystem( + self::getVfsRoot(), + $this->container->get('logger.channel.automatic_updates'), + $this->container->get('file_system') + ); + $this->assertEquals([], $readOnly->run()); + } + + /** + * Tests non-writable core and vendor directories on same logic disk. + * + * @covers ::run + */ + public function testSameLogicDiskNotWritable() { + $file_system = $this->createMock(FileSystemInterface::class); + $file_system->expects($this->once()) + ->method('copy') + ->willThrowException(new FileException()); + + $root = self::getVfsRoot(); + $readOnly = new ReadOnlyFilesystem( + $root, + $this->container->get('logger.channel.automatic_updates'), + $file_system + ); + $this->assertEquals(["Logical disk at \"$root\" is read only. Updates to Drupal cannot be applied against a read only file system."], $readOnly->run()); + } + + /** + * Tests the readiness check on read-only file system. + * + * @covers ::run + */ + public function testDifferentLogicDiskNotWritable() { + $root = self::getVfsRoot(); + + // Assert messages if both core and vendor directory are not writable. + $file_system = $this->createMock(FileSystemInterface::class); + $file_system->expects($this->any()) + ->method('copy') + ->willThrowException(new FileException()); + $readOnly = new TestReadOnlyFileSystem( + $root, + $this->container->get('logger.channel.automatic_updates'), + $file_system + ); + $this->assertEquals( + [ + "Drupal core filesystem at \"$root/core\" is read only. Updates to Drupal core cannot be applied against a read only file system.", + "Vendor filesystem at \"$root/vendor\" is read only. Updates to the site's code base cannot be applied against a read only file system.", + ], + $readOnly->run() + ); + + // Assert messages if core directory is not writable. + $file_system = $this->createMock(FileSystemInterface::class); + $file_system + ->method('copy') + ->withConsecutive( + ['vfs://root/core/core.api.php', 'vfs://root/core/core.api.php.automatic_updates'], + ['vfs://root/vendor/composer/LICENSE', 'vfs://root/vendor/composer/LICENSE.automatic_updates'] + ) + ->willReturnOnConsecutiveCalls(FALSE, TRUE); + $readOnly = new TestReadOnlyFileSystem( + $root, + $this->container->get('logger.channel.automatic_updates'), + $file_system + ); + $this->assertEquals( + ["Drupal core filesystem at \"$root/core\" is read only. Updates to Drupal core cannot be applied against a read only file system."], + $readOnly->run() + ); + + // Assert messages if vendor directory is not writable. + $file_system = $this->createMock(FileSystemInterface::class); + $file_system + ->method('copy') + ->withConsecutive( + ['vfs://root/core/core.api.php', 'vfs://root/core/core.api.php.automatic_updates'], + ['vfs://root/vendor/composer/LICENSE', 'vfs://root/vendor/composer/LICENSE.automatic_updates'] + ) + ->willReturnOnConsecutiveCalls(TRUE, FALSE); + $readOnly = new TestReadOnlyFileSystem( + $root, + $this->container->get('logger.channel.automatic_updates'), + $file_system + ); + $this->assertEquals( + ["Vendor filesystem at \"$root/vendor\" is read only. Updates to the site's code base cannot be applied against a read only file system."], + $readOnly->run() + ); + } + + /** + * Gets root of virtual Drupal directory. + * + * @return string + * The root. + */ + protected static function getVfsRoot() { + vfsStream::setup('root'); + vfsStream::create([ + 'core' => [ + 'core.api.php' => 'test', + ], + 'vendor' => [ + 'composer' => [ + 'LICENSE' => 'test', + ], + ], + ]); + return vfsStream::url('root'); + } + +} + +/** + * Test class to root and vendor directories in different logic disks. + * + * Calls to stat() does not work on \org\bovigo\vfs\vfsStream. + */ +class TestReadOnlyFileSystem extends ReadOnlyFilesystem { + + /** + * {@inheritdoc} + */ + protected function areSameLogicalDisk($root, $vendor) { + return FALSE; + } + +} diff --git a/tests/src/Kernel/ReadinessChecker/ReadOnlyTest.php b/tests/src/Kernel/ReadinessChecker/ReadOnlyTest.php deleted file mode 100644 index 567a1f63c39d8165bc551a33ba319e3b4beb747b..0000000000000000000000000000000000000000 --- a/tests/src/Kernel/ReadinessChecker/ReadOnlyTest.php +++ /dev/null @@ -1,115 +0,0 @@ -<?php - -namespace Drupal\Tests\automatic_updates\Kernel\ReadinessChecker; - -use Drupal\automatic_updates\ReadinessChecker\ReadOnlyFilesystem; -use Drupal\Core\File\Exception\FileException; -use Drupal\Core\File\Exception\FileWriteException; -use Drupal\Core\File\FileSystemInterface; -use Drupal\Core\StringTranslation\StringTranslationTrait; -use Drupal\KernelTests\KernelTestBase; -use Psr\Log\LoggerInterface; - -/** - * Tests read only readiness checking. - * - * @group automatic_updates - */ -class ReadOnlyTest extends KernelTestBase { - use StringTranslationTrait; - - /** - * {@inheritdoc} - */ - public static $modules = [ - 'automatic_updates', - 'system', - ]; - - /** - * Tests the functionality of read only filesystem readiness checks. - */ - public function testReadOnly() { - $messages = $filesystem = $this->container->get('automatic_updates.readonly_checker')->run(); - $this->assertEmpty($messages); - - $filesystem = $this->createMock(FileSystemInterface::class); - $filesystem - ->method('copy') - ->withAnyParameters() - ->will($this->onConsecutiveCalls( - $this->throwException(new FileWriteException('core.api.php')), - $this->throwException(new FileWriteException('core.api.php')), - $this->throwException(new FileWriteException('composer/LICENSE')), - 'full/file/path', - 'full/file/path' - ) - ); - $filesystem - ->method('delete') - ->withAnyParameters() - ->will($this->onConsecutiveCalls( - $this->throwException(new FileException('delete failed.')), - $this->throwException(new FileException('delete failed.')) - ) - ); - - $app_root = $this->container->get('app.root'); - $readonly = $this->getMockBuilder(ReadOnlyFilesystem::class) - ->setConstructorArgs([ - $app_root, - $this->createMock(LoggerInterface::class), - $filesystem, - ]) - ->setMethods([ - 'areSameLogicalDisk', - 'exists', - ]) - ->getMock(); - $readonly - ->method('areSameLogicalDisk') - ->withAnyParameters() - ->will($this->onConsecutiveCalls( - TRUE, - FALSE, - FALSE - ) - ); - $readonly - ->method('exists') - ->withAnyParameters() - ->will($this->onConsecutiveCalls( - FALSE, - TRUE, - TRUE, - TRUE - ) - ); - - // Test can't locate drupal. - $messages = $readonly->run(); - self::assertEquals([$this->t('The web root could not be located.')], $messages); - - // Test same logical disk. - $expected_messages = []; - $expected_messages[] = $this->t('Logical disk at "@app_root" is read only. Updates to Drupal cannot be applied against a read only file system.', ['@app_root' => $app_root]); - $messages = $readonly->run(); - self::assertEquals($expected_messages, $messages); - - // Test read-only. - $expected_messages = []; - $expected_messages[] = $this->t('Drupal core filesystem at "@core" is read only. Updates to Drupal core cannot be applied against a read only file system.', [ - '@core' => $app_root . DIRECTORY_SEPARATOR . 'core', - ]); - $expected_messages[] = $this->t('Vendor filesystem at "@vendor" is read only. Updates to the site\'s code base cannot be applied against a read only file system.', [ - '@vendor' => $app_root . DIRECTORY_SEPARATOR . 'vendor', - ]); - $messages = $readonly->run(); - self::assertEquals($expected_messages, $messages); - - // Test delete fails. - $messages = $readonly->run(); - self::assertEquals($expected_messages, $messages); - } - -} diff --git a/tests/src/Kernel/ReadinessChecker/VendorTest.php b/tests/src/Kernel/ReadinessChecker/VendorTest.php index e7ab4c5abc41be637c4ade1bd1d1320d9c34f15b..d1a00bbb7715cd1022db40936eced0cfc350fb6c 100644 --- a/tests/src/Kernel/ReadinessChecker/VendorTest.php +++ b/tests/src/Kernel/ReadinessChecker/VendorTest.php @@ -5,6 +5,7 @@ namespace Drupal\Tests\automatic_updates\Kernel\ReadinessChecker; use Drupal\automatic_updates\ReadinessChecker\Vendor; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\KernelTests\KernelTestBase; +use org\bovigo\vfs\vfsStream; /** * Tests locating vendor folder. @@ -28,24 +29,15 @@ class VendorTest extends KernelTestBase { $vendor = $this->container->get('automatic_updates.vendor'); $this->assertEmpty($vendor->run()); - $missing_vendor = $this->getMockBuilder(Vendor::class) - ->setConstructorArgs([ - $this->container->get('app.root'), - ]) - ->setMethods([ - 'exists', - ]) - ->getMock(); - $missing_vendor - ->method('exists') - ->withAnyParameters() - ->will($this->onConsecutiveCalls( - TRUE, - FALSE - ) - ); - $expected_messages = []; - $expected_messages[] = $this->t('The vendor folder could not be located.'); + vfsStream::setup('root'); + vfsStream::create([ + 'core' => [ + 'core.api.php' => 'test', + ], + ]); + $missing_vendor = new Vendor(vfsStream::url('root')); + $this->assertEquals([], $vendor->run()); + $expected_messages = [$this->t('The vendor folder could not be located.')]; self::assertEquals($expected_messages, $missing_vendor->run()); }