mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-15 09:04:10 +00:00
feat(db): chat log guild, team and filter result; chat flags
chat_log gets guild_id, team_id and filtered (migrations mysql 100, sqlite 83), and the chat log queries take a time range, a guild, a team, a conversation between two characters and paging back by id. Whispers are their own visibility switch (includeWhispers), apart from team and guild chat (includePrivate). Whisper partners and teams that talked are listed with counts. chat_flags, chat_flag_messages and chat_flag_events (mysql 101, sqlite 84) hold flagged chat with a copy of the messages around it, its status, note, linked player report and history. Open flags are in the dashboard snapshot. Tests on SQLite alone (dDatabaseSqliteTests) and in the MySQL parity tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,3 +14,19 @@ target_include_directories(dDatabaseTests PRIVATE
|
||||
target_link_libraries(dDatabaseTests ${COMMON_LIBRARIES} MariaDB::ConnCpp sqlite3 GTest::gtest_main)
|
||||
|
||||
gtest_discover_tests(dDatabaseTests)
|
||||
|
||||
# The chat history and chat flag tables on SQLite alone (no MariaDB/MySQL needed), so they always run.
|
||||
add_executable(dDatabaseSqliteTests "ChatHistorySqliteTests.cpp")
|
||||
|
||||
target_include_directories(dDatabaseSqliteTests PRIVATE
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/MySQL"
|
||||
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/SQLite"
|
||||
"${PROJECT_SOURCE_DIR}/dNet"
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/nlohmann"
|
||||
"${PROJECT_SOURCE_DIR}/thirdparty/SQLite"
|
||||
)
|
||||
|
||||
# dCommon uses raknet without linking it: link them as a group so the order of the static libraries does not matter
|
||||
target_link_libraries(dDatabaseSqliteTests glm::glm "dNet" "magic_enum" "$<LINK_GROUP:RESCAN,dDatabase,dDatabaseGame,dCommon,raknet>" MariaDB::ConnCpp sqlite3 GTest::gtest_main ${CMAKE_DL_LIBS})
|
||||
|
||||
gtest_discover_tests(dDatabaseSqliteTests)
|
||||
|
||||
309
tests/dDatabaseTests/ChatHistorySqliteTests.cpp
Normal file
309
tests/dDatabaseTests/ChatHistorySqliteTests.cpp
Normal file
@@ -0,0 +1,309 @@
|
||||
/**
|
||||
* The chat history and chat flag tables on SQLite, with no other database needed: a fresh SQLite file gets every
|
||||
* migration (MigrationRunner::RunMigrations, as the servers run them), then the IChatLog and IChatFlags methods are
|
||||
* checked against what was written. DatabaseParityTests compares the same methods with MySQL when one is set up.
|
||||
*/
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "Database.h"
|
||||
#include "dConfig.h"
|
||||
#include "Game.h"
|
||||
#include "Logger.h"
|
||||
#include "MigrationRunner.h"
|
||||
#include "SQLiteDatabase.h"
|
||||
|
||||
namespace Game {
|
||||
Logger* logger = nullptr;
|
||||
dConfig* config = nullptr;
|
||||
}
|
||||
|
||||
namespace {
|
||||
constexpr LWOOBJID ALICE = 1152921510000000001;
|
||||
constexpr LWOOBJID BOB = 1152921510000000002;
|
||||
constexpr LWOOBJID CAROL = 1152921510000000003;
|
||||
constexpr LWOOBJID TEAM = 1700000000000001;
|
||||
|
||||
std::filesystem::path g_Path;
|
||||
std::filesystem::path g_Log;
|
||||
std::unique_ptr<SQLiteDatabase> g_Db;
|
||||
|
||||
class SqliteEnvironment : public ::testing::Environment {
|
||||
public:
|
||||
void SetUp() override {
|
||||
const auto base = std::filesystem::temp_directory_path() / ("dDatabaseSqliteTests_" + std::to_string(getpid()));
|
||||
g_Path = base.string() + ".sqlite";
|
||||
g_Log = base.string() + ".log";
|
||||
for (const auto* suffix : { "", "-wal", "-shm" }) std::filesystem::remove(g_Path.string() + suffix);
|
||||
Game::logger = new Logger(g_Log.string(), false, false);
|
||||
setenv("DATABASE_TYPE", "sqlite", 1);
|
||||
setenv("SQLITE_DATABASE_PATH", g_Path.string().c_str(), 1);
|
||||
Game::config = new dConfig("dDatabaseSqliteTests.ini");
|
||||
Database::Connect();
|
||||
MigrationRunner::RunMigrations();
|
||||
Database::Destroy("dDatabaseSqliteTests migrations");
|
||||
g_Db = std::make_unique<SQLiteDatabase>();
|
||||
g_Db->Connect();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
if (g_Db) g_Db->Destroy("dDatabaseSqliteTests");
|
||||
g_Db.reset();
|
||||
for (const auto* suffix : { "", "-wal", "-shm" }) std::filesystem::remove(g_Path.string() + suffix);
|
||||
std::filesystem::remove(g_Log);
|
||||
delete Game::config;
|
||||
Game::config = nullptr;
|
||||
delete Game::logger;
|
||||
Game::logger = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
const auto* const g_Environment = ::testing::AddGlobalTestEnvironment(new SqliteEnvironment());
|
||||
|
||||
IChatLog::ChatMessage Message(int64_t time, const std::string& channel, LWOOBJID sender, const std::string& senderName, uint32_t account, const std::string& text) {
|
||||
IChatLog::ChatMessage m;
|
||||
m.time = time;
|
||||
m.channel = channel;
|
||||
m.senderId = sender;
|
||||
m.senderName = senderName;
|
||||
m.accountId = account;
|
||||
m.zoneId = 1100;
|
||||
m.instanceId = 2;
|
||||
m.message = text;
|
||||
return m;
|
||||
}
|
||||
|
||||
IChatLog::ChatMessage Whisper(int64_t time, LWOOBJID from, const std::string& fromName, LWOOBJID to, const std::string& toName, const std::string& text) {
|
||||
auto m = Message(time, "whisper", from, fromName, from == ALICE ? 1 : from == BOB ? 2 : 3, text);
|
||||
m.recipientId = to;
|
||||
m.recipientName = toName;
|
||||
return m;
|
||||
}
|
||||
|
||||
std::vector<std::string> Texts(const std::vector<IChatLog::ChatMessage>& messages) {
|
||||
std::vector<std::string> out;
|
||||
for (const auto& m : messages) out.push_back(m.message);
|
||||
return out;
|
||||
}
|
||||
|
||||
// Every test gets the same chat: zone chat, a guild, a team and whispers between three characters
|
||||
class ChatHistorySqlite : public ::testing::Test {
|
||||
protected:
|
||||
static inline std::vector<uint64_t> ids;
|
||||
|
||||
static void SetUpTestSuite() {
|
||||
ASSERT_TRUE(g_Db);
|
||||
auto& db = *g_Db;
|
||||
ids.push_back(db.InsertChatMessage(Message(1000, "zone", ALICE, "Alice", 1, "hello zone")));
|
||||
auto guild = Message(1010, "guild", ALICE, "Alice", 1, "guild hi");
|
||||
guild.guildId = 7;
|
||||
ids.push_back(db.InsertChatMessage(guild));
|
||||
auto team = Message(1020, "team", BOB, "Bob", 2, "team darn");
|
||||
team.teamId = TEAM;
|
||||
team.filtered = true;
|
||||
ids.push_back(db.InsertChatMessage(team));
|
||||
ids.push_back(db.InsertChatMessage(Whisper(1030, ALICE, "Alice", BOB, "Bob", "psst bob")));
|
||||
ids.push_back(db.InsertChatMessage(Whisper(1040, BOB, "Bob", ALICE, "Alice", "yes alice")));
|
||||
ids.push_back(db.InsertChatMessage(Whisper(1050, ALICE, "Alice", CAROL, "Carol", "hi carol")));
|
||||
auto blocked = Message(1060, "zone", BOB, "Bob", 2, "bad words");
|
||||
blocked.blocked = true;
|
||||
ids.push_back(db.InsertChatMessage(blocked));
|
||||
team.senderId = ALICE; team.senderName = "Alice"; team.accountId = 1; team.time = 1070; team.message = "team reply"; team.filtered = false;
|
||||
ids.push_back(db.InsertChatMessage(team));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
TEST_F(ChatHistorySqlite, StoresWhereEachMessageWent) {
|
||||
IChatLog::ChatQuery all;
|
||||
all.includePrivate = true;
|
||||
all.includeWhispers = true;
|
||||
const auto messages = g_Db->GetChatMessages(all);
|
||||
ASSERT_EQ(messages.size(), 8u);
|
||||
EXPECT_EQ(messages[1].guildId, 7);
|
||||
EXPECT_EQ(messages[2].teamId, TEAM);
|
||||
EXPECT_TRUE(messages[2].filtered);
|
||||
EXPECT_FALSE(messages[2].blocked);
|
||||
EXPECT_EQ(messages[3].recipientId, BOB);
|
||||
// Stopped by the filter is always filtered too
|
||||
EXPECT_TRUE(messages[6].blocked);
|
||||
EXPECT_TRUE(messages[6].filtered);
|
||||
}
|
||||
|
||||
TEST_F(ChatHistorySqlite, WhispersAndGroupChatNeedTheirOwnPermission) {
|
||||
IChatLog::ChatQuery zoneOnly;
|
||||
EXPECT_EQ(Texts(g_Db->GetChatMessages(zoneOnly)), (std::vector<std::string>{ "hello zone", "bad words" }));
|
||||
IChatLog::ChatQuery group;
|
||||
group.includePrivate = true;
|
||||
EXPECT_EQ(g_Db->CountChatMessages(group), 5u); // no whispers
|
||||
IChatLog::ChatQuery whispers;
|
||||
whispers.includeWhispers = true;
|
||||
EXPECT_EQ(g_Db->CountChatMessages(whispers), 5u); // no team or guild chat
|
||||
// Asking for a channel doesn't get round it
|
||||
whispers.channel = "team";
|
||||
EXPECT_EQ(g_Db->CountChatMessages(whispers), 0u);
|
||||
}
|
||||
|
||||
TEST_F(ChatHistorySqlite, ConversationBetweenTwoCharacters) {
|
||||
IChatLog::ChatQuery q;
|
||||
q.includeWhispers = true;
|
||||
q.channel = "whisper";
|
||||
q.characterId = ALICE;
|
||||
q.otherCharacterId = BOB;
|
||||
EXPECT_EQ(Texts(g_Db->GetChatMessages(q)), (std::vector<std::string>{ "psst bob", "yes alice" }));
|
||||
q.otherCharacterId = CAROL;
|
||||
EXPECT_EQ(Texts(g_Db->GetChatMessages(q)), (std::vector<std::string>{ "hi carol" }));
|
||||
}
|
||||
|
||||
TEST_F(ChatHistorySqlite, GuildTeamTimeAndPaging) {
|
||||
IChatLog::ChatQuery guild;
|
||||
guild.includePrivate = true;
|
||||
guild.guildId = 7;
|
||||
EXPECT_EQ(Texts(g_Db->GetChatMessages(guild)), (std::vector<std::string>{ "guild hi" }));
|
||||
IChatLog::ChatQuery team;
|
||||
team.includePrivate = true;
|
||||
team.teamId = TEAM;
|
||||
EXPECT_EQ(Texts(g_Db->GetChatMessages(team)), (std::vector<std::string>{ "team darn", "team reply" }));
|
||||
|
||||
IChatLog::ChatQuery range;
|
||||
range.includePrivate = true;
|
||||
range.includeWhispers = true;
|
||||
range.since = 1020;
|
||||
range.until = 1050;
|
||||
EXPECT_EQ(Texts(g_Db->GetChatMessages(range)), (std::vector<std::string>{ "team darn", "psst bob", "yes alice" }));
|
||||
|
||||
// Paging back through a conversation: the newest page, then the one before it
|
||||
IChatLog::ChatQuery page;
|
||||
page.includePrivate = true;
|
||||
page.includeWhispers = true;
|
||||
page.newestFirst = true;
|
||||
page.limit = 3;
|
||||
const auto newest = g_Db->GetChatMessages(page);
|
||||
ASSERT_EQ(newest.size(), 3u);
|
||||
EXPECT_EQ(newest.front().message, "team reply");
|
||||
page.beforeId = newest.back().id;
|
||||
EXPECT_EQ(Texts(g_Db->GetChatMessages(page)), (std::vector<std::string>{ "yes alice", "psst bob", "team darn" }));
|
||||
|
||||
IChatLog::ChatQuery account;
|
||||
account.includeWhispers = true;
|
||||
account.includePrivate = true;
|
||||
account.accountId = 2;
|
||||
EXPECT_EQ(g_Db->CountChatMessages(account), 3u);
|
||||
}
|
||||
|
||||
TEST_F(ChatHistorySqlite, WhisperPartnersNewestFirst) {
|
||||
const auto partners = g_Db->GetWhisperPartners(ALICE, 0, 10);
|
||||
ASSERT_EQ(partners.size(), 2u);
|
||||
EXPECT_EQ(partners[0].characterId, CAROL);
|
||||
EXPECT_EQ(partners[0].name, "Carol");
|
||||
EXPECT_EQ(partners[0].messages, 1u);
|
||||
EXPECT_EQ(partners[1].characterId, BOB);
|
||||
EXPECT_EQ(partners[1].name, "Bob");
|
||||
EXPECT_EQ(partners[1].messages, 2u);
|
||||
EXPECT_EQ(partners[1].firstTime, 1030);
|
||||
EXPECT_EQ(partners[1].lastTime, 1040);
|
||||
EXPECT_EQ(g_Db->CountWhisperPartners(ALICE), 2u);
|
||||
EXPECT_EQ(g_Db->CountWhisperPartners(BOB), 1u);
|
||||
EXPECT_EQ(g_Db->GetWhisperPartners(ALICE, 1, 10).size(), 1u);
|
||||
}
|
||||
|
||||
TEST_F(ChatHistorySqlite, TeamsWithWhoTalked) {
|
||||
const auto teams = g_Db->GetChatTeams(0, 0, 10);
|
||||
ASSERT_EQ(teams.size(), 1u);
|
||||
EXPECT_EQ(teams[0].teamId, TEAM);
|
||||
EXPECT_EQ(teams[0].messages, 2u);
|
||||
EXPECT_EQ(teams[0].senders, "Alice,Bob");
|
||||
EXPECT_EQ(g_Db->CountChatTeams(ALICE), 1u);
|
||||
EXPECT_EQ(g_Db->CountChatTeams(CAROL), 0u);
|
||||
EXPECT_TRUE(g_Db->GetChatTeams(CAROL, 0, 10).empty());
|
||||
}
|
||||
|
||||
TEST_F(ChatHistorySqlite, FlagsKeepTheirMessagesAndHistory) {
|
||||
IChatFlags::ChatFlag flag;
|
||||
flag.createdAt = 2000;
|
||||
flag.createdById = 3;
|
||||
flag.createdBy = "gm";
|
||||
flag.channel = "whisper";
|
||||
flag.characterId = ALICE;
|
||||
flag.characterName = "Alice";
|
||||
flag.accountId = 1;
|
||||
flag.firstMessageId = ids[3];
|
||||
flag.lastMessageId = ids[4];
|
||||
flag.excerpt = "Alice: psst bob / Bob: yes alice";
|
||||
flag.note = "rude";
|
||||
flag.messages = R"([{"id":4,"message":"psst bob","flagged":true}])";
|
||||
const auto id = g_Db->InsertChatFlag(flag, { ids[3], ids[4] });
|
||||
ASSERT_GT(id, 0u);
|
||||
|
||||
auto zoneFlag = flag;
|
||||
zoneFlag.channel = "zone";
|
||||
zoneFlag.characterId = BOB;
|
||||
zoneFlag.accountId = 2;
|
||||
const auto zoneId = g_Db->InsertChatFlag(zoneFlag, { ids[6] });
|
||||
|
||||
const auto stored = g_Db->GetChatFlag(id);
|
||||
ASSERT_TRUE(stored.has_value());
|
||||
EXPECT_EQ(stored->status, "open");
|
||||
EXPECT_EQ(stored->messages, flag.messages);
|
||||
EXPECT_EQ(stored->characterId, ALICE);
|
||||
EXPECT_FALSE(g_Db->GetChatFlag(id + 100).has_value());
|
||||
|
||||
// Which shown messages are flagged, with the newest flag on each
|
||||
const auto flagged = g_Db->GetFlaggedMessages({ ids[0], ids[3], ids[4], ids[6] });
|
||||
EXPECT_EQ(flagged, (std::vector<std::pair<uint64_t, uint64_t>>{ { ids[3], id }, { ids[4], id }, { ids[6], zoneId } }));
|
||||
EXPECT_TRUE(g_Db->GetFlaggedMessages({}).empty());
|
||||
|
||||
// A viewer without chat_dms doesn't get flags on whispers in the queue
|
||||
IChatFlags::ChatFlagQuery q;
|
||||
EXPECT_EQ(g_Db->CountChatFlags(q), 1u);
|
||||
q.includeWhispers = true;
|
||||
EXPECT_EQ(g_Db->CountChatFlags(q), 2u);
|
||||
const auto list = g_Db->GetChatFlags(q);
|
||||
ASSERT_EQ(list.size(), 2u);
|
||||
EXPECT_EQ(list[0].id, zoneId); // newest first
|
||||
EXPECT_TRUE(list[0].messages.empty()); // the list leaves the copy of the chat out
|
||||
q.characterId = ALICE;
|
||||
EXPECT_EQ(g_Db->CountChatFlags(q), 1u);
|
||||
q.characterId = 0;
|
||||
q.accountId = 2;
|
||||
EXPECT_EQ(g_Db->CountChatFlags(q), 1u);
|
||||
|
||||
EXPECT_TRUE(g_Db->UpdateChatFlag(id, "actioned", "muted 3 days", 42, 2100, "gm"));
|
||||
EXPECT_FALSE(g_Db->UpdateChatFlag(id + 100, "actioned", "", 0, 2100, "gm"));
|
||||
const auto updated = g_Db->GetChatFlag(id);
|
||||
EXPECT_EQ(updated->status, "actioned");
|
||||
EXPECT_EQ(updated->note, "muted 3 days");
|
||||
EXPECT_EQ(updated->playerReportId, 42u);
|
||||
EXPECT_EQ(updated->updatedBy, "gm");
|
||||
IChatFlags::ChatFlagQuery open;
|
||||
open.includeWhispers = true;
|
||||
open.status = "open";
|
||||
EXPECT_EQ(g_Db->CountChatFlags(open), 1u);
|
||||
|
||||
g_Db->InsertChatFlagEvent({ 0, id, 2000, 3, "gm", "created", "2 message(s)" });
|
||||
g_Db->InsertChatFlagEvent({ 0, id, 2100, 3, "gm", "actioned", "muted" });
|
||||
const auto events = g_Db->GetChatFlagEvents(id);
|
||||
ASSERT_EQ(events.size(), 2u);
|
||||
EXPECT_EQ(events[0].action, "created");
|
||||
EXPECT_EQ(events[1].action, "actioned");
|
||||
EXPECT_EQ(events[1].detail, "muted");
|
||||
EXPECT_TRUE(g_Db->GetChatFlagEvents(zoneId).empty());
|
||||
|
||||
// The badge count
|
||||
EXPECT_EQ(g_Db->GetDashboardSnapshot().openChatFlags, 1u);
|
||||
}
|
||||
|
||||
TEST_F(ChatHistorySqlite, PruningOldChatKeepsFlags) {
|
||||
IChatLog::ChatQuery all;
|
||||
all.includePrivate = true;
|
||||
all.includeWhispers = true;
|
||||
const auto before = g_Db->CountChatMessages(all);
|
||||
const auto flags = g_Db->CountChatFlags({ .includePrivate = true, .includeWhispers = true });
|
||||
EXPECT_EQ(g_Db->PruneLog(IDashboardAdmin::eLog::CHAT, 1035), 4u);
|
||||
EXPECT_EQ(g_Db->CountChatMessages(all), before - 4);
|
||||
EXPECT_EQ(g_Db->CountChatFlags({ .includePrivate = true, .includeWhispers = true }), flags);
|
||||
}
|
||||
@@ -73,7 +73,9 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IGuilds::GuildSummary, guild, memberCount, le
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IGuilds::GuildPage, total, filtered, guilds);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IApiKeys::ApiKey, id, accountId, name, note, keyHash, keyPrefix, permissions, readOnly, allowedIps, allowedPaths, rateLimit, dailyQuota, createdAt, createdBy, issuedAt, expiresAt, revokedAt, revokedBy, lastUsedAt, lastIp, requestCount, quotaDay, dayCount);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ICharacterSnapshots::CharacterSnapshot, id, characterId, takenAt, reason, actor, size, hash, compressed);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IChatLog::ChatMessage, id, time, channel, senderId, senderName, accountId, recipientId, recipientName, zoneId, instanceId, cloneId, message, blocked);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IChatLog::ChatMessage, id, time, channel, senderId, senderName, accountId, recipientId, recipientName, zoneId, instanceId, cloneId, message, blocked, guildId, teamId, filtered);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IChatLog::WhisperPartner, characterId, name, messages, firstTime, lastTime);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IChatLog::ChatTeam, teamId, messages, firstTime, lastTime, senders);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IDashboardAdmin::Webhook, id, name, url, format, events, secret, enabled, createdAt, lastSentAt, lastStatus, lastError);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IDashboardAdmin::Totp, encryptedSecret, enabledAt, lastStep);
|
||||
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(IDashboardStats::Snapshot, accounts, accountsMaxId, characters, pendingNames, properties, pendingProperties, playKeys, bugReports, unresolvedBugReports, petNames, pendingPetNames, activityLogMaxId, chatLogMaxId, commandLogMaxId, auditLogMaxId, mailMaxId, openEconomyFlags, economyFlagsMaxId);
|
||||
@@ -1459,30 +1461,50 @@ TEST_F(ParitySeeded, ChatLog) {
|
||||
ids.push_back(db.InsertChatMessage({ 0, 1700000010, "whisper", CHAR_BOB, "Bob", 2, CHAR_ALICE, "Alice", 1100, 1, 0, "psst 100% secret", false }));
|
||||
ids.push_back(db.InsertChatMessage({ 0, 1700000020, "zone", CHAR_BOB, "Bob", 2, 0, "", 1200, 0, 0, "bad word", true }));
|
||||
ids.push_back(db.InsertChatMessage({ 0, 1700000030, "web", 0, "Dashboard", 0, 0, "", 0, 0, 0, "Server restarting", false }));
|
||||
IChatLog::ChatMessage guild{ 0, 1700000040, "guild", CHAR_ALICE, "Alice", 1, 0, "", 1100, 1, 0, "guild hello", false };
|
||||
guild.guildId = 7;
|
||||
ids.push_back(db.InsertChatMessage(guild));
|
||||
IChatLog::ChatMessage team{ 0, 1700000050, "team", CHAR_BOB, "Bob", 2, 0, "", 1100, 1, 0, "team darn", false };
|
||||
team.teamId = 1700000000000001;
|
||||
team.filtered = true;
|
||||
ids.push_back(db.InsertChatMessage(team));
|
||||
team.senderId = CHAR_ALICE; team.senderName = "Alice"; team.time = 1700000055; team.filtered = false; team.message = "team reply";
|
||||
ids.push_back(db.InsertChatMessage(team));
|
||||
ids.push_back(db.InsertChatMessage({ 0, 1700000060, "whisper", CHAR_ALICE, "Alice", 1, CHAR_BOB, "Bob", 1100, 1, 0, "reply to bob", false }));
|
||||
ids.push_back(db.InsertChatMessage({ 0, 1700000070, "whisper", CHAR_ALICE, "Alice", 1, CHAR_GM, "GameMaster", 1100, 1, 0, "hi gm", false }));
|
||||
return ids;
|
||||
});
|
||||
const auto queries = [] {
|
||||
std::vector<std::pair<std::string, IChatLog::ChatQuery>> list;
|
||||
IChatLog::ChatQuery query;
|
||||
list.emplace_back("default", query);
|
||||
query.includePrivate = true; list.emplace_back("private", query);
|
||||
query.includePrivate = true; query.includeWhispers = true; list.emplace_back("private", query);
|
||||
query.newestFirst = true; list.emplace_back("newest", query);
|
||||
IChatLog::ChatQuery after; after.afterId = 1; after.includePrivate = true; list.emplace_back("after", after);
|
||||
IChatLog::ChatQuery after; after.afterId = 1; after.includePrivate = true; after.includeWhispers = true; list.emplace_back("after", after);
|
||||
IChatLog::ChatQuery channel; channel.channel = "zone"; list.emplace_back("channel", channel);
|
||||
IChatLog::ChatQuery character; character.characterId = CHAR_ALICE; character.includePrivate = true; list.emplace_back("character", character);
|
||||
IChatLog::ChatQuery character; character.characterId = CHAR_ALICE; character.includePrivate = true; character.includeWhispers = true; list.emplace_back("character", character);
|
||||
IChatLog::ChatQuery account; account.accountId = 2; list.emplace_back("account", account);
|
||||
IChatLog::ChatQuery zone; zone.zoneId = 1100; zone.instanceId = 1; list.emplace_back("zone", zone);
|
||||
IChatLog::ChatQuery instance0; instance0.instanceId = 0; list.emplace_back("instance 0", instance0);
|
||||
IChatLog::ChatQuery since; since.since = 1700000015; since.includePrivate = true; list.emplace_back("since", since);
|
||||
IChatLog::ChatQuery search; search.search = "100%"; search.includePrivate = true; list.emplace_back("search percent", search);
|
||||
IChatLog::ChatQuery searchName; searchName.search = "bob"; searchName.includePrivate = true; list.emplace_back("search name", searchName);
|
||||
IChatLog::ChatQuery since; since.since = 1700000015; since.includePrivate = true; since.includeWhispers = true; list.emplace_back("since", since);
|
||||
IChatLog::ChatQuery search; search.search = "100%"; search.includePrivate = true; search.includeWhispers = true; list.emplace_back("search percent", search);
|
||||
IChatLog::ChatQuery searchName; searchName.search = "bob"; searchName.includePrivate = true; searchName.includeWhispers = true; list.emplace_back("search name", searchName);
|
||||
IChatLog::ChatQuery blocked; blocked.blockedOnly = true; list.emplace_back("blocked", blocked);
|
||||
IChatLog::ChatQuery page; page.limit = 1; page.offset = 1; page.includePrivate = true; list.emplace_back("page", page);
|
||||
IChatLog::ChatQuery page; page.limit = 1; page.offset = 1; page.includePrivate = true; page.includeWhispers = true; list.emplace_back("page", page);
|
||||
IChatLog::ChatQuery groupOnly; groupOnly.includePrivate = true; list.emplace_back("team and guild without whispers", groupOnly);
|
||||
IChatLog::ChatQuery whispersOnly; whispersOnly.includeWhispers = true; list.emplace_back("whispers without team and guild", whispersOnly);
|
||||
IChatLog::ChatQuery pair; pair.channel = "whisper"; pair.includeWhispers = true; pair.characterId = CHAR_ALICE; pair.otherCharacterId = CHAR_BOB; list.emplace_back("conversation", pair);
|
||||
IChatLog::ChatQuery guildQ; guildQ.includePrivate = true; guildQ.guildId = 7; list.emplace_back("guild", guildQ);
|
||||
IChatLog::ChatQuery teamQ; teamQ.includePrivate = true; teamQ.teamId = 1700000000000001; list.emplace_back("team", teamQ);
|
||||
IChatLog::ChatQuery until; until.until = 1700000020; until.includePrivate = true; until.includeWhispers = true; list.emplace_back("until", until);
|
||||
IChatLog::ChatQuery before; before.beforeId = 3; before.includePrivate = true; before.includeWhispers = true; before.newestFirst = true; list.emplace_back("before", before);
|
||||
return list;
|
||||
}();
|
||||
for (const auto& [name, query] : queries) {
|
||||
Both("GetChatMessages " + name, [&](GameDatabase& db) { return json{ db.GetChatMessages(query), db.CountChatMessages(query) }; });
|
||||
}
|
||||
Both("GetWhisperPartners", [](GameDatabase& db) { return json{ db.GetWhisperPartners(CHAR_ALICE, 0, 10), db.CountWhisperPartners(CHAR_ALICE), db.GetWhisperPartners(CHAR_ALICE, 1, 1), db.GetWhisperPartners(CHAR_GM, 0, 10) }; });
|
||||
Both("GetChatTeams", [](GameDatabase& db) { return json{ db.GetChatTeams(0, 0, 10), db.CountChatTeams(0), db.GetChatTeams(CHAR_ALICE, 0, 10), db.CountChatTeams(CHAR_GM) }; });
|
||||
Both("PruneLog chat", [](GameDatabase& db) { return db.PruneLog(IDashboardAdmin::eLog::CHAT, 1700000015); });
|
||||
Both("GetDashboardSnapshot", [](GameDatabase& db) { return db.GetDashboardSnapshot(); });
|
||||
}
|
||||
@@ -1632,7 +1654,7 @@ TEST_F(ParitySeeded, SearchEscaping) {
|
||||
Both("GetShowcaseProperties wildcard search", [](GameDatabase& db) { IProperty::ShowcaseQuery query; query.search = "%"; return db.GetShowcaseProperties(query); });
|
||||
Both("GetShowcaseProperties underscore search", [](GameDatabase& db) { IProperty::ShowcaseQuery query; query.search = "_"; return db.GetShowcaseProperties(query); });
|
||||
Both("GetChatMessages wildcard search", [](GameDatabase& db) {
|
||||
IChatLog::ChatQuery query; query.includePrivate = true;
|
||||
IChatLog::ChatQuery query; query.includePrivate = true; query.includeWhispers = true;
|
||||
json out = json::array();
|
||||
for (const auto* text : { "%", "_", "!", "100%", "'" }) {
|
||||
query.search = text;
|
||||
|
||||
Reference in New Issue
Block a user