]> git.lizzy.rs Git - minetest.git/blob - src/unittest/test_authdatabase.cpp
Replace auth.txt with SQLite auth database (#7279)
[minetest.git] / src / unittest / test_authdatabase.cpp
1 /*
2 Minetest
3 Copyright (C) 2018 bendeutsch, Ben Deutsch <ben@bendeutsch.de>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "test.h"
21
22 #include <algorithm>
23 #include "database/database-files.h"
24 #include "database/database-sqlite3.h"
25 #include "util/string.h"
26 #include "filesys.h"
27
28 namespace
29 {
30 // Anonymous namespace to create classes that are only
31 // visible to this file
32 //
33 // These are helpers that return a *AuthDatabase and
34 // allow us to run the same tests on different databases and
35 // database acquisition strategies.
36
37 class AuthDatabaseProvider
38 {
39 public:
40         virtual ~AuthDatabaseProvider() = default;
41         virtual AuthDatabase *getAuthDatabase() = 0;
42 };
43
44 class FixedProvider : public AuthDatabaseProvider
45 {
46 public:
47         FixedProvider(AuthDatabase *auth_db) : auth_db(auth_db){};
48         virtual ~FixedProvider(){};
49         virtual AuthDatabase *getAuthDatabase() { return auth_db; };
50
51 private:
52         AuthDatabase *auth_db;
53 };
54
55 class FilesProvider : public AuthDatabaseProvider
56 {
57 public:
58         FilesProvider(const std::string &dir) : dir(dir){};
59         virtual ~FilesProvider() { delete auth_db; };
60         virtual AuthDatabase *getAuthDatabase()
61         {
62                 delete auth_db;
63                 auth_db = new AuthDatabaseFiles(dir);
64                 return auth_db;
65         };
66
67 private:
68         std::string dir;
69         AuthDatabase *auth_db = nullptr;
70 };
71
72 class SQLite3Provider : public AuthDatabaseProvider
73 {
74 public:
75         SQLite3Provider(const std::string &dir) : dir(dir){};
76         virtual ~SQLite3Provider() { delete auth_db; };
77         virtual AuthDatabase *getAuthDatabase()
78         {
79                 delete auth_db;
80                 auth_db = new AuthDatabaseSQLite3(dir);
81                 return auth_db;
82         };
83
84 private:
85         std::string dir;
86         AuthDatabase *auth_db = nullptr;
87 };
88 }
89
90 class TestAuthDatabase : public TestBase
91 {
92 public:
93         TestAuthDatabase()
94         {
95                 TestManager::registerTestModule(this);
96                 // fixed directory, for persistence
97                 test_dir = getTestTempDirectory();
98         }
99         const char *getName() { return "TestAuthDatabase"; }
100
101         void runTests(IGameDef *gamedef);
102         void runTestsForCurrentDB();
103
104         void testRecallFail();
105         void testCreate();
106         void testRecall();
107         void testChange();
108         void testRecallChanged();
109         void testChangePrivileges();
110         void testRecallChangedPrivileges();
111         void testListNames();
112         void testDelete();
113
114 private:
115         std::string test_dir;
116         AuthDatabaseProvider *auth_provider;
117 };
118
119 static TestAuthDatabase g_test_instance;
120
121 void TestAuthDatabase::runTests(IGameDef *gamedef)
122 {
123         // Each set of tests is run twice for each database type:
124         // one where we reuse the same AuthDatabase object (to test local caching),
125         // and one where we create a new AuthDatabase object for each call
126         // (to test actual persistence).
127
128         rawstream << "-------- Files database (same object)" << std::endl;
129
130         AuthDatabase *auth_db = new AuthDatabaseFiles(test_dir);
131         auth_provider = new FixedProvider(auth_db);
132
133         runTestsForCurrentDB();
134
135         delete auth_db;
136         delete auth_provider;
137
138         // reset database
139         fs::DeleteSingleFileOrEmptyDirectory(test_dir + DIR_DELIM + "auth.txt");
140
141         rawstream << "-------- Files database (new objects)" << std::endl;
142
143         auth_provider = new FilesProvider(test_dir);
144
145         runTestsForCurrentDB();
146
147         delete auth_provider;
148
149         rawstream << "-------- SQLite3 database (same object)" << std::endl;
150
151         auth_db = new AuthDatabaseSQLite3(test_dir);
152         auth_provider = new FixedProvider(auth_db);
153
154         runTestsForCurrentDB();
155
156         delete auth_db;
157         delete auth_provider;
158
159         // reset database
160         fs::DeleteSingleFileOrEmptyDirectory(test_dir + DIR_DELIM + "auth.sqlite");
161
162         rawstream << "-------- SQLite3 database (new objects)" << std::endl;
163
164         auth_provider = new SQLite3Provider(test_dir);
165
166         runTestsForCurrentDB();
167
168         delete auth_provider;
169 }
170
171 ////////////////////////////////////////////////////////////////////////////////
172
173 void TestAuthDatabase::runTestsForCurrentDB()
174 {
175         TEST(testRecallFail);
176         TEST(testCreate);
177         TEST(testRecall);
178         TEST(testChange);
179         TEST(testRecallChanged);
180         TEST(testChangePrivileges);
181         TEST(testRecallChangedPrivileges);
182         TEST(testListNames);
183         TEST(testDelete);
184         TEST(testRecallFail);
185 }
186
187 void TestAuthDatabase::testRecallFail()
188 {
189         AuthDatabase *auth_db = auth_provider->getAuthDatabase();
190         AuthEntry authEntry;
191
192         // no such user yet
193         UASSERT(!auth_db->getAuth("TestName", authEntry));
194 }
195
196 void TestAuthDatabase::testCreate()
197 {
198         AuthDatabase *auth_db = auth_provider->getAuthDatabase();
199         AuthEntry authEntry;
200
201         authEntry.name = "TestName";
202         authEntry.password = "TestPassword";
203         authEntry.privileges.emplace_back("shout");
204         authEntry.privileges.emplace_back("interact");
205         authEntry.last_login = 1000;
206         UASSERT(auth_db->createAuth(authEntry));
207 }
208
209 void TestAuthDatabase::testRecall()
210 {
211         AuthDatabase *auth_db = auth_provider->getAuthDatabase();
212         AuthEntry authEntry;
213
214         UASSERT(auth_db->getAuth("TestName", authEntry));
215         UASSERTEQ(std::string, authEntry.name, "TestName");
216         UASSERTEQ(std::string, authEntry.password, "TestPassword");
217         // the order of privileges is unimportant
218         std::sort(authEntry.privileges.begin(), authEntry.privileges.end());
219         UASSERTEQ(std::string, str_join(authEntry.privileges, ","), "interact,shout");
220 }
221
222 void TestAuthDatabase::testChange()
223 {
224         AuthDatabase *auth_db = auth_provider->getAuthDatabase();
225         AuthEntry authEntry;
226
227         UASSERT(auth_db->getAuth("TestName", authEntry));
228         authEntry.password = "NewPassword";
229         authEntry.last_login = 1002;
230         UASSERT(auth_db->saveAuth(authEntry));
231 }
232
233 void TestAuthDatabase::testRecallChanged()
234 {
235         AuthDatabase *auth_db = auth_provider->getAuthDatabase();
236         AuthEntry authEntry;
237
238         UASSERT(auth_db->getAuth("TestName", authEntry));
239         UASSERTEQ(std::string, authEntry.password, "NewPassword");
240         // the order of privileges is unimportant
241         std::sort(authEntry.privileges.begin(), authEntry.privileges.end());
242         UASSERTEQ(std::string, str_join(authEntry.privileges, ","), "interact,shout");
243         UASSERTEQ(u64, authEntry.last_login, 1002);
244 }
245
246 void TestAuthDatabase::testChangePrivileges()
247 {
248         AuthDatabase *auth_db = auth_provider->getAuthDatabase();
249         AuthEntry authEntry;
250
251         UASSERT(auth_db->getAuth("TestName", authEntry));
252         authEntry.privileges.clear();
253         authEntry.privileges.emplace_back("interact");
254         authEntry.privileges.emplace_back("fly");
255         authEntry.privileges.emplace_back("dig");
256         UASSERT(auth_db->saveAuth(authEntry));
257 }
258
259 void TestAuthDatabase::testRecallChangedPrivileges()
260 {
261         AuthDatabase *auth_db = auth_provider->getAuthDatabase();
262         AuthEntry authEntry;
263
264         UASSERT(auth_db->getAuth("TestName", authEntry));
265         // the order of privileges is unimportant
266         std::sort(authEntry.privileges.begin(), authEntry.privileges.end());
267         UASSERTEQ(std::string, str_join(authEntry.privileges, ","), "dig,fly,interact");
268 }
269
270 void TestAuthDatabase::testListNames()
271 {
272
273         AuthDatabase *auth_db = auth_provider->getAuthDatabase();
274         std::vector<std::string> list;
275
276         AuthEntry authEntry;
277
278         authEntry.name = "SecondName";
279         authEntry.password = "SecondPassword";
280         authEntry.privileges.emplace_back("shout");
281         authEntry.privileges.emplace_back("interact");
282         authEntry.last_login = 1003;
283         auth_db->createAuth(authEntry);
284
285         auth_db->listNames(list);
286         // not necessarily sorted, so sort before comparing
287         std::sort(list.begin(), list.end());
288         UASSERTEQ(std::string, str_join(list, ","), "SecondName,TestName");
289 }
290
291 void TestAuthDatabase::testDelete()
292 {
293         AuthDatabase *auth_db = auth_provider->getAuthDatabase();
294
295         UASSERT(!auth_db->deleteAuth("NoSuchName"));
296         UASSERT(auth_db->deleteAuth("TestName"));
297         // second try, expect failure
298         UASSERT(!auth_db->deleteAuth("TestName"));
299 }