Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
sqlite_backend_test.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, πŸ€β˜€πŸŒ•πŸŒ₯ 🌊
3// See the LICENSE file in the project root for full license information.
4
15#include <atomic>
16#include <chrono>
17#include <cstdio>
18#include <filesystem>
19#include <gtest/gtest.h>
20#include <memory>
21#include <thread>
22#include <vector>
23
26
27using namespace database;
28using namespace database::backends;
29using namespace database::core;
30
35class SQLiteBackendTest : public ::testing::Test {
36protected:
37 std::unique_ptr<sqlite_backend> backend_;
38 std::string test_db_path_;
39
40 void SetUp() override {
41 backend_ = std::make_unique<sqlite_backend>();
42 test_db_path_ = "test_sqlite_" + std::to_string(std::time(nullptr)) + ".db";
43 }
44
45 void TearDown() override {
46 if (backend_ && backend_->is_initialized()) {
47 backend_->shutdown();
48 }
49 if (std::filesystem::exists(test_db_path_)) {
50 std::filesystem::remove(test_db_path_);
51 }
52 }
53
55 connection_config config;
56 config.database = ":memory:";
57 return backend_->initialize(config).is_ok();
58 }
59
61 connection_config config;
62 config.database = test_db_path_;
63 return backend_->initialize(config).is_ok();
64 }
65
67 return backend_->execute_query("CREATE TABLE IF NOT EXISTS test_table ("
68 " id INTEGER PRIMARY KEY AUTOINCREMENT,"
69 " name TEXT NOT NULL,"
70 " value REAL,"
71 " active INTEGER DEFAULT 1"
72 ")").is_ok();
73 }
74};
75
76//=============================================================================
77// Database Type Tests
78//=============================================================================
79
80TEST_F(SQLiteBackendTest, DatabaseTypeReturnsSQLite) {
81 EXPECT_EQ(backend_->type(), database_types::sqlite);
82}
83
84//=============================================================================
85// Connection Tests
86//=============================================================================
87
88TEST_F(SQLiteBackendTest, ConnectToMemoryDatabase) {
89#ifdef USE_SQLITE
90 EXPECT_TRUE(connectToMemory());
91 EXPECT_TRUE(backend_->is_initialized());
92#else
93 GTEST_SKIP() << "SQLite support not compiled";
94#endif
95}
96
97TEST_F(SQLiteBackendTest, ConnectToFileDatabase) {
98#ifdef USE_SQLITE
99 EXPECT_TRUE(connectToFile());
100 EXPECT_TRUE(std::filesystem::exists(test_db_path_));
101#else
102 GTEST_SKIP() << "SQLite support not compiled";
103#endif
104}
105
106TEST_F(SQLiteBackendTest, ShutdownWithoutConnection) {
107 auto result = backend_->shutdown();
108 EXPECT_TRUE(result.is_ok());
109}
110
111TEST_F(SQLiteBackendTest, ShutdownAfterConnection) {
112#ifdef USE_SQLITE
113 EXPECT_TRUE(connectToMemory());
114 EXPECT_TRUE(backend_->shutdown().is_ok());
115 EXPECT_FALSE(backend_->is_initialized());
116#else
117 GTEST_SKIP() << "SQLite support not compiled";
118#endif
119}
120
121//=============================================================================
122// CREATE Query Tests
123//=============================================================================
124
125TEST_F(SQLiteBackendTest, ExecuteQueryWithoutConnection) {
126 EXPECT_FALSE(backend_->execute_query("CREATE TABLE test (id INTEGER)").is_ok());
127}
128
129TEST_F(SQLiteBackendTest, CreateTableQuery) {
130#ifdef USE_SQLITE
131 EXPECT_TRUE(connectToMemory());
132 EXPECT_TRUE(backend_->execute_query(
133 "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)").is_ok());
134#else
135 GTEST_SKIP() << "SQLite support not compiled";
136#endif
137}
138
139//=============================================================================
140// INSERT Query Tests
141//=============================================================================
142
143TEST_F(SQLiteBackendTest, InsertQueryWithoutConnection) {
144 auto result = backend_->execute_query("INSERT INTO test VALUES (1, 'test')");
145 EXPECT_FALSE(result.is_ok());
146}
147
148TEST_F(SQLiteBackendTest, InsertSingleRow) {
149#ifdef USE_SQLITE
150 EXPECT_TRUE(connectToMemory());
151 EXPECT_TRUE(createTestTable());
152
153 auto result = backend_->execute_query(
154 "INSERT INTO test_table (name, value) VALUES ('item1', 10.5)");
155 EXPECT_TRUE(result.is_ok());
156#else
157 GTEST_SKIP() << "SQLite support not compiled";
158#endif
159}
160
161//=============================================================================
162// SELECT Query Tests
163//=============================================================================
164
165TEST_F(SQLiteBackendTest, SelectQueryWithoutConnection) {
166 auto result = backend_->select_query("SELECT * FROM test");
167 EXPECT_FALSE(result.is_ok());
168}
169
170TEST_F(SQLiteBackendTest, SelectFromEmptyTable) {
171#ifdef USE_SQLITE
172 EXPECT_TRUE(connectToMemory());
173 EXPECT_TRUE(createTestTable());
174
175 auto result = backend_->select_query("SELECT * FROM test_table");
176 EXPECT_TRUE(result.is_ok());
177 EXPECT_TRUE(result.value().empty());
178#else
179 GTEST_SKIP() << "SQLite support not compiled";
180#endif
181}
182
183TEST_F(SQLiteBackendTest, SelectWithCondition) {
184#ifdef USE_SQLITE
185 EXPECT_TRUE(connectToMemory());
186 EXPECT_TRUE(createTestTable());
187
188 backend_->execute_query(
189 "INSERT INTO test_table (name, value) VALUES ('item1', 10.0)");
190 backend_->execute_query(
191 "INSERT INTO test_table (name, value) VALUES ('item2', 20.0)");
192 backend_->execute_query(
193 "INSERT INTO test_table (name, value) VALUES ('item3', 30.0)");
194
195 auto result =
196 backend_->select_query("SELECT * FROM test_table WHERE value > 15.0");
197 EXPECT_TRUE(result.is_ok());
198 EXPECT_EQ(result.value().size(), 2u);
199#else
200 GTEST_SKIP() << "SQLite support not compiled";
201#endif
202}
203
204//=============================================================================
205// UPDATE Query Tests
206//=============================================================================
207
208TEST_F(SQLiteBackendTest, UpdateQueryWithoutConnection) {
209 auto result = backend_->execute_query("UPDATE test SET name = 'new'");
210 EXPECT_FALSE(result.is_ok());
211}
212
213TEST_F(SQLiteBackendTest, UpdateSingleRow) {
214#ifdef USE_SQLITE
215 EXPECT_TRUE(connectToMemory());
216 EXPECT_TRUE(createTestTable());
217
218 backend_->execute_query(
219 "INSERT INTO test_table (name, value) VALUES ('old_name', 10.0)");
220
221 auto result = backend_->execute_query(
222 "UPDATE test_table SET name = 'new_name' WHERE id = 1");
223 EXPECT_TRUE(result.is_ok());
224#else
225 GTEST_SKIP() << "SQLite support not compiled";
226#endif
227}
228
229//=============================================================================
230// DELETE Query Tests
231//=============================================================================
232
233TEST_F(SQLiteBackendTest, DeleteQueryWithoutConnection) {
234 auto result = backend_->execute_query("DELETE FROM test WHERE id = 1");
235 EXPECT_FALSE(result.is_ok());
236}
237
238TEST_F(SQLiteBackendTest, DeleteSingleRow) {
239#ifdef USE_SQLITE
240 EXPECT_TRUE(connectToMemory());
241 EXPECT_TRUE(createTestTable());
242
243 backend_->execute_query(
244 "INSERT INTO test_table (name, value) VALUES ('item1', 10.0)");
245 backend_->execute_query(
246 "INSERT INTO test_table (name, value) VALUES ('item2', 20.0)");
247
248 auto result =
249 backend_->execute_query("DELETE FROM test_table WHERE id = 1");
250 EXPECT_TRUE(result.is_ok());
251#else
252 GTEST_SKIP() << "SQLite support not compiled";
253#endif
254}
255
256//=============================================================================
257// Transaction Tests
258//=============================================================================
259
260TEST_F(SQLiteBackendTest, TransactionCommit) {
261#ifdef USE_SQLITE
262 EXPECT_TRUE(connectToMemory());
263 EXPECT_TRUE(createTestTable());
264
265 EXPECT_TRUE(backend_->begin_transaction().is_ok());
266 EXPECT_TRUE(backend_->in_transaction());
267 backend_->execute_query(
268 "INSERT INTO test_table (name, value) VALUES ('trans_item', 50.0)");
269 EXPECT_TRUE(backend_->commit_transaction().is_ok());
270 EXPECT_FALSE(backend_->in_transaction());
271
272 auto result = backend_->select_query(
273 "SELECT * FROM test_table WHERE name = 'trans_item'");
274 EXPECT_TRUE(result.is_ok());
275 EXPECT_EQ(result.value().size(), 1u);
276#else
277 GTEST_SKIP() << "SQLite support not compiled";
278#endif
279}
280
281TEST_F(SQLiteBackendTest, TransactionRollback) {
282#ifdef USE_SQLITE
283 EXPECT_TRUE(connectToMemory());
284 EXPECT_TRUE(createTestTable());
285
286 EXPECT_TRUE(backend_->begin_transaction().is_ok());
287 backend_->execute_query(
288 "INSERT INTO test_table (name, value) VALUES ('rollback_item', 50.0)");
289 EXPECT_TRUE(backend_->rollback_transaction().is_ok());
290
291 auto result = backend_->select_query(
292 "SELECT * FROM test_table WHERE name = 'rollback_item'");
293 EXPECT_TRUE(result.is_ok());
294 EXPECT_TRUE(result.value().empty());
295#else
296 GTEST_SKIP() << "SQLite support not compiled";
297#endif
298}
299
300//=============================================================================
301// Thread Safety Tests
302//=============================================================================
303
304TEST_F(SQLiteBackendTest, ConcurrentReads) {
305#ifdef USE_SQLITE
306 EXPECT_TRUE(connectToMemory());
307 EXPECT_TRUE(createTestTable());
308
309 for (int i = 0; i < 100; ++i) {
310 backend_->execute_query(
311 "INSERT INTO test_table (name, value) VALUES ('item" +
312 std::to_string(i) + "', " + std::to_string(i * 1.5) + ")");
313 }
314
315 std::atomic<int> success_count{0};
316 std::vector<std::thread> threads;
317
318 for (int t = 0; t < 10; ++t) {
319 threads.emplace_back([this, &success_count]() {
320 for (int i = 0; i < 10; ++i) {
321 auto result =
322 backend_->select_query("SELECT COUNT(*) as cnt FROM test_table");
323 if (result.is_ok() && std::get<int64_t>(result.value()[0].at("cnt")) == 100) {
324 success_count++;
325 }
326 }
327 });
328 }
329
330 for (auto &t : threads) {
331 t.join();
332 }
333
334 EXPECT_EQ(success_count.load(), 100);
335#else
336 GTEST_SKIP() << "SQLite support not compiled";
337#endif
338}
339
340//=============================================================================
341// Edge Case Tests
342//=============================================================================
343
344TEST_F(SQLiteBackendTest, SpecialCharactersInData) {
345#ifdef USE_SQLITE
346 EXPECT_TRUE(connectToMemory());
347 EXPECT_TRUE(createTestTable());
348
349 backend_->execute_query(
350 "INSERT INTO test_table (name, value) VALUES ('test''s data', 10.0)");
351
352 auto result = backend_->select_query("SELECT name FROM test_table");
353 ASSERT_TRUE(result.is_ok());
354 ASSERT_EQ(result.value().size(), 1u);
355 EXPECT_EQ(std::get<std::string>(result.value()[0].at("name")), "test's data");
356#else
357 GTEST_SKIP() << "SQLite support not compiled";
358#endif
359}
360
362#ifdef USE_SQLITE
363 EXPECT_TRUE(connectToMemory());
364 EXPECT_TRUE(createTestTable());
365
366 backend_->execute_query(
367 "INSERT INTO test_table (name, value) VALUES ('ν•œκΈ€ν…ŒμŠ€νŠΈ', 10.0)");
368
369 auto result = backend_->select_query("SELECT name FROM test_table");
370 ASSERT_TRUE(result.is_ok());
371 ASSERT_EQ(result.value().size(), 1u);
372 EXPECT_EQ(std::get<std::string>(result.value()[0].at("name")), "ν•œκΈ€ν…ŒμŠ€νŠΈ");
373#else
374 GTEST_SKIP() << "SQLite support not compiled";
375#endif
376}
377
378int main(int argc, char **argv) {
379 ::testing::InitGoogleTest(&argc, argv);
380 return RUN_ALL_TESTS();
381}
Test fixture for SQLite backend tests.
std::unique_ptr< sqlite_backend > backend_
Abstract interface for database backends.
SQLite database backend plugin implementation.
TEST_F(SQLiteBackendTest, DatabaseTypeReturnsSQLite)
Configuration for database connection.
#define ASSERT_EQ(expected, actual, message)
#define ASSERT_TRUE(condition, message)