<commit_before><?php

use Fist\Container\Container;
use Fist\Database\Connectors\ConnectionInterface;
use Fist\Database\Connectors\MysqlConnection;
use Fist\Database\Connectors\SqliteConnection;
use Fist\Database\Database;
use Fist\Database\DatabaseException;
use Fist\Database\Query\Statement;
use Fist\Repository\ArrayRepository;
use Fist\Repository\ContainerRepository;
use Fist\Repository\RepositoryInterface;
use Fist\Testing\TestCase;
use Fist\Testing\WithDatabase;

class DatabaseConnectionTest extends TestCase
{
    use WithDatabase;

    protected $testConnections = [
        'mysql' => [
            'driver'   => 'mysql',
            'database' => 'database',
            'hostname' => 'localhost',
            'username' => '',
            'password' => '',
        ],
        'sqlite' => [
            'driver'   => 'sqlite',
            'database' => ':memory:', // in-memory
            'hostname' => 'localhost',
            'username' => '',
            'password' => '',
        ],
        'tmp' => [
            'driver'   => 'sqlite',
            'database' => '', // temporary
            'hostname' => 'localhost',
            'username' => '',
            'password' => '',
        ],
    ];

    public function testConnectionBuildingUsingArrayRepository()
    {
        $db = new Database(
            new ArrayRepository()
        );

        $this->assertInstanceOf(RepositoryInterface::class, $db->getRepository());
        $this->assertInstanceOf(ArrayRepository::class, $db->getRepository());
    }

    public function testConnectionBuildingUsingContainerRepository()
    {
        $db = new Database(
            new ContainerRepository(
                $container = new Container()
            )
        );

        $this->assertInstanceOf(RepositoryInterface::class, $db->getRepository());
        $this->assertInstanceOf(ContainerRepository::class, $db->getRepository());
    }

    public function testDefaultConnection()
    {
        $db = new Database(
            new ArrayRepository()
        );

        $this->setDatabaseConnectionsAndDrivers($db);

        $this->assertEquals('default', $db->getDefaultConnection());
        $this->throwsException(function () use ($db) {
            $this->assertInstanceOf(MysqlConnection::class, $db->connection());
        }, DatabaseException::class, 'Connection [default] does not exists.');

        $db->setDefaultConnection('mysql');

        $this->assertEquals('mysql', $db->getDefaultConnection());
        $this->throwsException(function () use ($db) {
            $this->assertInstanceOf(MysqlConnection::class, $db->connection());
        }, PDOException::class, [
            "SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO)",
            "SQLSTATE[HY000] [1044] Access denied for user ''@'localhost' to database 'database'",
        ]);

        $db->setDefaultConnection('sqlite');

        $this->assertEquals('sqlite', $db->getDefaultConnection());
        $this->assertInstanceOf(SqliteConnection::class, $db->connection());
    }

    public function testDefaultDriver()
    {
        $db = new Database(
            new ArrayRepository()
        );

        $this->setDatabaseConnectionsAndDrivers($db);
        $db->setConnection('default', []);

        $this->assertEquals('mysql', $db->getDefaultDriver());
        $this->throwsException(function () use ($db) {
            $db->connection()->statement('SELECT * FROM test');
        }, PDOException::class, 'SQLSTATE[3D000]: Invalid catalog name: 1046 No database selected');

        $db->setDefaultDriver('sqlite');
        $this->assertEquals('sqlite', $db->getDefaultDriver());
        $this->assertInstanceOf(SqliteConnection::class, $db->connection());
    }

    public function testConnectionSwapping()
    {
        $db = new Database(
            new ArrayRepository()
        );

        $this->setDatabaseConnectionsAndDrivers($db);
        $db->setDefaultConnection('mysql');

        $this->throwsException(function () use ($db) {
            $this->assertInstanceOf(MysqlConnection::class, $db->connection());
        }, PDOException::class, [
            "SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO)",
            "SQLSTATE[HY000] [1044] Access denied for user ''@'localhost' to database 'database'",
        ]);

        $this->assertInstanceOf(SqliteConnection::class, $db->connection('sqlite'));
    }

