Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
test_immutable_query_builder.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
11#include <gtest/gtest.h>
12#include <string>
13
15
16using namespace database;
17
18//=============================================================================
19// immutable_query_builder Tests
20//=============================================================================
21
22class ImmutableQueryBuilderTest : public ::testing::Test {
23protected:
24 // Default builder on "users" table
26};
27
28// -- Construction --
29
30TEST_F(ImmutableQueryBuilderTest, DefaultBuildSelectsStar) {
31 std::string sql = builder_.build();
32 EXPECT_NE(sql.find("SELECT *"), std::string::npos);
33 EXPECT_NE(sql.find("\"users\""), std::string::npos);
34}
35
36// -- select() --
37
38TEST_F(ImmutableQueryBuilderTest, SelectSpecificColumns) {
39 auto q = builder_.select({"id", "name", "email"});
40 std::string sql = q.build();
41 EXPECT_NE(sql.find("\"id\""), std::string::npos);
42 EXPECT_NE(sql.find("\"name\""), std::string::npos);
43 EXPECT_NE(sql.find("\"email\""), std::string::npos);
44 EXPECT_EQ(sql.find("*"), std::string::npos);
45}
46
47// -- where() --
48
49TEST_F(ImmutableQueryBuilderTest, WhereWithFieldOpValue) {
50 auto q = builder_.where("active", "=", true);
51 std::string sql = q.build();
52 EXPECT_NE(sql.find("WHERE"), std::string::npos);
53}
54
55TEST_F(ImmutableQueryBuilderTest, WhereMultipleConditionsJoinedByAnd) {
56 auto q = builder_
57 .where("active", "=", true)
58 .where("age", ">", int64_t{18});
59 std::string sql = q.build();
60 EXPECT_NE(sql.find("AND"), std::string::npos);
61}
62
63TEST_F(ImmutableQueryBuilderTest, WhereWithQueryCondition) {
64 query_condition cond("status", "=", std::string("active"));
65 auto q = builder_.where(cond);
66 std::string sql = q.build();
67 EXPECT_NE(sql.find("WHERE"), std::string::npos);
68 EXPECT_NE(sql.find("status"), std::string::npos);
69}
70
71// -- order_by() --
72
74 auto q = builder_.order_by("name", sort_order::asc);
75 std::string sql = q.build();
76 EXPECT_NE(sql.find("ORDER BY"), std::string::npos);
77 EXPECT_NE(sql.find("ASC"), std::string::npos);
78}
79
80TEST_F(ImmutableQueryBuilderTest, OrderByDescending) {
81 auto q = builder_.order_by("created_at", sort_order::desc);
82 std::string sql = q.build();
83 EXPECT_NE(sql.find("DESC"), std::string::npos);
84}
85
86TEST_F(ImmutableQueryBuilderTest, OrderByMultipleFields) {
87 auto q = builder_
88 .order_by("name", sort_order::asc)
89 .order_by("created_at", sort_order::desc);
90 std::string sql = q.build();
91 EXPECT_NE(sql.find("ASC"), std::string::npos);
92 EXPECT_NE(sql.find("DESC"), std::string::npos);
93}
94
95// -- limit() and offset() --
96
98 auto q = builder_.limit(10);
99 std::string sql = q.build();
100 EXPECT_NE(sql.find("LIMIT 10"), std::string::npos);
101}
102
104 auto q = builder_.offset(20);
105 std::string sql = q.build();
106 EXPECT_NE(sql.find("OFFSET 20"), std::string::npos);
107}
108
110 auto q = builder_.limit(10).offset(20);
111 std::string sql = q.build();
112 EXPECT_NE(sql.find("LIMIT 10"), std::string::npos);
113 EXPECT_NE(sql.find("OFFSET 20"), std::string::npos);
114}
115
116// -- join() --
117
119 auto q = builder_.join("orders", "users.id = orders.user_id", join_type::inner);
120 std::string sql = q.build();
121 EXPECT_NE(sql.find("INNER JOIN"), std::string::npos);
122 EXPECT_NE(sql.find("users.id = orders.user_id"), std::string::npos);
123}
124
126 auto q = builder_.join("orders", "users.id = orders.user_id", join_type::left);
127 std::string sql = q.build();
128 EXPECT_NE(sql.find("LEFT JOIN"), std::string::npos);
129}
130
132 auto q = builder_.join("orders", "users.id = orders.user_id", join_type::right);
133 std::string sql = q.build();
134 EXPECT_NE(sql.find("RIGHT JOIN"), std::string::npos);
135}
136
138 auto q = builder_.join("orders", "users.id = orders.user_id", join_type::full_outer);
139 std::string sql = q.build();
140 EXPECT_NE(sql.find("FULL OUTER JOIN"), std::string::npos);
141}
142
144 auto q = builder_.join("roles", "1=1", join_type::cross);
145 std::string sql = q.build();
146 EXPECT_NE(sql.find("CROSS JOIN"), std::string::npos);
147}
148
149// -- group_by() and having() --
150
152 auto q = builder_.group_by({"department"});
153 std::string sql = q.build();
154 EXPECT_NE(sql.find("GROUP BY"), std::string::npos);
155 EXPECT_NE(sql.find("\"department\""), std::string::npos);
156}
157
158TEST_F(ImmutableQueryBuilderTest, GroupByMultipleFields) {
159 auto q = builder_.group_by({"department", "status"});
160 std::string sql = q.build();
161 EXPECT_NE(sql.find("GROUP BY"), std::string::npos);
162 EXPECT_NE(sql.find("\"department\""), std::string::npos);
163 EXPECT_NE(sql.find("\"status\""), std::string::npos);
164}
165
167 auto q = builder_.group_by({"department"}).having("COUNT(*) > 5");
168 std::string sql = q.build();
169 EXPECT_NE(sql.find("HAVING COUNT(*) > 5"), std::string::npos);
170}
171
172// -- build_for_database() --
173
174TEST_F(ImmutableQueryBuilderTest, PostgresUsesDoubleQuotes) {
175 auto q = builder_.select({"id", "name"});
176 std::string sql = q.build_for_database(database_types::postgres);
177 EXPECT_NE(sql.find("\"id\""), std::string::npos);
178 EXPECT_NE(sql.find("\"name\""), std::string::npos);
179 EXPECT_NE(sql.find("\"users\""), std::string::npos);
180}
181
182TEST_F(ImmutableQueryBuilderTest, SqliteUsesDoubleQuotes) {
183 auto q = builder_.select({"id"});
184 std::string sql = q.build_for_database(database_types::sqlite);
185 EXPECT_NE(sql.find("\"id\""), std::string::npos);
186 EXPECT_NE(sql.find("\"users\""), std::string::npos);
187}
188
189TEST_F(ImmutableQueryBuilderTest, DefaultBuildUsesPostgresDialect) {
190 auto q = builder_.select({"id"});
191 std::string default_sql = q.build();
192 std::string pg_sql = q.build_for_database(database_types::postgres);
193 EXPECT_EQ(default_sql, pg_sql);
194}
195
196// -- Immutability verification --
197
198TEST_F(ImmutableQueryBuilderTest, OriginalUnchangedAfterSelect) {
199 auto q = builder_.select({"id"});
200 std::string original_sql = builder_.build();
201 std::string new_sql = q.build();
202 EXPECT_NE(original_sql, new_sql);
203 EXPECT_NE(original_sql.find("*"), std::string::npos);
204 EXPECT_EQ(new_sql.find("*"), std::string::npos);
205}
206
207TEST_F(ImmutableQueryBuilderTest, OriginalUnchangedAfterWhere) {
208 auto q = builder_.where("id", "=", int64_t{1});
209 std::string original_sql = builder_.build();
210 EXPECT_EQ(original_sql.find("WHERE"), std::string::npos);
211}
212
213TEST_F(ImmutableQueryBuilderTest, OriginalUnchangedAfterLimit) {
214 auto q = builder_.limit(10);
215 std::string original_sql = builder_.build();
216 EXPECT_EQ(original_sql.find("LIMIT"), std::string::npos);
217}
218
219TEST_F(ImmutableQueryBuilderTest, ChainingProducesCorrectQuery) {
220 auto q = builder_
221 .select({"id", "name"})
222 .where("active", "=", true)
223 .order_by("name", sort_order::asc)
224 .limit(10)
225 .offset(0);
226
227 std::string sql = q.build();
228 EXPECT_NE(sql.find("SELECT"), std::string::npos);
229 EXPECT_NE(sql.find("WHERE"), std::string::npos);
230 EXPECT_NE(sql.find("ORDER BY"), std::string::npos);
231 EXPECT_NE(sql.find("LIMIT 10"), std::string::npos);
232 EXPECT_NE(sql.find("OFFSET 0"), std::string::npos);
233}
234
235// -- Full query composition --
236
237TEST_F(ImmutableQueryBuilderTest, ComplexQueryComposition) {
238 auto q = builder_
239 .select({"u.id", "u.name", "COUNT(o.id)"})
240 .join("orders", "u.id = o.user_id", join_type::left)
241 .where("u.active", "=", true)
242 .group_by({"u.id", "u.name"})
243 .having("COUNT(o.id) > 0")
244 .order_by("u.name", sort_order::asc)
245 .limit(50);
246
247 std::string sql = q.build();
248 EXPECT_NE(sql.find("SELECT"), std::string::npos);
249 EXPECT_NE(sql.find("LEFT JOIN"), std::string::npos);
250 EXPECT_NE(sql.find("WHERE"), std::string::npos);
251 EXPECT_NE(sql.find("GROUP BY"), std::string::npos);
252 EXPECT_NE(sql.find("HAVING"), std::string::npos);
253 EXPECT_NE(sql.find("ORDER BY"), std::string::npos);
254 EXPECT_NE(sql.find("LIMIT 50"), std::string::npos);
255}
Thread-safe immutable query builder using functional programming style.
std::string build() const
Builds the final SQL query string.
Represents a WHERE condition in a query.
TEST_F(ImmutableQueryBuilderTest, DefaultBuildSelectsStar)