mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-04-09 17:26:58 +00:00
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:
committed by
GitHub
parent
6ad65fcfca
commit
ce7b771a7c
@@ -1,6 +1,7 @@
|
|||||||
#ifndef TESTSQLDATABASE_H
|
#ifndef TESTSQLDATABASE_H
|
||||||
#define TESTSQLDATABASE_H
|
#define TESTSQLDATABASE_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include "GameDatabase.h"
|
#include "GameDatabase.h"
|
||||||
|
|
||||||
@@ -113,11 +114,36 @@ class TestSQLDatabase : public GameDatabase {
|
|||||||
m_CharacterReputation[charId] = reputation;
|
m_CharacterReputation[charId] = reputation;
|
||||||
};
|
};
|
||||||
std::vector<IPropertyReputationContribution::ContributionInfo> GetPropertyReputationContributions(
|
std::vector<IPropertyReputationContribution::ContributionInfo> GetPropertyReputationContributions(
|
||||||
const LWOOBJID propertyId, const std::string& date) override { return {}; };
|
const LWOOBJID propertyId, const std::string& date) override {
|
||||||
|
const auto key = std::make_pair(propertyId, date);
|
||||||
|
if (m_PropertyContributions.contains(key)) {
|
||||||
|
return m_PropertyContributions.at(key);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
};
|
||||||
void UpdatePropertyReputationContribution(
|
void UpdatePropertyReputationContribution(
|
||||||
const LWOOBJID propertyId, const LWOOBJID playerId,
|
const LWOOBJID propertyId, const LWOOBJID playerId,
|
||||||
const std::string& date, const uint32_t reputationGained) override {};
|
const std::string& date, const uint32_t reputationGained) override {
|
||||||
void UpdatePropertyReputation(const LWOOBJID propertyId, const uint32_t reputation) override {};
|
const auto key = std::make_pair(propertyId, date);
|
||||||
|
auto& entries = m_PropertyContributions[key];
|
||||||
|
for (auto& entry : entries) {
|
||||||
|
if (entry.playerId == playerId) {
|
||||||
|
entry.reputationGained = reputationGained;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entries.push_back({ playerId, reputationGained });
|
||||||
|
};
|
||||||
|
void UpdatePropertyReputation(const LWOOBJID propertyId, const uint32_t reputation) override {
|
||||||
|
m_PropertyReputation[propertyId] = reputation;
|
||||||
|
};
|
||||||
|
// Test helper: retrieve the property reputation stored via UpdatePropertyReputation.
|
||||||
|
uint32_t GetPropertyReputation(const LWOOBJID propertyId) const {
|
||||||
|
if (m_PropertyReputation.contains(propertyId)) {
|
||||||
|
return m_PropertyReputation.at(propertyId);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsNameInUse(const std::string_view name) override { return false; };
|
bool IsNameInUse(const std::string_view name) override { return false; };
|
||||||
std::optional<IPropertyContents::Model> GetModel(const LWOOBJID modelID) override { return {}; }
|
std::optional<IPropertyContents::Model> GetModel(const LWOOBJID modelID) override { return {}; }
|
||||||
@@ -125,6 +151,8 @@ class TestSQLDatabase : public GameDatabase {
|
|||||||
std::optional<IUgc::Model> GetUgcModel(const LWOOBJID ugcId) override { return {}; }
|
std::optional<IUgc::Model> GetUgcModel(const LWOOBJID ugcId) override { return {}; }
|
||||||
private:
|
private:
|
||||||
std::unordered_map<LWOOBJID, int64_t> m_CharacterReputation;
|
std::unordered_map<LWOOBJID, int64_t> m_CharacterReputation;
|
||||||
|
std::unordered_map<LWOOBJID, uint32_t> m_PropertyReputation;
|
||||||
|
std::map<std::pair<LWOOBJID, std::string>, std::vector<IPropertyReputationContribution::ContributionInfo>> m_PropertyContributions;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!TESTSQLDATABASE_H
|
#endif //!TESTSQLDATABASE_H
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
set(DCOMPONENTS_TESTS
|
set(DCOMPONENTS_TESTS
|
||||||
"DestroyableComponentTests.cpp"
|
"DestroyableComponentTests.cpp"
|
||||||
"PetComponentTests.cpp"
|
"PetComponentTests.cpp"
|
||||||
|
"PropertyReputationTests.cpp"
|
||||||
"SimplePhysicsComponentTests.cpp"
|
"SimplePhysicsComponentTests.cpp"
|
||||||
"SavingTests.cpp"
|
"SavingTests.cpp"
|
||||||
)
|
)
|
||||||
|
|||||||
186
tests/dGameTests/dComponentsTests/PropertyReputationTests.cpp
Normal file
186
tests/dGameTests/dComponentsTests/PropertyReputationTests.cpp
Normal 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());
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user