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.
 
 
 
 
 
 

365 lines
11 KiB

#include "infrastructure/repositories/FileItemRepository.h"
#include "domain/entities/Item.h"
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <filesystem>
#include <fstream>
#include <optional>
#include <chrono>
using namespace nxl::autostore;
using Catch::Matchers::Equals;
namespace Test {
constexpr const char* TEST_ITEM_ID_1 = "item123";
constexpr const char* TEST_ITEM_ID_2 = "item456";
constexpr const char* TEST_ITEM_NAME_1 = "testitem";
constexpr const char* TEST_ITEM_NAME_2 = "anotheritem";
constexpr const char* TEST_ORDER_URL_1 = "https://example.com/order1";
constexpr const char* TEST_ORDER_URL_2 = "https://example.com/order2";
constexpr const char* TEST_USER_ID_1 = "user123";
constexpr const char* TEST_USER_ID_2 = "user456";
constexpr const char* NON_EXISTENT_ID = "nonexistent";
constexpr const char* NON_EXISTENT_USER_ID = "nonexistentuser";
constexpr const char* TEST_DIR_NAME = "autostore_test";
constexpr const char* TEST_DB_FILE_NAME = "test_items.json";
// Helper function to create a test item with default values
domain::Item
createTestItem(const std::string& id = TEST_ITEM_ID_1,
const std::string& name = TEST_ITEM_NAME_1,
const std::string& orderUrl = TEST_ORDER_URL_1,
const std::string& userId = TEST_USER_ID_1,
const std::chrono::system_clock::time_point& expirationDate =
std::chrono::system_clock::now() + std::chrono::hours(24))
{
domain::Item item;
item.id = id;
item.name = name;
item.orderUrl = orderUrl;
item.userId = userId;
item.expirationDate = expirationDate;
return item;
}
// Helper function to create a second test item
domain::Item createSecondTestItem()
{
return createTestItem(
TEST_ITEM_ID_2, TEST_ITEM_NAME_2, TEST_ORDER_URL_2, TEST_USER_ID_2,
std::chrono::system_clock::now() + std::chrono::hours(48));
}
// Helper function to set up test environment
std::string setupTestEnvironment()
{
std::filesystem::path testDir =
std::filesystem::temp_directory_path() / TEST_DIR_NAME;
std::filesystem::create_directories(testDir);
std::string testDbPath = (testDir / TEST_DB_FILE_NAME).string();
// Clean up any existing test file
if (std::filesystem::exists(testDbPath)) {
std::filesystem::remove(testDbPath);
}
return testDbPath;
}
// Helper function to clean up test environment
void cleanupTestEnvironment()
{
std::filesystem::path testDir =
std::filesystem::temp_directory_path() / TEST_DIR_NAME;
if (std::filesystem::exists(testDir)) {
std::filesystem::remove_all(testDir);
}
}
// Helper function to verify item properties match expected values
void verifyItemProperties(const domain::Item& item,
const std::string& expectedId,
const std::string& expectedName,
const std::string& expectedOrderUrl,
const std::string& expectedUserId)
{
REQUIRE(item.id == expectedId);
REQUIRE(item.name == expectedName);
REQUIRE(item.orderUrl == expectedOrderUrl);
REQUIRE(item.userId == expectedUserId);
}
// Helper function to verify item properties match default test item values
void verifyDefaultTestItem(const domain::Item& item)
{
verifyItemProperties(item, TEST_ITEM_ID_1, TEST_ITEM_NAME_1, TEST_ORDER_URL_1,
TEST_USER_ID_1);
}
// Helper function to verify item properties match second test item values
void verifySecondTestItem(const domain::Item& item)
{
verifyItemProperties(item, TEST_ITEM_ID_2, TEST_ITEM_NAME_2, TEST_ORDER_URL_2,
TEST_USER_ID_2);
}
} // namespace Test
TEST_CASE("FileItemRepository Integration Tests",
"[integration][FileItemRepository]")
{
// Setup test environment
std::string testDbPath = Test::setupTestEnvironment();
SECTION("when a new item is saved then it can be found by id")
{
// Given
infrastructure::FileItemRepository repository(testDbPath);
domain::Item testItem = Test::createTestItem();
// When
repository.save(testItem);
// Then
auto foundItem = repository.findById(Test::TEST_ITEM_ID_1);
REQUIRE(foundItem.has_value());
Test::verifyDefaultTestItem(*foundItem);
}
SECTION("when a new item is saved then it can be found by user")
{
// Given
infrastructure::FileItemRepository repository(testDbPath);
domain::Item testItem = Test::createTestItem();
// When
repository.save(testItem);
// Then
auto userItems = repository.findByUser(Test::TEST_USER_ID_1);
REQUIRE(userItems.size() == 1);
Test::verifyDefaultTestItem(userItems[0]);
}
SECTION("when multiple items are saved then findAll returns all items")
{
// Given
infrastructure::FileItemRepository repository(testDbPath);
domain::Item firstItem = Test::createTestItem();
domain::Item secondItem = Test::createSecondTestItem();
// When
repository.save(firstItem);
repository.save(secondItem);
// Then
auto allItems = repository.findAll();
REQUIRE(allItems.size() == 2);
// Verify both items are present (order doesn't matter)
bool foundFirst = false;
bool foundSecond = false;
for (const auto& item : allItems) {
if (item.id == Test::TEST_ITEM_ID_1) {
Test::verifyDefaultTestItem(item);
foundFirst = true;
} else if (item.id == Test::TEST_ITEM_ID_2) {
Test::verifySecondTestItem(item);
foundSecond = true;
}
}
REQUIRE(foundFirst);
REQUIRE(foundSecond);
}
SECTION("when multiple items for same user are saved then findByUser returns "
"all user items")
{
// Given
infrastructure::FileItemRepository repository(testDbPath);
domain::Item firstItem = Test::createTestItem();
domain::Item secondItem =
Test::createTestItem("item789", "thirditem", "https://example.com/order3",
Test::TEST_USER_ID_1);
// When
repository.save(firstItem);
repository.save(secondItem);
// Then
auto userItems = repository.findByUser(Test::TEST_USER_ID_1);
REQUIRE(userItems.size() == 2);
// Verify both items are present (order doesn't matter)
bool foundFirst = false;
bool foundSecond = false;
for (const auto& item : userItems) {
if (item.id == Test::TEST_ITEM_ID_1) {
Test::verifyDefaultTestItem(item);
foundFirst = true;
} else if (item.id == "item789") {
REQUIRE(item.name == "thirditem");
REQUIRE(item.orderUrl == "https://example.com/order3");
REQUIRE(item.userId == Test::TEST_USER_ID_1);
foundSecond = true;
}
}
REQUIRE(foundFirst);
REQUIRE(foundSecond);
}
SECTION("when an existing item is saved then it is updated")
{
// Given
infrastructure::FileItemRepository repository(testDbPath);
domain::Item testItem = Test::createTestItem();
repository.save(testItem);
// When
testItem.name = "updateditemname";
testItem.orderUrl = "https://updated.example.com/order";
testItem.userId = Test::TEST_USER_ID_2;
repository.save(testItem);
// Then
auto foundItem = repository.findById(Test::TEST_ITEM_ID_1);
REQUIRE(foundItem.has_value());
REQUIRE(foundItem->id == Test::TEST_ITEM_ID_1);
REQUIRE(foundItem->name == "updateditemname");
REQUIRE(foundItem->orderUrl == "https://updated.example.com/order");
REQUIRE(foundItem->userId == Test::TEST_USER_ID_2);
}
SECTION("when an item is removed then it cannot be found by id")
{
// Given
infrastructure::FileItemRepository repository(testDbPath);
domain::Item testItem = Test::createTestItem();
repository.save(testItem);
// When
repository.remove(Test::TEST_ITEM_ID_1);
// Then
auto foundItem = repository.findById(Test::TEST_ITEM_ID_1);
REQUIRE_FALSE(foundItem.has_value());
}
SECTION("when an item is removed then it is not in findByUser")
{
// Given
infrastructure::FileItemRepository repository(testDbPath);
domain::Item testItem = Test::createTestItem();
repository.save(testItem);
// When
repository.remove(Test::TEST_ITEM_ID_1);
// Then
auto userItems = repository.findByUser(Test::TEST_USER_ID_1);
REQUIRE(userItems.empty());
}
SECTION("when an item is removed then it is not in findAll")
{
// Given
infrastructure::FileItemRepository repository(testDbPath);
domain::Item firstItem = Test::createTestItem();
domain::Item secondItem = Test::createSecondTestItem();
repository.save(firstItem);
repository.save(secondItem);
// When
repository.remove(Test::TEST_ITEM_ID_1);
// Then
auto allItems = repository.findAll();
REQUIRE(allItems.size() == 1);
Test::verifySecondTestItem(allItems[0]);
}
SECTION(
"when findById is called with non-existent id then it returns nullopt")
{
// Given
infrastructure::FileItemRepository repository(testDbPath);
// When
auto foundItem = repository.findById(Test::NON_EXISTENT_ID);
// Then
REQUIRE_FALSE(foundItem.has_value());
}
SECTION("when findByUser is called with non-existent user id then it returns "
"empty vector")
{
// Given
infrastructure::FileItemRepository repository(testDbPath);
domain::Item testItem = Test::createTestItem();
repository.save(testItem);
// When
auto userItems = repository.findByUser(Test::NON_EXISTENT_USER_ID);
// Then
REQUIRE(userItems.empty());
}
SECTION("when remove is called with non-existent id then it does nothing")
{
// Given
infrastructure::FileItemRepository repository(testDbPath);
domain::Item testItem = Test::createTestItem();
repository.save(testItem);
// When
repository.remove(Test::NON_EXISTENT_ID);
// Then
auto allItems = repository.findAll();
REQUIRE(allItems.size() == 1);
Test::verifyDefaultTestItem(allItems[0]);
}
SECTION(
"when repository is created with existing data file then it loads the data")
{
// Given
{
infrastructure::FileItemRepository firstRepository(testDbPath);
domain::Item testItem = Test::createTestItem();
firstRepository.save(testItem);
}
// When
infrastructure::FileItemRepository secondRepository(testDbPath);
// Then
auto foundItem = secondRepository.findById(Test::TEST_ITEM_ID_1);
REQUIRE(foundItem.has_value());
Test::verifyDefaultTestItem(*foundItem);
}
SECTION("when repository is created with non-existent data file then it "
"starts empty")
{
// Given
std::filesystem::path testDir =
std::filesystem::temp_directory_path() / Test::TEST_DIR_NAME;
std::string nonExistentDbPath = (testDir / "nonexistent.json").string();
// When
infrastructure::FileItemRepository repository(nonExistentDbPath);
// Then
auto allItems = repository.findAll();
REQUIRE(allItems.empty());
}
// Clean up test environment
Test::cleanupTestEnvironment();
}