165 lines
6.6 KiB
PHP
165 lines
6.6 KiB
PHP
<?php
|
||
|
||
namespace Tests\Unit;
|
||
|
||
use PHPUnit\Framework\TestCase;
|
||
use Services\DataBase\Classes\PdoConnection;
|
||
use Services\DataBase\Classes\Profiler;
|
||
use System\Classes\Config;
|
||
use System\Classes\MyException;
|
||
|
||
class PdoConnectionTest extends TestCase
|
||
{
|
||
private PdoConnection $connection;
|
||
|
||
protected function setUp(): void
|
||
{
|
||
$config = Config::get('db', 'default') ?? [];
|
||
$this->connection = new PdoConnection($config);
|
||
|
||
try {
|
||
$this->connection->pdo();
|
||
} catch (MyException $e) {
|
||
$this->markTestSkipped('Нет живого подключения к MariaDB (заполните App/config/config.local.php): ' . $e->getMessage());
|
||
}
|
||
|
||
$this->connection->exec('DROP TABLE IF EXISTS pdo_connection_test');
|
||
$this->connection->exec('CREATE TABLE pdo_connection_test (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255))');
|
||
}
|
||
|
||
protected function tearDown(): void
|
||
{
|
||
Profiler::reset();
|
||
|
||
if (isset($this->connection)) {
|
||
try {
|
||
$this->connection->exec('DROP TABLE IF EXISTS pdo_connection_test');
|
||
} catch (MyException) {
|
||
// соединения не было — нечего дропать
|
||
}
|
||
}
|
||
}
|
||
|
||
public function testFailoverThrowsWithAllHostErrors(): void
|
||
{
|
||
// Чисто логика failover — не требует живой БД, оба хоста заведомо недоступны
|
||
$connection = new PdoConnection(['hosts' => ['bad-host-1', 'bad-host-2'], 'dbname' => 'x', 'user' => 'x', 'password' => 'x']);
|
||
|
||
try {
|
||
$connection->pdo();
|
||
$this->fail('Ожидалось MyException');
|
||
} catch (MyException $e) {
|
||
$this->assertStringContainsString('bad-host-1', $e->getMessage());
|
||
$this->assertStringContainsString('bad-host-2', $e->getMessage());
|
||
}
|
||
}
|
||
|
||
public function testQueryInsertsAndSelects(): void
|
||
{
|
||
$this->connection->query('INSERT INTO pdo_connection_test (name) VALUES (?)', ['foo']);
|
||
$row = $this->connection->query('SELECT * FROM pdo_connection_test WHERE name = ?', ['foo'])->fetch();
|
||
|
||
$this->assertSame('foo', $row['name']);
|
||
}
|
||
|
||
public function testLastInsertId(): void
|
||
{
|
||
$this->connection->query('INSERT INTO pdo_connection_test (name) VALUES (?)', ['foo']);
|
||
$this->assertNotFalse($this->connection->lastInsertId());
|
||
}
|
||
|
||
public function testCommitPersistsChanges(): void
|
||
{
|
||
$this->connection->beginTransaction();
|
||
$this->connection->query('INSERT INTO pdo_connection_test (name) VALUES (?)', ['committed']);
|
||
$this->connection->commit();
|
||
|
||
$row = $this->connection->query('SELECT * FROM pdo_connection_test WHERE name = ?', ['committed'])->fetch();
|
||
$this->assertNotFalse($row);
|
||
}
|
||
|
||
public function testRollbackDiscardsChanges(): void
|
||
{
|
||
$this->connection->beginTransaction();
|
||
$this->connection->query('INSERT INTO pdo_connection_test (name) VALUES (?)', ['rolled_back']);
|
||
$this->connection->rollback();
|
||
|
||
$row = $this->connection->query('SELECT * FROM pdo_connection_test WHERE name = ?', ['rolled_back'])->fetch();
|
||
$this->assertFalse($row);
|
||
}
|
||
|
||
public function testNestedTransactionRollbackDoesNotAffectOuter(): void
|
||
{
|
||
$this->connection->beginTransaction();
|
||
$this->connection->query('INSERT INTO pdo_connection_test (name) VALUES (?)', ['outer']);
|
||
|
||
$this->connection->beginTransaction(); // SAVEPOINT sp_1
|
||
$this->connection->query('INSERT INTO pdo_connection_test (name) VALUES (?)', ['inner']);
|
||
$this->connection->rollback(); // ROLLBACK TO SAVEPOINT sp_1 — не трогает 'outer'
|
||
|
||
$this->connection->commit(); // фиксирует внешнюю транзакцию
|
||
|
||
$outer = $this->connection->query('SELECT * FROM pdo_connection_test WHERE name = ?', ['outer'])->fetch();
|
||
$inner = $this->connection->query('SELECT * FROM pdo_connection_test WHERE name = ?', ['inner'])->fetch();
|
||
|
||
$this->assertNotFalse($outer);
|
||
$this->assertFalse($inner);
|
||
}
|
||
|
||
public function testTransactionHelperRollsBackOnException(): void
|
||
{
|
||
try {
|
||
$this->connection->transaction(function (PdoConnection $connection) {
|
||
$connection->query('INSERT INTO pdo_connection_test (name) VALUES (?)', ['should_rollback']);
|
||
throw new \RuntimeException('boom');
|
||
});
|
||
$this->fail('Ожидалось RuntimeException');
|
||
} catch (\RuntimeException) {
|
||
// ожидаемо
|
||
}
|
||
|
||
$row = $this->connection->query('SELECT * FROM pdo_connection_test WHERE name = ?', ['should_rollback'])->fetch();
|
||
$this->assertFalse($row);
|
||
}
|
||
|
||
public function testManualPrepareExecuteIsProfiledToo(): void
|
||
{
|
||
// Регрессия: профилирование должно ловить любой execute(), а не только PdoConnection::query() —
|
||
// Repository сам делает $this->pdo->prepare()->execute() напрямую (см. get()/create()/...).
|
||
Profiler::reset();
|
||
|
||
$stmt = $this->connection->prepare('INSERT INTO pdo_connection_test (name) VALUES (?)');
|
||
$stmt->execute(['foo']);
|
||
|
||
$this->assertSame(1, Profiler::count());
|
||
}
|
||
|
||
public function testInTransaction(): void
|
||
{
|
||
$this->assertFalse($this->connection->inTransaction());
|
||
$this->connection->beginTransaction();
|
||
$this->assertTrue($this->connection->inTransaction());
|
||
$this->connection->commit();
|
||
$this->assertFalse($this->connection->inTransaction());
|
||
}
|
||
|
||
public function testSqliteDialectConnectsAndQueries(): void
|
||
{
|
||
// dialect — не требует живой MariaDB (в отличие от остальных тестов класса, см. setUp()),
|
||
// регрессия на DSN/подключение для диалекта, отличного от дефолтного mysql
|
||
$connection = new PdoConnection(['dialect' => 'sqlite', 'dbname' => ':memory:']);
|
||
$connection->exec('CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)');
|
||
$connection->query('INSERT INTO t (name) VALUES (?)', ['hello']);
|
||
|
||
$row = $connection->query('SELECT * FROM t')->fetch();
|
||
$this->assertSame('hello', $row['name']);
|
||
|
||
$connection->beginTransaction();
|
||
$connection->query('INSERT INTO t (name) VALUES (?)', ['rolled_back']);
|
||
$connection->rollback();
|
||
|
||
$count = $connection->query('SELECT COUNT(*) AS c FROM t')->fetch();
|
||
$this->assertSame(1, (int) $count['c']);
|
||
}
|
||
}
|