minetest/src/unittest/test_authdatabase.cpp

297 lines
7.8 KiB
C++

/*
Minetest
Copyright (C) 2018 bendeutsch, Ben Deutsch <ben@bendeutsch.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "test.h"
#include <algorithm>
#include "database/database-files.h"
#include "database/database-sqlite3.h"
#include "util/string.h"
#include "filesys.h"
namespace
{
// Anonymous namespace to create classes that are only
// visible to this file
//
// These are helpers that return a *AuthDatabase and
// allow us to run the same tests on different databases and
// database acquisition strategies.
class AuthDatabaseProvider
{
public:
virtual ~AuthDatabaseProvider() = default;
virtual AuthDatabase *getAuthDatabase() = 0;
};
class FixedProvider : public AuthDatabaseProvider
{
public:
FixedProvider(AuthDatabase *auth_db) : auth_db(auth_db){};
virtual ~FixedProvider(){};
virtual AuthDatabase *getAuthDatabase() { return auth_db; };
private:
AuthDatabase *auth_db;
};
class FilesProvider : public AuthDatabaseProvider
{
public:
FilesProvider(const std::string &dir) : dir(dir){};
virtual ~FilesProvider() { delete auth_db; };
virtual AuthDatabase *getAuthDatabase()
{
delete auth_db;
auth_db = new AuthDatabaseFiles(dir);
return auth_db;
};
private:
std::string dir;
AuthDatabase *auth_db = nullptr;
};
class SQLite3Provider : public AuthDatabaseProvider
{
public:
SQLite3Provider(const std::string &dir) : dir(dir){};
virtual ~SQLite3Provider() { delete auth_db; };
virtual AuthDatabase *getAuthDatabase()
{
delete auth_db;
auth_db = new AuthDatabaseSQLite3(dir);
return auth_db;
};
private:
std::string dir;
AuthDatabase *auth_db = nullptr;
};
}
class TestAuthDatabase : public TestBase
{
public:
TestAuthDatabase() { TestManager::registerTestModule(this); }
const char *getName() { return "TestAuthDatabase"; }
void runTests(IGameDef *gamedef);
void runTestsForCurrentDB();
void testRecallFail();
void testCreate();
void testRecall();
void testChange();
void testRecallChanged();
void testChangePrivileges();
void testRecallChangedPrivileges();
void testListNames();
void testDelete();
private:
AuthDatabaseProvider *auth_provider;
};
static TestAuthDatabase g_test_instance;
void TestAuthDatabase::runTests(IGameDef *gamedef)
{
// fixed directory, for persistence
thread_local const std::string test_dir = getTestTempDirectory();
// Each set of tests is run twice for each database type:
// one where we reuse the same AuthDatabase object (to test local caching),
// and one where we create a new AuthDatabase object for each call
// (to test actual persistence).
rawstream << "-------- Files database (same object)" << std::endl;
AuthDatabase *auth_db = new AuthDatabaseFiles(test_dir);
auth_provider = new FixedProvider(auth_db);
runTestsForCurrentDB();
delete auth_db;
delete auth_provider;
// reset database
fs::DeleteSingleFileOrEmptyDirectory(test_dir + DIR_DELIM + "auth.txt");
rawstream << "-------- Files database (new objects)" << std::endl;
auth_provider = new FilesProvider(test_dir);
runTestsForCurrentDB();
delete auth_provider;
rawstream << "-------- SQLite3 database (same object)" << std::endl;
auth_db = new AuthDatabaseSQLite3(test_dir);
auth_provider = new FixedProvider(auth_db);
runTestsForCurrentDB();
delete auth_db;
delete auth_provider;
// reset database
fs::DeleteSingleFileOrEmptyDirectory(test_dir + DIR_DELIM + "auth.sqlite");
rawstream << "-------- SQLite3 database (new objects)" << std::endl;
auth_provider = new SQLite3Provider(test_dir);
runTestsForCurrentDB();
delete auth_provider;
}
////////////////////////////////////////////////////////////////////////////////
void TestAuthDatabase::runTestsForCurrentDB()
{
TEST(testRecallFail);
TEST(testCreate);
TEST(testRecall);
TEST(testChange);
TEST(testRecallChanged);
TEST(testChangePrivileges);
TEST(testRecallChangedPrivileges);
TEST(testListNames);
TEST(testDelete);
TEST(testRecallFail);
}
void TestAuthDatabase::testRecallFail()
{
AuthDatabase *auth_db = auth_provider->getAuthDatabase();
AuthEntry authEntry;
// no such user yet
UASSERT(!auth_db->getAuth("TestName", authEntry));
}
void TestAuthDatabase::testCreate()
{
AuthDatabase *auth_db = auth_provider->getAuthDatabase();
AuthEntry authEntry;
authEntry.name = "TestName";
authEntry.password = "TestPassword";
authEntry.privileges.emplace_back("shout");
authEntry.privileges.emplace_back("interact");
authEntry.last_login = 1000;
UASSERT(auth_db->createAuth(authEntry));
}
void TestAuthDatabase::testRecall()
{
AuthDatabase *auth_db = auth_provider->getAuthDatabase();
AuthEntry authEntry;
UASSERT(auth_db->getAuth("TestName", authEntry));
UASSERTEQ(std::string, authEntry.name, "TestName");
UASSERTEQ(std::string, authEntry.password, "TestPassword");
// the order of privileges is unimportant
std::sort(authEntry.privileges.begin(), authEntry.privileges.end());
UASSERTEQ(std::string, str_join(authEntry.privileges, ","), "interact,shout");
}
void TestAuthDatabase::testChange()
{
AuthDatabase *auth_db = auth_provider->getAuthDatabase();
AuthEntry authEntry;
UASSERT(auth_db->getAuth("TestName", authEntry));
authEntry.password = "NewPassword";
authEntry.last_login = 1002;
UASSERT(auth_db->saveAuth(authEntry));
}
void TestAuthDatabase::testRecallChanged()
{
AuthDatabase *auth_db = auth_provider->getAuthDatabase();
AuthEntry authEntry;
UASSERT(auth_db->getAuth("TestName", authEntry));
UASSERTEQ(std::string, authEntry.password, "NewPassword");
// the order of privileges is unimportant
std::sort(authEntry.privileges.begin(), authEntry.privileges.end());
UASSERTEQ(std::string, str_join(authEntry.privileges, ","), "interact,shout");
UASSERTEQ(u64, authEntry.last_login, 1002);
}
void TestAuthDatabase::testChangePrivileges()
{
AuthDatabase *auth_db = auth_provider->getAuthDatabase();
AuthEntry authEntry;
UASSERT(auth_db->getAuth("TestName", authEntry));
authEntry.privileges.clear();
authEntry.privileges.emplace_back("interact");
authEntry.privileges.emplace_back("fly");
authEntry.privileges.emplace_back("dig");
UASSERT(auth_db->saveAuth(authEntry));
}
void TestAuthDatabase::testRecallChangedPrivileges()
{
AuthDatabase *auth_db = auth_provider->getAuthDatabase();
AuthEntry authEntry;
UASSERT(auth_db->getAuth("TestName", authEntry));
// the order of privileges is unimportant
std::sort(authEntry.privileges.begin(), authEntry.privileges.end());
UASSERTEQ(std::string, str_join(authEntry.privileges, ","), "dig,fly,interact");
}
void TestAuthDatabase::testListNames()
{
AuthDatabase *auth_db = auth_provider->getAuthDatabase();
std::vector<std::string> list;
AuthEntry authEntry;
authEntry.name = "SecondName";
authEntry.password = "SecondPassword";
authEntry.privileges.emplace_back("shout");
authEntry.privileges.emplace_back("interact");
authEntry.last_login = 1003;
auth_db->createAuth(authEntry);
auth_db->listNames(list);
// not necessarily sorted, so sort before comparing
std::sort(list.begin(), list.end());
UASSERTEQ(std::string, str_join(list, ","), "SecondName,TestName");
}
void TestAuthDatabase::testDelete()
{
AuthDatabase *auth_db = auth_provider->getAuthDatabase();
UASSERT(!auth_db->deleteAuth("NoSuchName"));
UASSERT(auth_db->deleteAuth("TestName"));
// second try, expect failure
UASSERT(!auth_db->deleteAuth("TestName"));
}