    public function testDriverConnectionAreShared()
    {
        $db = new Database(
            $repository = new ContainerRepository($container = new Container(), 'db.')
        );

        $this->setDatabaseConnectionsAndDrivers($db);
        $db->setDefaultConnection('sqlite');

        $this->runOnDatabaseConnections($db, ['sqlite', 'tmp'], function (ConnectionInterface $connection) {
            $connection->statement('CREATE TABLE items (name VARCHAR(50))');
        });

        $this->runOnDatabaseConnections($db, [
            'sqlite',
            'tmp',
            'sqlite',
            'tmp',
            null,
        ], function (ConnectionInterface $connection) {
            $this->throwsException(function () use ($connection) {
                $connection->statement('CREATE TABLE items (name VARCHAR(50))');
            }, PDOException::class, 'SQLSTATE[HY000]: General error: 1 table items already exists');
        });
    }

    public function testDriverSwapping()
    {
        $db = new Database(
            $repository = new ContainerRepository($container = new Container(), 'db.')
        );

        $this->setDatabaseConnectionsAndDrivers($db);
        $db->setDefaultConnection('sqlite');

        $connection = $db->connection();

        // Change from in-memory to temporary database
        $db->setConnection('sqlite', [
            'driver' => 'mysql',
        ]);

        // Connection fails using the mysql setup since it's not setup.
        // So we expect it to fail
        $this->throwsException(function () use ($db) {
            $db->connection()->statement('SELECT * FROM test');
        }, PDOException::class, 'SQLSTATE[3D000]: Invalid catalog name: 1046 No database selected');
    }

    public function testSharedDriverConnectionsUpdateSettings()
    {
        $db = new Database(
            $repository = new ContainerRepository($container = new Container(), 'db.')
        );

        $this->setDatabaseConnectionsAndDrivers($db);
        $db->setDefaultConnection('sqlite');

        $db->statement('CREATE TABLE items (name VARCHAR(50))');

        // Change from in-memory to temporary database
        $db->setConnection('sqlite', [
            'driver'   => 'sqlite',
            'database' => '',
        ]);

        // The change should make sure that this does not fail,
        // even that it is the same driver and same connection,
        // but since the connection variable have been changed.
        $db->statement('CREATE TABLE items (name VARCHAR(50))');
    }

