class WritableFileSystemValidatorTest

Unit tests the file system permissions validator.

This validator is tested functionally in Automatic Updates' build tests, since those give us control over the file system permissions.

@covers \Drupal\package_manager\Validator\WritableFileSystemValidator @group package_manager @internal

Hierarchy

Expanded class hierarchy of WritableFileSystemValidatorTest

See also

\Drupal\Tests\auto_updates\Build\CoreUpdateTest::assertReadOnlyFileSystemError()

File

core/modules/package_manager/tests/src/Kernel/WritableFileSystemValidatorTest.php, line 25

Namespace

Drupal\Tests\package_manager\Kernel
View source
class WritableFileSystemValidatorTest extends PackageManagerKernelTestBase {
    
    /**
     * Data provider for testWritable().
     *
     * @return mixed[][]
     *   The test cases.
     */
    public static function providerWritable() : array {
        // @see \Drupal\Tests\package_manager\Traits\ValidationTestTrait::resolvePlaceholdersInArrayValuesWithRealPaths()
        $drupal_root_error = t('The Drupal directory "<PROJECT_ROOT>/web" is not writable.');
        $vendor_error = t('The vendor directory "<VENDOR_DIR>" is not writable.');
        $project_root_error = t('The project root directory "<PROJECT_ROOT>" is not writable.');
        $summary = t('The file system is not writable.');
        $writable_permission = 0777;
        $non_writable_permission = 0550;
        return [
            'root and vendor are writable, nested web root' => [
                $writable_permission,
                $writable_permission,
                $writable_permission,
                'web',
                [],
            ],
            'root writable, vendor not writable, nested web root' => [
                $writable_permission,
                $writable_permission,
                $non_writable_permission,
                'web',
                [
                    ValidationResult::createError([
                        $vendor_error,
                    ], $summary),
                ],
            ],
            'root not writable, vendor writable, nested web root' => [
                $non_writable_permission,
                $non_writable_permission,
                $writable_permission,
                'web',
                [
                    ValidationResult::createError([
                        $drupal_root_error,
                        $project_root_error,
                    ], $summary),
                ],
            ],
            'nothing writable, nested web root' => [
                $non_writable_permission,
                $non_writable_permission,
                $non_writable_permission,
                'web',
                [
                    ValidationResult::createError([
                        $drupal_root_error,
                        $project_root_error,
                        $vendor_error,
                    ], $summary),
                ],
            ],
            'root and vendor are writable, non-nested web root' => [
                $writable_permission,
                $writable_permission,
                $writable_permission,
                '',
                [],
            ],
            'root writable, vendor not writable, non-nested web root' => [
                $writable_permission,
                $writable_permission,
                $non_writable_permission,
                '',
                [
                    ValidationResult::createError([
                        $vendor_error,
                    ], $summary),
                ],
            ],
            'root not writable, vendor writable, non-nested web root' => [
                $non_writable_permission,
                $non_writable_permission,
                $writable_permission,
                '',
                [
                    ValidationResult::createError([
                        $project_root_error,
                    ], $summary),
                ],
            ],
            'nothing writable, non-nested web root' => [
                $non_writable_permission,
                $non_writable_permission,
                $non_writable_permission,
                '',
                [
                    ValidationResult::createError([
                        $project_root_error,
                        $vendor_error,
                    ], $summary),
                ],
            ],
        ];
    }
    
    /**
     * Tests the file system permissions validator.
     *
     * @param int $root_permissions
     *   The file permissions for the root folder.
     * @param int $webroot_permissions
     *   The file permissions for the web root folder.
     * @param int $vendor_permissions
     *   The file permissions for the vendor folder.
     * @param string $webroot_relative_directory
     *   The web root path, relative to the project root, or an empty string if
     *   the web root and project root are the same.
     * @param \Drupal\package_manager\ValidationResult[] $expected_results
     *   The expected validation results.
     *
     * @dataProvider providerWritable
     */
    public function testWritable(int $root_permissions, int $webroot_permissions, int $vendor_permissions, string $webroot_relative_directory, array $expected_results) : void {
        $this->setUpPermissions($root_permissions, $webroot_permissions, $vendor_permissions, $webroot_relative_directory);
        $this->assertStatusCheckResults($expected_results);
        $this->assertResults($expected_results, PreCreateEvent::class);
    }
    
