diff --git a/CMakeLists.txt b/CMakeLists.txt index 71f195385..8635e3dd7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -496,6 +496,7 @@ if(WITH_CLIENT OR WITH_ORACLE ) add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_network ${CMAKE_BINARY_DIR}/libcockatrice_network) + add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_server_dev ${CMAKE_BINARY_DIR}/libcockatrice_server_dev) endif() add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_deck_list ${CMAKE_BINARY_DIR}/libcockatrice_deck_list) add_subdirectory(${CMAKE_SOURCE_DIR}/libcockatrice_rng ${CMAKE_BINARY_DIR}/libcockatrice_rng) diff --git a/libcockatrice_server_dev/CMakeLists.txt b/libcockatrice_server_dev/CMakeLists.txt new file mode 100644 index 000000000..d5c2c2bf4 --- /dev/null +++ b/libcockatrice_server_dev/CMakeLists.txt @@ -0,0 +1,12 @@ +set(HEADERS libcockatrice/server/dev/servatrice_in_memory_database_interface.h) + +add_library( + libcockatrice_server_dev STATIC ${HEADERS} libcockatrice/server/dev/servatrice_in_memory_database_interface.cpp +) + +target_include_directories(libcockatrice_server_dev PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +target_link_libraries(libcockatrice_server_dev PUBLIC libcockatrice_network_server_remote) + +# Dev-only code: linked by the server tests and the servatrice-dev executable, never by the shipped +# servatrice binary. diff --git a/libcockatrice_server_dev/libcockatrice/server/dev/servatrice_in_memory_database_interface.cpp b/libcockatrice_server_dev/libcockatrice/server/dev/servatrice_in_memory_database_interface.cpp new file mode 100644 index 000000000..6e4cc1af5 --- /dev/null +++ b/libcockatrice_server_dev/libcockatrice/server/dev/servatrice_in_memory_database_interface.cpp @@ -0,0 +1,83 @@ +#include "libcockatrice/server/dev/servatrice_in_memory_database_interface.h" + +Servatrice_InMemoryDatabaseInterface::Servatrice_InMemoryDatabaseInterface(QObject *parent) + : Server_DatabaseInterface(parent) +{ +} + +void Servatrice_InMemoryDatabaseInterface::addUser(const ServerInfo_User &user) +{ + users.insert(QString::fromStdString(user.name()), user); +} + +void Servatrice_InMemoryDatabaseInterface::setAuthenticationResult(AuthenticationResult result) +{ + authenticationResult = result; +} + +void Servatrice_InMemoryDatabaseInterface::setActiveUserCount(int count) +{ + activeUserCount = count; +} + +const QList &Servatrice_InMemoryDatabaseInterface::getLogEntries() const +{ + return logEntries; +} + +AuthenticationResult Servatrice_InMemoryDatabaseInterface::checkUserPassword(Server_ProtocolHandler *, + const QString &, + const QString &, + const QString &, + QString &, + int &, + bool) +{ + return authenticationResult; +} + +ServerInfo_User Servatrice_InMemoryDatabaseInterface::getUserData(const QString &name, bool withId) +{ + const auto it = users.constFind(name); + if (it == users.constEnd()) { + return {}; + } + + ServerInfo_User user = *it; + if (!withId) { + user.clear_id(); + } + return user; +} + +int Servatrice_InMemoryDatabaseInterface::getNextGameId() +{ + return nextGameId++; +} + +int Servatrice_InMemoryDatabaseInterface::getNextReplayId() +{ + return nextReplayId++; +} + +int Servatrice_InMemoryDatabaseInterface::getActiveUserCount(QString) +{ + return activeUserCount; +} + +void Servatrice_InMemoryDatabaseInterface::logMessage(const int, + const QString &senderName, + const QString &, + const QString &logMessage, + LogMessage_TargetType targetType, + const int targetId, + const QString &targetName, + const QString &channel) +{ + logEntries.append(LogEntry{.senderName = senderName, + .message = logMessage, + .targetType = targetType, + .targetId = targetId, + .targetName = targetName, + .channel = channel}); +} diff --git a/libcockatrice_server_dev/libcockatrice/server/dev/servatrice_in_memory_database_interface.h b/libcockatrice_server_dev/libcockatrice/server/dev/servatrice_in_memory_database_interface.h new file mode 100644 index 000000000..b7cc7fa6a --- /dev/null +++ b/libcockatrice_server_dev/libcockatrice/server/dev/servatrice_in_memory_database_interface.h @@ -0,0 +1,72 @@ +#ifndef SERVATRICE_IN_MEMORY_DATABASE_INTERFACE_H +#define SERVATRICE_IN_MEMORY_DATABASE_INTERFACE_H + +#include "server_database_interface.h" + +#include +#include +#include + +/** + * @brief In-memory Server_DatabaseInterface for tests and the dev server. + * + * Holds the little state a server needs to come up without a SQL database: + * registered users, the authentication outcome, the logged messages, the active + * user count and the game/replay id allocators. Calls that need persistence keep + * the interface's fail-closed defaults. + */ +class Servatrice_InMemoryDatabaseInterface : public Server_DatabaseInterface +{ +public: + /** @brief One recorded logMessage() call. */ + struct LogEntry + { + QString senderName; + QString message; + LogMessage_TargetType targetType; + int targetId; + QString targetName; + QString channel; + }; + + explicit Servatrice_InMemoryDatabaseInterface(QObject *parent = nullptr); + + /** @brief Registers the user getUserData() returns; the id is only exposed to withId lookups. */ + void addUser(const ServerInfo_User &user); + /** @brief Sets the result every checkUserPassword() call returns. */ + void setAuthenticationResult(AuthenticationResult result); + /** @brief Sets the value getActiveUserCount() reports. */ + void setActiveUserCount(int count); + + const QList &getLogEntries() const; + + AuthenticationResult checkUserPassword(Server_ProtocolHandler * /* handler */, + const QString & /* user */, + const QString & /* password */, + const QString & /* clientId */, + QString & /* reasonStr */, + int & /* secondsLeft */, + bool /* passwordNeedsHash */) override; + ServerInfo_User getUserData(const QString &name, bool withId = false) override; + int getNextGameId() override; + int getNextReplayId() override; + int getActiveUserCount(QString connectionType = QString()) override; + void logMessage(const int /* senderId */, + const QString &senderName, + const QString & /* senderIp */, + const QString &logMessage, + LogMessage_TargetType targetType, + const int targetId, + const QString &targetName, + const QString &channel = QString()) override; + +private: + QHash users; + AuthenticationResult authenticationResult = NotLoggedIn; + int activeUserCount = 0; + int nextGameId = 1; + int nextReplayId = 1; + QList logEntries; +}; + +#endif // SERVATRICE_IN_MEMORY_DATABASE_INTERFACE_H diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6eb59fc72..02e8e72f2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -366,6 +366,7 @@ cockatrice_add_test( LIBS libcockatrice_network_server_remote libcockatrice_rng + libcockatrice_server_dev LABELS server ) @@ -377,6 +378,7 @@ cockatrice_add_test( LIBS libcockatrice_network_server_remote libcockatrice_rng + libcockatrice_server_dev LABELS server ) @@ -388,6 +390,7 @@ cockatrice_add_test( LIBS libcockatrice_network_server_remote libcockatrice_rng + libcockatrice_server_dev LABELS server ) @@ -423,6 +426,7 @@ cockatrice_add_test( LIBS libcockatrice_network_server_remote libcockatrice_rng + libcockatrice_server_dev LABELS server ) diff --git a/tests/movecard_tests/CMakeLists.txt b/tests/movecard_tests/CMakeLists.txt index 35d7b44ed..09ce6220d 100755 --- a/tests/movecard_tests/CMakeLists.txt +++ b/tests/movecard_tests/CMakeLists.txt @@ -6,6 +6,7 @@ cockatrice_add_test( LIBS libcockatrice_network_server_remote libcockatrice_rng + libcockatrice_server_dev LABELS server ) @@ -18,6 +19,7 @@ cockatrice_add_test( LIBS libcockatrice_network_server_remote libcockatrice_rng + libcockatrice_server_dev LABELS server ) diff --git a/tests/movecard_tests/server_test_helpers.h b/tests/movecard_tests/server_test_helpers.h index fd2ed6c17..2a194b850 100644 --- a/tests/movecard_tests/server_test_helpers.h +++ b/tests/movecard_tests/server_test_helpers.h @@ -1,42 +1,11 @@ +#include "libcockatrice/server/dev/servatrice_in_memory_database_interface.h" #include "server.h" -#include "server_database_interface.h" - -class MockDatabaseInterface : public Server_DatabaseInterface -{ -public: - AuthenticationResult checkUserPassword(Server_ProtocolHandler *, - const QString &, - const QString &, - const QString &, - QString &, - int &, - bool) override - { - return NotLoggedIn; - } - ServerInfo_User getUserData(const QString &, bool) override - { - return ServerInfo_User(); - } - int getNextGameId() override - { - return 1; - } - int getNextReplayId() override - { - return 1; - } - int getActiveUserCount(QString) override - { - return 1; - } -}; class FakeServer : public Server { public: FakeServer() { - setDatabaseInterface(new MockDatabaseInterface()); + setDatabaseInterface(new Servatrice_InMemoryDatabaseInterface()); } }; diff --git a/tests/server_forced_password_change_test.cpp b/tests/server_forced_password_change_test.cpp index 9717b610b..71c7d02c2 100644 --- a/tests/server_forced_password_change_test.cpp +++ b/tests/server_forced_password_change_test.cpp @@ -5,8 +5,8 @@ #include "libcockatrice/protocol/pb/response.pb.h" #include "libcockatrice/protocol/pb/server_message.pb.h" +#include "libcockatrice/server/dev/servatrice_in_memory_database_interface.h" #include "server.h" -#include "server_database_interface.h" #include "server_protocolhandler.h" #include @@ -22,49 +22,21 @@ RNG_Abstract *rng = nullptr; // referenced by the server_remote library namespace { -class MockDatabaseInterface : public Server_DatabaseInterface -{ -public: - AuthenticationResult checkUserPassword(Server_ProtocolHandler *, - const QString &, - const QString &, - const QString &, - QString &, - int &, - bool) override - { - return PasswordChangeRequired; - } - ServerInfo_User getUserData(const QString &name, bool withId) override - { - ServerInfo_User user; - user.set_name(name.toStdString()); - user.set_user_level(ServerInfo_User::IsUser | ServerInfo_User::IsRegistered | ServerInfo_User::IsModerator); - if (withId) { - user.set_id(1); - } - return user; - } - int getNextReplayId() override - { - return 1; - } - int getNextGameId() override - { - return 1; - } - int getActiveUserCount(QString) override - { - return 0; - } -}; - class FakeServer : public Server { public: FakeServer() { - setDatabaseInterface(new MockDatabaseInterface()); + auto *db = new Servatrice_InMemoryDatabaseInterface(); + db->setAuthenticationResult(PasswordChangeRequired); + + ServerInfo_User user; + user.set_name("mod"); + user.set_user_level(ServerInfo_User::IsUser | ServerInfo_User::IsRegistered | ServerInfo_User::IsModerator); + user.set_id(1); + db->addUser(user); + + setDatabaseInterface(db); } }; diff --git a/tests/server_game_join_test.cpp b/tests/server_game_join_test.cpp index 93895ab5c..b12c77143 100644 --- a/tests/server_game_join_test.cpp +++ b/tests/server_game_join_test.cpp @@ -7,8 +7,8 @@ #include "game/role_info.h" #include "game/server_game.h" #include "libcockatrice/protocol/pb/response.pb.h" +#include "libcockatrice/server/dev/servatrice_in_memory_database_interface.h" #include "server.h" -#include "server_database_interface.h" #include "server_room.h" #include @@ -23,43 +23,12 @@ RNG_Abstract *rng = nullptr; // referenced by the server_remote library namespace { -class MockDatabaseInterface : public Server_DatabaseInterface -{ -public: - AuthenticationResult checkUserPassword(Server_ProtocolHandler *, - const QString &, - const QString &, - const QString &, - QString &, - int &, - bool) override - { - return NotLoggedIn; - } - int getNextReplayId() override - { - return 1; - } - int getNextGameId() override - { - return 1; - } - int getActiveUserCount(QString) override - { - return 0; - } - ServerInfo_User getUserData(const QString &, bool) override - { - return ServerInfo_User(); - } -}; - class FakeServer : public Server { public: FakeServer() { - setDatabaseInterface(new MockDatabaseInterface()); + setDatabaseInterface(new Servatrice_InMemoryDatabaseInterface()); } }; diff --git a/tests/server_room_channels_test.cpp b/tests/server_room_channels_test.cpp index 006c9fa39..e2cc26dc6 100644 --- a/tests/server_room_channels_test.cpp +++ b/tests/server_room_channels_test.cpp @@ -5,8 +5,8 @@ #include "libcockatrice/protocol/pb/response.pb.h" #include "libcockatrice/protocol/pb/server_message.pb.h" +#include "libcockatrice/server/dev/servatrice_in_memory_database_interface.h" #include "server.h" -#include "server_database_interface.h" #include "server_protocolhandler.h" #include "server_room.h" @@ -33,60 +33,6 @@ RNG_Abstract *rng = nullptr; // referenced by the server_remote library namespace { -class MockDatabaseInterface : public Server_DatabaseInterface -{ -public: - struct LogEntry - { - QString senderName; - QString message; - LogMessage_TargetType targetType; - int targetId; - QString targetName; - QString channel; - }; - QList logEntries; - - void logMessage(const int /* senderId */, - const QString &senderName, - const QString & /* senderIp */, - const QString &message, - LogMessage_TargetType targetType, - const int targetId, - const QString &targetName, - const QString &channel = QString()) override - { - logEntries.append({senderName, message, targetType, targetId, targetName, channel}); - } - - AuthenticationResult checkUserPassword(Server_ProtocolHandler *, - const QString &, - const QString &, - const QString &, - QString &, - int &, - bool) override - { - return NotLoggedIn; - } - int getNextReplayId() override - { - return 1; - } - int getNextGameId() override - { - return 1; - } - int getActiveUserCount(QString) override - { - return 0; - } - ServerInfo_User getUserData(const QString &, bool) override - { - return ServerInfo_User(); - } -}; - class FakeServer : public Server { public: @@ -149,7 +95,7 @@ protected: class RoomChannelTest : public ::testing::Test { protected: - MockDatabaseInterface db; + Servatrice_InMemoryDatabaseInterface db; FakeServer server{&db}; QList catalog; Server_Room *room = nullptr; @@ -360,7 +306,7 @@ TEST_F(RoomChannelTest, UserBelowRequiredLevelCannotSendToChannel) EXPECT_TRUE(channelSayEvents(mod).isEmpty()); EXPECT_TRUE(channelSayEvents(user).isEmpty()); bool loggedAsChannel = false; - for (const MockDatabaseInterface::LogEntry &entry : db.logEntries) { + for (const Servatrice_InMemoryDatabaseInterface::LogEntry &entry : db.getLogEntries()) { loggedAsChannel |= entry.targetType == Server_DatabaseInterface::MessageTargetRoomChannel; } EXPECT_FALSE(loggedAsChannel); @@ -406,7 +352,7 @@ TEST_F(RoomChannelTest, UnknownChannelIdIsRejected) EXPECT_EQ(user.lastResponseCode, Response::RespInvalidCommand); EXPECT_TRUE(channelSayEvents(user).isEmpty()); - EXPECT_TRUE(db.logEntries.isEmpty()); + EXPECT_TRUE(db.getLogEntries().isEmpty()); } TEST_F(RoomChannelTest, MissingAccessLevelDefaultsToPublic) @@ -515,13 +461,13 @@ TEST_F(RoomChannelTest, AuditLoggingTagsChannelMessages) sendMainSay(user, "main log line"); sendChannelSay(user, "lobby", "channel log line"); - ASSERT_EQ(db.logEntries.size(), 2); - EXPECT_EQ(db.logEntries.at(0).targetType, Server_DatabaseInterface::MessageTargetRoom); - EXPECT_TRUE(db.logEntries.at(0).channel.isEmpty()); - EXPECT_EQ(db.logEntries.at(1).targetType, Server_DatabaseInterface::MessageTargetRoomChannel); - EXPECT_EQ(db.logEntries.at(1).channel, "lobby"); - EXPECT_EQ(db.logEntries.at(1).senderName, "user"); - EXPECT_EQ(db.logEntries.at(1).message, "channel log line"); + ASSERT_EQ(db.getLogEntries().size(), 2); + EXPECT_EQ(db.getLogEntries().at(0).targetType, Server_DatabaseInterface::MessageTargetRoom); + EXPECT_TRUE(db.getLogEntries().at(0).channel.isEmpty()); + EXPECT_EQ(db.getLogEntries().at(1).targetType, Server_DatabaseInterface::MessageTargetRoomChannel); + EXPECT_EQ(db.getLogEntries().at(1).channel, "lobby"); + EXPECT_EQ(db.getLogEntries().at(1).senderName, "user"); + EXPECT_EQ(db.getLogEntries().at(1).message, "channel log line"); } TEST_F(RoomChannelTest, PublicChannelRelaysOverIslButRestrictedChannelsDoNot)