    public function testRawQueries()
    {<commit_msg>Add more test exception cases<commit_after><?php

use Fist\Container\Container;
use Fist\Database\Connectors\ConnectionInterface;
use Fist\Database\Connectors\MysqlConnection;
use Fist\Database\Connectors\SqliteConnection;
use Fist\Database\Database;
use Fist\Database\DatabaseException;
use Fist\Database\Query\Statement;
use Fist\Repository\ArrayRepository;
use Fist\Repository\ContainerRepository;
use Fist\Repository\RepositoryInterface;
use Fist\Testing\TestCase;
use Fist\Testing\WithDatabase;

class DatabaseConnectionTest extends TestCase
{
    use WithDatabase;

    protected $testConnections = [
        'mysql' => [
            'driver'   => 'mysql',
            'database' => 'database',
            'hostname' => 'localhost',
            'username' => '',
            'password' => '',
        ],
        'sqlite' => [
            'driver'   => 'sqlite',
            'database' => ':memory:', // in-memory
            'hostname' => 'localhost',
            'username' => '',
            'password' => '',
        ],
        'tmp' => [
            'driver'   => 'sqlite',
            'database' => '', // temporary
            'hostname' => 'localhost',
            'username' => '',
            'password' => '',
        ],
    ];

    public function testConnectionBuildingUsingArrayRepository()
    {
        $db = new Database(
            new ArrayRepository()
        );

        $this->assertInstanceOf(RepositoryInterface::class, $db->getRepository());
        $this->assertInstanceOf(ArrayRepository::class, $db->getRepository());
    }

    public function testConnectionBuildingUsingContainerRepository()
    {
        $db = new Database(
            new ContainerRepository(
                $container = new Container()
            )
        );

        $this->assertInstanceOf(RepositoryInterface::class, $db->getRepository());
        $this->assertInstanceOf(ContainerRepository::class, $db->getRepository());
    }

    public function testDefaultConnection()
    {
        $db = new Database(
            new ArrayRepository()
        );

        $this->setDatabaseConnectionsAndDrivers($db);

        $this->assertEquals('default', $db->getDefaultConnection());
        $this->throwsException(function () use ($db) {
            $this->assertInstanceOf(MysqlConnection::class, $db->connection());
        }, DatabaseException::class, 'Connection [default] does not exists.');

        $db->setDefaultConnection('mysql');

        $this->assertEquals('mysql', $db->getDefaultConnection());
        $this->throwsException(function () use ($db) {
            $this->assertInstanceOf(MysqlConnection::class, $db->connection());
        }, PDOException::class, [
            "SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO)",
            "SQLSTATE[HY000] [1044] Access denied for user ''@'localhost' to database 'database'",
        ]);

        $db->setDefaultConnection('sqlite');

        $this->assertEquals('sqlite', $db->getDefaultConnection());
        $this->assertInstanceOf(SqliteConnection::class, $db->connection());
    }

    public function testDefaultDriver()
    {
        $db = new Database(
            new ArrayRepository()
        );

        $this->setDatabaseConnectionsAndDrivers($db);
        $db->setConnection('default', []);

        $this->assertEquals('mysql', $db->getDefaultDriver());
        $this->throwsException(function () use ($db) {
            $db->connection()->statement('SELECT * FROM test');
        }, PDOException::class, [
            'SQLSTATE[3D000]: Invalid catalog name: 1046 No database selected',
            "SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO)",
        ]);

        $db->setDefaultDriver('sqlite');
        $this->assertEquals('sqlite', $db->getDefaultDriver());
        $this->assertInstanceOf(SqliteConnection::class, $db->connection());
    }

    public function testConnectionSwapping()
    {
        $db = new Database(
            new ArrayRepository()
        );

        $this->setDatabaseConnectionsAndDrivers($db);
        $db->setDefaultConnection('mysql');

        $this->throwsException(function () use ($db) {
            $this->assertInstanceOf(MysqlConnection::class, $db->connection());
        }, PDOException::class, [
            "SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO)",
            "SQLSTATE[HY000] [1044] Access denied for user ''@'localhost' to database 'database'",
        ]);

        $this->assertInstanceOf(SqliteConnection::class, $db->connection('sqlite'));
    }

    public function testDriverConnectionAreShared()
    {
        $db = new Database(
            $repository = new ContainerRepository($container = new Container(), 'db.')
        );

        $this->setDatabaseConnectionsAndDrivers($db);
        $db->setDefaultConnection('sqlite');

        $this->runOnDatabaseConnections($db, ['sqlite', 'tmp'], function (ConnectionInterface $connection) {
            $connection->statement('CREATE TABLE items (name VARCHAR(50))');
        });

        $this->runOnDatabaseConnections($db, [
            'sqlite',
            'tmp',
            'sqlite',
            'tmp',
            null,
        ], function (ConnectionInterface $connection) {
            $this->throwsException(function () use ($connection) {
                $connection->statement('CREATE TABLE items (name VARCHAR(50))');
            }, PDOException::class, 'SQLSTATE[HY000]: General error: 1 table items already exists');
        });
    }

    public function testDriverSwapping()
    {
        $db = new Database(
            $repository = new ContainerRepository($container = new Container(), 'db.')
        );

        $this->setDatabaseConnectionsAndDrivers($db);
        $db->setDefaultConnection('sqlite');

        $connection = $db->connection();

        // Change from in-memory to temporary database
        $db->setConnection('sqlite', [
            'driver' => 'mysql',
        ]);

        // Connection fails using the mysql setup since it's not setup.
        // So we expect it to fail
        $this->throwsException(function () use ($db) {
            $db->connection()->statement('SELECT * FROM test');
        }, PDOException::class, [
            'SQLSTATE[3D000]: Invalid catalog name: 1046 No database selected',
            "SQLSTATE[HY000] [1045] Access denied for user ''@'localhost' (using password: NO)",
        ]);
    }

    public function testSharedDriverConnectionsUpdateSettings()
    {
        $db = new Database(
            $repository = new ContainerRepository($container = new Container(), 'db.')
        );

        $this->setDatabaseConnectionsAndDrivers($db);
        $db->setDefaultConnection('sqlite');

        $db->statement('CREATE TABLE items (name VARCHAR(50))');

        // Change from in-memory to temporary database
        $db->setConnection('sqlite', [
            'driver'   => 'sqlite',
            'database' => '',
        ]);

        // The change should make sure that this does not fail,
        // even that it is the same driver and same connection,
        // but since the connection variable have been changed.
        $db->statement('CREATE TABLE items (name VARCHAR(50))');
    }

    public function testRawQueries()
    {
{'fraction_non_alphanumeric': 0.1437125748502994, 'fraction_numerical': 0.008773151371675255, 'mean_word_length': 2.542046855733662, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 171, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6323280', 'n_tokens_mistral': 3803, 'n_tokens_neox': 3660, 'n_words': 796}