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.
 
 
 
 
 
 

312 lines
9.0 KiB

#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
#include <autostore/infrastructure/repositories/FileUserRepository.h>
#include <autostore/domain/entities/User.h>
#include <filesystem>
#include <fstream>
#include <optional>
using namespace nxl::autostore;
using Catch::Matchers::Equals;
namespace Test {
// Constants for magic strings and numbers
constexpr const char* TEST_USER_ID_1 = "user123";
constexpr const char* TEST_USER_ID_2 = "user456";
constexpr const char* TEST_USERNAME_1 = "testuser";
constexpr const char* TEST_USERNAME_2 = "anotheruser";
constexpr const char* TEST_PASSWORD_HASH_1 = "hashedpassword123";
constexpr const char* TEST_PASSWORD_HASH_2 = "hashedpassword456";
constexpr const char* NON_EXISTENT_ID = "nonexistent";
constexpr const char* NON_EXISTENT_USERNAME = "nonexistentuser";
constexpr const char* TEST_DIR_NAME = "autostore_test";
constexpr const char* TEST_DB_FILE_NAME = "test_users.json";
// Helper function to create a test user with default values
domain::User
createTestUser(const std::string& id = TEST_USER_ID_1,
const std::string& username = TEST_USERNAME_1,
const std::string& passwordHash = TEST_PASSWORD_HASH_1)
{
domain::User user;
user.id = id;
user.username = username;
user.passwordHash = passwordHash;
return user;
}
// Helper function to create a second test user
domain::User createSecondTestUser()
{
return createTestUser(TEST_USER_ID_2, TEST_USERNAME_2, TEST_PASSWORD_HASH_2);
}
// 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 user properties match expected values
void verifyUserProperties(const domain::User& user,
const std::string& expectedId,
const std::string& expectedUsername,
const std::string& expectedPasswordHash)
{
REQUIRE(user.id == expectedId);
REQUIRE(user.username == expectedUsername);
REQUIRE(user.passwordHash == expectedPasswordHash);
}
// Helper function to verify user properties match default test user values
void verifyDefaultTestUser(const domain::User& user)
{
verifyUserProperties(user, TEST_USER_ID_1, TEST_USERNAME_1,
TEST_PASSWORD_HASH_1);
}
// Helper function to verify user properties match second test user values
void verifySecondTestUser(const domain::User& user)
{
verifyUserProperties(user, TEST_USER_ID_2, TEST_USERNAME_2,
TEST_PASSWORD_HASH_2);
}
} // namespace Test
TEST_CASE("FileUserRepository Integration Tests",
"[integration][FileUserRepository]")
{
// Setup test environment
std::string testDbPath = Test::setupTestEnvironment();
SECTION("when a new user is saved then it can be found by id")
{
// Given
infrastructure::FileUserRepository repository(testDbPath);
domain::User testUser = Test::createTestUser();
// When
repository.save(testUser);
// Then
auto foundUser = repository.findById(Test::TEST_USER_ID_1);
REQUIRE(foundUser.has_value());
Test::verifyDefaultTestUser(*foundUser);
}
SECTION("when a new user is saved then it can be found by username")
{
// Given
infrastructure::FileUserRepository repository(testDbPath);
domain::User testUser = Test::createTestUser();
// When
repository.save(testUser);
// Then
auto foundUser = repository.findByUsername(Test::TEST_USERNAME_1);
REQUIRE(foundUser.has_value());
Test::verifyDefaultTestUser(*foundUser);
}
SECTION("when multiple users are saved then findAll returns all users")
{
// Given
infrastructure::FileUserRepository repository(testDbPath);
domain::User firstUser = Test::createTestUser();
domain::User secondUser = Test::createSecondTestUser();
// When
repository.save(firstUser);
repository.save(secondUser);
// Then
auto allUsers = repository.findAll();
REQUIRE(allUsers.size() == 2);
// Verify both users are present (order doesn't matter)
bool foundFirst = false;
bool foundSecond = false;
for (const auto& user : allUsers) {
if (user.id == Test::TEST_USER_ID_1) {
Test::verifyDefaultTestUser(user);
foundFirst = true;
} else if (user.id == Test::TEST_USER_ID_2) {
Test::verifySecondTestUser(user);
foundSecond = true;
}
}
REQUIRE(foundFirst);
REQUIRE(foundSecond);
}
SECTION("when an existing user is saved then it is updated")
{
// Given
infrastructure::FileUserRepository repository(testDbPath);
domain::User testUser = Test::createTestUser();
repository.save(testUser);
// When
testUser.username = "updatedusername";
testUser.passwordHash = "updatedpasswordhash";
repository.save(testUser);
// Then
auto foundUser = repository.findById(Test::TEST_USER_ID_1);
REQUIRE(foundUser.has_value());
REQUIRE(foundUser->id == Test::TEST_USER_ID_1);
REQUIRE(foundUser->username == "updatedusername");
REQUIRE(foundUser->passwordHash == "updatedpasswordhash");
}
SECTION("when a user is removed then it cannot be found by id")
{
// Given
infrastructure::FileUserRepository repository(testDbPath);
domain::User testUser = Test::createTestUser();
repository.save(testUser);
// When
repository.remove(Test::TEST_USER_ID_1);
// Then
auto foundUser = repository.findById(Test::TEST_USER_ID_1);
REQUIRE_FALSE(foundUser.has_value());
}
SECTION("when a user is removed then it cannot be found by username")
{
// Given
infrastructure::FileUserRepository repository(testDbPath);
domain::User testUser = Test::createTestUser();
repository.save(testUser);
// When
repository.remove(Test::TEST_USER_ID_1);
// Then
auto foundUser = repository.findByUsername(Test::TEST_USERNAME_1);
REQUIRE_FALSE(foundUser.has_value());
}
SECTION("when a user is removed then it is not in findAll")
{
// Given
infrastructure::FileUserRepository repository(testDbPath);
domain::User firstUser = Test::createTestUser();
domain::User secondUser = Test::createSecondTestUser();
repository.save(firstUser);
repository.save(secondUser);
// When
repository.remove(Test::TEST_USER_ID_1);
// Then
auto allUsers = repository.findAll();
REQUIRE(allUsers.size() == 1);
Test::verifySecondTestUser(allUsers[0]);
}
SECTION(
"when findById is called with non-existent id then it returns nullopt")
{
// Given
infrastructure::FileUserRepository repository(testDbPath);
// When
auto foundUser = repository.findById(Test::NON_EXISTENT_ID);
// Then
REQUIRE_FALSE(foundUser.has_value());
}
SECTION("when findByUsername is called with non-existent username then it "
"returns nullopt")
{
// Given
infrastructure::FileUserRepository repository(testDbPath);
// When
auto foundUser = repository.findByUsername(Test::NON_EXISTENT_USERNAME);
// Then
REQUIRE_FALSE(foundUser.has_value());
}
SECTION("when remove is called with non-existent id then it does nothing")
{
// Given
infrastructure::FileUserRepository repository(testDbPath);
domain::User testUser = Test::createTestUser();
repository.save(testUser);
// When
repository.remove(Test::NON_EXISTENT_ID);
// Then
auto allUsers = repository.findAll();
REQUIRE(allUsers.size() == 1);
Test::verifyDefaultTestUser(allUsers[0]);
}
SECTION(
"when repository is created with existing data file then it loads the data")
{
// Given
{
infrastructure::FileUserRepository firstRepository(testDbPath);
domain::User testUser = Test::createTestUser();
firstRepository.save(testUser);
}
// When
infrastructure::FileUserRepository secondRepository(testDbPath);
// Then
auto foundUser = secondRepository.findById(Test::TEST_USER_ID_1);
REQUIRE(foundUser.has_value());
Test::verifyDefaultTestUser(*foundUser);
}
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::FileUserRepository repository(nonExistentDbPath);
// Then
auto allUsers = repository.findAll();
REQUIRE(allUsers.empty());
}
// Clean up test environment
Test::cleanupTestEnvironment();
}