<?php

namespace SilverStripe\Dev\Tests;

use SilverStripe\Dev\Install\MySQLDatabaseConfigurationHelper;
use SilverStripe\Dev\SapphireTest;

class MySQLDatabaseConfigurationHelperTest extends SapphireTest
{

    /**
     * Tests that invalid names are disallowed
     */
    public function testInvalidDatabaseNames()
    {
        $helper = new MySQLDatabaseConfigurationHelper();

        // Reject filename unsafe characters
        $this->assertEmpty($helper->checkValidDatabaseName('database%name'));
        $this->assertEmpty($helper->checkValidDatabaseName('database?name'));
        $this->assertEmpty($helper->checkValidDatabaseName('database|name'));
        $this->assertEmpty($helper->checkValidDatabaseName('database<name'));
        $this->assertEmpty($helper->checkValidDatabaseName('database"name'));

        // Reject additional characters
        $this->assertEmpty($helper->checkValidDatabaseName('database.name'));
        $this->assertEmpty($helper->checkValidDatabaseName('database\name'));
        $this->assertEmpty($helper->checkValidDatabaseName('database/name'));

        // Reject blank
        $this->assertEmpty($helper->checkValidDatabaseName(""));
    }

    /**
     * Tests that valid names are allowed
     */
    public function testValidDatabaseNames()
    {
        $helper = new MySQLDatabaseConfigurationHelper();

        // Names with spaces
        $this->assertNotEmpty($helper->checkValidDatabaseName('database name'));

        // Basic latin characters
        $this->assertNotEmpty($helper->checkValidDatabaseName('database_name'));
        $this->assertNotEmpty($helper->checkValidDatabaseName('UPPERCASE_NAME'));
        $this->assertNotEmpty($helper->checkValidDatabaseName('name_with_numbers_1234'));

        // Extended unicode names
        $this->assertNotEmpty($helper->checkValidDatabaseName('亝亞亟')); // U+4E9D, U+4E9E, U+4E9F
        $this->assertNotEmpty($helper->checkValidDatabaseName('おかが')); // U+304A, U+304B, U+304C
        $this->assertNotEmpty($helper->checkValidDatabaseName('¶»Ã')); // U+00B6, U+00BB, U+00C3
    }

    public function testDatabaseCreateCheck()
    {

        $helper = new MySQLDatabaseConfigurationHelper();

        // Accept all privileges
        $this->assertNotEmpty(
            $helper->checkDatabasePermissionGrant(
                'database_name',
                'create',
                "GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY PASSWORD 'XXXX' WITH GRANT OPTION"
            )
        );

        // Accept create (mysql syntax)
        $this->assertNotEmpty(
            $helper->checkDatabasePermissionGrant(
                'database_name',
                'create',
                "GRANT CREATE, SELECT ON *.* TO 'root'@'localhost' IDENTIFIED BY PASSWORD 'XXXX' WITH GRANT OPTION"
            )
        );

        // Accept create on this database only
        $this->assertNotEmpty(
            $helper->checkDatabasePermissionGrant(
                'database_name',
                'create',
                "GRANT ALL PRIVILEGES, CREATE ON \"database_name\".* TO 'root'@'localhost' IDENTIFIED BY PASSWORD 'XXXX'"
                . " WITH GRANT OPTION"
            )
        );

        // Accept create on this database only
        $this->assertNotEmpty(
            $helper->checkDatabasePermissionGrant(
                'database_name',
                'create',
                "GRANT ALL PRIVILEGES, CREATE ON \"database\\_name\".* TO 'root'@'localhost' IDENTIFIED BY PASSWORD 'XXXX'"
                . " WITH GRANT OPTION"
            )
        );

        // Accept create on any database (alternate wildcard syntax)
        $this->assertNotEmpty(
            $helper->checkDatabasePermissionGrant(
                'database_name',
                'create',
                "GRANT CREATE ON \"%\".* TO 'root'@'localhost' IDENTIFIED BY PASSWORD 'XXXX' WITH GRANT OPTION"
            )
        );
    }

    public function testDatabaseCreateFail()
    {

        $helper = new MySQLDatabaseConfigurationHelper();

        // Don't be fooled by create routine
        $this->assertEmpty(
            $helper->checkDatabasePermissionGrant(
                'database_name',
                'create',
                "GRANT SELECT, CREATE ROUTINE ON *.* TO 'user'@'localhost' IDENTIFIED BY PASSWORD 'XXXX' WITH GRANT OPTION"
            )
        );

        // Or create view
        $this->assertEmpty(
            $helper->checkDatabasePermissionGrant(
                'database_name',
                'create',
                "GRANT CREATE VIEW, SELECT ON *.* TO 'user'@'localhost' IDENTIFIED BY PASSWORD 'XXXX' WITH GRANT OPTION"
            )
        );

        // Don't accept permission if only given on a single subtable
        $this->assertEmpty(
            $helper->checkDatabasePermissionGrant(
                'database_name',
                'create',
                "GRANT CREATE, SELECT ON *.\"onetable\" TO 'user'@'localhost' IDENTIFIED BY PASSWORD 'XXXX' "
                . "WITH GRANT OPTION"
            )
        );

        // Don't accept permission on wrong database
        $this->assertEmpty(
            $helper->checkDatabasePermissionGrant(
                'database_name',
                'create',
                "GRANT ALL PRIVILEGES, CREATE ON \"wrongdb\".* TO 'user'@'localhost' IDENTIFIED BY PASSWORD 'XXXX' "
                . "WITH GRANT OPTION"
            )
        );

        // Don't accept wrong permission
        $this->assertEmpty(
            $helper->checkDatabasePermissionGrant(
                'database_name',
                'create',
                "GRANT UPDATE ON \"%\".* TO 'user'@'localhost' IDENTIFIED BY PASSWORD 'XXXX' WITH GRANT OPTION"
            )
        );

        // Don't accept sneaky table name
        $this->assertEmpty(
            $helper->checkDatabasePermissionGrant(
                'grant create on . to',
                'create',
                "GRANT UPDATE ON \"grant create on . to\".* TO 'user'@'localhost' IDENTIFIED BY PASSWORD 'XXXX' WITH "
                . "GRANT OPTION"
            )
        );
    }
}