StagedDBUpdateValidatorTest.php

Namespace

Drupal\Tests\package_manager\Kernel

File

core/modules/package_manager/tests/src/Kernel/StagedDBUpdateValidatorTest.php

View source
<?php

declare (strict_types=1);
namespace Drupal\Tests\package_manager\Kernel;

use Drupal\package_manager\PathLocator;
use Drupal\package_manager\ValidationResult;

/**
 * @covers \Drupal\package_manager\Validator\SandboxDatabaseUpdatesValidator
 * @group package_manager
 * @internal
 */
class StagedDBUpdateValidatorTest extends PackageManagerKernelTestBase {
    
    /**
     * {@inheritdoc}
     */
    protected function setUp() : void {
        parent::setUp();
        $this->container
            ->get('theme_installer')
            ->install([
            'stark',
        ]);
        $this->assertFalse($this->container
            ->get('module_handler')
            ->moduleExists('views'));
        $this->assertFalse($this->container
            ->get('theme_handler')
            ->themeExists('olivero'));
        // Ensure that all the extensions we're testing with have database update
        // files in the active directory.
        $active_dir = $this->container
            ->get(PathLocator::class)
            ->getProjectRoot();
        // System and Stark are installed, so they are used to test what happens
        // when database updates are detected in installed extensions. Views and
        // Olivero are not installed, so they are used to test what happens when
        // non-installed extensions have database updates.
        $extensions = [
            'core/modules/system',
            'core/themes/stark',
            'core/modules/views',
            'core/themes/olivero',
        ];
        foreach ($extensions as $extension_path) {
            $extension_path = $active_dir . '/' . $extension_path;
            mkdir($extension_path, 0777, TRUE);
            $extension_name = basename($extension_path);
            // Ensure each extension has a .install and a .post_update.php file with
            // an empty update function in it.
            foreach ([
                'install',
                'post_update.php',
            ] as $suffix) {
                $function_name = match ($suffix) {    'install' => $extension_name . '_update_1000',
                    'post_update.php' => $extension_name . '_post_update_test',
                
                };
                file_put_contents("{$extension_path}/{$extension_name}.{$suffix}", "<?php\nfunction {$function_name}() {}");
            }
        }
    }
    
    /**
     * Data provider for ::testStagedDatabaseUpdates().
     *
     * @return array[]
     *   The test cases.
     */
    public static function providerStagedDatabaseUpdate() : array {
        $summary = t('Database updates have been detected in the following extensions.');
        return [
            'schema update in installed module' => [
                'core/modules/system',
                'install',
                [
                    ValidationResult::createWarning([
                        t('System'),
                    ], $summary),
                ],
            ],
            'post-update in installed module' => [
                'core/modules/system',
                'post_update.php',
                [
                    ValidationResult::createWarning([
                        t('System'),
                    ], $summary),
                ],
            ],
            'schema update in installed theme' => [
                'core/themes/stark',
                'install',
                [
                    ValidationResult::createWarning([
                        t('Stark'),
                    ], $summary),
                ],
            ],
            'post-update in installed theme' => [
                'core/themes/stark',
                'post_update.php',
                [
                    ValidationResult::createWarning([
                        t('Stark'),
                    ], $summary),
                ],
            ],
            // The validator should ignore changes in any extensions that aren't
            // installed.
'schema update in non-installed module' => [
                'core/modules/views',
                'install',
                [],
            ],
            'post-update in non-installed module' => [
                'core/modules/views',
                'post_update.php',
                [],
            ],
            'schema update in non-installed theme' => [
                'core/themes/olivero',
                'install',
                [],
            ],
            'post-update in non-installed theme' => [
                'core/themes/olivero',
                'post_update.php',
                [],
            ],
        ];
    }
    
    /**
     * Tests validation of staged database updates.
     *
     * @param string $extension_dir
     *   The directory of the extension that should have database updates,
     *   relative to the stage directory.
     * @param string $file_extension
     *   The extension of the update file, without the leading period. Must be
     *   either `install` or `post_update.php`.
     * @param \Drupal\package_manager\ValidationResult[] $expected_results
     *   The expected validation results.
     *
     * @dataProvider providerStagedDatabaseUpdate
     */
    public function testStagedDatabaseUpdate(string $extension_dir, string $file_extension, array $expected_results) : void {
        $extension_name = basename($extension_dir);
        $relative_file_path = $extension_dir . '/' . $extension_name . '.' . $file_extension;
        $stage = $this->createStage();
        $stage->create();
        // Nothing has been changed in the stage, so ensure the validator doesn't
        // detect any changes.
        $this->assertStatusCheckResults([], $stage);
        $staged_update_file = $stage->getSandboxDirectory() . '/' . $relative_file_path;
        $this->assertFileIsWritable($staged_update_file);
        // Now add a "real" update function -- either a schema update or a
        // post-update, depending on what $file_extension is -- and ensure that the
        // validator detects it.
        $update_function_name = match ($file_extension) {    'install' => $extension_name . '_update_1001',
            'post_update.php' => $extension_name . '_post_update_' . $this->randomMachineName(),
        
        };
        file_put_contents($staged_update_file, "function {$update_function_name}() {}\n", FILE_APPEND);
        $this->assertStatusCheckResults($expected_results, $stage);
        // Add a bunch of functions which are named similarly to real schema update
        // and post-update functions, but not quite right, to ensure they are
        // ignored by the validator. Also throw an anonymous function in there to
        // ensure those are ignored as well.
        $code = <<<END
<?php
function {<span class="php-variable">$extension_name</span>}_update() { \$foo = function () {}; }
function {<span class="php-variable">$extension_name</span>}_update_string_123() {}
function {<span class="php-variable">$extension_name</span>}_update__123() {}
function ({<span class="php-variable">$extension_name</span>}}__post_update_test() {}
function ({<span class="php-variable">$extension_name</span>}}_post_update() {}
END;
        file_put_contents($staged_update_file, $code);
        $this->assertStatusCheckResults([], $stage);
        // If the update file is deleted from the stage, the validator should not
        // detect any database updates.
        unlink($staged_update_file);
        $this->assertStatusCheckResults([], $stage);
        // If the update file doesn't exist in the active directory, but does exist
        // in the stage with a legitimate schema update or post-update function, the
        // validator should detect it.
        $project_root = $this->container
            ->get(PathLocator::class)
            ->getProjectRoot();
        unlink($project_root . '/' . $relative_file_path);
        file_put_contents($staged_update_file, "<?php\nfunction {$update_function_name}() {}");
        $this->assertStatusCheckResults($expected_results, $stage);
    }
    
    /**
     * Tests that the validator disregards unclaimed stages.
     */
    public function testUnclaimedStage() : void {
        $stage = $this->createStage();
        $stage->create();
        $this->assertStatusCheckResults([], $stage);
        // A new, unclaimed stage should be ignored by the validator.
        $this->assertStatusCheckResults([], $this->createStage());
    }

}

Classes

Title Deprecated Summary
StagedDBUpdateValidatorTest @covers \Drupal\package_manager\Validator\SandboxDatabaseUpdatesValidator @group package_manager @internal

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.