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