Bicycle/tests/Unit/AccountRateRepositoryTest.php
2026-08-15 19:40:29 +03:00

148 lines
5.1 KiB
PHP

<?php
namespace Tests\Unit;
use App\Repositories\AccountRateRepository;
use PHPUnit\Framework\TestCase;
use ReflectionClass;
use Services\Database;
use Services\DataBase\Classes\PdoConnection;
use Services\DataBase\Classes\Profiler;
use System\Classes\Config;
use System\Classes\MyException;
use System\Classes\Repository;
/**
* AccountRateRepository жёстко привязан к боевой таблице account_rates конструктором — для
* тестов подменяем её на временную (тот же приём, что и в BudgetRepositoryTest/CategoryRepositoryTest:
* прямой вызов Repository::__construct(), минуя AccountRateRepository::__construct()).
*/
class AccountRateRepositoryTestDouble extends AccountRateRepository
{
public function __construct(?string $connection = null)
{
Repository::__construct('account_rate_repository_test_rates', connection: $connection);
}
}
class AccountRateRepositoryTest extends TestCase
{
private PdoConnection $connection;
private AccountRateRepositoryTestDouble $repository;
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 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);
}
}