rippled
Loading...
Searching...
No Matches
SociDB_test.cpp
1#include <test/jtx/TestSuite.h>
2
3#include <xrpld/core/SociDB.h>
4
5#include <xrpl/basics/BasicConfig.h>
6#include <xrpl/basics/contract.h>
7
8#include <boost/algorithm/string.hpp>
9#include <boost/filesystem.hpp>
10
11namespace ripple {
12class SociDB_test final : public TestSuite
13{
14private:
15 static void
17 BasicConfig& config,
18 boost::filesystem::path const& dbPath)
19 {
20 config.overwrite("sqdb", "backend", "sqlite");
21 auto value = dbPath.string();
22 if (!value.empty())
23 config.legacy("database_path", value);
24 }
25
26 static void
27 cleanupDatabaseDir(boost::filesystem::path const& dbPath)
28 {
29 using namespace boost::filesystem;
30 if (!exists(dbPath) || !is_directory(dbPath) || !is_empty(dbPath))
31 return;
32 remove(dbPath);
33 }
34
35 static void
36 setupDatabaseDir(boost::filesystem::path const& dbPath)
37 {
38 using namespace boost::filesystem;
39 if (!exists(dbPath))
40 {
41 create_directory(dbPath);
42 return;
43 }
44
45 if (!is_directory(dbPath))
46 {
47 // someone created a file where we want to put out directory
48 Throw<std::runtime_error>(
49 "Cannot create directory: " + dbPath.string());
50 }
51 }
52 static boost::filesystem::path
54 {
55 return boost::filesystem::current_path() / "socidb_test_databases";
56 }
57
58public:
60 {
61 try
62 {
64 }
65 catch (std::exception const&)
66 {
67 }
68 }
70 {
71 try
72 {
74 }
75 catch (std::exception const&)
76 {
77 }
78 }
79 void
81 {
82 // confirm that files are given the correct exensions
83 testcase("sqliteFileNames");
87 {{"peerfinder", ".sqlite"},
88 {"state", ".db"},
89 {"random", ".db"},
90 {"validators", ".sqlite"}});
91
92 for (auto const& i : d)
93 {
94 DBConfig sc(c, i.first);
95 BEAST_EXPECT(
96 boost::ends_with(sc.connectionString(), i.first + i.second));
97 }
98 }
99 void
101 {
102 testcase("open");
103 BasicConfig c;
105 DBConfig sc(c, "SociTestDB");
106 std::vector<std::string> const stringData(
107 {"String1", "String2", "String3"});
108 std::vector<int> const intData({1, 2, 3});
109 auto checkValues = [this, &stringData, &intData](soci::session& s) {
110 // Check values in db
111 std::vector<std::string> stringResult(20 * stringData.size());
112 std::vector<int> intResult(20 * intData.size());
113 s << "SELECT StringData, IntData FROM SociTestTable;",
114 soci::into(stringResult), soci::into(intResult);
115 BEAST_EXPECT(
116 stringResult.size() == stringData.size() &&
117 intResult.size() == intData.size());
118 for (int i = 0; i < stringResult.size(); ++i)
119 {
120 auto si = std::distance(
121 stringData.begin(),
122 std::find(
123 stringData.begin(), stringData.end(), stringResult[i]));
124 auto ii = std::distance(
125 intData.begin(),
126 std::find(intData.begin(), intData.end(), intResult[i]));
127 BEAST_EXPECT(si == ii && si < stringResult.size());
128 }
129 };
130
131 {
132 soci::session s;
133 sc.open(s);
134 s << "CREATE TABLE IF NOT EXISTS SociTestTable ("
135 " Key INTEGER PRIMARY KEY,"
136 " StringData TEXT,"
137 " IntData INTEGER"
138 ");";
139
140 s << "INSERT INTO SociTestTable (StringData, IntData) VALUES "
141 "(:stringData, :intData);",
142 soci::use(stringData), soci::use(intData);
143 checkValues(s);
144 }
145 {
146 // Check values in db after session was closed
147 soci::session s;
148 sc.open(s);
149 checkValues(s);
150 }
151 {
152 namespace bfs = boost::filesystem;
153 // Remove the database
154 bfs::path dbPath(sc.connectionString());
155 if (bfs::is_regular_file(dbPath))
156 bfs::remove(dbPath);
157 }
158 }
159
160 void
162 {
163 testcase("select");
164 BasicConfig c;
166 DBConfig sc(c, "SociTestDB");
169 std::vector<std::int64_t> const bid({-10, -20, -30});
172 std::vector<std::int32_t> const id({-1, -2, -3});
173
174 {
175 soci::session s;
176 sc.open(s);
177
178 s << "DROP TABLE IF EXISTS STT;";
179
180 s << "CREATE TABLE STT ("
181 " I INTEGER,"
182 " UI INTEGER UNSIGNED,"
183 " BI BIGINT,"
184 " UBI BIGINT UNSIGNED"
185 ");";
186
187 s << "INSERT INTO STT (I, UI, BI, UBI) VALUES "
188 "(:id, :idu, :bid, :bidu);",
189 soci::use(id), soci::use(uid), soci::use(bid), soci::use(ubid);
190
191 try
192 {
193 std::int32_t ig = 0;
194 std::uint32_t uig = 0;
195 std::int64_t big = 0;
196 std::uint64_t ubig = 0;
197 s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig),
198 soci::into(uig), soci::into(big), soci::into(ubig);
199 BEAST_EXPECT(
200 ig == id[0] && uig == uid[0] && big == bid[0] &&
201 ubig == ubid[0]);
202 }
203 catch (std::exception&)
204 {
205 fail();
206 }
207 try
208 {
209 // SOCI requires boost::optional (not std::optional) as
210 // parameters.
211 boost::optional<std::int32_t> ig;
212 // Known bug: https://github.com/SOCI/soci/issues/926
213 // boost::optional<std::uint32_t> uig;
214 uint32_t uig = 0;
215 boost::optional<std::int64_t> big;
216 boost::optional<std::uint64_t> ubig;
217 s << "SELECT I, UI, BI, UBI from STT;", soci::into(ig),
218 soci::into(uig), soci::into(big), soci::into(ubig);
219 BEAST_EXPECT(
220 *ig == id[0] && uig == uid[0] && *big == bid[0] &&
221 *ubig == ubid[0]);
222 }
223 catch (std::exception&)
224 {
225 fail();
226 }
227 // There are too many issues when working with soci::row and
228 // boost::tuple. DO NOT USE soci row! I had a set of workarounds to
229 // make soci row less error prone, I'm keeping these tests in case I
230 // try to add soci::row and boost::tuple back into soci.
231#if 0
232 try
233 {
234 std::int32_t ig = 0;
235 std::uint32_t uig = 0;
236 std::int64_t big = 0;
237 std::uint64_t ubig = 0;
238 soci::row r;
239 s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
240 ig = r.get<std::int32_t>(0);
241 uig = r.get<std::uint32_t>(1);
242 big = r.get<std::int64_t>(2);
243 ubig = r.get<std::uint64_t>(3);
244 BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] &&
245 ubig == ubid[0]);
246 }
247 catch (std::exception&)
248 {
249 fail ();
250 }
251 try
252 {
253 std::int32_t ig = 0;
254 std::uint32_t uig = 0;
255 std::int64_t big = 0;
256 std::uint64_t ubig = 0;
257 soci::row r;
258 s << "SELECT I, UI, BI, UBI from STT", soci::into (r);
259 ig = r.get<std::int32_t>("I");
260 uig = r.get<std::uint32_t>("UI");
261 big = r.get<std::int64_t>("BI");
262 ubig = r.get<std::uint64_t>("UBI");
263 BEAST_EXPECT(ig == id[0] && uig == uid[0] && big == bid[0] &&
264 ubig == ubid[0]);
265 }
266 catch (std::exception&)
267 {
268 fail ();
269 }
270 try
271 {
272 boost::tuple<std::int32_t,
275 std::uint64_t> d;
276 s << "SELECT I, UI, BI, UBI from STT", soci::into (d);
277 BEAST_EXPECT(get<0>(d) == id[0] && get<1>(d) == uid[0] &&
278 get<2>(d) == bid[0] && get<3>(d) == ubid[0]);
279 }
280 catch (std::exception&)
281 {
282 fail ();
283 }
284#endif
285 }
286 {
287 namespace bfs = boost::filesystem;
288 // Remove the database
289 bfs::path dbPath(sc.connectionString());
290 if (bfs::is_regular_file(dbPath))
291 bfs::remove(dbPath);
292 }
293 }
294 void
296 {
297 testcase("deleteWithSubselect");
298 BasicConfig c;
300 DBConfig sc(c, "SociTestDB");
301 {
302 soci::session s;
303 sc.open(s);
304 char const* dbInit[] = {
305 "BEGIN TRANSACTION;",
306 "CREATE TABLE Ledgers ( \
307 LedgerHash CHARACTER(64) PRIMARY KEY, \
308 LedgerSeq BIGINT UNSIGNED \
309 );",
310 "CREATE INDEX SeqLedger ON Ledgers(LedgerSeq);"};
311 for (auto const c : dbInit)
312 s << c;
313 char lh[65];
314 memset(lh, 'a', 64);
315 lh[64] = '\0';
316 int toIncIndex = 63;
317 int const numRows = 16;
318 std::vector<std::string> ledgerHashes;
319 std::vector<int> ledgerIndexes;
320 ledgerHashes.reserve(numRows);
321 ledgerIndexes.reserve(numRows);
322 for (int i = 0; i < numRows; ++i)
323 {
324 ++lh[toIncIndex];
325 if (lh[toIncIndex] == 'z')
326 --toIncIndex;
327 ledgerHashes.emplace_back(lh);
328 ledgerIndexes.emplace_back(i);
329 }
330 s << "INSERT INTO Ledgers (LedgerHash, LedgerSeq) VALUES "
331 "(:lh, :li);",
332 soci::use(ledgerHashes), soci::use(ledgerIndexes);
333
334 std::vector<int> ledgersLS(numRows * 2);
335 s << "SELECT LedgerSeq FROM Ledgers;", soci::into(ledgersLS);
336 BEAST_EXPECT(ledgersLS.size() == numRows);
337 }
338 namespace bfs = boost::filesystem;
339 // Remove the database
340 bfs::path dbPath(sc.connectionString());
341 if (bfs::is_regular_file(dbPath))
342 bfs::remove(dbPath);
343 }
344 void
352};
353
354BEAST_DEFINE_TESTSUITE(SociDB, core, ripple);
355
356} // namespace ripple
testcase_t testcase
Memberspace for declaring test cases.
Definition suite.h:152
void fail(String const &reason, char const *file, int line)
Record a failure.
Definition suite.h:530
Holds unparsed configuration information.
void overwrite(std::string const &section, std::string const &key, std::string const &value)
Overwrite a key/value pair with a command line argument If the section does not exist it is created.
void legacy(std::string const &section, std::string value)
Set a value that is not a key/value pair.
DBConfig is used when a client wants to delay opening a soci::session after parsing the config parame...
Definition SociDB.h:42
void open(soci::session &s) const
Definition SociDB.cpp:76
std::string connectionString() const
Definition SociDB.cpp:70
static void setupDatabaseDir(boost::filesystem::path const &dbPath)
static void setupSQLiteConfig(BasicConfig &config, boost::filesystem::path const &dbPath)
void run() override
Runs the suite.
static boost::filesystem::path getDatabasePath()
static void cleanupDatabaseDir(boost::filesystem::path const &dbPath)
void testSQLiteDeleteWithSubselect()
T distance(T... args)
T emplace_back(T... args)
T find(T... args)
T max(T... args)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:6
T reserve(T... args)
T size(T... args)