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 account_rate_repository_test_rates'); $this->connection->exec( 'CREATE TABLE account_rate_repository_test_rates ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, account_id INT UNSIGNED NOT NULL, rate_percent DECIMAL(5,2) NOT NULL, valid_from DATE NOT NULL, valid_to DATE NULL )' ); $this->repository = new AccountRateRepositoryTestDouble(); } protected function tearDown(): void { Profiler::reset(); if (isset($this->connection)) { try { $this->connection->exec('DROP TABLE IF EXISTS account_rate_repository_test_rates'); } catch (MyException) { } } // Database::instance() кэширует подключение статически — сбрасываем между тестами $ref = new ReflectionClass(Database::class); $prop = $ref->getProperty('_instances'); $prop->setAccessible(true); $prop->setValue(null, []); } public function testSetRateWithNoCurrentRateJustCreates(): void { $this->repository->setRate(1, 5.5, '2026-01-01'); $current = $this->repository->getCurrentRate(1); $this->assertNotFalse($current); $this->assertSame('5.50', $current->rate_percent); $this->assertSame('2026-01-01', $current->valid_from); $this->assertNull($current->valid_to); } public function testSetRateClosesPreviousAndOpensNew(): void { $this->repository->setRate(1, 5.5, '2026-01-01'); $this->repository->setRate(1, 6.0, '2026-03-01'); $rows = $this->repository->getList('*', ['account_id' => 1], 'id'); $this->assertCount(2, $rows); $this->assertSame('2026-01-01', $rows[0]->valid_from); $this->assertSame('2026-02-28', $rows[0]->valid_to); $this->assertSame('2026-03-01', $rows[1]->valid_from); $this->assertNull($rows[1]->valid_to); $current = $this->repository->getCurrentRate(1); $this->assertSame('6.00', $current->rate_percent); } public function testSetRateRejectsDateNotAfterCurrentValidFrom(): void { $this->repository->setRate(1, 5.5, '2026-03-01'); $this->expectException(MyException::class); $this->repository->setRate(1, 6.0, '2026-03-01'); } public function testSetRateRejectsDateBeforeCurrentValidFrom(): void { $this->repository->setRate(1, 5.5, '2026-03-01'); $this->expectException(MyException::class); $this->repository->setRate(1, 6.0, '2026-01-01'); } public function testSetRateIsolatedPerAccount(): void { $this->repository->setRate(1, 5.5, '2026-01-01'); $this->repository->setRate(2, 4.0, '2026-01-01'); $this->assertSame('5.50', $this->repository->getCurrentRate(1)->rate_percent); $this->assertSame('4.00', $this->repository->getCurrentRate(2)->rate_percent); } public function testSetRateRollsBackOnRejectedDate(): void { $this->repository->setRate(1, 5.5, '2026-03-01'); try { $this->repository->setRate(1, 6.0, '2026-01-01'); } catch (MyException) { } // Отклонённая правка не должна была ни закрыть текущую строку, ни создать новую. $rows = $this->repository->getList('*', ['account_id' => 1], 'id'); $this->assertCount(1, $rows); $this->assertNull($rows[0]->valid_to); } }