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.
392 lines
12 KiB
392 lines
12 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 |
|
auto savedItemId = repository.save(testItem); |
|
|
|
// Then |
|
auto foundItem = repository.findById(savedItemId); |
|
REQUIRE(foundItem.has_value()); |
|
REQUIRE(foundItem->id == savedItemId); |
|
REQUIRE(foundItem->name == Test::TEST_ITEM_NAME_1); |
|
REQUIRE(foundItem->orderUrl == Test::TEST_ORDER_URL_1); |
|
REQUIRE(foundItem->userId == Test::TEST_USER_ID_1); |
|
} |
|
|
|
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 |
|
auto savedItemId = repository.save(testItem); |
|
|
|
// Then |
|
auto userItems = repository.findByOwner(Test::TEST_USER_ID_1); |
|
REQUIRE(userItems.size() == 1); |
|
REQUIRE(userItems[0].id == savedItemId); |
|
REQUIRE(userItems[0].name == Test::TEST_ITEM_NAME_1); |
|
REQUIRE(userItems[0].orderUrl == Test::TEST_ORDER_URL_1); |
|
REQUIRE(userItems[0].userId == Test::TEST_USER_ID_1); |
|
} |
|
|
|
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 |
|
auto firstItemId = repository.save(firstItem); |
|
auto secondItemId = repository.save(secondItem); |
|
|
|
// Then |
|
auto allItems = |
|
repository.findWhere([](const domain::Item&) { return true; }); |
|
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 == firstItemId) { |
|
REQUIRE(item.name == Test::TEST_ITEM_NAME_1); |
|
REQUIRE(item.orderUrl == Test::TEST_ORDER_URL_1); |
|
REQUIRE(item.userId == Test::TEST_USER_ID_1); |
|
foundFirst = true; |
|
} else if (item.id == secondItemId) { |
|
REQUIRE(item.name == Test::TEST_ITEM_NAME_2); |
|
REQUIRE(item.orderUrl == Test::TEST_ORDER_URL_2); |
|
REQUIRE(item.userId == Test::TEST_USER_ID_2); |
|
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 |
|
auto firstItemId = repository.save(firstItem); |
|
auto secondItemId = repository.save(secondItem); |
|
|
|
// Then |
|
auto userItems = repository.findByOwner(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 == firstItemId) { |
|
REQUIRE(item.name == Test::TEST_ITEM_NAME_1); |
|
REQUIRE(item.orderUrl == Test::TEST_ORDER_URL_1); |
|
REQUIRE(item.userId == Test::TEST_USER_ID_1); |
|
foundFirst = true; |
|
} else if (item.id == secondItemId) { |
|
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(); |
|
auto savedItemId = repository.save(testItem); |
|
|
|
// When |
|
testItem.id = savedItemId; |
|
testItem.name = "updateditemname"; |
|
testItem.orderUrl = "https://updated.example.com/order"; |
|
testItem.userId = Test::TEST_USER_ID_2; |
|
auto updatedItemId = repository.save(testItem); |
|
|
|
// Then |
|
REQUIRE(savedItemId == updatedItemId); // ID should not change for updates |
|
auto foundItem = repository.findById(updatedItemId); |
|
REQUIRE(foundItem.has_value()); |
|
REQUIRE(foundItem->id == updatedItemId); |
|
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(); |
|
auto savedItemId = repository.save(testItem); |
|
|
|
// When |
|
repository.remove(savedItemId); |
|
|
|
// Then |
|
auto foundItem = repository.findById(savedItemId); |
|
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(); |
|
auto savedItemId = repository.save(testItem); |
|
|
|
// When |
|
repository.remove(savedItemId); |
|
|
|
// Then |
|
auto userItems = repository.findByOwner(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(); |
|
auto firstItemId = repository.save(firstItem); |
|
auto secondItemId = repository.save(secondItem); |
|
|
|
// When |
|
repository.remove(firstItemId); |
|
|
|
// Then |
|
auto allItems = |
|
repository.findWhere([](const domain::Item&) { return true; }); |
|
REQUIRE(allItems.size() == 1); |
|
REQUIRE(allItems[0].id == secondItemId); |
|
REQUIRE(allItems[0].name == Test::TEST_ITEM_NAME_2); |
|
REQUIRE(allItems[0].orderUrl == Test::TEST_ORDER_URL_2); |
|
REQUIRE(allItems[0].userId == Test::TEST_USER_ID_2); |
|
} |
|
|
|
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.findByOwner(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(); |
|
auto savedItemId = repository.save(testItem); |
|
|
|
// When |
|
repository.remove(Test::NON_EXISTENT_ID); |
|
|
|
// Then |
|
auto allItems = |
|
repository.findWhere([](const domain::Item&) { return true; }); |
|
REQUIRE(allItems.size() == 1); |
|
REQUIRE(allItems[0].id == savedItemId); |
|
REQUIRE(allItems[0].name == Test::TEST_ITEM_NAME_1); |
|
REQUIRE(allItems[0].orderUrl == Test::TEST_ORDER_URL_1); |
|
REQUIRE(allItems[0].userId == Test::TEST_USER_ID_1); |
|
} |
|
|
|
SECTION( |
|
"when repository is created with existing data file then it loads the data") |
|
{ |
|
// Given |
|
std::string savedItemId; |
|
{ |
|
infrastructure::FileItemRepository firstRepository(testDbPath); |
|
domain::Item testItem = Test::createTestItem(); |
|
savedItemId = firstRepository.save(testItem); |
|
} |
|
|
|
// When |
|
infrastructure::FileItemRepository secondRepository(testDbPath); |
|
|
|
// Then |
|
auto foundItem = secondRepository.findById(savedItemId); |
|
REQUIRE(foundItem.has_value()); |
|
REQUIRE(foundItem->id == savedItemId); |
|
REQUIRE(foundItem->name == Test::TEST_ITEM_NAME_1); |
|
REQUIRE(foundItem->orderUrl == Test::TEST_ORDER_URL_1); |
|
REQUIRE(foundItem->userId == Test::TEST_USER_ID_1); |
|
} |
|
|
|
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.findWhere([](const domain::Item&) { return true; }); |
|
REQUIRE(allItems.empty()); |
|
} |
|
|
|
// Clean up test environment |
|
Test::cleanupTestEnvironment(); |
|
} |