diff --git a/package_manager/tests/src/Functional/ExcludedPathsTest.php b/package_manager/tests/src/Functional/ExcludedPathsTest.php deleted file mode 100644 index 184c36206eea67165596b009bba1a8873c6bb44c..0000000000000000000000000000000000000000 --- a/package_manager/tests/src/Functional/ExcludedPathsTest.php +++ /dev/null @@ -1,161 +0,0 @@ -<?php - -namespace Drupal\Tests\package_manager\Functional; - -use Drupal\Core\Database\Connection; -use Drupal\Core\Site\Settings; -use Drupal\package_manager\PathLocator; -use Drupal\package_manager\Stage; -use Drupal\Tests\BrowserTestBase; - -/** - * @covers \Drupal\package_manager\EventSubscriber\ExcludedPathsSubscriber - * - * @group package_manager - */ -class ExcludedPathsTest extends BrowserTestBase { - - /** - * {@inheritdoc} - */ - protected $defaultTheme = 'stark'; - - /** - * {@inheritdoc} - */ - protected static $modules = [ - 'package_manager', - 'package_manager_bypass', - ]; - - /** - * {@inheritdoc} - */ - protected function prepareSettings() { - parent::prepareSettings(); - - // Disable the filesystem permissions validator, since we cannot guarantee - // that the current code base will be writable in all testing situations. - // We test this validator functionally in Automatic Updates' build tests, - // since those do give us control over the filesystem permissions. - // @see \Drupal\Tests\automatic_updates\Build\CoreUpdateTest::assertReadOnlyFileSystemError() - // @see \Drupal\Tests\package_manager\Kernel\WritableFileSystemValidatorTest - $this->writeSettings([ - 'settings' => [ - 'package_manager_bypass_stager' => (object) [ - 'value' => FALSE, - 'required' => TRUE, - ], - 'package_manager_bypass_validators' => (object) [ - 'value' => ['package_manager.validator.file_system'], - 'required' => TRUE, - ], - ], - ]); - } - - /** - * Tests that certain paths are excluded from staging areas. - */ - public function testExcludedPaths(): void { - $active_dir = __DIR__ . '/../../fixtures/fake_site'; - - $path_locator = $this->prophesize(PathLocator::class); - $path_locator->getActiveDirectory()->willReturn($active_dir); - - $site_path = 'sites/example.com'; - - // Ensure that we are using directories within the fake site fixture for - // public and private files. - $settings = Settings::getAll(); - $settings['file_public_path'] = "$site_path/files"; - $settings['file_private_path'] = 'private'; - new Settings($settings); - - /** @var \Drupal\package_manager\EventSubscriber\ExcludedPathsSubscriber $subscriber */ - $subscriber = $this->container->get('package_manager.excluded_paths_subscriber'); - $reflector = new \ReflectionObject($subscriber); - $property = $reflector->getProperty('sitePath'); - $property->setAccessible(TRUE); - $property->setValue($subscriber, $site_path); - - // Mock a SQLite database connection to a file in the active directory. The - // file should not be staged. - $database = $this->prophesize(Connection::class); - $database->driver()->willReturn('sqlite'); - $database->getConnectionOptions()->willReturn([ - 'database' => $site_path . '/db.sqlite', - ]); - $property = $reflector->getProperty('database'); - $property->setAccessible(TRUE); - $property->setValue($subscriber, $database->reveal()); - - $stage = new class( - $path_locator->reveal(), - $this->container->get('package_manager.beginner'), - $this->container->get('package_manager.stager'), - $this->container->get('package_manager.committer'), - $this->container->get('file_system'), - $this->container->get('event_dispatcher'), - $this->container->get('tempstore.shared'), - ) extends Stage { - - /** - * The directory where staging areas will be created. - * - * @var string - */ - public static $stagingRoot; - - /** - * {@inheritdoc} - */ - protected static function getStagingRoot(): string { - return static::$stagingRoot; - } - - }; - $stage::$stagingRoot = $this->siteDirectory . '/stage'; - $stage_dir = $stage::$stagingRoot . DIRECTORY_SEPARATOR . $stage->create(); - $this->assertDirectoryExists($stage_dir); - - $ignore = [ - 'sites/simpletest', - 'vendor/.htaccess', - 'vendor/web.config', - "$site_path/files/ignore.txt", - 'private/ignore.txt', - "$site_path/settings.php", - "$site_path/settings.local.php", - "$site_path/services.yml", - // SQLite databases and their support files should always be ignored. - "$site_path/db.sqlite", - "$site_path/db.sqlite-shm", - "$site_path/db.sqlite-wal", - // Default site-specific settings files should be ignored. - 'sites/default/settings.php', - 'sites/default/settings.local.php', - 'sites/default/services.yml', - ]; - foreach ($ignore as $path) { - $this->assertFileExists("$active_dir/$path"); - $this->assertFileDoesNotExist("$stage_dir/$path"); - } - // A non-excluded file in the default site directory should be staged. - $this->assertFileExists("$stage_dir/sites/default/stage.txt"); - - // A new file added to the staging area in an excluded directory, should not - // be copied to the active directory. - $file = "$stage_dir/sites/default/no-copy.txt"; - touch($file); - $this->assertFileExists($file); - $stage->apply(); - $this->assertFileDoesNotExist("$active_dir/sites/default/no-copy.txt"); - - // The ignored files should still be in the active directory. - foreach ($ignore as $path) { - $this->assertFileExists("$active_dir/$path"); - } - } - -} diff --git a/package_manager/tests/src/Kernel/ExcludedPathsTest.php b/package_manager/tests/src/Kernel/ExcludedPathsTest.php new file mode 100644 index 0000000000000000000000000000000000000000..b9a8eadbe36a68e429f99ef60562c5b48319d4e7 --- /dev/null +++ b/package_manager/tests/src/Kernel/ExcludedPathsTest.php @@ -0,0 +1,227 @@ +<?php + +namespace Drupal\Tests\package_manager\Kernel; + +use Drupal\Core\Database\Connection; +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\package_manager\EventSubscriber\ExcludedPathsSubscriber; +use Drupal\package_manager\PathLocator; +use org\bovigo\vfs\vfsStream; + +/** + * @covers \Drupal\package_manager\EventSubscriber\ExcludedPathsSubscriber + * + * @group package_manager + */ +class ExcludedPathsTest extends PackageManagerKernelTestBase { + + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + + // Ensure that any staging directories created by TestStage are created + // in the virtual file system. + TestStage::$stagingRoot = $this->vfsRoot->url(); + + // We need to rebuild the container after setting a private file path, since + // the private stream wrapper is only registered if this setting is set. + // @see \Drupal\Core\CoreServiceProvider::register() + $this->setSetting('file_private_path', 'private'); + $kernel = $this->container->get('kernel'); + $kernel->rebuildContainer(); + $this->container = $kernel->getContainer(); + } + + /** + * {@inheritdoc} + */ + public function register(ContainerBuilder $container) { + // Normally, package_manager_bypass will disable all the actual staging + // operations. In this case, we want to perform them so that we can be sure + // that files are staged as expected. + $this->setSetting('package_manager_bypass_stager', FALSE); + + $container->getDefinition('package_manager.excluded_paths_subscriber') + ->setClass(TestExcludedPathsSubscriber::class); + + parent::register($container); + } + + /** + * {@inheritdoc} + */ + protected function disableValidators(ContainerBuilder $container): void { + parent::disableValidators($container); + + // Disable the disk space validator, since it tries to inspect the file + // system in ways that vfsStream doesn't support, like calling stat() and + // disk_free_space(). + $container->removeDefinition('package_manager.validator.disk_space'); + + // Disable the lock file and Composer settings validators, since in this + // test we have an imaginary file system without any Composer files. + $container->removeDefinition('package_manager.validator.lock_file'); + } + + /** + * Tests that certain paths are excluded from staging operations. + */ + public function testExcludedPaths(): void { + $site = [ + 'composer.json' => '{}', + 'private' => [ + 'ignore.txt' => 'This file should never be staged.', + ], + 'sites' => [ + 'default' => [ + 'services.yml' => <<<END +# This file should never be staged. +must_not_be: 'empty' +END, + 'settings.local.php' => <<<END +<?php + +/** + * @file + * This file should never be staged. + */ +END, + 'settings.php' => <<<END +<?php + +/** + * @file + * This file should never be staged. + */ +END, + 'stage.txt' => 'This file should be staged.', + ], + 'example.com' => [ + 'files' => [ + 'ignore.txt' => 'This file should never be staged.', + ], + 'db.sqlite' => 'This file should never be staged.', + 'db.sqlite-shm' => 'This file should never be staged.', + 'db.sqlite-wal' => 'This file should never be staged.', + 'services.yml' => <<<END +# This file should never be staged. +key: "value" +END, + 'settings.local.php' => <<<END +<?php + +/** + * @file + * This file should never be staged. + */ +END, + 'settings.php' => <<<END +<?php + +/** + * @file + * This file should never be staged. + */ +END, + ], + 'simpletest' => [ + 'ignore.txt' => 'This file should never be staged.', + ], + ], + 'vendor' => [ + '.htaccess' => '# This file should never be staged.', + 'web.config' => 'This file should never be staged.', + ], + ]; + vfsStream::create(['active' => $site], $this->vfsRoot); + + $active_dir = $this->vfsRoot->getChild('active')->url(); + + $path_locator = $this->prophesize(PathLocator::class); + $path_locator->getActiveDirectory()->willReturn($active_dir); + $path_locator->getProjectRoot()->willReturn($active_dir); + $path_locator->getWebRoot()->willReturn(''); + $path_locator->getVendorDirectory()->willReturn("$active_dir/vendor"); + $this->container->set('package_manager.path_locator', $path_locator->reveal()); + + $site_path = 'sites/example.com'; + // Ensure that we are using directories within the fake site fixture for + // public and private files. + $this->setSetting('file_public_path', "$site_path/files"); + + /** @var \Drupal\Tests\package_manager\Kernel\TestExcludedPathsSubscriber $subscriber */ + $subscriber = $this->container->get('package_manager.excluded_paths_subscriber'); + $subscriber->sitePath = $site_path; + + // Mock a SQLite database connection to a file in the active directory. The + // file should not be staged. + $database = $this->prophesize(Connection::class); + $database->driver()->willReturn('sqlite'); + $database->getConnectionOptions()->willReturn([ + 'database' => $site_path . '/db.sqlite', + ]); + $subscriber->database = $database->reveal(); + + $stage = $this->createStage(); + $stage->create(); + $stage_dir = $stage->getStageDirectory(); + + $ignore = [ + 'sites/simpletest', + 'vendor/.htaccess', + 'vendor/web.config', + "$site_path/files/ignore.txt", + 'private/ignore.txt', + "$site_path/settings.php", + "$site_path/settings.local.php", + "$site_path/services.yml", + // SQLite databases and their support files should always be ignored. + "$site_path/db.sqlite", + "$site_path/db.sqlite-shm", + "$site_path/db.sqlite-wal", + // Default site-specific settings files should be ignored. + 'sites/default/settings.php', + 'sites/default/settings.local.php', + 'sites/default/services.yml', + ]; + foreach ($ignore as $path) { + $this->assertFileExists("$active_dir/$path"); + $this->assertFileDoesNotExist("$stage_dir/$path"); + } + // A non-excluded file in the default site directory should be staged. + $this->assertFileExists("$stage_dir/sites/default/stage.txt"); + + // A new file added to the staging area in an excluded directory, should not + // be copied to the active directory. + $file = "$stage_dir/sites/default/no-copy.txt"; + touch($file); + $this->assertFileExists($file); + $stage->apply(); + $this->assertFileDoesNotExist("$active_dir/sites/default/no-copy.txt"); + + // The ignored files should still be in the active directory. + foreach ($ignore as $path) { + $this->assertFileExists("$active_dir/$path"); + } + } + +} + +/** + * A test-only implementation of the excluded path event subscriber. + */ +class TestExcludedPathsSubscriber extends ExcludedPathsSubscriber { + + /** + * {@inheritdoc} + */ + public $sitePath; + + /** + * {@inheritdoc} + */ + public $database; + +} diff --git a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php index 6a36af152f85dd574aa43086999a65c73ea95f0c..548535b52892244477dbeacce2db838026ef36b9 100644 --- a/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php +++ b/package_manager/tests/src/Kernel/PackageManagerKernelTestBase.php @@ -25,6 +25,14 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase { 'package_manager_bypass', ]; + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + $this->installConfig('package_manager'); + } + /** * {@inheritdoc} */ @@ -118,6 +126,20 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase { */ class TestStage extends Stage { + /** + * The directory where staging areas will be created. + * + * @var string + */ + public static $stagingRoot; + + /** + * {@inheritdoc} + */ + protected static function getStagingRoot(): string { + return static::$stagingRoot ?: parent::getStagingRoot(); + } + /** * {@inheritdoc} */