|
|
|
@ -15,11 +15,21 @@ use Psr\Log\LoggerInterface; |
|
|
|
|
|
|
|
|
|
|
|
class AddItemTest extends TestCase |
|
|
|
class AddItemTest extends TestCase |
|
|
|
{ |
|
|
|
{ |
|
|
|
|
|
|
|
private const MOCKED_NOW = '2023-01-01 12:00:00'; |
|
|
|
|
|
|
|
private const NOT_EXPIRED_DATE = '2023-01-02 12:00:00'; |
|
|
|
|
|
|
|
private const EXPIRED_DATE = '2022-12-31 12:00:00'; |
|
|
|
|
|
|
|
private const ITEM_ID = 'test-item-id'; |
|
|
|
|
|
|
|
private const ITEM_NAME = 'Test Item'; |
|
|
|
|
|
|
|
private const ORDER_URL = 'http://example.com/order'; |
|
|
|
|
|
|
|
private const USER_ID = 'test-user-id'; |
|
|
|
|
|
|
|
private const DATE_FORMAT = 'Y-m-d H:i:s'; |
|
|
|
|
|
|
|
|
|
|
|
private AddItem $addItem; |
|
|
|
private AddItem $addItem; |
|
|
|
private IItemRepository&\PHPUnit\Framework\MockObject\MockObject $itemRepository; |
|
|
|
private IItemRepository&\PHPUnit\Framework\MockObject\MockObject $itemRepository; |
|
|
|
private IOrderService&\PHPUnit\Framework\MockObject\MockObject $orderService; |
|
|
|
private IOrderService&\PHPUnit\Framework\MockObject\MockObject $orderService; |
|
|
|
private ITimeProvider&\PHPUnit\Framework\MockObject\MockObject $timeProvider; |
|
|
|
private ITimeProvider&\PHPUnit\Framework\MockObject\MockObject $timeProvider; |
|
|
|
private LoggerInterface&\PHPUnit\Framework\MockObject\MockObject $logger; |
|
|
|
private LoggerInterface&\PHPUnit\Framework\MockObject\MockObject $logger; |
|
|
|
|
|
|
|
private DateTimeImmutable $fixedCurrentTime; |
|
|
|
|
|
|
|
|
|
|
|
protected function setUp(): void |
|
|
|
protected function setUp(): void |
|
|
|
{ |
|
|
|
{ |
|
|
|
@ -28,6 +38,9 @@ class AddItemTest extends TestCase |
|
|
|
$this->timeProvider = $this->createMock(ITimeProvider::class); |
|
|
|
$this->timeProvider = $this->createMock(ITimeProvider::class); |
|
|
|
$this->logger = $this->createMock(LoggerInterface::class); |
|
|
|
$this->logger = $this->createMock(LoggerInterface::class); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->fixedCurrentTime = new DateTimeImmutable(self::MOCKED_NOW); |
|
|
|
|
|
|
|
$this->timeProvider->method('now')->willReturn($this->fixedCurrentTime); |
|
|
|
|
|
|
|
|
|
|
|
$this->addItem = new AddItem( |
|
|
|
$this->addItem = new AddItem( |
|
|
|
$this->itemRepository, |
|
|
|
$this->itemRepository, |
|
|
|
$this->orderService, |
|
|
|
$this->orderService, |
|
|
|
@ -36,69 +49,104 @@ class AddItemTest extends TestCase |
|
|
|
); |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function testExecuteShouldSaveItemWhenNotExpired(): void |
|
|
|
private function createTestItem(): array |
|
|
|
{ |
|
|
|
{ |
|
|
|
$userId = 'test-user-id'; |
|
|
|
return [ |
|
|
|
$itemName = 'Test Item'; |
|
|
|
'name' => self::ITEM_NAME, |
|
|
|
$expirationDate = new DateTimeImmutable('+1 day'); |
|
|
|
'expirationDate' => new DateTimeImmutable(self::NOT_EXPIRED_DATE), // 1 day in the future |
|
|
|
$orderUrl = 'http://example.com/order'; |
|
|
|
'orderUrl' => self::ORDER_URL, |
|
|
|
|
|
|
|
'userId' => self::USER_ID |
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$this->timeProvider->method('now') |
|
|
|
private function createExpiredTestItem(): array |
|
|
|
->willReturn(new DateTimeImmutable()); |
|
|
|
{ |
|
|
|
|
|
|
|
return [ |
|
|
|
|
|
|
|
'name' => self::ITEM_NAME, |
|
|
|
|
|
|
|
'expirationDate' => new DateTimeImmutable(self::EXPIRED_DATE), // 1 day in the past |
|
|
|
|
|
|
|
'orderUrl' => self::ORDER_URL, |
|
|
|
|
|
|
|
'userId' => self::USER_ID |
|
|
|
|
|
|
|
]; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Capture the saved item |
|
|
|
private function createItemWithExpiration(string $expiration): array |
|
|
|
$savedItem = null; |
|
|
|
{ |
|
|
|
$this->itemRepository->expects($this->once()) |
|
|
|
return [ |
|
|
|
->method('save') |
|
|
|
'name' => self::ITEM_NAME, |
|
|
|
->with($this->callback(function (Item $item) use ($itemName, $orderUrl, $userId, &$savedItem) { |
|
|
|
'expirationDate' => new DateTimeImmutable($expiration), |
|
|
|
$savedItem = $item; |
|
|
|
'orderUrl' => self::ORDER_URL, |
|
|
|
return $item->getName() === $itemName && |
|
|
|
'userId' => self::USER_ID |
|
|
|
$item->getOrderUrl() === $orderUrl && |
|
|
|
]; |
|
|
|
$item->getUserId() === $userId && |
|
|
|
} |
|
|
|
!$item->isOrdered(); |
|
|
|
|
|
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->orderService->expects($this->never()) |
|
|
|
private function getItemMatcher(array $expectedItem): callable |
|
|
|
->method('orderItem'); |
|
|
|
{ |
|
|
|
|
|
|
|
return function (Item $item) use ($expectedItem) { |
|
|
|
|
|
|
|
return $item->getName() === $expectedItem['name'] && |
|
|
|
|
|
|
|
$item->getOrderUrl() === $expectedItem['orderUrl'] && |
|
|
|
|
|
|
|
$item->getUserId() === $expectedItem['userId']; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Mock findById to return the saved item |
|
|
|
public function testWhenItemNotExpiredThenItemSaved(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
$this->itemRepository->expects($this->once()) |
|
|
|
$this->itemRepository->expects($this->once()) |
|
|
|
->method('findById') |
|
|
|
->method('save') |
|
|
|
->willReturnCallback(function ($id) use (&$savedItem) { |
|
|
|
->with($this->callback($this->getItemMatcher($testItem))); |
|
|
|
return $savedItem; |
|
|
|
|
|
|
|
}); |
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$resultId = $this->addItem->execute($itemName, $expirationDate->format('Y-m-d H:i:s'), $orderUrl, $userId); |
|
|
|
public function testWhenItemNotExpiredThenOrderIsNotPlaced(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
|
|
|
|
|
|
|
|
// Retrieve the saved item to verify its properties |
|
|
|
$this->orderService->expects($this->never())->method('orderItem'); |
|
|
|
$result = $this->itemRepository->findById($resultId); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->assertSame($itemName, $result->getName()); |
|
|
|
// When & Then |
|
|
|
// Compare DateTime objects without microseconds |
|
|
|
$this->addItem->execute( |
|
|
|
$this->assertEquals($expirationDate->format('Y-m-d H:i:s'), $result->getExpirationDate()->format('Y-m-d H:i:s')); |
|
|
|
$testItem['name'], |
|
|
|
$this->assertSame($orderUrl, $result->getOrderUrl()); |
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
$this->assertSame($userId, $result->getUserId()); |
|
|
|
$testItem['orderUrl'], |
|
|
|
$this->assertFalse($result->isOrdered()); |
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function testExecuteShouldPlaceOrderWhenItemIsExpired(): void |
|
|
|
public function testWhenItemNotExpiredThenNewItemIdIsReturned(): void |
|
|
|
{ |
|
|
|
{ |
|
|
|
$userId = 'test-user-id'; |
|
|
|
// Given |
|
|
|
$itemName = 'Test Item'; |
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
$expirationDate = new DateTimeImmutable('-1 day'); |
|
|
|
$this->orderService->expects($this->never())->method('orderItem'); |
|
|
|
$orderUrl = 'http://example.com/order'; |
|
|
|
|
|
|
|
|
|
|
|
// When |
|
|
|
|
|
|
|
$resultId = $this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
$this->timeProvider->method('now') |
|
|
|
// Then |
|
|
|
->willReturn(new DateTimeImmutable()); |
|
|
|
$this->assertNotNull($resultId); |
|
|
|
|
|
|
|
$this->assertNotEmpty($resultId); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenItemIsExpiredThenOrderPlaced(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createExpiredTestItem(); |
|
|
|
|
|
|
|
|
|
|
|
$savedItem = null; |
|
|
|
|
|
|
|
$orderedItem = null; |
|
|
|
$orderedItem = null; |
|
|
|
$this->itemRepository->expects($this->once()) |
|
|
|
$this->itemRepository->expects($this->never())->method('save'); |
|
|
|
->method('save') |
|
|
|
|
|
|
|
->with($this->callback(function (Item $item) use (&$savedItem) { |
|
|
|
|
|
|
|
$savedItem = $item; |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->orderService->expects($this->once()) |
|
|
|
$this->orderService->expects($this->once()) |
|
|
|
->method('orderItem') |
|
|
|
->method('orderItem') |
|
|
|
@ -107,106 +155,340 @@ class AddItemTest extends TestCase |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
})); |
|
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
// Mock findById to return the ordered item |
|
|
|
|
|
|
|
$this->itemRepository->expects($this->once()) |
|
|
|
|
|
|
|
->method('findById') |
|
|
|
|
|
|
|
->willReturnCallback(function ($id) use (&$orderedItem) { |
|
|
|
|
|
|
|
// Mark the item as ordered before returning it |
|
|
|
|
|
|
|
if ($orderedItem) { |
|
|
|
|
|
|
|
$orderedItem->markAsOrdered(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return $orderedItem; |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$resultId = $this->addItem->execute($itemName, $expirationDate->format('Y-m-d H:i:s'), $orderUrl, $userId); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Retrieve the saved item to verify its properties |
|
|
|
// When |
|
|
|
$result = $this->itemRepository->findById($resultId); |
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
$this->assertTrue($result->isOrdered()); |
|
|
|
// Then |
|
|
|
|
|
|
|
$this->assertNotNull($orderedItem); |
|
|
|
|
|
|
|
$this->assertEquals($testItem['name'], $orderedItem?->getName()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function testExecuteShouldThrowExceptionWhenItemNameIsEmpty(): void |
|
|
|
public function testWhenItemNameIsEmptyThenExceptionThrown(): void |
|
|
|
{ |
|
|
|
{ |
|
|
|
$userId = 'test-user-id'; |
|
|
|
// Given |
|
|
|
$itemName = ''; |
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
$expirationDate = new DateTimeImmutable('+1 day'); |
|
|
|
$testItem['name'] = ''; |
|
|
|
$orderUrl = 'http://example.com/order'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->expectException(\AutoStore\Application\Exceptions\ApplicationException::class); |
|
|
|
$this->expectException(\AutoStore\Application\Exceptions\ApplicationException::class); |
|
|
|
$this->expectExceptionMessage('Failed to add item: Item name cannot be empty'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->addItem->execute($itemName, $expirationDate->format('Y-m-d H:i:s'), $orderUrl, $userId); |
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function testExecuteShouldThrowExceptionWhenOrderUrlIsEmpty(): void |
|
|
|
public function testWhenOrderUrlIsEmptyThenExceptionThrown(): void |
|
|
|
{ |
|
|
|
{ |
|
|
|
$userId = 'test-user-id'; |
|
|
|
// Given |
|
|
|
$itemName = 'Test Item'; |
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
$expirationDate = new DateTimeImmutable('+1 day'); |
|
|
|
$testItem['orderUrl'] = ''; |
|
|
|
$orderUrl = ''; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->expectException(\AutoStore\Application\Exceptions\ApplicationException::class); |
|
|
|
$this->expectException(\AutoStore\Application\Exceptions\ApplicationException::class); |
|
|
|
$this->expectExceptionMessage('Failed to add item: Order URL cannot be empty'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->addItem->execute($itemName, $expirationDate->format('Y-m-d H:i:s'), $orderUrl, $userId); |
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function testExecuteShouldThrowExceptionWhenUserIdIsEmpty(): void |
|
|
|
public function testWhenUserIdIsEmptyThenExceptionThrown(): void |
|
|
|
{ |
|
|
|
{ |
|
|
|
$userId = ''; |
|
|
|
// Given |
|
|
|
$itemName = 'Test Item'; |
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
$expirationDate = new DateTimeImmutable('+1 day'); |
|
|
|
$testItem['userId'] = ''; |
|
|
|
$orderUrl = 'http://example.com/order'; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->expectException(\AutoStore\Application\Exceptions\ApplicationException::class); |
|
|
|
$this->expectException(\AutoStore\Application\Exceptions\ApplicationException::class); |
|
|
|
$this->expectExceptionMessage('Failed to add item: User ID cannot be empty'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->addItem->execute($itemName, $expirationDate->format('Y-m-d H:i:s'), $orderUrl, $userId); |
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function testExecuteShouldLogErrorWhenOrderServiceFails(): void |
|
|
|
public function testWhenOrderServiceFailsThenErrorLogged(): void |
|
|
|
{ |
|
|
|
{ |
|
|
|
$userId = 'test-user-id'; |
|
|
|
$userId = self::USER_ID; |
|
|
|
$itemName = 'Test Item'; |
|
|
|
$itemName = self::ITEM_NAME; |
|
|
|
$expirationDate = new DateTimeImmutable('-1 day'); |
|
|
|
$expirationDate = new DateTimeImmutable(self::EXPIRED_DATE); |
|
|
|
$orderUrl = 'http://example.com/order'; |
|
|
|
$orderUrl = self::ORDER_URL; |
|
|
|
|
|
|
|
|
|
|
|
$this->timeProvider->method('now') |
|
|
|
|
|
|
|
->willReturn(new DateTimeImmutable()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Mock the repository to return a saved item |
|
|
|
// Mock the repository to return a saved item |
|
|
|
$savedItem = null; |
|
|
|
$this->itemRepository->expects($this->never())->method('save'); |
|
|
|
$this->itemRepository->expects($this->once()) |
|
|
|
|
|
|
|
->method('save') |
|
|
|
|
|
|
|
->with($this->callback(function (Item $item) use (&$savedItem) { |
|
|
|
|
|
|
|
$savedItem = $item; |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
})); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Mock the order service to throw an exception |
|
|
|
// Mock the order service to throw an exception |
|
|
|
$this->orderService->expects($this->once()) |
|
|
|
$this->orderService->expects($this->once()) |
|
|
|
->method('orderItem') |
|
|
|
->method('orderItem') |
|
|
|
->willThrowException(new \RuntimeException('Order service failed')); |
|
|
|
->willThrowException(new \RuntimeException('Order service failed')); |
|
|
|
|
|
|
|
|
|
|
|
$this->logger->expects($this->once()) |
|
|
|
$this->logger->expects($this->once())->method('error'); |
|
|
|
->method('error') |
|
|
|
|
|
|
|
->with($this->stringContains('Failed to place order for expired item')); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Mock findById to return the saved item |
|
|
|
|
|
|
|
$this->itemRepository->expects($this->once()) |
|
|
|
|
|
|
|
->method('findById') |
|
|
|
|
|
|
|
->willReturnCallback(function ($id) use (&$savedItem) { |
|
|
|
|
|
|
|
return $savedItem; |
|
|
|
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The handler should not throw an exception when the order service fails |
|
|
|
// The handler should not throw an exception when the order service fails |
|
|
|
// It should log the error and continue |
|
|
|
// It should log the error and continue |
|
|
|
$resultId = $this->addItem->execute($itemName, $expirationDate->format('Y-m-d H:i:s'), $orderUrl, $userId); |
|
|
|
$this->addItem->execute($itemName, $expirationDate->format(self::DATE_FORMAT), $orderUrl, $userId); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenItemIsExpiredThenOrderIsPlaced(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createExpiredTestItem(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->orderService->expects($this->once())->method('orderItem'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenItemIsExpiredThenItemIsNotSaved(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createExpiredTestItem(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->itemRepository->expects($this->never())->method('save'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenItemIsExpiredThenNullIdIsReturned(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createExpiredTestItem(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When |
|
|
|
|
|
|
|
$resultId = $this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Then |
|
|
|
|
|
|
|
$this->assertNull($resultId); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenItemExpirationDateIsExactlyCurrentTimeThenItemIsSaved(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createItemWithExpiration($this->fixedCurrentTime->format(self::DATE_FORMAT)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->itemRepository->expects($this->never())->method('save'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenItemExpirationDateIsExactlyCurrentTimeThenOrderIsPlaced(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createItemWithExpiration($this->fixedCurrentTime->format(self::DATE_FORMAT)); |
|
|
|
|
|
|
|
|
|
|
|
// Retrieve the saved item to verify its properties |
|
|
|
$this->orderService->expects($this->once())->method('orderItem'); |
|
|
|
$result = $this->itemRepository->findById($resultId); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->assertFalse($result->isOrdered()); |
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenItemExpirationDateIsExactlyCurrentTimeThenNullIdIsReturned(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createItemWithExpiration($this->fixedCurrentTime->format(self::DATE_FORMAT)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When |
|
|
|
|
|
|
|
$resultId = $this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Then |
|
|
|
|
|
|
|
$this->assertNull($resultId); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenItemExpirationDateIsInFutureThenItemSaved(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->itemRepository->expects($this->once())->method('save'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When |
|
|
|
|
|
|
|
$resultId = $this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Then |
|
|
|
|
|
|
|
$this->assertNotEmpty($resultId); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenRepositorySaveThrowsExceptionThenRuntimeExceptionThrown(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
|
|
|
|
$expectedException = new \RuntimeException('Repository error'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->itemRepository->expects($this->once()) |
|
|
|
|
|
|
|
->method('save') |
|
|
|
|
|
|
|
->willThrowException($expectedException); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->expectException(\RuntimeException::class); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenRepositorySaveThrowsExceptionThenOrderIsNotPlaced(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
|
|
|
|
$expectedException = new \RuntimeException('Repository error'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->itemRepository->expects($this->once()) |
|
|
|
|
|
|
|
->method('save') |
|
|
|
|
|
|
|
->willThrowException($expectedException); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->orderService->expects($this->never())->method('orderItem'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->expectException(\RuntimeException::class); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenOrderServiceThrowsExceptionThenRuntimeExceptionThrown(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createExpiredTestItem(); |
|
|
|
|
|
|
|
$expectedException = new \RuntimeException('Order service error'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->itemRepository->expects($this->never())->method('save'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->orderService->expects($this->once()) |
|
|
|
|
|
|
|
->method('orderItem') |
|
|
|
|
|
|
|
->willThrowException($expectedException); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When & Then |
|
|
|
|
|
|
|
// The implementation logs the exception and does not throw, so we just call execute |
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenClockThrowsExceptionThenRuntimeExceptionThrown(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
|
|
|
|
$expectedException = new \RuntimeException('Clock error'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->timeProvider->method('now') |
|
|
|
|
|
|
|
->willThrowException($expectedException); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->itemRepository->expects($this->never())->method('save'); |
|
|
|
|
|
|
|
$this->orderService->expects($this->never())->method('orderItem'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->expectException(\RuntimeException::class); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenClockThrowsExceptionThenItemIsNotSaved(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
|
|
|
|
$expectedException = new \RuntimeException('Clock error'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->timeProvider->method('now') |
|
|
|
|
|
|
|
->willThrowException($expectedException); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->itemRepository->expects($this->never())->method('save'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->expectException(\RuntimeException::class); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function testWhenClockThrowsExceptionThenOrderIsNotPlaced(): void |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
// Given |
|
|
|
|
|
|
|
$testItem = $this->createTestItem(); |
|
|
|
|
|
|
|
$expectedException = new \RuntimeException('Clock error'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->timeProvider->method('now') |
|
|
|
|
|
|
|
->willThrowException($expectedException); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->orderService->expects($this->never())->method('orderItem'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// When & Then |
|
|
|
|
|
|
|
$this->expectException(\RuntimeException::class); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$this->addItem->execute( |
|
|
|
|
|
|
|
$testItem['name'], |
|
|
|
|
|
|
|
$testItem['expirationDate']->format(self::DATE_FORMAT), |
|
|
|
|
|
|
|
$testItem['orderUrl'], |
|
|
|
|
|
|
|
$testItem['userId'] |
|
|
|
|
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|