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.
 
 
 
 
 
 

223 lines
6.7 KiB

#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 <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <trompeloeil.hpp>
#include <memory>
#include <stdexcept>
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);
}
}