mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-11-11 04:23:29 -08:00
[Server] Add a servatrice-dev executable on the in-memory database
This commit is contained in:
parent
905e9c101a
commit
95b3294c46
8 changed files with 358 additions and 27 deletions
|
|
@ -1,28 +1,34 @@
|
||||||
#include "libcockatrice/server/dev/servatrice_in_memory_database_interface.h"
|
#include "libcockatrice/server/dev/servatrice_in_memory_database_interface.h"
|
||||||
|
|
||||||
Servatrice_InMemoryDatabaseInterface::Servatrice_InMemoryDatabaseInterface(QObject *parent)
|
Servatrice_InMemoryDatabaseInterface::Servatrice_InMemoryDatabaseInterface(QObject *parent)
|
||||||
: Server_DatabaseInterface(parent)
|
: Server_DatabaseInterface(parent), state(std::make_shared<State>())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Servatrice_InMemoryDatabaseInterface::addUser(const ServerInfo_User &user)
|
void Servatrice_InMemoryDatabaseInterface::addUser(const ServerInfo_User &user)
|
||||||
{
|
{
|
||||||
users.insert(QString::fromStdString(user.name()), user);
|
state->users.insert(QString::fromStdString(user.name()), user);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Servatrice_InMemoryDatabaseInterface::setAuthenticationResult(AuthenticationResult result)
|
void Servatrice_InMemoryDatabaseInterface::setAuthenticationResult(AuthenticationResult result)
|
||||||
{
|
{
|
||||||
authenticationResult = result;
|
state->authenticationResult = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Servatrice_InMemoryDatabaseInterface::setActiveUserCount(int count)
|
void Servatrice_InMemoryDatabaseInterface::setActiveUserCount(int count)
|
||||||
{
|
{
|
||||||
activeUserCount = count;
|
state->activeUserCount = count;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QList<Servatrice_InMemoryDatabaseInterface::LogEntry> &Servatrice_InMemoryDatabaseInterface::getLogEntries() const
|
void Servatrice_InMemoryDatabaseInterface::addRoom(const BootstrapRoomRecord &room)
|
||||||
{
|
{
|
||||||
return logEntries;
|
state->rooms.append(room);
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Servatrice_InMemoryDatabaseInterface::LogEntry> Servatrice_InMemoryDatabaseInterface::getLogEntries() const
|
||||||
|
{
|
||||||
|
QMutexLocker locker(&state->mutex);
|
||||||
|
return state->logEntries;
|
||||||
}
|
}
|
||||||
|
|
||||||
AuthenticationResult Servatrice_InMemoryDatabaseInterface::checkUserPassword(Server_ProtocolHandler *,
|
AuthenticationResult Servatrice_InMemoryDatabaseInterface::checkUserPassword(Server_ProtocolHandler *,
|
||||||
|
|
@ -33,14 +39,18 @@ AuthenticationResult Servatrice_InMemoryDatabaseInterface::checkUserPassword(Ser
|
||||||
int &,
|
int &,
|
||||||
bool)
|
bool)
|
||||||
{
|
{
|
||||||
return authenticationResult;
|
return state->authenticationResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerInfo_User Servatrice_InMemoryDatabaseInterface::getUserData(const QString &name, bool withId)
|
ServerInfo_User Servatrice_InMemoryDatabaseInterface::getUserData(const QString &name, bool withId)
|
||||||
{
|
{
|
||||||
const auto it = users.constFind(name);
|
const auto it = state->users.constFind(name);
|
||||||
if (it == users.constEnd()) {
|
if (it == state->users.constEnd()) {
|
||||||
return {};
|
// The MySQL implementation always names the requested user, with the base user level.
|
||||||
|
ServerInfo_User user;
|
||||||
|
user.set_name(name.toStdString());
|
||||||
|
user.set_user_level(ServerInfo_User::IsUser);
|
||||||
|
return user;
|
||||||
}
|
}
|
||||||
|
|
||||||
ServerInfo_User user = *it;
|
ServerInfo_User user = *it;
|
||||||
|
|
@ -52,17 +62,31 @@ ServerInfo_User Servatrice_InMemoryDatabaseInterface::getUserData(const QString
|
||||||
|
|
||||||
int Servatrice_InMemoryDatabaseInterface::getNextGameId()
|
int Servatrice_InMemoryDatabaseInterface::getNextGameId()
|
||||||
{
|
{
|
||||||
return nextGameId++;
|
QMutexLocker locker(&state->mutex);
|
||||||
|
return state->nextGameId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Servatrice_InMemoryDatabaseInterface::getNextReplayId()
|
int Servatrice_InMemoryDatabaseInterface::getNextReplayId()
|
||||||
{
|
{
|
||||||
return nextReplayId++;
|
QMutexLocker locker(&state->mutex);
|
||||||
|
return state->nextReplayId++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Servatrice_InMemoryDatabaseInterface::getActiveUserCount(QString)
|
int Servatrice_InMemoryDatabaseInterface::getActiveUserCount(QString)
|
||||||
{
|
{
|
||||||
return activeUserCount;
|
return state->activeUserCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<Server_DatabaseInterface::BootstrapRoomRecord> Servatrice_InMemoryDatabaseInterface::getBootstrapRooms(int)
|
||||||
|
{
|
||||||
|
return state->rooms;
|
||||||
|
}
|
||||||
|
|
||||||
|
Server_DatabaseInterface *Servatrice_InMemoryDatabaseInterface::createWorkerInterface(int)
|
||||||
|
{
|
||||||
|
auto *worker = new Servatrice_InMemoryDatabaseInterface();
|
||||||
|
worker->state = state;
|
||||||
|
return worker;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Servatrice_InMemoryDatabaseInterface::logMessage(const int,
|
void Servatrice_InMemoryDatabaseInterface::logMessage(const int,
|
||||||
|
|
@ -74,10 +98,11 @@ void Servatrice_InMemoryDatabaseInterface::logMessage(const int,
|
||||||
const QString &targetName,
|
const QString &targetName,
|
||||||
const QString &channel)
|
const QString &channel)
|
||||||
{
|
{
|
||||||
logEntries.append(LogEntry{.senderName = senderName,
|
QMutexLocker locker(&state->mutex);
|
||||||
.message = logMessage,
|
state->logEntries.append(LogEntry{.senderName = senderName,
|
||||||
.targetType = targetType,
|
.message = logMessage,
|
||||||
.targetId = targetId,
|
.targetType = targetType,
|
||||||
.targetName = targetName,
|
.targetId = targetId,
|
||||||
.channel = channel});
|
.targetName = targetName,
|
||||||
|
.channel = channel});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@
|
||||||
|
|
||||||
#include <QHash>
|
#include <QHash>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
|
#include <QMutex>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief In-memory Server_DatabaseInterface for tests and the dev server.
|
* @brief In-memory Server_DatabaseInterface for tests and the dev server.
|
||||||
|
|
@ -37,8 +39,10 @@ public:
|
||||||
void setAuthenticationResult(AuthenticationResult result);
|
void setAuthenticationResult(AuthenticationResult result);
|
||||||
/** @brief Sets the value getActiveUserCount() reports. */
|
/** @brief Sets the value getActiveUserCount() reports. */
|
||||||
void setActiveUserCount(int count);
|
void setActiveUserCount(int count);
|
||||||
|
/** @brief Adds a room the SQL room bootstrap returns, in insertion order. */
|
||||||
|
void addRoom(const BootstrapRoomRecord &room);
|
||||||
|
|
||||||
const QList<LogEntry> &getLogEntries() const;
|
QList<LogEntry> getLogEntries() const;
|
||||||
|
|
||||||
AuthenticationResult checkUserPassword(Server_ProtocolHandler * /* handler */,
|
AuthenticationResult checkUserPassword(Server_ProtocolHandler * /* handler */,
|
||||||
const QString & /* user */,
|
const QString & /* user */,
|
||||||
|
|
@ -51,6 +55,8 @@ public:
|
||||||
int getNextGameId() override;
|
int getNextGameId() override;
|
||||||
int getNextReplayId() override;
|
int getNextReplayId() override;
|
||||||
int getActiveUserCount(QString connectionType = QString()) override;
|
int getActiveUserCount(QString connectionType = QString()) override;
|
||||||
|
QList<BootstrapRoomRecord> getBootstrapRooms(int serverId) override;
|
||||||
|
Server_DatabaseInterface *createWorkerInterface(int instanceId) override;
|
||||||
void logMessage(const int /* senderId */,
|
void logMessage(const int /* senderId */,
|
||||||
const QString &senderName,
|
const QString &senderName,
|
||||||
const QString & /* senderIp */,
|
const QString & /* senderIp */,
|
||||||
|
|
@ -61,12 +67,20 @@ public:
|
||||||
const QString &channel = QString()) override;
|
const QString &channel = QString()) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QHash<QString, ServerInfo_User> users;
|
/** @brief The state every interface created by createWorkerInterface() shares. */
|
||||||
AuthenticationResult authenticationResult = NotLoggedIn;
|
struct State
|
||||||
int activeUserCount = 0;
|
{
|
||||||
int nextGameId = 1;
|
QHash<QString, ServerInfo_User> users;
|
||||||
int nextReplayId = 1;
|
AuthenticationResult authenticationResult = NotLoggedIn;
|
||||||
QList<LogEntry> logEntries;
|
int activeUserCount = 0;
|
||||||
|
int nextGameId = 1;
|
||||||
|
int nextReplayId = 1;
|
||||||
|
QList<BootstrapRoomRecord> rooms;
|
||||||
|
QList<LogEntry> logEntries;
|
||||||
|
mutable QMutex mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<State> state;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // SERVATRICE_IN_MEMORY_DATABASE_INTERFACE_H
|
#endif // SERVATRICE_IN_MEMORY_DATABASE_INTERFACE_H
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,15 @@ target_precompile_headers(servatrice PRIVATE "${CMAKE_SOURCE_DIR}/cmake/pch/qtco
|
||||||
|
|
||||||
target_link_libraries(servatrice PRIVATE servatrice_core)
|
target_link_libraries(servatrice PRIVATE servatrice_core)
|
||||||
|
|
||||||
|
# The dev server runs the shipped startup on the in-memory database. It is never installed.
|
||||||
|
add_executable(servatrice-dev dev/main.cpp)
|
||||||
|
|
||||||
|
target_precompile_headers(servatrice-dev PRIVATE "${CMAKE_SOURCE_DIR}/cmake/pch/qtcore_pch.h")
|
||||||
|
|
||||||
|
target_include_directories(servatrice-dev PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||||||
|
|
||||||
|
target_link_libraries(servatrice-dev PRIVATE servatrice_core libcockatrice_server_dev)
|
||||||
|
|
||||||
# install rules
|
# install rules
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
|
|
|
||||||
193
servatrice/dev/main.cpp
Normal file
193
servatrice/dev/main.cpp
Normal file
|
|
@ -0,0 +1,193 @@
|
||||||
|
/** @file main.cpp
|
||||||
|
* @brief Servatrice dev server: the real server startup on an in-memory database.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
#include "libcockatrice/rng/rng_abstract.h"
|
||||||
|
#include "libcockatrice/rng/rng_sfmt.h"
|
||||||
|
#include "libcockatrice/server/dev/servatrice_in_memory_database_interface.h"
|
||||||
|
#include "libcockatrice/utility/cryptoutil.h"
|
||||||
|
#include "servatrice.h"
|
||||||
|
#include "server_logger.h"
|
||||||
|
#include "server_room.h"
|
||||||
|
#include "settingscache.h"
|
||||||
|
#include "smtpclient.h"
|
||||||
|
#include "version_string.h"
|
||||||
|
|
||||||
|
#include <QCommandLineOption>
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QMetaObject>
|
||||||
|
#include <QString>
|
||||||
|
#include <QThread>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <google/protobuf/stubs/common.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @brief The room the dev server always offers, so a client has somewhere to log in to. */
|
||||||
|
void seedDevDatabase(Servatrice_InMemoryDatabaseInterface &database)
|
||||||
|
{
|
||||||
|
Server_DatabaseInterface::BootstrapRoomRecord lobby;
|
||||||
|
lobby.id = 1;
|
||||||
|
lobby.name = "Dev Lobby";
|
||||||
|
lobby.description = "In-memory room for local development.";
|
||||||
|
lobby.permissionLevel = "none";
|
||||||
|
lobby.privateLevel = "none";
|
||||||
|
lobby.autoJoin = true;
|
||||||
|
lobby.chatHistorySize = 100;
|
||||||
|
lobby.gameTypes = {"Commander"};
|
||||||
|
database.addRoom(lobby);
|
||||||
|
|
||||||
|
// AuthenticationNone on the shipped server reports every login as an unknown user, which is
|
||||||
|
// what makes a client connect as a guest.
|
||||||
|
database.setAuthenticationResult(UnknownUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief The shipped server with its database replaced by the in-memory implementation. */
|
||||||
|
class DevServatrice : public Servatrice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Servatrice_InMemoryDatabaseInterface *getInMemoryDatabase() const
|
||||||
|
{
|
||||||
|
return inMemoryDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
Server_DatabaseInterface *createMainDatabaseInterface() override
|
||||||
|
{
|
||||||
|
inMemoryDatabase = new Servatrice_InMemoryDatabaseInterface(this);
|
||||||
|
seedDevDatabase(*inMemoryDatabase);
|
||||||
|
return inMemoryDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Servatrice_InMemoryDatabaseInterface *inMemoryDatabase = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** @brief Routes Qt log output through the server logger, as the shipped binary does. */
|
||||||
|
void logThroughServerLogger(QtMsgType, const QMessageLogContext &, const QString &message)
|
||||||
|
{
|
||||||
|
logger->logMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dumpState(Servatrice &server, const Servatrice_InMemoryDatabaseInterface &database)
|
||||||
|
{
|
||||||
|
std::cout << "servatrice-dev: users=" << server.getUsersCount() << std::endl;
|
||||||
|
|
||||||
|
for (const auto *room : server.getRooms()) {
|
||||||
|
std::cout << "servatrice-dev: room id=" << room->getId() << " name=\"" << room->getName().toStdString() << "\""
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "servatrice-dev: log entries=" << database.getLogEntries().size() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QCoreApplication app(argc, argv);
|
||||||
|
QCoreApplication::setOrganizationName("Cockatrice");
|
||||||
|
QCoreApplication::setApplicationName("Servatrice-dev");
|
||||||
|
QCoreApplication::setApplicationVersion(VERSION_STRING);
|
||||||
|
|
||||||
|
QCommandLineParser parser;
|
||||||
|
parser.setApplicationDescription("Servatrice on an in-memory database.");
|
||||||
|
parser.addHelpOption();
|
||||||
|
parser.addVersionOption();
|
||||||
|
|
||||||
|
QCommandLineOption portOption({"p", "port"}, "TCP port to listen on; 0 picks an ephemeral port.", "port", "0");
|
||||||
|
QCommandLineOption seedOption("seed", "Seed for the game RNG; random when omitted.", "seed");
|
||||||
|
QCommandLineOption dumpStateOption("dump-state", "Print the server state after startup and keep serving.");
|
||||||
|
parser.addOption(portOption);
|
||||||
|
parser.addOption(seedOption);
|
||||||
|
parser.addOption(dumpStateOption);
|
||||||
|
parser.process(app);
|
||||||
|
|
||||||
|
bool portOk = false;
|
||||||
|
const int port = parser.value(portOption).toInt(&portOk);
|
||||||
|
if (!portOk || port < 0 || port > 65535) {
|
||||||
|
std::cerr << "servatrice-dev: invalid port" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString seedValue = parser.value(seedOption);
|
||||||
|
bool seedOk = false;
|
||||||
|
const quint32 seed =
|
||||||
|
seedValue.isEmpty() ? static_cast<quint32>(CryptoUtil::randomUInt64()) : seedValue.toUInt(&seedOk);
|
||||||
|
if (!seedValue.isEmpty() && !seedOk) {
|
||||||
|
std::cerr << "servatrice-dev: invalid seed" << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The shipped server treats DATABASE_URL as "use MySQL"; the dev server has no database.
|
||||||
|
qunsetenv("DATABASE_URL");
|
||||||
|
|
||||||
|
// Never touch the user's servatrice.ini: the dev server writes its throwaway settings to a
|
||||||
|
// temporary file it deletes on exit.
|
||||||
|
const QString configPath =
|
||||||
|
QDir(QDir::tempPath())
|
||||||
|
.filePath("servatrice-dev-" + QString::number(QCoreApplication::applicationPid()) + ".ini");
|
||||||
|
settingsCache = new SettingsCache(configPath);
|
||||||
|
settingsCache->setValue("server/port", port);
|
||||||
|
settingsCache->setValue("server/number_pools", 1);
|
||||||
|
settingsCache->setValue("server/websocket_number_pools", 0);
|
||||||
|
settingsCache->setValue("database/type", "none");
|
||||||
|
settingsCache->setValue("authentication/method", "none");
|
||||||
|
settingsCache->setValue("rooms/method", "sql");
|
||||||
|
settingsCache->setValue("server/writelog", false);
|
||||||
|
settingsCache->sync();
|
||||||
|
|
||||||
|
loggerThread = new QThread;
|
||||||
|
loggerThread->setObjectName("logger");
|
||||||
|
logger = new ServerLogger(true, false, QString());
|
||||||
|
logger->moveToThread(loggerThread);
|
||||||
|
loggerThread->start();
|
||||||
|
QMetaObject::invokeMethod(logger, "startLog", Qt::BlockingQueuedConnection, Q_ARG(QString, QString()));
|
||||||
|
qInstallMessageHandler(logThroughServerLogger);
|
||||||
|
|
||||||
|
std::cout << "servatrice-dev: rng seed=" << seed << std::endl;
|
||||||
|
rng = new RNG_SFMT(seed);
|
||||||
|
smtpClient = new SmtpClient();
|
||||||
|
|
||||||
|
auto *server = new DevServatrice();
|
||||||
|
QObject::connect(server, &QObject::destroyed, &app, &QCoreApplication::quit, Qt::QueuedConnection);
|
||||||
|
|
||||||
|
int retval = EXIT_SUCCESS;
|
||||||
|
if (!server->initServer()) {
|
||||||
|
std::cerr << "servatrice-dev: initialization failed" << std::endl;
|
||||||
|
retval = EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
const int listeningPort = server->getGameServerPort();
|
||||||
|
if (listeningPort <= 0) {
|
||||||
|
std::cerr << "servatrice-dev: the server is not listening" << std::endl;
|
||||||
|
retval = EXIT_FAILURE;
|
||||||
|
} else {
|
||||||
|
std::cout << "servatrice-dev: listening on port " << listeningPort << std::endl;
|
||||||
|
if (parser.isSet(dumpStateOption)) {
|
||||||
|
dumpState(*server, *server->getInMemoryDatabase());
|
||||||
|
}
|
||||||
|
retval = QCoreApplication::exec();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
qInstallMessageHandler(nullptr);
|
||||||
|
|
||||||
|
delete smtpClient;
|
||||||
|
delete rng;
|
||||||
|
delete settingsCache;
|
||||||
|
QFile::remove(configPath);
|
||||||
|
|
||||||
|
logger->deleteLater();
|
||||||
|
loggerThread->wait();
|
||||||
|
delete loggerThread;
|
||||||
|
|
||||||
|
google::protobuf::ShutdownProtobufLibrary();
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
@ -261,7 +261,7 @@ void Servatrice_IslServer::incomingConnection(qintptr socketDescriptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
Servatrice::Servatrice(QObject *parent)
|
Servatrice::Servatrice(QObject *parent)
|
||||||
: Server(parent), authenticationMethod(AuthenticationNone), uptime(0), txBytes(0), rxBytes(0),
|
: Server(parent), authenticationMethod(AuthenticationNone), gameServer(nullptr), uptime(0), txBytes(0), rxBytes(0),
|
||||||
shutdownTimer(nullptr)
|
shutdownTimer(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
@ -319,6 +319,11 @@ Server_DatabaseInterface *Servatrice::createMainDatabaseInterface()
|
||||||
return new Servatrice_DatabaseInterface(-1, this);
|
return new Servatrice_DatabaseInterface(-1, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Servatrice::getGameServerPort() const
|
||||||
|
{
|
||||||
|
return gameServer != nullptr && gameServer->isListening() ? gameServer->serverPort() : -1;
|
||||||
|
}
|
||||||
|
|
||||||
bool Servatrice::initServer()
|
bool Servatrice::initServer()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -259,6 +259,8 @@ public:
|
||||||
{
|
{
|
||||||
return getAuthenticationStrictness() == AuthenticationStrict;
|
return getAuthenticationStrictness() == AuthenticationStrict;
|
||||||
}
|
}
|
||||||
|
/** @brief The port the game server listens on, or -1 when it is not listening. */
|
||||||
|
int getGameServerPort() const;
|
||||||
/** @brief Key sealing legacy password backups, or empty when they are disabled. */
|
/** @brief Key sealing legacy password backups, or empty when they are disabled. */
|
||||||
QByteArray getLegacyBackupKey() const;
|
QByteArray getLegacyBackupKey() const;
|
||||||
/** @brief Days a legacy password backup may go unused before it is purged. */
|
/** @brief Days a legacy password backup may go unused before it is purged. */
|
||||||
|
|
|
||||||
|
|
@ -430,6 +430,17 @@ cockatrice_add_test(
|
||||||
LABELS
|
LABELS
|
||||||
server
|
server
|
||||||
)
|
)
|
||||||
|
cockatrice_add_test(
|
||||||
|
NAME
|
||||||
|
servatrice_in_memory_database_test
|
||||||
|
SOURCES
|
||||||
|
servatrice_in_memory_database_test.cpp
|
||||||
|
LIBS
|
||||||
|
libcockatrice_server_dev
|
||||||
|
LABELS
|
||||||
|
server
|
||||||
|
)
|
||||||
|
|
||||||
cockatrice_add_test(
|
cockatrice_add_test(
|
||||||
NAME
|
NAME
|
||||||
private_chat_paging_test
|
private_chat_paging_test
|
||||||
|
|
|
||||||
72
tests/servatrice_in_memory_database_test.cpp
Normal file
72
tests/servatrice_in_memory_database_test.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
/** @file servatrice_in_memory_database_test.cpp
|
||||||
|
* @brief Tests for the in-memory server database shared between the main interface and its workers.
|
||||||
|
* @ingroup Tests
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "libcockatrice/server/dev/servatrice_in_memory_database_interface.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include <libcockatrice/rng/rng_abstract.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
RNG_Abstract *rng = nullptr; // referenced by the server_remote library
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
TEST(ServatriceInMemoryDatabaseTest, WorkersShareTheSeededState)
|
||||||
|
{
|
||||||
|
Servatrice_InMemoryDatabaseInterface mainDatabase;
|
||||||
|
mainDatabase.setAuthenticationResult(UnknownUser);
|
||||||
|
|
||||||
|
ServerInfo_User user;
|
||||||
|
user.set_name("tester");
|
||||||
|
user.set_id(7);
|
||||||
|
mainDatabase.addUser(user);
|
||||||
|
|
||||||
|
Server_DatabaseInterface::BootstrapRoomRecord room;
|
||||||
|
room.id = 3;
|
||||||
|
room.name = "Lobby";
|
||||||
|
mainDatabase.addRoom(room);
|
||||||
|
|
||||||
|
std::unique_ptr<Server_DatabaseInterface> worker(mainDatabase.createWorkerInterface(0));
|
||||||
|
ASSERT_NE(nullptr, worker);
|
||||||
|
|
||||||
|
QString reason;
|
||||||
|
int secondsLeft = 0;
|
||||||
|
EXPECT_EQ(UnknownUser, worker->checkUserPassword(nullptr, "tester", "", "", reason, secondsLeft, false));
|
||||||
|
|
||||||
|
const ServerInfo_User fetched = worker->getUserData("tester", true);
|
||||||
|
EXPECT_EQ(7, fetched.id());
|
||||||
|
EXPECT_FALSE(worker->getUserData("tester", false).has_id());
|
||||||
|
|
||||||
|
const auto rooms = worker->getBootstrapRooms(0);
|
||||||
|
ASSERT_EQ(1, rooms.size());
|
||||||
|
EXPECT_EQ(3, rooms.first().id);
|
||||||
|
EXPECT_EQ("Lobby", rooms.first().name);
|
||||||
|
|
||||||
|
// A worker records the messages it logs, and the main interface sees them.
|
||||||
|
worker->logMessage(1, "tester", "127.0.0.1", "hello", Server_DatabaseInterface::MessageTargetRoom, 3, "Lobby");
|
||||||
|
EXPECT_EQ(1, mainDatabase.getLogEntries().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ServatriceInMemoryDatabaseTest, UnknownUsersAreNamedLikeTheMySqlImplementation)
|
||||||
|
{
|
||||||
|
Servatrice_InMemoryDatabaseInterface database;
|
||||||
|
|
||||||
|
const ServerInfo_User guest = database.getUserData("stranger", true);
|
||||||
|
EXPECT_EQ("stranger", QString::fromStdString(guest.name()));
|
||||||
|
EXPECT_EQ(ServerInfo_User::IsUser, guest.user_level());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ServatriceInMemoryDatabaseTest, IdsAdvancePerInterfaceState)
|
||||||
|
{
|
||||||
|
Servatrice_InMemoryDatabaseInterface database;
|
||||||
|
|
||||||
|
EXPECT_EQ(1, database.getNextGameId());
|
||||||
|
EXPECT_EQ(2, database.getNextGameId());
|
||||||
|
EXPECT_EQ(1, database.getNextReplayId());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
Loading…
Add table
Add a link
Reference in a new issue