Skip to content
Snippets Groups Projects
Commit 41d71503 authored by Adam G-H's avatar Adam G-H
Browse files

Issue #3226570 by phenaproxima, tedbow: Ensure that SQLite databases are...

Issue #3226570 by phenaproxima, tedbow: Ensure that SQLite databases are excluded from the staging area
parent 05cc57cb
No related branches found
No related tags found
1 merge request!13Issue #3226570: Ensure exclusions are working
......@@ -131,5 +131,6 @@ services:
- '%site.path%'
- '@file_system'
- '@stream_wrapper_manager'
- '@database'
tags:
- { name: event_subscriber }
......@@ -2,6 +2,7 @@
namespace Drupal\package_manager\EventSubscriber;
use Drupal\Core\Database\Connection;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\StreamWrapper\LocalStream;
use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
......@@ -43,7 +44,14 @@ class ExcludedPathsSubscriber implements EventSubscriberInterface {
protected $streamWrapperManager;
/**
* Constructs an UpdateSubscriber.
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $database;
/**
* Constructs an ExcludedPathsSubscriber.
*
* @param string $app_root
* The Drupal root.
......@@ -53,12 +61,15 @@ class ExcludedPathsSubscriber implements EventSubscriberInterface {
* The file system service.
* @param \Drupal\Core\StreamWrapper\StreamWrapperManagerInterface $stream_wrapper_manager
* The stream wrapper manager service.
* @param \Drupal\Core\Database\Connection $database
* The database connection.
*/
public function __construct(string $app_root, string $site_path, FileSystemInterface $file_system, StreamWrapperManagerInterface $stream_wrapper_manager) {
public function __construct(string $app_root, string $site_path, FileSystemInterface $file_system, StreamWrapperManagerInterface $stream_wrapper_manager, Connection $database) {
$this->appRoot = $app_root;
$this->sitePath = $site_path;
$this->fileSystem = $file_system;
$this->streamWrapperManager = $stream_wrapper_manager;
$this->database = $database;
}
/**
......@@ -113,6 +124,17 @@ class ExcludedPathsSubscriber implements EventSubscriberInterface {
$event->excludePath($this->sitePath . DIRECTORY_SEPARATOR . $settings_file);
$event->excludePath($default_site . DIRECTORY_SEPARATOR . $settings_file);
}
// If the database is SQLite, it might be located in the active directory
// and we should not stage it.
if ($this->database->driver() === 'sqlite') {
$options = $this->database->getConnectionOptions();
$database = str_replace($this->appRoot, NULL, $options['database']);
$database = ltrim($database, '/');
$event->excludePath($database);
$event->excludePath("$database-shm");
$event->excludePath("$database-wal");
}
}
/**
......
This file should never be staged.
This file should never be staged.
This file should never be staged.
......@@ -2,6 +2,7 @@
namespace Drupal\Tests\package_manager\Functional;
use Drupal\Core\Database\Driver\sqlite\Connection;
use Drupal\Core\Site\Settings;
use Drupal\package_manager\PathLocator;
use Drupal\package_manager\Stage;
......@@ -64,10 +65,12 @@ class ExcludedPathsTest extends BrowserTestBase {
$path_locator->getActiveDirectory()->willReturn($active_dir);
$path_locator->getStageDirectory()->willReturn($stage_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'] = 'sites/example.com/files';
$settings['file_public_path'] = "$site_path/files";
$settings['file_private_path'] = 'private';
new Settings($settings);
......@@ -76,7 +79,18 @@ class ExcludedPathsTest extends BrowserTestBase {
$reflector = new \ReflectionObject($subscriber);
$property = $reflector->getProperty('sitePath');
$property->setAccessible(TRUE);
$property->setValue($subscriber, 'sites/example.com');
$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 Stage(
$path_locator->reveal(),
......@@ -91,11 +105,16 @@ class ExcludedPathsTest extends BrowserTestBase {
$this->assertDirectoryExists($stage_dir);
$this->assertDirectoryNotExists("$stage_dir/sites/simpletest");
$this->assertFileNotExists("$stage_dir/vendor/web.config");
$this->assertDirectoryNotExists("$stage_dir/sites/example.com/files");
$this->assertDirectoryNotExists("$stage_dir/$site_path/files");
$this->assertDirectoryNotExists("$stage_dir/private");
$this->assertFileNotExists("$stage_dir/sites/example.com/settings.php");
$this->assertFileNotExists("$stage_dir/sites/example.com/settings.local.php");
$this->assertFileNotExists("$stage_dir/sites/example.com/services.yml");
$this->assertFileNotExists("$stage_dir/$site_path/settings.php");
$this->assertFileNotExists("$stage_dir/$site_path/settings.local.php");
$this->assertFileNotExists("$stage_dir/$site_path/services.yml");
// SQLite databases and their support files should never be staged.
$this->assertFileNotExists("$stage_dir/$site_path/db.sqlite");
$this->assertFileNotExists("$stage_dir/$site_path/db.sqlite-shm");
$this->assertFileNotExists("$stage_dir/$site_path/db.sqlite-wal");
// Default site-specific settings files should never be staged.
$this->assertFileNotExists("$stage_dir/sites/default/settings.php");
$this->assertFileNotExists("$stage_dir/sites/default/settings.local.php");
$this->assertFileNotExists("$stage_dir/sites/default/services.yml");
......
<?php
namespace Drupal\Tests\package_manager\Kernel;
use Drupal\Core\Database\Driver\sqlite\Connection;
use Drupal\package_manager\Event\PreCreateEvent;
use Drupal\package_manager\EventSubscriber\ExcludedPathsSubscriber;
/**
* @covers \Drupal\package_manager\EventSubscriber\ExcludedPathsSubscriber
*
* @group package_manager
*/
class ExcludedPathsSubscriberTest extends PackageManagerKernelTestBase {
/**
* Data provider for ::testSqliteDatabaseExcluded().
*
* @return array[]
* Sets of arguments to pass to the test method.
*/
public function providerSqliteDatabaseExcluded(): array {
$drupal_root = $this->getDrupalRoot();
return [
'relative path, in site directory' => [
'sites/example.com/db.sqlite',
[
'sites/example.com/db.sqlite',
'sites/example.com/db.sqlite-shm',
'sites/example.com/db.sqlite-wal',
],
],
'relative path, at root' => [
'db.sqlite',
[
'db.sqlite',
'db.sqlite-shm',
'db.sqlite-wal',
],
],
'absolute path, in site directory' => [
$drupal_root . '/sites/example.com/db.sqlite',
[
'sites/example.com/db.sqlite',
'sites/example.com/db.sqlite-shm',
'sites/example.com/db.sqlite-wal',
],
],
'absolute path, at root' => [
$drupal_root . '/db.sqlite',
[
'db.sqlite',
'db.sqlite-shm',
'db.sqlite-wal',
],
],
];
}
/**
* Tests that SQLite database paths are excluded from the staging area.
*
* The exclusion of SQLite databases from the staging area is functionally
* tested by \Drupal\Tests\package_manager\Functional\ExcludedPathsTest. The
* purpose of this test is to ensure that SQLite database paths are processed
* properly (e.g., converting an absolute path to a relative path) before
* being flagged for exclusion.
*
* @param string $database
* The path of the SQLite database, as set in the database connection
* options.
* @param string[] $expected_exclusions
* The database paths which should be flagged for exclusion.
*
* @dataProvider providerSqliteDatabaseExcluded
*
* @see \Drupal\Tests\package_manager\Functional\ExcludedPathsTest
*/
public function testSqliteDatabaseExcluded(string $database, array $expected_exclusions): void {
$connection = $this->prophesize(Connection::class);
$connection->driver()->willReturn('sqlite');
$connection->getConnectionOptions()->willReturn(['database' => $database]);
$subscriber = new ExcludedPathsSubscriber(
$this->getDrupalRoot(),
'sites/default',
$this->container->get('file_system'),
$this->container->get('stream_wrapper_manager'),
$connection->reveal()
);
$event = new PreCreateEvent($this->createStage());
$subscriber->preCreate($event);
// All of the expected exclusions should be flagged.
$this->assertEmpty(array_diff($expected_exclusions, $event->getExcludedPaths()));
}
}
......@@ -46,16 +46,13 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase {
}
/**
* Asserts validation results are returned from a stage life cycle event.
* Creates a stage object for testing purposes.
*
* @param \Drupal\package_manager\ValidationResult[] $expected_results
* The expected validation results.
* @param string|null $event_class
* (optional) The class of the event which should return the results. Must
* be passed if $expected_results is not empty.
* @return \Drupal\Tests\package_manager\Kernel\TestStage
* A stage object, with test-only modifications.
*/
protected function assertResults(array $expected_results, string $event_class = NULL): void {
$stage = new TestStage(
protected function createStage(): TestStage {
return new TestStage(
$this->container->get('package_manager.path_locator'),
$this->container->get('package_manager.beginner'),
$this->container->get('package_manager.stager'),
......@@ -63,6 +60,19 @@ abstract class PackageManagerKernelTestBase extends KernelTestBase {
$this->container->get('package_manager.cleaner'),
$this->container->get('event_dispatcher'),
);
}
/**
* Asserts validation results are returned from a stage life cycle event.
*
* @param \Drupal\package_manager\ValidationResult[] $expected_results
* The expected validation results.
* @param string|null $event_class
* (optional) The class of the event which should return the results. Must
* be passed if $expected_results is not empty.
*/
protected function assertResults(array $expected_results, string $event_class = NULL): void {
$stage = $this->createStage();
try {
$stage->create();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment