Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
mock_database.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
5#include "mock_database.h"
6#include <algorithm>
7
8namespace database::testing {
9
10// mock_database implementation
12 : db_type_(database_types::none)
13 , initialized_(false)
14 , connect_result_(true)
15 , in_transaction_(false)
16 , default_rows_affected_(1)
17{
18}
19
21 : db_type_(other.db_type_)
22 , initialized_(other.initialized_)
23 , connect_result_(other.connect_result_)
24 , in_transaction_(other.in_transaction_)
25 , connection_string_(std::move(other.connection_string_))
26 , default_result_(std::move(other.default_result_))
27 , default_rows_affected_(other.default_rows_affected_)
28 , last_error_(std::move(other.last_error_))
29 , expectations_(std::move(other.expectations_))
30 , executed_queries_(std::move(other.executed_queries_))
31{
32 other.initialized_ = false;
33 other.in_transaction_ = false;
34}
35
37 if (this != &other) {
38 db_type_ = other.db_type_;
39 initialized_ = other.initialized_;
40 connect_result_ = other.connect_result_;
41 in_transaction_ = other.in_transaction_;
42 connection_string_ = std::move(other.connection_string_);
43 default_result_ = std::move(other.default_result_);
44 default_rows_affected_ = other.default_rows_affected_;
45 last_error_ = std::move(other.last_error_);
46 expectations_ = std::move(other.expectations_);
47 executed_queries_ = std::move(other.executed_queries_);
48 other.initialized_ = false;
49 other.in_transaction_ = false;
50 }
51 return *this;
52}
53
57
58kcenon::common::VoidResult mock_database::initialize(const core::connection_config& config) {
59 std::lock_guard<std::mutex> lock(mutex_);
60 connection_string_ = config.host + ":" + std::to_string(config.port) + "/" + config.database;
61 if (connect_result_) {
62 initialized_ = true;
63 return kcenon::common::VoidResult::ok(std::monostate{});
64 }
65 last_error_ = "Connection failed";
66 return kcenon::common::VoidResult::err(kcenon::common::error_info{"Connection failed"});
67}
68
69kcenon::common::VoidResult mock_database::shutdown() {
70 std::lock_guard<std::mutex> lock(mutex_);
71 initialized_ = false;
72 in_transaction_ = false;
73 return kcenon::common::VoidResult::ok(std::monostate{});
74}
75
77 return initialized_;
78}
79
80kcenon::common::Result<core::database_result> mock_database::select_query(const std::string& query_string) {
81 std::lock_guard<std::mutex> lock(mutex_);
82 record_query(query_string);
83
84 if (auto* exp = find_expectation(query_string)) {
85 if (exp->should_throw()) {
86 last_error_ = exp->get_error_message();
87 return kcenon::common::Result<core::database_result>::err(kcenon::common::error_info{last_error_});
88 }
89 return kcenon::common::Result<core::database_result>::ok(exp->get_result());
90 }
91 return kcenon::common::Result<core::database_result>::ok(default_result_);
92}
93
94kcenon::common::VoidResult mock_database::execute_query(const std::string& query_string) {
95 std::lock_guard<std::mutex> lock(mutex_);
96 record_query(query_string);
97
98 if (auto* exp = find_expectation(query_string)) {
99 if (exp->should_throw()) {
100 last_error_ = exp->get_error_message();
101 return kcenon::common::VoidResult::err(kcenon::common::error_info{last_error_});
102 }
103 if (!exp->get_execute_result()) {
104 last_error_ = "Query execution failed";
105 return kcenon::common::VoidResult::err(kcenon::common::error_info{last_error_});
106 }
107 }
108 return kcenon::common::VoidResult::ok(std::monostate{});
109}
110
111kcenon::common::VoidResult mock_database::begin_transaction() {
112 std::lock_guard<std::mutex> lock(mutex_);
113 if (in_transaction_) {
114 return kcenon::common::VoidResult::err(kcenon::common::error_info{"Transaction already active"});
115 }
116 in_transaction_ = true;
117 return kcenon::common::VoidResult::ok(std::monostate{});
118}
119
120kcenon::common::VoidResult mock_database::commit_transaction() {
121 std::lock_guard<std::mutex> lock(mutex_);
122 if (!in_transaction_) {
123 return kcenon::common::VoidResult::err(kcenon::common::error_info{"No active transaction"});
124 }
125 in_transaction_ = false;
126 return kcenon::common::VoidResult::ok(std::monostate{});
127}
128
129kcenon::common::VoidResult mock_database::rollback_transaction() {
130 std::lock_guard<std::mutex> lock(mutex_);
131 if (!in_transaction_) {
132 return kcenon::common::VoidResult::err(kcenon::common::error_info{"No active transaction"});
133 }
134 in_transaction_ = false;
135 return kcenon::common::VoidResult::ok(std::monostate{});
136}
137
139 return in_transaction_;
140}
141
142std::string mock_database::last_error() const {
143 return last_error_;
144}
145
146std::map<std::string, std::string> mock_database::connection_info() const {
147 std::map<std::string, std::string> info;
148 info["connection_string"] = connection_string_;
149 info["initialized"] = initialized_ ? "true" : "false";
150 info["in_transaction"] = in_transaction_ ? "true" : "false";
151 return info;
152}
153
158
160 connect_result_ = result;
161 return *this;
162}
163
168
171 return *this;
172}
173
175 expectation exp;
176 exp.for_query(query, match_type::EXACT);
177 return expectation_builder(this, std::move(exp));
178}
179
180expectation_builder mock_database::expect_pattern(const std::string& regex_pattern) {
181 expectation exp;
182 exp.for_pattern(regex_pattern);
183 return expectation_builder(this, std::move(exp));
184}
185
187 expectation exp;
188 exp.for_any();
189 return expectation_builder(this, std::move(exp));
190}
191
196
198 initialized_ = false;
199 return *this;
200}
201
203 std::lock_guard<std::mutex> lock(mutex_);
204 return std::all_of(expectations_.begin(), expectations_.end(),
205 [](const expectation& exp) { return exp.is_satisfied(); });
206}
207
208std::vector<std::string> mock_database::get_executed_queries() const {
209 std::lock_guard<std::mutex> lock(mutex_);
210 return executed_queries_;
211}
212
214 std::lock_guard<std::mutex> lock(mutex_);
215 return executed_queries_.size();
216}
217
218size_t mock_database::get_query_count(const std::string& pattern) const {
219 std::lock_guard<std::mutex> lock(mutex_);
220 std::regex re(pattern);
221 return std::count_if(executed_queries_.begin(), executed_queries_.end(),
222 [&re](const std::string& q) { return std::regex_search(q, re); });
223}
224
226 std::lock_guard<std::mutex> lock(mutex_);
227 expectations_.clear();
228 executed_queries_.clear();
229 initialized_ = false;
230 in_transaction_ = false;
231 connection_string_.clear();
232 last_error_.clear();
233}
234
236 std::lock_guard<std::mutex> lock(mutex_);
237 expectations_.clear();
238}
239
241 std::lock_guard<std::mutex> lock(mutex_);
242 executed_queries_.clear();
243}
244
246 return initialized_;
247}
248
250 return connection_string_;
251}
252
253void mock_database::record_query(const std::string& query) {
254 executed_queries_.push_back(query);
255}
256
258 for (auto& exp : expectations_) {
259 if (exp.matches(query) && exp.can_be_invoked()) {
260 return &exp;
261 }
262 }
263 return nullptr;
264}
265
266// mock_database_builder implementation
268 : mock_(std::make_unique<mock_database>())
269{
270}
271
275
276mock_database mock_database_builder::with_data(const std::string& table_name, const core::database_result& data) {
277 mock_database db;
278 db.expect_pattern("SELECT.*FROM.*" + table_name).will_return(data);
279 return db;
280}
281
283 mock_database db;
284 db.expect_any().will_fail(error);
285 return db;
286}
287
289 mock_->set_database_type(type);
290 return *this;
291}
292
294 mock_->set_default_select_result(result);
295 return *this;
296}
297
299 mock_->simulate_connection_failure();
300 return *this;
301}
302
304 return std::move(*mock_);
305}
306
307} // namespace database::testing
Fluent builder for expectations.
expectation_builder & will_return(const core::database_result &result)
expectation_builder & will_fail(const std::string &error_message)
Single query expectation with configurable behavior.
expectation & for_pattern(const std::string &pattern)
expectation & for_query(const std::string &query, match_type type=match_type::EXACT)
Builder for common mock configurations.
std::unique_ptr< mock_database > mock_
static mock_database with_data(const std::string &table_name, const core::database_result &data)
mock_database_builder & with_type(database_types type)
mock_database_builder & with_default_result(const core::database_result &result)
static mock_database failing_database(const std::string &error="Mock database error")
mock_database_builder & that_fails_on_connect()
Configurable mock for database_backend interface.
kcenon::common::VoidResult initialize(const core::connection_config &config) override
Initialize the database backend.
mock_database & set_default_select_result(const core::database_result &result)
std::vector< std::string > executed_queries_
void record_query(const std::string &query)
std::map< std::string, std::string > connection_info() const override
Get backend-specific connection information.
std::vector< expectation > expectations_
mock_database & set_database_type(database_types type)
expectation_builder expect_pattern(const std::string &regex_pattern)
std::string last_error() const override
Get last error message from backend.
kcenon::common::Result< core::database_result > select_query(const std::string &query_string) override
Execute a SELECT query.
kcenon::common::VoidResult rollback_transaction() override
Rollback the current transaction.
mock_database & operator=(const mock_database &)=delete
expectation_builder expect_query(const std::string &query)
mock_database & set_connect_result(bool result)
mock_database & simulate_connection_failure()
std::vector< std::string > get_executed_queries() const
bool is_initialized() const override
Check if backend is initialized and ready.
kcenon::common::VoidResult execute_query(const std::string &query_string) override
Execute a general SQL query (DDL, DML)
kcenon::common::VoidResult commit_transaction() override
Commit the current transaction.
kcenon::common::VoidResult shutdown() override
Shutdown the database backend gracefully.
core::database_result default_result_
std::string get_connection_string() const
bool in_transaction() const override
Check if backend is currently in a transaction.
database_types type() const override
Get the database type of this backend.
expectation * find_expectation(const std::string &query)
mock_database & set_default_rows_affected(uint64_t rows)
kcenon::common::VoidResult begin_transaction() override
Begin a transaction.
std::vector< database_row > database_result
@ EXACT
Exact string match.
database_types
Represents various database backends or modes.
@ none
No specific database type is set.
Configuration for database connection.