Bicycle/tests/Unit/CategoryRepositoryTest.php
2026-08-15 10:39:22 +03:00

179 lines
6.9 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace Tests\Unit;
use App\Repositories\CategoryRepository;
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;
/**
* CategoryRepository жёстко привязан к боевой таблице categories конструктором — для тестов
* подменяем таблицу на временную через прямой вызов Repository::__construct(), минуя
* CategoryRepository::__construct() (тот же приём, что и RepositoryTestItemRepository
* в RepositoryTest.php, только поверх реального класса, а не заглушки).
*
* Группа (category_groups) — самостоятельная сущность (см. схему), но на уровне
* CategoryRepository это просто INT-колонка group_id без FK — сюда её реальным существованием
* можно не заниматься, getTree()/findDuplicate() работают с сырым значением как есть.
*/
class CategoryRepositoryTestDouble extends CategoryRepository
{
public function __construct(?string $connection = null)
{
Repository::__construct('category_repository_test_categories', connection: $connection);
}
}
class CategoryRepositoryTest extends TestCase
{
private PdoConnection $connection;
private CategoryRepositoryTestDouble $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 category_repository_test_categories');
$this->connection->exec(
'CREATE TABLE category_repository_test_categories (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id INT UNSIGNED NOT NULL,
group_id INT UNSIGNED NULL,
title VARCHAR(128) NOT NULL,
type ENUM(\'income\',\'expense\') NOT NULL,
is_delete TINYINT(1) NOT NULL DEFAULT 0,
`order` INT NOT NULL DEFAULT 0,
linked_expense_categorie_id INT UNSIGNED NULL,
formula_pct DECIMAL(5,2) NULL
)'
);
$this->repository = new CategoryRepositoryTestDouble();
}
protected function tearDown(): void
{
Profiler::reset();
if (isset($this->connection)) {
try {
$this->connection->exec('DROP TABLE IF EXISTS category_repository_test_categories');
} catch (MyException) {
}
}
$ref = new ReflectionClass(Database::class);
$prop = $ref->getProperty('_instances');
$prop->setAccessible(true);
$prop->setValue(null, []);
}
public function testFindActiveDuplicateFindsSameTitleTypeGroup(): void
{
$this->repository->create([
'user_id' => 1, 'group_id' => null, 'title' => 'Зарплата', 'type' => 'income',
'is_delete' => false, 'order' => 0,
]);
$duplicate = $this->repository->findActiveDuplicate(1, 'income', null, 'Зарплата');
$this->assertNotFalse($duplicate);
$this->assertSame('Зарплата', $duplicate->title);
}
public function testFindActiveDuplicateIgnoresDifferentType(): void
{
$this->repository->create([
'user_id' => 1, 'group_id' => null, 'title' => 'Прочее', 'type' => 'income',
'is_delete' => false, 'order' => 0,
]);
$this->assertFalse($this->repository->findActiveDuplicate(1, 'expense', null, 'Прочее'));
}
public function testFindActiveDuplicateExcludesGivenId(): void
{
$id = $this->repository->create([
'user_id' => 1, 'group_id' => null, 'title' => 'Транспорт', 'type' => 'expense',
'is_delete' => false, 'order' => 0,
]);
$this->assertFalse($this->repository->findActiveDuplicate(1, 'expense', null, 'Транспорт', $id));
}
public function testFindArchivedDuplicateOnlyMatchesArchived(): void
{
$this->repository->create([
'user_id' => 1, 'group_id' => null, 'title' => 'Такси', 'type' => 'expense',
'is_delete' => true, 'order' => 0,
]);
$this->assertFalse($this->repository->findActiveDuplicate(1, 'expense', null, 'Такси'));
$archived = $this->repository->findArchivedDuplicate(1, 'expense', null, 'Такси');
$this->assertNotFalse($archived);
$this->assertSame('Такси', $archived->title);
}
public function testGetTreeGroupsByGroupId(): void
{
// group_id тут условный (42) — CategoryRepository не проверяет, что за ним реально стоит
// строка category_groups, это забота CategoriesController::parseGroupId().
$group_id = 42;
$this->repository->create([
'user_id' => 1, 'group_id' => null, 'title' => 'Без группы', 'type' => 'expense',
'is_delete' => false, 'order' => 0,
]);
$this->repository->create([
'user_id' => 1, 'group_id' => $group_id, 'title' => 'Такси', 'type' => 'expense',
'is_delete' => false, 'order' => 0,
]);
// архивная — не должна попасть в дерево
$this->repository->create([
'user_id' => 1, 'group_id' => $group_id, 'title' => 'Метро', 'type' => 'expense',
'is_delete' => true, 'order' => 0,
]);
$tree = $this->repository->getTree(1);
$this->assertCount(1, $tree[0]);
$this->assertSame('Без группы', $tree[0][0]->title);
$this->assertCount(1, $tree[$group_id]);
$this->assertSame('Такси', $tree[$group_id][0]->title);
}
public function testGetTreeScopedByUser(): void
{
$this->repository->create([
'user_id' => 1, 'group_id' => null, 'title' => 'Моя статья', 'type' => 'expense',
'is_delete' => false, 'order' => 0,
]);
$this->repository->create([
'user_id' => 2, 'group_id' => null, 'title' => 'Чужая статья', 'type' => 'expense',
'is_delete' => false, 'order' => 0,
]);
$tree = $this->repository->getTree(1);
$this->assertCount(1, $tree[0]);
$this->assertSame('Моя статья', $tree[0][0]->title);
}
}