#include "infrastructure/repositories/FileItemRepository.h" #include "domain/entities/Item.h" #include #include #include #include #include #include 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(); }