Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
universal_query_builder_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
10#include <gtest/gtest.h>
12#include <string>
13#include <map>
14
15namespace database::tests
16{
17
18class UniversalQueryBuilderTest : public ::testing::Test
19{
20protected:
21 void SetUp() override
22 {
23 }
24
25 void TearDown() override
26 {
27 }
28};
29
30//=============================================================================
31// Builder Selection Tests
32//=============================================================================
33
35{
37
38 auto query = builder.select({"*"})
39 .from("users")
40 .build();
41
42 EXPECT_TRUE(query.find("SELECT") != std::string::npos);
43 EXPECT_TRUE(query.find("\"users\"") != std::string::npos); // PostgreSQL uses double quotes
44}
45
47{
49
50 auto query = builder.select({"*"})
51 .from("users")
52 .build();
53
54 EXPECT_TRUE(query.find("SELECT") != std::string::npos);
55 EXPECT_TRUE(query.find("[users]") != std::string::npos); // SQLite uses square brackets
56}
57
58#ifdef USE_MONGODB
59// Note: Universal builder's MongoDB support requires find() to be called
60// This is expected behavior - collection().limit() alone doesn't set operation type
61TEST_F(UniversalQueryBuilderTest, MongoDBBuilder)
62{
64
65 // MongoDB universal builder doesn't fully support all MongoDB operations
66 // Test that the builder is created without throwing
67 EXPECT_NO_THROW(builder.collection("users"));
68}
69#endif // USE_MONGODB
70
71#ifdef USE_REDIS
72TEST_F(UniversalQueryBuilderTest, RedisBuilder)
73{
74 query_builder builder(database_types::redis);
75
76 auto query = builder.key("user:1")
77 .build();
78
79 EXPECT_TRUE(query.find("GET") != std::string::npos);
80}
81#endif // USE_REDIS
82
83//=============================================================================
84// For Database Switch Tests
85//=============================================================================
86
88{
90
92
93 auto query = builder.select({"*"})
94 .from("users")
95 .build();
96
97 EXPECT_TRUE(query.find("[users]") != std::string::npos); // SQLite syntax
98}
99
100//=============================================================================
101// SQL Interface Tests
102//=============================================================================
103
105{
107
108 auto query = builder.select({"id", "name", "email"})
109 .from("users")
110 .where("active", "=", core::database_value{true})
111 .order_by("name", sort_order::asc)
112 .limit(10)
113 .build();
114
115 EXPECT_TRUE(query.find("SELECT") != std::string::npos);
116 EXPECT_TRUE(query.find("FROM") != std::string::npos);
117 EXPECT_TRUE(query.find("WHERE") != std::string::npos);
118 EXPECT_TRUE(query.find("ORDER BY") != std::string::npos);
119 EXPECT_TRUE(query.find("LIMIT") != std::string::npos);
120}
121
123{
125
126 auto query = builder.select({"u.id", "u.name", "o.total"})
127 .from("users u")
128 .join("orders o", "u.id = o.user_id")
129 .build();
130
131 EXPECT_TRUE(query.find("JOIN") != std::string::npos);
132}
133
134//=============================================================================
135// Insert/Update Tests
136//=============================================================================
137
139{
141
142 std::map<std::string, core::database_value> data;
143 data["name"] = core::database_value{std::string("John")};
144 data["email"] = core::database_value{std::string("john@example.com")};
145
146 auto query = builder.insert_into("users").values(data).build();
147
148 EXPECT_TRUE(query.find("INSERT INTO") != std::string::npos);
149 EXPECT_TRUE(query.find("VALUES") != std::string::npos);
150}
151
153{
155
156 auto query = builder.update("users")
157 .set("status", core::database_value{std::string("active")})
158 .where("id", "=", core::database_value{int64_t(1)})
159 .build();
160
161 EXPECT_TRUE(query.find("UPDATE") != std::string::npos);
162 EXPECT_TRUE(query.find("SET") != std::string::npos);
163 EXPECT_TRUE(query.find("WHERE") != std::string::npos);
164}
165
166//=============================================================================
167// Reset Tests
168//=============================================================================
169
171{
173
174 builder.select({"*"}).from("users").limit(10);
175 builder.reset();
176
177 // After reset, building should work with new query
178 auto query = builder.select({"*"}).from("products").build();
179 EXPECT_TRUE(query.find("products") != std::string::npos);
180}
181
182//=============================================================================
183// NoSQL Interface Tests
184//=============================================================================
185
186#ifdef USE_MONGODB
187// Note: MongoDB universal builder requires find() to set operation type
188TEST_F(UniversalQueryBuilderTest, MongoDBCollection)
189{
191
192 // Universal builder's collection().limit() doesn't set operation type
193 // This is expected behavior - full MongoDB operations need mongodb_query_builder directly
194 EXPECT_NO_THROW(builder.collection("users").limit(10));
195}
196#endif // USE_MONGODB
197
198#ifdef USE_REDIS
199TEST_F(UniversalQueryBuilderTest, RedisKey)
200{
201 query_builder builder(database_types::redis);
202
203 auto query = builder.key("user:1")
204 .build();
205
206 EXPECT_TRUE(query.find("GET") != std::string::npos);
207 EXPECT_TRUE(query.find("user:1") != std::string::npos);
208}
209#endif // USE_REDIS
210
211//=============================================================================
212// Default Constructor Tests
213//=============================================================================
214
216{
217 query_builder builder;
218
219 // Default is none, so build should return empty
220 auto query = builder.build();
221 EXPECT_TRUE(query.empty());
222}
223
224TEST_F(UniversalQueryBuilderTest, SetDatabaseAfterConstruction)
225{
226 query_builder builder;
227
229
230 auto query = builder.select({"*"})
231 .from("users")
232 .build();
233
234 EXPECT_TRUE(query.find("SELECT") != std::string::npos);
235}
236
237//=============================================================================
238// Cross-Database Compatibility Tests
239//=============================================================================
240
241TEST_F(UniversalQueryBuilderTest, SameSQLAcrossDialects)
242{
243 // Build same logical query for different databases
244 std::vector<database_types> sql_types = {
247 };
248
249 for (auto db_type : sql_types) {
250 query_builder builder(db_type);
251
252 auto query = builder.select({"id", "name"})
253 .from("users")
254 .where("active", "=", core::database_value{true})
255 .limit(10)
256 .build();
257
258 EXPECT_TRUE(query.find("SELECT") != std::string::npos);
259 EXPECT_TRUE(query.find("FROM") != std::string::npos);
260 EXPECT_TRUE(query.find("WHERE") != std::string::npos);
261 EXPECT_TRUE(query.find("LIMIT") != std::string::npos);
262 }
263}
264
265//=============================================================================
266// Error Handling Tests
267//=============================================================================
268
270{
272
273 auto query = builder.select({"*"}).from("users").build();
274
275 EXPECT_TRUE(query.empty());
276}
277
278//=============================================================================
279// Chaining Tests
280//=============================================================================
281
283{
285
286 // All methods should return reference for chaining
287 auto& ref1 = builder.for_database(database_types::sqlite);
288 auto& ref2 = ref1.select({"*"});
289 auto& ref3 = ref2.from("users");
290 auto& ref4 = ref3.where("active", "=", core::database_value{true});
291 auto& ref5 = ref4.order_by("name");
292 auto& ref6 = ref5.limit(10);
293
294 auto query = ref6.build();
295 EXPECT_TRUE(query.find("SELECT") != std::string::npos);
296}
297
298//=============================================================================
299// MongoDB Universal Interface Tests
300//=============================================================================
301
302#ifdef USE_MONGODB
303TEST_F(UniversalQueryBuilderTest, MongoDBInsert)
304{
306
307 std::map<std::string, core::database_value> data;
308 data["name"] = core::database_value{std::string("John")};
309
310 // MongoDB operations use insert_into().values() for inserts
311 auto query = builder.collection("users")
312 .insert_into("users")
313 .values(data)
314 .build();
315
316 // MongoDB dialect should handle this as an insert operation
317 // The exact output format depends on the mongodb_dialect implementation
318 EXPECT_FALSE(query.empty());
319}
320#endif // USE_MONGODB
321
322//=============================================================================
323// Limit Across Databases
324//=============================================================================
325
327{
329
330 auto query = builder.select({"*"})
331 .from("users")
332 .limit(10)
333 .build();
334
335 EXPECT_TRUE(query.find("LIMIT 10") != std::string::npos);
336}
337
338#ifdef USE_MONGODB
339// Note: MongoDB limit requires find() to be called to set operation type
340TEST_F(UniversalQueryBuilderTest, LimitForMongoDB)
341{
343
344 // MongoDB limit without find() doesn't set operation type
345 // This is expected behavior - use mongodb_query_builder directly for full functionality
346 EXPECT_NO_THROW(builder.collection("users").limit(10));
347}
348#endif // USE_MONGODB
349
350} // namespace database::tests
Universal query builder that adapts to different database types.
query_builder & select(const std::vector< std::string > &columns)
query_builder & for_database(database_types db_type)
std::string build() const
query_builder & where(const std::string &field, const std::string &op, const core::database_value &value)
query_builder & limit(size_t count)
query_builder & update(const std::string &table)
query_builder & from(const std::string &table)
query_builder & order_by(const std::string &column, sort_order order=sort_order::asc)
query_builder & set(const std::string &field, const core::database_value &value)
query_builder & join(const std::string &table, const std::string &condition, join_type type=join_type::inner)
query_builder & insert_into(const std::string &table)
query_builder & values(const std::map< std::string, core::database_value > &data)
std::variant< std::string, int64_t, double, bool, std::nullptr_t > database_value
TEST_F(SQLQueryBuilderTest, SimpleSelect)
@ none
No specific database type is set.
@ mongodb
Indicates a MongoDB database (future implementation).
@ sqlite
Indicates a SQLite database.
@ redis
Indicates a Redis database (future implementation).
@ postgres
Indicates a PostgreSQL database.