#include "application/commands/AddItem.h" #include "domain/entities/Item.h" #include "mocks/MockItemRepository.h" #include "mocks/MockTimeProvider.h" #include "mocks/MockOrderService.h" #include "helpers/AddItemTestHelpers.h" #include #include #include #include #include using trompeloeil::_; using namespace nxl::autostore; using namespace std::chrono; TEST_CASE("AddItem Unit Tests", "[unit][AddItem]") { test::MockItemRepository mockRepository; test::MockTimeProvider mockClock; test::MockOrderService mockOrderService; SECTION( "when user id is present and item is not expired then the item is saved") { // Given auto testItem = test::createTestItem(); auto expectedItemId = "saved_item_id"; REQUIRE_CALL(mockRepository, save(testItem)).RETURN(expectedItemId); REQUIRE_CALL(mockClock, now()).RETURN(test::TEST_TIMEPOINT_NOW); FORBID_CALL(mockOrderService, orderItem(_)); application::AddItem addItem(mockRepository, mockClock, mockOrderService); // When auto resultItemId = addItem.execute(std::move(testItem)); // Then REQUIRE(resultItemId == expectedItemId); } SECTION("when item has null user id then a runtime error is thrown") { // Given auto testItem = test::createTestItem(); testItem.userId = domain::User::NULL_ID; FORBID_CALL(mockRepository, save(_)); FORBID_CALL(mockClock, now()); FORBID_CALL(mockOrderService, orderItem(_)); application::AddItem addItem(mockRepository, mockClock, mockOrderService); // When & Then REQUIRE_THROWS_AS(addItem.execute(std::move(testItem)), std::runtime_error); } SECTION("when item is expired then the order is placed") { // Given auto testItem = test::createExpiredTestItem(); REQUIRE_CALL(mockClock, now()).RETURN(test::TEST_TIMEPOINT_NOW); REQUIRE_CALL(mockOrderService, orderItem(_)); FORBID_CALL(mockRepository, save(_)); application::AddItem addItem(mockRepository, mockClock, mockOrderService); // When addItem.execute(std::move(testItem)); // Then // Order was placed (verified by REQUIRE_CALL above) } SECTION("when item is expired then null id is returned") { // Given auto testItem = test::createExpiredTestItem(); REQUIRE_CALL(mockClock, now()).RETURN(test::TEST_TIMEPOINT_NOW); REQUIRE_CALL(mockOrderService, orderItem(_)); FORBID_CALL(mockRepository, save(_)); application::AddItem addItem(mockRepository, mockClock, mockOrderService); // When auto resultItemId = addItem.execute(std::move(testItem)); // Then REQUIRE(resultItemId == domain::Item::NULL_ID); } SECTION("when item expiration date is exactly current time then the order is " "placed") { // Given auto testItem = test::createTestItem(); testItem.expirationDate = test::TEST_TIMEPOINT_NOW; REQUIRE_CALL(mockClock, now()).RETURN(test::TEST_TIMEPOINT_NOW); REQUIRE_CALL(mockOrderService, orderItem(_)); FORBID_CALL(mockRepository, save(_)); application::AddItem addItem(mockRepository, mockClock, mockOrderService); // When addItem.execute(std::move(testItem)); // Then // Order was placed (verified by REQUIRE_CALL above) } SECTION("when item expiration date is exactly current time then null id is " "returned") { // Given auto testItem = test::createTestItem(); testItem.expirationDate = test::TEST_TIMEPOINT_NOW; REQUIRE_CALL(mockClock, now()).RETURN(test::TEST_TIMEPOINT_NOW); REQUIRE_CALL(mockOrderService, orderItem(_)); FORBID_CALL(mockRepository, save(_)); application::AddItem addItem(mockRepository, mockClock, mockOrderService); // When auto resultItemId = addItem.execute(std::move(testItem)); // Then REQUIRE(resultItemId == domain::Item::NULL_ID); } SECTION("when item expiration date is in the future then the item is saved") { // Given auto testItem = test::createTestItem(); auto expectedItemId = "saved_item_id"; REQUIRE_CALL(mockClock, now()).RETURN(test::TEST_TIMEPOINT_NOW); REQUIRE_CALL(mockRepository, save(testItem)).RETURN(expectedItemId); FORBID_CALL(mockOrderService, orderItem(_)); application::AddItem addItem(mockRepository, mockClock, mockOrderService); // When addItem.execute(std::move(testItem)); // Then // Item was saved (verified by REQUIRE_CALL above) } SECTION( "when item expiration date is in the future then the item id is returned") { // Given auto testItem = test::createTestItem(); auto expectedItemId = "saved_item_id"; REQUIRE_CALL(mockClock, now()).RETURN(test::TEST_TIMEPOINT_NOW); REQUIRE_CALL(mockRepository, save(testItem)).RETURN(expectedItemId); FORBID_CALL(mockOrderService, orderItem(_)); application::AddItem addItem(mockRepository, mockClock, mockOrderService); // When auto resultItemId = addItem.execute(std::move(testItem)); // Then REQUIRE(resultItemId == expectedItemId); } SECTION( "when repository save throws exception then a runtime error is thrown") { // Given auto testItem = test::createTestItem(); auto expectedException = std::runtime_error("Repository error"); REQUIRE_CALL(mockClock, now()).RETURN(test::TEST_TIMEPOINT_NOW); REQUIRE_CALL(mockRepository, save(testItem)).THROW(expectedException); FORBID_CALL(mockOrderService, orderItem(_)); application::AddItem addItem(mockRepository, mockClock, mockOrderService); // When & Then REQUIRE_THROWS_AS(addItem.execute(std::move(testItem)), std::runtime_error); } SECTION("when order service throws exception then a runtime error is thrown") { // Given auto testItem = test::createExpiredTestItem(); auto expectedException = std::runtime_error("Order service error"); REQUIRE_CALL(mockClock, now()).RETURN(test::TEST_TIMEPOINT_NOW); REQUIRE_CALL(mockOrderService, orderItem(_)).THROW(expectedException); FORBID_CALL(mockRepository, save(_)); application::AddItem addItem(mockRepository, mockClock, mockOrderService); // When & Then REQUIRE_THROWS_AS(addItem.execute(std::move(testItem)), std::runtime_error); } SECTION("when clock throws exception then a runtime error is thrown") { // Given auto testItem = test::createTestItem(); auto expectedException = std::runtime_error("Clock error"); REQUIRE_CALL(mockClock, now()).THROW(expectedException); FORBID_CALL(mockRepository, save(_)); FORBID_CALL(mockOrderService, orderItem(_)); application::AddItem addItem(mockRepository, mockClock, mockOrderService); // When & Then REQUIRE_THROWS_AS(addItem.execute(std::move(testItem)), std::runtime_error); } }