171 lines
6.2 KiB
PHP
171 lines
6.2 KiB
PHP
<?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, только поверх реального класса, а не заглушки).
|
||
*/
|
||
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,
|
||
parent_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 testFindActiveDuplicateFindsSameTitleTypeParent(): void
|
||
{
|
||
$this->repository->create([
|
||
'user_id' => 1, 'parent_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, 'parent_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, 'parent_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, 'parent_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 testGetTreeGroupsByParentId(): void
|
||
{
|
||
$group_id = $this->repository->create([
|
||
'user_id' => 1, 'parent_id' => null, 'title' => 'Транспорт', 'type' => 'expense',
|
||
'is_delete' => false, 'order' => 0,
|
||
]);
|
||
|
||
$this->repository->create([
|
||
'user_id' => 1, 'parent_id' => $group_id, 'title' => 'Такси', 'type' => 'expense',
|
||
'is_delete' => false, 'order' => 0,
|
||
]);
|
||
|
||
// архивная — не должна попасть в дерево
|
||
$this->repository->create([
|
||
'user_id' => 1, 'parent_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, 'parent_id' => null, 'title' => 'Моя статья', 'type' => 'expense',
|
||
'is_delete' => false, 'order' => 0,
|
||
]);
|
||
$this->repository->create([
|
||
'user_id' => 2, 'parent_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);
|
||
}
|
||
}
|