src/PhpunitTestCase.php

Summary

Maintainability
C
1 day
Test Coverage
<?php

declare(strict_types=1);

namespace atk4\schema;

use atk4\core\AtkPhpunit;
use atk4\data\Model;
use atk4\data\Persistence;
use atk4\dsql\Connection;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Platforms\MySqlPlatform;

// NOTE: This class should stay here in this namespace because other repos rely on it. For example, atk4\data tests
class PhpunitTestCase extends AtkPhpunit\TestCase
{
    /** @var Persistence|Persistence\Sql Persistence instance */
    public $db;

    /** @var array Array of database table names */
    public $tables;

    /** @var bool Debug mode enabled/disabled. In debug mode will use Dumper persistence */
    public $debug = false;

    /** @var string DSN string */
    protected $dsn;

    /**
     * Setup test database.
     */
    protected function setUp(): void
    {
        parent::setUp();

        // establish connection
        $this->dsn = ($this->debug ? ('dumper:') : '') . ($GLOBALS['DB_DSN'] ?? 'sqlite::memory:');
        $user = $GLOBALS['DB_USER'] ?? null;
        $pass = $GLOBALS['DB_PASSWD'] ?? null;

        $this->db = Persistence::connect($this->dsn, $user, $pass);

        // reset DB autoincrement to 1, tests rely on it
        if ($this->getDatabasePlatform() instanceof MySqlPlatform) {
            $this->db->connection->expr('SET @@auto_increment_offset=1, @@auto_increment_increment=1')->execute();
        }
    }

    protected function tearDown(): void
    {
        $this->db = null;

        parent::tearDown(); // TODO: Change the autogenerated stub
    }

    protected function getDatabasePlatform(): AbstractPlatform
    {
        return $this->db->connection->getDatabasePlatform();
    }

    /**
     * Create and return appropriate Migration object.
     *
     * @param Connection|Persistence|Model $m
     *
     * @return Migration
     */
    public function getMigrator($model = null)
    {
        return \atk4\schema\Migration::of($model ?: $this->db);
    }

    /**
     * Use this method to clean up tables after you have created them,
     * so that your database would be ready for the next test.
     *
     * @param string $table Table name
     */
    public function dropTable($table)
    {
        $this->getMigrator()->table($table)->drop();
    }

    /**
     * Sets database into a specific test.
     *
     * @param array $db_data
     * @param bool  $import_data Should we import data of just create table
     */
    public function setDb($db_data, $import_data = true)
    {
        $this->tables = array_keys($db_data);

        // create tables
        foreach ($db_data as $table => $data) {
            $migrator = $this->getMigrator();

            // drop table
            $migrator->table($table)->drop();

            // create table and fields from first row of data
            $first_row = current($data);
            if ($first_row) {
                foreach ($first_row as $field => $row) {
                    if ($field === 'id') {
                        $migrator->id('id');

                        continue;
                    }

                    if (is_int($row)) {
                        $migrator->field($field, ['type' => 'integer']);

                        continue;
                    } elseif (is_float($row)) {
                        $migrator->field($field, ['type' => 'float']);

                        continue;
                    } elseif ($row instanceof \DateTime) {
                        $migrator->field($field, ['type' => 'datetime']);

                        continue;
                    }

                    $migrator->field($field);
                }
            }

            if (!isset($first_row['id'])) {
                $migrator->id();
            }

            $migrator->create();

            // import data
            if ($import_data) {
                $has_id = (bool) key($data);

                foreach ($data as $id => $row) {
                    $query = $this->db->dsql();
                    if ($id === '_') {
                        continue;
                    }

                    $query->table($table);
                    $query->set($row);

                    if (!isset($row['id']) && $has_id) {
                        $query->set('id', $id);
                    }

                    $query->insert();
                }
            }
        }
    }

    /**
     * Return database data.
     *
     * @param array $tables Array of tables
     *
     * @return array
     */
    public function getDb($tables = null, bool $no_id = false)
    {
        $tables = $tables ?: $this->tables;

        if (is_string($tables)) {
            $tables = array_map('trim', explode(',', $tables));
        }

        $ret = [];

        foreach ($tables as $table) {
            $data2 = [];

            $s = $this->db->dsql();
            $data = $s->table($table)->get();

            foreach ($data as &$row) {
                foreach ($row as &$val) {
                    if (is_int($val)) {
                        $val = (int) $val;
                    }
                }

                if ($no_id) {
                    unset($row['id']);
                    $data2[] = $row;
                } else {
                    $data2[$row['id']] = $row;
                }
            }

            $ret[$table] = $data2;
        }

        return $ret;
    }

    /**
     * Return escape character of current DB connection.
     *
     * @return string
     */
    public function getEscapeChar()
    {
        return $this->getProtected($this->db->dsql(), 'escape_char');
    }
}