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']); } }