Multiple implementations of the same back-end application. The aim is to provide quick, side-by-side comparisons of different technologies (languages, frameworks, libraries) while preserving consistent business logic across all implementations.
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

<?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());
}
}