    /**
     * Tests the file system permissions validator during pre-apply.
     *
     * @param int $root_permissions
     *   The file permissions for the root folder.
     * @param int $webroot_permissions
     *   The file permissions for the web root folder.
     * @param int $vendor_permissions
     *   The file permissions for the vendor folder.
     * @param string $webroot_relative_directory
     *   The web root path, relative to the project root, or an empty string if
     *   the web root and project root are the same.
     * @param \Drupal\package_manager\ValidationResult[] $expected_results
     *   The expected validation results.
     *
     * @dataProvider providerWritable
     */
    public function testWritableDuringPreApply(int $root_permissions, int $webroot_permissions, int $vendor_permissions, string $webroot_relative_directory, array $expected_results) : void {
        $this->addEventTestListener(function () use ($webroot_permissions, $root_permissions, $vendor_permissions, $webroot_relative_directory) : void {
            $this->setUpPermissions($root_permissions, $webroot_permissions, $vendor_permissions, $webroot_relative_directory);
            // During pre-apply we don't care whether the staging root is writable.
            
            /** @var \Drupal\package_manager_bypass\MockPathLocator $path_locator */
            $path_locator = $this->container
                ->get(PathLocator::class);
            $this->assertTrue(chmod($path_locator->getStagingRoot(), 0550));
        });
        $this->assertResults($expected_results, PreApplyEvent::class);
    }
    
    /**
     * Sets the permissions of the test project's directories.
     *
     * @param int $root_permissions
     *   The permissions for the project root.
     * @param int $web_root_permissions
     *   The permissions for the web root.
     * @param int $vendor_permissions
     *   The permissions for the vendor directory.
     * @param string $relative_web_root
     *   The web root path, relative to the project root, or an empty string if
     *   the web root and project root are the same.
     */
    private function setUpPermissions(int $root_permissions, int $web_root_permissions, int $vendor_permissions, string $relative_web_root) : void {
        
        /** @var \Drupal\package_manager_bypass\MockPathLocator $path_locator */
        $path_locator = $this->container
            ->get(PathLocator::class);
        $project_root = $web_root = $path_locator->getProjectRoot();
        $vendor_dir = $path_locator->getVendorDirectory();
        // Create the web root directory, if necessary.
        if (!empty($relative_web_root)) {
            $web_root .= '/' . $relative_web_root;
            mkdir($web_root);
        }
        $path_locator->setPaths($project_root, $vendor_dir, $relative_web_root, $path_locator->getStagingRoot());
        // We need to set the vendor directory and web root permissions first
        // because they may be located inside the project root.
        $this->assertTrue(chmod($vendor_dir, $vendor_permissions));
        if ($project_root !== $web_root) {
            $this->assertTrue(chmod($web_root, $web_root_permissions));
        }
        $this->assertTrue(chmod($project_root, $root_permissions));
    }
    
    /**
     * Data provider for ::testStagingRootPermissions().
     *
     * @return mixed[][]
     *   The test cases.
     */
    public static function providerStagingRootPermissions() : array {
        $writable_permission = 0777;
        $non_writable_permission = 0550;
        $summary = t('The file system is not writable.');
        return [
            'writable stage root exists' => [
                $writable_permission,
                [],
                FALSE,
            ],
            'write-protected stage root exists' => [
                $non_writable_permission,
                [
                    ValidationResult::createError([
                        t('The stage root directory "<STAGE_ROOT>" is not writable.'),
                    ], $summary),
                ],
                FALSE,
            ],
            'stage root directory does not exist, parent directory not writable' => [
                $non_writable_permission,
                [
                    ValidationResult::createError([
                        t('The stage root directory will not able to be created at "<STAGE_ROOT_PARENT>".'),
                    ], $summary),
                ],
                TRUE,
            ],
        ];
    }
    
    /**
     * Tests that the stage root's permissions are validated.
     *
     * @param int $permissions
     *   The file permissions to apply to the stage root directory, or its parent
     *   directory, depending on the value of $delete_staging_root.
     * @param array $expected_results
     *   The expected validation results.
     * @param bool $delete_staging_root
     *   Whether the stage root directory will exist at all.
     *
     * @dataProvider providerStagingRootPermissions
     */
    public function testStagingRootPermissions(int $permissions, array $expected_results, bool $delete_staging_root) : void {
        $dir = $this->container
            ->get(PathLocator::class)
            ->getStagingRoot();
        if ($delete_staging_root) {
            $fs = new Filesystem();
            $fs->remove($dir);
            $dir = dirname($dir);
        }
        $this->assertTrue(chmod($dir, $permissions));
        $this->assertStatusCheckResults($expected_results);
        $this->assertResults($expected_results, PreCreateEvent::class);
    }

}

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