test: add property and character reputation unit tests

Agent-Logs-Url: https://github.com/DarkflameUniverse/DarkflameServer/sessions/603b2808-f042-447c-ba49-e4a8d9f87856

Co-authored-by: aronwk-aaron <26027722+aronwk-aaron@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-04-05 08:40:50 +00:00
committed by GitHub
parent 6ad65fcfca
commit ce7b771a7c
3 changed files with 218 additions and 3 deletions

View File

@@ -1,6 +1,7 @@
set(DCOMPONENTS_TESTS
"DestroyableComponentTests.cpp"
"PetComponentTests.cpp"
"PropertyReputationTests.cpp"
"SimplePhysicsComponentTests.cpp"
"SavingTests.cpp"
)

View File

@@ -0,0 +1,186 @@
#include "GameDependencies.h"
#include <gtest/gtest.h>
#include "Character.h"
#include "CharacterComponent.h"
#include "Database.h"
#include "Entity.h"
#include "GameDatabase/TestSQL/TestSQLDatabase.h"
// ---------------------------------------------------------------------------
// CharacterComponent reputation tests
// ---------------------------------------------------------------------------
class CharacterReputationTest : public GameDependenciesTest {
protected:
std::unique_ptr<Entity> entity;
std::unique_ptr<Character> character;
CharacterComponent* characterComponent = nullptr;
void SetUp() override {
SetUpDependencies();
entity = std::make_unique<Entity>(1, GameDependenciesTest::info);
character = std::make_unique<Character>(1, nullptr);
entity->SetCharacter(character.get());
character->SetEntity(entity.get());
characterComponent = entity->AddComponent<CharacterComponent>(-1, character.get(), UNASSIGNED_SYSTEM_ADDRESS);
}
void TearDown() override {
entity->SetCharacter(nullptr);
entity.reset();
character.reset();
TearDownDependencies();
}
};
// SetReputation must persist using the character's raw DB id (Character::GetID),
// not the runtime object id (Character::GetObjectID with the CHARACTER bit set).
TEST_F(CharacterReputationTest, SetReputationUsesCharacterDBId) {
constexpr int64_t repValue = 12345;
characterComponent->SetReputation(repValue);
// Reputation must be stored at the DB char id (GetID() == 1).
EXPECT_EQ(Database::Get()->GetCharacterReputation(character->GetID()), repValue);
}
// SetReputation / GetReputation round-trip via in-memory TestSQLDatabase.
TEST_F(CharacterReputationTest, ReputationRoundTrip) {
characterComponent->SetReputation(500);
EXPECT_EQ(characterComponent->GetReputation(), 500);
EXPECT_EQ(Database::Get()->GetCharacterReputation(character->GetID()), 500);
}
// The CharacterComponent constructor must load reputation from the DB using
// Character::GetID() as the lookup key.
TEST_F(CharacterReputationTest, LoadReputationFromDBOnConstruction) {
Database::Get()->SetCharacterReputation(character->GetID(), 9876);
// Re-construct the component; the constructor reads from DB.
auto* freshComp = entity->AddComponent<CharacterComponent>(-1, character.get(), UNASSIGNED_SYSTEM_ADDRESS);
EXPECT_EQ(freshComp->GetReputation(), 9876);
}
// In the test context, Character::GetObjectID() returns LWOOBJID_EMPTY (0) because
// UpdateInfoFromDatabase() is never called for test characters. This mirrors the
// production scenario where GetObjectID() carries the CHARACTER bit and therefore
// differs from GetID(). Verify the two keys are treated independently.
TEST_F(CharacterReputationTest, DBIdAndObjectIdAreDistinctKeys) {
// Precondition: the two IDs must differ so the test is meaningful.
ASSERT_NE(character->GetID(), character->GetObjectID());
characterComponent->SetReputation(42);
// Reputation stored at GetID().
EXPECT_EQ(Database::Get()->GetCharacterReputation(character->GetID()), 42);
// No reputation stored at GetObjectID() (wrong key).
EXPECT_EQ(Database::Get()->GetCharacterReputation(character->GetObjectID()), 0);
}
// ---------------------------------------------------------------------------
// TestSQLDatabase property reputation + contribution storage tests
// ---------------------------------------------------------------------------
class PropertyReputationDBTest : public GameDependenciesTest {
protected:
TestSQLDatabase* testDB = nullptr;
void SetUp() override {
SetUpDependencies();
testDB = dynamic_cast<TestSQLDatabase*>(Database::Get());
ASSERT_NE(testDB, nullptr);
}
void TearDown() override {
TearDownDependencies();
}
};
// UpdatePropertyReputation / GetPropertyReputation round-trip.
TEST_F(PropertyReputationDBTest, PropertyReputationRoundTrip) {
constexpr LWOOBJID propertyId = 42;
Database::Get()->UpdatePropertyReputation(propertyId, 100);
EXPECT_EQ(testDB->GetPropertyReputation(propertyId), 100u);
}
// Overwriting property reputation replaces the previous value.
TEST_F(PropertyReputationDBTest, PropertyReputationOverwrite) {
constexpr LWOOBJID propertyId = 42;
Database::Get()->UpdatePropertyReputation(propertyId, 100);
Database::Get()->UpdatePropertyReputation(propertyId, 250);
EXPECT_EQ(testDB->GetPropertyReputation(propertyId), 250u);
}
// UpdatePropertyReputationContribution stores using the given player ID (expected
// to be the character DB id, not the runtime object id with bits set).
TEST_F(PropertyReputationDBTest, ContributionStoredByCharDBId) {
constexpr LWOOBJID propertyId = 5;
constexpr LWOOBJID charDbId = 1; // raw DB id, no object bits
const std::string date = "2024-01-01";
Database::Get()->UpdatePropertyReputationContribution(propertyId, charDbId, date, 10);
const auto contributions = Database::Get()->GetPropertyReputationContributions(propertyId, date);
ASSERT_EQ(contributions.size(), 1u);
EXPECT_EQ(contributions[0].playerId, charDbId);
EXPECT_EQ(contributions[0].reputationGained, 10u);
}
// A second UpdatePropertyReputationContribution for the same player must upsert
// (update the existing entry) rather than append a duplicate.
TEST_F(PropertyReputationDBTest, ContributionUpsertUpdatesExistingEntry) {
constexpr LWOOBJID propertyId = 5;
constexpr LWOOBJID charDbId = 1;
const std::string date = "2024-01-01";
Database::Get()->UpdatePropertyReputationContribution(propertyId, charDbId, date, 10);
Database::Get()->UpdatePropertyReputationContribution(propertyId, charDbId, date, 30);
const auto contributions = Database::Get()->GetPropertyReputationContributions(propertyId, date);
ASSERT_EQ(contributions.size(), 1u);
EXPECT_EQ(contributions[0].reputationGained, 30u);
}
// Contributions from different dates must not bleed into each other.
TEST_F(PropertyReputationDBTest, ContributionsAreIsolatedByDate) {
constexpr LWOOBJID propertyId = 5;
constexpr LWOOBJID charDbId = 1;
Database::Get()->UpdatePropertyReputationContribution(propertyId, charDbId, "2024-01-01", 10);
Database::Get()->UpdatePropertyReputationContribution(propertyId, charDbId, "2024-01-02", 20);
const auto day1 = Database::Get()->GetPropertyReputationContributions(propertyId, "2024-01-01");
const auto day2 = Database::Get()->GetPropertyReputationContributions(propertyId, "2024-01-02");
ASSERT_EQ(day1.size(), 1u);
EXPECT_EQ(day1[0].reputationGained, 10u);
ASSERT_EQ(day2.size(), 1u);
EXPECT_EQ(day2[0].reputationGained, 20u);
}
// Multiple distinct players can each have their own contribution entry per property/date.
TEST_F(PropertyReputationDBTest, MultiplePlayerContributionsForSameProperty) {
constexpr LWOOBJID propertyId = 5;
const std::string date = "2024-01-01";
Database::Get()->UpdatePropertyReputationContribution(propertyId, 1, date, 10);
Database::Get()->UpdatePropertyReputationContribution(propertyId, 2, date, 20);
const auto contributions = Database::Get()->GetPropertyReputationContributions(propertyId, date);
ASSERT_EQ(contributions.size(), 2u);
const auto FindPlayerRep = [&](const LWOOBJID playerId) {
for (const auto& c : contributions) {
if (c.playerId == playerId) return c.reputationGained;
}
return 0u;
};
EXPECT_EQ(FindPlayerRep(1), 10u);
EXPECT_EQ(FindPlayerRep(2), 20u);
}
// Querying contributions for a date with no entries returns an empty vector.
TEST_F(PropertyReputationDBTest, NoContributionsReturnsEmpty) {
const auto contributions = Database::Get()->GetPropertyReputationContributions(99, "2024-01-01");
EXPECT_TRUE(contributions.empty());
}