- Migrate ShopStatuses module to Domain + DI architecture - Add ShopStatusRepository, ShopStatusesController with color picker - Convert front\factory\ShopStatuses to facade - Add FormFieldType::COLOR with HTML5 color picker - Move documentation files to docs/ folder (PROJECT_STRUCTURE, REFACTORING_PLAN, CHANGELOG, FORM_EDIT_SYSTEM, TESTING, DATABASE_STRUCTURE) - Tests: 254 tests, 736 assertions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
254 lines
8.4 KiB
PHP
254 lines
8.4 KiB
PHP
<?php
|
|
namespace Tests\Unit\Domain\ShopStatus;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use Domain\ShopStatus\ShopStatusRepository;
|
|
|
|
class ShopStatusRepositoryTest extends TestCase
|
|
{
|
|
public function testFindReturnsNullForNegativeId(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$mockDb->expects($this->never())->method('get');
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$this->assertNull($repository->find(-1));
|
|
}
|
|
|
|
public function testFindReturnsNullWhenNotFound(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$mockDb->expects($this->once())
|
|
->method('get')
|
|
->with('pp_shop_statuses', '*', ['id' => 99])
|
|
->willReturn(null);
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$this->assertNull($repository->find(99));
|
|
}
|
|
|
|
public function testFindReturnsStatusWithIdZero(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$mockDb->expects($this->once())
|
|
->method('get')
|
|
->with('pp_shop_statuses', '*', ['id' => 0])
|
|
->willReturn([
|
|
'id' => '0',
|
|
'status' => 'zamówienie złożone',
|
|
'color' => '#ff0000',
|
|
'o' => '0',
|
|
'apilo_status_id' => '5',
|
|
]);
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$result = $repository->find(0);
|
|
|
|
$this->assertIsArray($result);
|
|
$this->assertSame(0, $result['id']);
|
|
$this->assertSame('zamówienie złożone', $result['status']);
|
|
$this->assertSame('#ff0000', $result['color']);
|
|
$this->assertSame(0, $result['o']);
|
|
$this->assertSame(5, $result['apilo_status_id']);
|
|
}
|
|
|
|
public function testFindNormalizesNullApiloStatusId(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$mockDb->expects($this->once())
|
|
->method('get')
|
|
->with('pp_shop_statuses', '*', ['id' => 1])
|
|
->willReturn([
|
|
'id' => '1',
|
|
'status' => 'zamówienie opłacone',
|
|
'color' => '',
|
|
'o' => '1',
|
|
'apilo_status_id' => null,
|
|
]);
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$result = $repository->find(1);
|
|
|
|
$this->assertIsArray($result);
|
|
$this->assertSame(1, $result['id']);
|
|
$this->assertNull($result['apilo_status_id']);
|
|
}
|
|
|
|
public function testSaveUpdatesColorAndApiloStatusId(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$updateRow = null;
|
|
$updateWhere = null;
|
|
|
|
$mockDb->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($table, $row, $where) use (&$updateRow, &$updateWhere) {
|
|
$this->assertSame('pp_shop_statuses', $table);
|
|
$updateRow = $row;
|
|
$updateWhere = $where;
|
|
return true;
|
|
});
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$id = $repository->save(3, [
|
|
'color' => ' #00ff00 ',
|
|
'apilo_status_id' => '12',
|
|
]);
|
|
|
|
$this->assertSame(3, $id);
|
|
$this->assertSame('#00ff00', $updateRow['color']);
|
|
$this->assertSame(12, $updateRow['apilo_status_id']);
|
|
$this->assertSame(['id' => 3], $updateWhere);
|
|
}
|
|
|
|
public function testSaveWithIdZeroWorks(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$mockDb->expects($this->once())
|
|
->method('update')
|
|
->with('pp_shop_statuses', $this->anything(), ['id' => 0]);
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$id = $repository->save(0, ['color' => '#aabbcc']);
|
|
|
|
$this->assertSame(0, $id);
|
|
}
|
|
|
|
public function testSaveWithEmptyApiloStatusIdSetsNull(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$updateRow = null;
|
|
|
|
$mockDb->expects($this->once())
|
|
->method('update')
|
|
->willReturnCallback(function ($table, $row) use (&$updateRow) {
|
|
$updateRow = $row;
|
|
});
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$repository->save(1, ['color' => '#000', 'apilo_status_id' => '']);
|
|
|
|
$this->assertNull($updateRow['apilo_status_id']);
|
|
}
|
|
|
|
public function testSaveRejectsNegativeId(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$mockDb->expects($this->never())->method('update');
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$this->assertSame(0, $repository->save(-1, ['color' => '#fff']));
|
|
}
|
|
|
|
public function testGetApiloStatusIdReturnsValue(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$mockDb->expects($this->once())
|
|
->method('get')
|
|
->with('pp_shop_statuses', 'apilo_status_id', ['id' => 4])
|
|
->willReturn(15);
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$this->assertSame(15, $repository->getApiloStatusId(4));
|
|
}
|
|
|
|
public function testGetApiloStatusIdReturnsNullWhenNotSet(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$mockDb->expects($this->once())
|
|
->method('get')
|
|
->willReturn(null);
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$this->assertNull($repository->getApiloStatusId(4));
|
|
}
|
|
|
|
public function testGetByIntegrationStatusIdForApilo(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$mockDb->expects($this->once())
|
|
->method('get')
|
|
->with('pp_shop_statuses', 'id', ['apilo_status_id' => 15])
|
|
->willReturn(4);
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$this->assertSame(4, $repository->getByIntegrationStatusId('apilo', 15));
|
|
}
|
|
|
|
public function testGetByIntegrationStatusIdReturnsNullForUnknownIntegration(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$mockDb->expects($this->never())->method('get');
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$this->assertNull($repository->getByIntegrationStatusId('unknown', 1));
|
|
}
|
|
|
|
public function testAllStatusesReturnsOrderedList(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$mockDb->expects($this->once())
|
|
->method('select')
|
|
->with('pp_shop_statuses', ['id', 'status'], ['ORDER' => ['o' => 'ASC']])
|
|
->willReturn([
|
|
['id' => 0, 'status' => 'złożone'],
|
|
['id' => 1, 'status' => 'opłacone'],
|
|
]);
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$result = $repository->allStatuses();
|
|
|
|
$this->assertSame([0 => 'złożone', 1 => 'opłacone'], $result);
|
|
}
|
|
|
|
public function testListForAdminWhitelistsSortAndDirection(): void
|
|
{
|
|
$mockDb = $this->createMock(\medoo::class);
|
|
$queries = [];
|
|
|
|
$mockDb->method('query')
|
|
->willReturnCallback(function ($sql, $params = []) use (&$queries) {
|
|
$queries[] = ['sql' => $sql, 'params' => $params];
|
|
|
|
if (strpos($sql, 'COUNT(0)') !== false) {
|
|
return new class {
|
|
public function fetchAll()
|
|
{
|
|
return [[2]];
|
|
}
|
|
};
|
|
}
|
|
|
|
return new class {
|
|
public function fetchAll()
|
|
{
|
|
return [[
|
|
'id' => 0,
|
|
'status' => 'złożone',
|
|
'color' => '#fff',
|
|
'o' => 0,
|
|
'apilo_status_id' => null,
|
|
]];
|
|
}
|
|
};
|
|
});
|
|
|
|
$repository = new ShopStatusRepository($mockDb);
|
|
$result = $repository->listForAdmin(
|
|
[],
|
|
'status DESC; DROP TABLE pp_shop_statuses; --',
|
|
'DESC; DELETE FROM pp_users; --',
|
|
1,
|
|
999
|
|
);
|
|
|
|
$this->assertCount(2, $queries);
|
|
$dataSql = $queries[1]['sql'];
|
|
|
|
$this->assertMatchesRegularExpression('/ORDER BY\s+ss\.o\s+ASC,\s+ss\.id\s+ASC/i', $dataSql);
|
|
$this->assertStringNotContainsString('DROP TABLE', $dataSql);
|
|
$this->assertStringNotContainsString('DELETE FROM pp_users', $dataSql);
|
|
$this->assertMatchesRegularExpression('/LIMIT\s+100\s+OFFSET\s+0/i', $dataSql);
|
|
}
|
|
}
|