Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
test_entity_metadata.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>
11#include <string>
12
13#include "database/orm/entity.h"
14
15using namespace database::orm;
16
17//=============================================================================
18// field_metadata Tests
19//=============================================================================
20
21class FieldMetadataTest : public ::testing::Test {};
22
23// -- Constructor and accessors --
24
25TEST_F(FieldMetadataTest, ConstructsWithNameAndType) {
26 field_metadata field("id", "int64_t");
27 EXPECT_EQ(field.name(), "id");
28 EXPECT_EQ(field.type_name(), "int64_t");
29}
30
31TEST_F(FieldMetadataTest, DefaultConstraintIsNone) {
32 field_metadata field("name", "std::string");
33 EXPECT_FALSE(field.is_primary_key());
34 EXPECT_FALSE(field.is_not_null());
35 EXPECT_FALSE(field.is_unique());
36 EXPECT_FALSE(field.is_auto_increment());
37 EXPECT_FALSE(field.has_index());
38 EXPECT_FALSE(field.is_foreign_key());
39 EXPECT_FALSE(field.has_default_now());
40}
41
42TEST_F(FieldMetadataTest, PrimaryKeyConstraint) {
43 field_metadata field("id", "int64_t", field_constraint::primary_key);
44 EXPECT_TRUE(field.is_primary_key());
45 EXPECT_FALSE(field.is_not_null());
46}
47
48TEST_F(FieldMetadataTest, CombinedConstraints) {
49 field_metadata field("id", "int64_t",
50 field_constraint::primary_key | field_constraint::auto_increment);
51 EXPECT_TRUE(field.is_primary_key());
52 EXPECT_TRUE(field.is_auto_increment());
53 EXPECT_FALSE(field.is_not_null());
54}
55
56TEST_F(FieldMetadataTest, NotNullAndUniqueConstraints) {
57 field_metadata field("email", "std::string",
58 field_constraint::not_null | field_constraint::unique);
59 EXPECT_TRUE(field.is_not_null());
60 EXPECT_TRUE(field.is_unique());
61 EXPECT_FALSE(field.is_primary_key());
62}
63
64TEST_F(FieldMetadataTest, IndexConstraintWithName) {
65 field_metadata field("name", "std::string",
66 field_constraint::index, "idx_users_name");
67 EXPECT_TRUE(field.has_index());
68 EXPECT_EQ(field.index_name(), "idx_users_name");
69}
70
71TEST_F(FieldMetadataTest, ForeignKeyConstraint) {
72 field_metadata field("user_id", "int64_t",
73 field_constraint::foreign_key, "", "users", "id");
74 EXPECT_TRUE(field.is_foreign_key());
75 EXPECT_EQ(field.foreign_table(), "users");
76 EXPECT_EQ(field.foreign_field(), "id");
77}
78
79TEST_F(FieldMetadataTest, DefaultNowConstraint) {
80 field_metadata field("created_at", "std::chrono::system_clock::time_point",
81 field_constraint::default_now);
82 EXPECT_TRUE(field.has_default_now());
83}
84
85// -- to_sql_definition() --
86
87TEST_F(FieldMetadataTest, SqlDefinitionInt32) {
88 field_metadata field("count", "int32_t");
89 EXPECT_EQ(field.to_sql_definition(), "count INTEGER");
90}
91
92TEST_F(FieldMetadataTest, SqlDefinitionInt64) {
93 field_metadata field("id", "int64_t");
94 EXPECT_EQ(field.to_sql_definition(), "id BIGINT");
95}
96
97TEST_F(FieldMetadataTest, SqlDefinitionDouble) {
98 field_metadata field("price", "double");
99 EXPECT_EQ(field.to_sql_definition(), "price DOUBLE PRECISION");
100}
101
102TEST_F(FieldMetadataTest, SqlDefinitionString) {
103 field_metadata field("name", "std::string");
104 EXPECT_EQ(field.to_sql_definition(), "name VARCHAR(255)");
105}
106
107TEST_F(FieldMetadataTest, SqlDefinitionBool) {
108 field_metadata field("active", "bool");
109 EXPECT_EQ(field.to_sql_definition(), "active BOOLEAN");
110}
111
112TEST_F(FieldMetadataTest, SqlDefinitionTimestamp) {
113 field_metadata field("created_at", "std::chrono::system_clock::time_point");
114 EXPECT_EQ(field.to_sql_definition(), "created_at TIMESTAMP");
115}
116
117TEST_F(FieldMetadataTest, SqlDefinitionUnknownTypeFallsBackToText) {
118 field_metadata field("data", "custom_type");
119 EXPECT_EQ(field.to_sql_definition(), "data TEXT");
120}
121
122TEST_F(FieldMetadataTest, SqlDefinitionPrimaryKey) {
123 field_metadata field("id", "int64_t", field_constraint::primary_key);
124 std::string sql = field.to_sql_definition();
125 EXPECT_NE(sql.find("PRIMARY KEY"), std::string::npos);
126}
127
128TEST_F(FieldMetadataTest, SqlDefinitionAutoIncrement) {
129 field_metadata field("id", "int64_t",
130 field_constraint::primary_key | field_constraint::auto_increment);
131 std::string sql = field.to_sql_definition();
132 EXPECT_NE(sql.find("PRIMARY KEY"), std::string::npos);
133 EXPECT_NE(sql.find("AUTO_INCREMENT"), std::string::npos);
134}
135
136TEST_F(FieldMetadataTest, SqlDefinitionNotNull) {
137 field_metadata field("name", "std::string", field_constraint::not_null);
138 std::string sql = field.to_sql_definition();
139 EXPECT_NE(sql.find("NOT NULL"), std::string::npos);
140}
141
142TEST_F(FieldMetadataTest, SqlDefinitionUniqueNonPK) {
143 field_metadata field("email", "std::string", field_constraint::unique);
144 std::string sql = field.to_sql_definition();
145 EXPECT_NE(sql.find("UNIQUE"), std::string::npos);
146}
147
148TEST_F(FieldMetadataTest, SqlDefinitionPrimaryKeyOmitsNotNull) {
149 // Primary keys are implicitly NOT NULL, so NOT NULL should not appear
150 field_metadata field("id", "int64_t",
151 field_constraint::primary_key | field_constraint::not_null);
152 std::string sql = field.to_sql_definition();
153 EXPECT_NE(sql.find("PRIMARY KEY"), std::string::npos);
154 EXPECT_EQ(sql.find("NOT NULL"), std::string::npos);
155}
156
157TEST_F(FieldMetadataTest, SqlDefinitionPrimaryKeyOmitsUnique) {
158 // Primary keys are implicitly UNIQUE, so UNIQUE should not appear
159 field_metadata field("id", "int64_t",
160 field_constraint::primary_key | field_constraint::unique);
161 std::string sql = field.to_sql_definition();
162 EXPECT_NE(sql.find("PRIMARY KEY"), std::string::npos);
163 EXPECT_EQ(sql.find("UNIQUE"), std::string::npos);
164}
165
166TEST_F(FieldMetadataTest, SqlDefinitionDefaultNow) {
167 field_metadata field("created_at", "std::chrono::system_clock::time_point",
168 field_constraint::default_now);
169 std::string sql = field.to_sql_definition();
170 EXPECT_NE(sql.find("DEFAULT CURRENT_TIMESTAMP"), std::string::npos);
171}
172
173// -- has_constraint helper function --
174
175TEST_F(FieldMetadataTest, HasConstraintFunction) {
176 EXPECT_TRUE(has_constraint(
177 field_constraint::primary_key | field_constraint::not_null,
178 field_constraint::primary_key));
179 EXPECT_TRUE(has_constraint(
180 field_constraint::primary_key | field_constraint::not_null,
181 field_constraint::not_null));
182 EXPECT_FALSE(has_constraint(
183 field_constraint::primary_key | field_constraint::not_null,
184 field_constraint::unique));
185}
186
187//=============================================================================
188// entity_metadata Tests
189//=============================================================================
190
191class EntityMetadataTest : public ::testing::Test {
192protected:
193 void SetUp() override {
194 meta_ = std::make_unique<entity_metadata>("users");
195
196 meta_->add_field(field_metadata("id", "int64_t",
197 field_constraint::primary_key | field_constraint::auto_increment));
198 meta_->add_field(field_metadata("name", "std::string",
199 field_constraint::not_null));
200 meta_->add_field(field_metadata("email", "std::string",
201 field_constraint::not_null | field_constraint::unique | field_constraint::index,
202 "idx_users_email"));
203 meta_->add_field(field_metadata("department_id", "int64_t",
204 field_constraint::foreign_key, "", "departments", "id"));
205 meta_->add_field(field_metadata("created_at", "std::chrono::system_clock::time_point",
206 field_constraint::default_now));
207 }
208
209 std::unique_ptr<entity_metadata> meta_;
210};
211
213 EXPECT_EQ(meta_->table_name(), "users");
214}
215
217 EXPECT_EQ(meta_->fields().size(), 5u);
218}
219
220TEST_F(EntityMetadataTest, AddFieldStoresInOrder) {
221 EXPECT_EQ(meta_->fields()[0].name(), "id");
222 EXPECT_EQ(meta_->fields()[1].name(), "name");
223 EXPECT_EQ(meta_->fields()[2].name(), "email");
224 EXPECT_EQ(meta_->fields()[3].name(), "department_id");
225 EXPECT_EQ(meta_->fields()[4].name(), "created_at");
226}
227
228TEST_F(EntityMetadataTest, GetPrimaryKeyFindsId) {
229 const auto* pk = meta_->get_primary_key();
230 ASSERT_NE(pk, nullptr);
231 EXPECT_EQ(pk->name(), "id");
232 EXPECT_TRUE(pk->is_primary_key());
233}
234
235TEST_F(EntityMetadataTest, GetPrimaryKeyReturnsNullWhenMissing) {
236 entity_metadata meta("no_pk_table");
237 meta.add_field(field_metadata("name", "std::string"));
238 EXPECT_EQ(meta.get_primary_key(), nullptr);
239}
240
241TEST_F(EntityMetadataTest, GetIndexesFindsIndexedFields) {
242 auto indexes = meta_->get_indexes();
243 ASSERT_EQ(indexes.size(), 1u);
244 EXPECT_EQ(indexes[0]->name(), "email");
245}
246
247TEST_F(EntityMetadataTest, GetForeignKeysFindsFK) {
248 auto fks = meta_->get_foreign_keys();
249 ASSERT_EQ(fks.size(), 1u);
250 EXPECT_EQ(fks[0]->name(), "department_id");
251 EXPECT_EQ(fks[0]->foreign_table(), "departments");
252 EXPECT_EQ(fks[0]->foreign_field(), "id");
253}
254
255TEST_F(EntityMetadataTest, CreateTableSqlContainsTableName) {
256 std::string sql = meta_->create_table_sql();
257 EXPECT_NE(sql.find("CREATE TABLE IF NOT EXISTS users"), std::string::npos);
258}
259
260TEST_F(EntityMetadataTest, CreateTableSqlContainsAllColumns) {
261 std::string sql = meta_->create_table_sql();
262 EXPECT_NE(sql.find("id BIGINT PRIMARY KEY AUTO_INCREMENT"), std::string::npos);
263 EXPECT_NE(sql.find("name VARCHAR(255) NOT NULL"), std::string::npos);
264 EXPECT_NE(sql.find("email VARCHAR(255) NOT NULL UNIQUE"), std::string::npos);
265 EXPECT_NE(sql.find("department_id BIGINT"), std::string::npos);
266 EXPECT_NE(sql.find("created_at TIMESTAMP"), std::string::npos);
267}
268
269TEST_F(EntityMetadataTest, CreateTableSqlContainsForeignKeyConstraint) {
270 std::string sql = meta_->create_table_sql();
271 EXPECT_NE(sql.find("FOREIGN KEY (department_id) REFERENCES departments(id)"),
272 std::string::npos);
273}
274
275TEST_F(EntityMetadataTest, CreateIndexesSqlWithNamedIndex) {
276 std::string sql = meta_->create_indexes_sql();
277 EXPECT_NE(sql.find("CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)"),
278 std::string::npos);
279}
280
281TEST_F(EntityMetadataTest, CreateIndexesSqlAutoGeneratesNameWhenMissing) {
282 entity_metadata meta("products");
283 meta.add_field(field_metadata("category", "std::string", field_constraint::index));
284
285 std::string sql = meta.create_indexes_sql();
286 EXPECT_NE(sql.find("CREATE INDEX IF NOT EXISTS idx_products_category ON products(category)"),
287 std::string::npos);
288}
289
290TEST_F(EntityMetadataTest, CreateIndexesSqlEmptyWhenNoIndexes) {
291 entity_metadata meta("simple");
292 meta.add_field(field_metadata("name", "std::string"));
293
294 std::string sql = meta.create_indexes_sql();
295 EXPECT_TRUE(sql.empty());
296}
297
298TEST_F(EntityMetadataTest, EmptyTableProducesMinimalDDL) {
299 entity_metadata meta("empty_table");
300 std::string sql = meta.create_table_sql();
301 EXPECT_NE(sql.find("CREATE TABLE IF NOT EXISTS empty_table"), std::string::npos);
302}
303
304// -- Constraint helper functions --
305
306TEST_F(EntityMetadataTest, ConstraintHelperFunctions) {
307 EXPECT_EQ(static_cast<int>(primary_key()), static_cast<int>(field_constraint::primary_key));
308 EXPECT_EQ(static_cast<int>(not_null()), static_cast<int>(field_constraint::not_null));
309 EXPECT_EQ(static_cast<int>(unique()), static_cast<int>(field_constraint::unique));
310 EXPECT_EQ(static_cast<int>(auto_increment()), static_cast<int>(field_constraint::auto_increment));
311 EXPECT_EQ(static_cast<int>(default_now()), static_cast<int>(field_constraint::default_now));
312}
std::unique_ptr< entity_metadata > meta_
Metadata for entire entities including table mapping and relationships.
Definition entity.h:118
std::string create_table_sql() const
Definition entity.cpp:123
void add_field(const field_metadata &field)
Definition entity.cpp:87
std::string create_indexes_sql() const
Definition entity.cpp:146
const field_metadata * get_primary_key() const
Definition entity.cpp:92
Metadata for entity fields including constraints and relationships.
Definition entity.h:78
std::string to_sql_definition() const
Definition entity.cpp:39
bool is_foreign_key() const
Definition entity.h:99
const std::string & index_name() const
Definition entity.h:90
bool is_primary_key() const
Definition entity.h:94
bool is_auto_increment() const
Definition entity.h:97
const std::string & name() const
Definition entity.h:87
const std::string & type_name() const
Definition entity.h:88
bool has_default_now() const
Definition entity.h:100
const std::string & foreign_table() const
Definition entity.h:91
const std::string & foreign_field() const
Definition entity.h:92
field_constraint auto_increment()
Definition entity.h:303
field_constraint unique()
Definition entity.h:302
field_constraint not_null()
Definition entity.h:301
field_constraint primary_key()
Definition entity.h:300
bool has_constraint(field_constraint constraints, field_constraint check)
Definition entity.h:69
field_constraint default_now()
Definition entity.h:304
#define ASSERT_EQ(expected, actual, message)
TEST_F(FieldMetadataTest, ConstructsWithNameAndType)