You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
325 lines
10 KiB
325 lines
10 KiB
<?php |
|
|
|
declare(strict_types=1); |
|
|
|
namespace AutoStore\Tests\Infrastructure\Repositories; |
|
|
|
use AutoStore\Application\Exceptions\ApplicationException; |
|
use AutoStore\Domain\Entities\Item; |
|
use AutoStore\Domain\Filters\FilterSpecification; |
|
use AutoStore\Infrastructure\Repositories\FileItemRepository; |
|
use DateTimeImmutable; |
|
use PHPUnit\Framework\TestCase; |
|
use Psr\Log\LoggerInterface; |
|
|
|
class FileItemRepositoryTest extends TestCase |
|
{ |
|
private const ITEM_ID_1 = 'test-id-1'; |
|
private const ITEM_ID_2 = 'test-id-2'; |
|
private const ITEM_ID_3 = 'test-id-3'; |
|
private const EXPIRED_ID = 'expired-id'; |
|
private const VALID_ID = 'valid-id'; |
|
private const NON_EXISTENT_ID = 'non-existent-id'; |
|
private const ITEM_NAME_1 = 'Test Item 1'; |
|
private const ITEM_NAME_2 = 'Test Item 2'; |
|
private const ITEM_NAME_3 = 'Test Item 3'; |
|
private const EXPIRED_NAME = 'Expired Item'; |
|
private const VALID_NAME = 'Valid Item'; |
|
private const ORDER_URL_1 = 'http://example.com/order1'; |
|
private const ORDER_URL_2 = 'http://example.com/order2'; |
|
private const ORDER_URL_3 = 'http://example.com/order3'; |
|
private const EXPIRED_ORDER_URL = 'http://example.com/expired-order'; |
|
private const VALID_ORDER_URL = 'http://example.com/valid-order'; |
|
private const USER_ID_1 = 'user-id-1'; |
|
private const USER_ID_2 = 'user-id-2'; |
|
private const USER_ID = 'user-id'; |
|
private const DATE_FORMAT = 'Y-m-d H:i:s'; |
|
|
|
private FileItemRepository $repository; |
|
private string $testStoragePath; |
|
private LoggerInterface&\PHPUnit\Framework\MockObject\MockObject $logger; |
|
|
|
protected function setUp(): void |
|
{ |
|
$this->testStoragePath = $GLOBALS['test_storage_path']; |
|
|
|
// Clean up any existing test files |
|
array_map('unlink', glob("$this->testStoragePath/*.json")); |
|
|
|
$this->logger = $this->createMock(LoggerInterface::class); |
|
$this->repository = new FileItemRepository($this->testStoragePath, $this->logger); |
|
} |
|
|
|
protected function tearDown(): void |
|
{ |
|
// Clean up test files |
|
array_map('unlink', glob("$this->testStoragePath/*.json")); |
|
} |
|
|
|
private function createTestItem1(): Item |
|
{ |
|
return new Item( |
|
self::ITEM_ID_1, |
|
self::ITEM_NAME_1, |
|
new DateTimeImmutable('+1 day'), |
|
self::ORDER_URL_1, |
|
self::USER_ID_1 |
|
); |
|
} |
|
|
|
private function createTestItem2(): Item |
|
{ |
|
return new Item( |
|
self::ITEM_ID_2, |
|
self::ITEM_NAME_2, |
|
new DateTimeImmutable('+2 days'), |
|
self::ORDER_URL_2, |
|
self::USER_ID_2 |
|
); |
|
} |
|
|
|
private function createTestItem3(): Item |
|
{ |
|
return new Item( |
|
self::ITEM_ID_3, |
|
self::ITEM_NAME_3, |
|
new DateTimeImmutable('+3 days'), |
|
self::ORDER_URL_3, |
|
self::USER_ID_1 |
|
); |
|
} |
|
|
|
private function createExpiredItem(): Item |
|
{ |
|
return new Item( |
|
self::EXPIRED_ID, |
|
self::EXPIRED_NAME, |
|
new DateTimeImmutable('-1 day'), |
|
self::EXPIRED_ORDER_URL, |
|
self::USER_ID |
|
); |
|
} |
|
|
|
private function createValidItem(): Item |
|
{ |
|
return new Item( |
|
self::VALID_ID, |
|
self::VALID_NAME, |
|
new DateTimeImmutable('+1 day'), |
|
self::VALID_ORDER_URL, |
|
self::USER_ID |
|
); |
|
} |
|
|
|
private function createExpiredItemForUser1(): Item |
|
{ |
|
return new Item( |
|
self::ITEM_ID_1, |
|
self::ITEM_NAME_1, |
|
new DateTimeImmutable('-1 day'), |
|
self::ORDER_URL_1, |
|
self::USER_ID_1 |
|
); |
|
} |
|
|
|
private function createValidItemForUser1(): Item |
|
{ |
|
return new Item( |
|
self::ITEM_ID_2, |
|
self::ITEM_NAME_2, |
|
new DateTimeImmutable('+1 day'), |
|
self::ORDER_URL_2, |
|
self::USER_ID_1 |
|
); |
|
} |
|
|
|
private function createExpiredItemForUser2(): Item |
|
{ |
|
return new Item( |
|
self::ITEM_ID_3, |
|
self::ITEM_NAME_3, |
|
new DateTimeImmutable('-1 day'), |
|
self::ORDER_URL_3, |
|
self::USER_ID_2 |
|
); |
|
} |
|
|
|
public function testWhenItemIsSavedThenFileIsCreated(): void |
|
{ |
|
// Given |
|
$item = $this->createTestItem1(); |
|
|
|
// When |
|
$this->repository->save($item); |
|
|
|
// Then |
|
$filePath = $this->testStoragePath . '/items.json'; |
|
$this->assertFileExists($filePath); |
|
|
|
$fileContent = file_get_contents($filePath); |
|
$data = json_decode($fileContent, true); |
|
|
|
$this->assertIsArray($data); |
|
$this->assertCount(1, $data); |
|
|
|
// Compare the essential fields |
|
$this->assertEquals($item->getId(), $data[$item->getId()]['id']); |
|
$this->assertEquals($item->getName(), $data[$item->getId()]['name']); |
|
$this->assertEquals($item->getOrderUrl(), $data[$item->getId()]['orderUrl']); |
|
$this->assertEquals($item->getUserId(), $data[$item->getId()]['userId']); |
|
} |
|
|
|
public function testWhenItemExistsThenFindByIdReturnsItem(): void |
|
{ |
|
// Given |
|
$item = $this->createTestItem1(); |
|
$this->repository->save($item); |
|
|
|
// When |
|
$foundItem = $this->repository->findById(self::ITEM_ID_1); |
|
|
|
// Then |
|
$this->assertNotNull($foundItem); |
|
$this->assertSame($item->getId(), $foundItem->getId()); |
|
$this->assertSame($item->getName(), $foundItem->getName()); |
|
} |
|
|
|
public function testWhenItemDoesNotExistThenFindByIdReturnsNull(): void |
|
{ |
|
// When |
|
$foundItem = $this->repository->findById(self::NON_EXISTENT_ID); |
|
|
|
// Then |
|
$this->assertNull($foundItem); |
|
} |
|
|
|
public function testWhenUserHasMultipleItemsThenFindByUserIdReturnsAllUserItems(): void |
|
{ |
|
// Given |
|
$item1 = $this->createTestItem1(); |
|
$item2 = $this->createTestItem2(); |
|
$item3 = $this->createTestItem3(); |
|
|
|
$this->repository->save($item1); |
|
$this->repository->save($item2); |
|
$this->repository->save($item3); |
|
|
|
// When |
|
$userItems = $this->repository->findByUserId(self::USER_ID_1); |
|
|
|
// Then |
|
$this->assertCount(2, $userItems); |
|
$this->assertContainsEquals($item1->getId(), array_map(fn($i) => $i->getId(), $userItems)); |
|
$this->assertContainsEquals($item3->getId(), array_map(fn($i) => $i->getId(), $userItems)); |
|
} |
|
|
|
public function testWhenMultipleItemsExistThenFindAllReturnsAllItems(): void |
|
{ |
|
// Given |
|
$item1 = $this->createTestItem1(); |
|
$item2 = $this->createTestItem2(); |
|
|
|
$this->repository->save($item1); |
|
$this->repository->save($item2); |
|
|
|
// When |
|
$allItems = $this->repository->findAll(); |
|
|
|
// Then |
|
$this->assertCount(2, $allItems); |
|
$this->assertContainsEquals($item1->getId(), array_map(fn($i) => $i->getId(), $allItems)); |
|
$this->assertContainsEquals($item2->getId(), array_map(fn($i) => $i->getId(), $allItems)); |
|
} |
|
|
|
public function testWhenItemIsDeletedThenItIsNoLongerFound(): void |
|
{ |
|
// Given |
|
$item = $this->createTestItem1(); |
|
$this->repository->save($item); |
|
|
|
// When |
|
$this->repository->delete(self::ITEM_ID_1); |
|
|
|
// Then |
|
$foundItem = $this->repository->findById(self::ITEM_ID_1); |
|
$this->assertNull($foundItem); |
|
} |
|
|
|
public function testWhenNonExistentItemIsDeletedThenExceptionIsThrown(): void |
|
{ |
|
// Given & When & Then |
|
$this->expectException(ApplicationException::class); |
|
$this->expectExceptionMessage("Item '" . self::NON_EXISTENT_ID . "' not found"); |
|
|
|
$this->repository->delete(self::NON_EXISTENT_ID); |
|
} |
|
|
|
public function testWhenFilteringByExpirationThenOnlyExpiredItemsAreReturned(): void |
|
{ |
|
// Given |
|
$expiredItem = $this->createExpiredItem(); |
|
$validItem = $this->createValidItem(); |
|
|
|
$this->repository->save($expiredItem); |
|
$this->repository->save($validItem); |
|
|
|
// When |
|
$now = new DateTimeImmutable(); |
|
$specification = new FilterSpecification('expirationDate'); |
|
$specification->lessEq($now->format(self::DATE_FORMAT)); |
|
|
|
$expiredItems = $this->repository->findWhere($specification); |
|
|
|
// Then |
|
$this->assertCount(1, $expiredItems); |
|
$this->assertSame($expiredItem->getId(), $expiredItems[0]->getId()); |
|
} |
|
|
|
public function testWhenFilteringByUserIdThenOnlyUserItemsAreReturned(): void |
|
{ |
|
// Given |
|
$item1 = $this->createTestItem1(); |
|
$item2 = $this->createTestItem2(); |
|
$item3 = $this->createTestItem3(); |
|
|
|
$this->repository->save($item1); |
|
$this->repository->save($item2); |
|
$this->repository->save($item3); |
|
|
|
// When |
|
$specification = new FilterSpecification('userId'); |
|
$specification->equals(self::USER_ID_1); |
|
|
|
$userItems = $this->repository->findWhere($specification); |
|
|
|
// Then |
|
$this->assertCount(2, $userItems); |
|
$this->assertContainsEquals($item1->getId(), array_map(fn($i) => $i->getId(), $userItems)); |
|
$this->assertContainsEquals($item3->getId(), array_map(fn($i) => $i->getId(), $userItems)); |
|
} |
|
|
|
public function testWhenUsingComplexFilterThenOnlyMatchingItemsAreReturned(): void |
|
{ |
|
// Given |
|
$item1 = $this->createExpiredItemForUser1(); |
|
$item2 = $this->createValidItemForUser1(); |
|
$item3 = $this->createExpiredItemForUser2(); |
|
|
|
$this->repository->save($item1); |
|
$this->repository->save($item2); |
|
$this->repository->save($item3); |
|
|
|
// When |
|
$now = new DateTimeImmutable(); |
|
$specification = new FilterSpecification('userId'); |
|
$specification->equals(self::USER_ID_1) |
|
->and('expirationDate') |
|
->lessEq($now->format(self::DATE_FORMAT)); |
|
|
|
$filteredItems = $this->repository->findWhere($specification); |
|
|
|
// Then |
|
$this->assertCount(1, $filteredItems); |
|
$this->assertSame($item1->getId(), $filteredItems[0]->getId()); |
|
} |
|
} |