toasty_driver_integration_suite/tests/
embed_enum_unit.rs1use toasty::schema::{
2 app::FieldTy,
3 mapping::{self, FieldEnum, FieldPrimitive},
4};
5
6use crate::{helpers::column, prelude::*};
7
8use toasty_core::{
9 driver::Operation,
10 stmt::{Assignment, BinaryOp, Expr, ExprSet, Statement},
11};
12
13#[driver_test(id(ID))]
19pub async fn create_and_query_enum(t: &mut Test) -> Result<()> {
20 #[derive(Debug, PartialEq, toasty::Embed)]
21 enum Status {
22 #[column(variant = 1)]
23 Pending,
24 #[column(variant = 2)]
25 Active,
26 #[column(variant = 3)]
27 Done,
28 }
29
30 #[derive(Debug, toasty::Model)]
31 struct User {
32 #[key]
33 #[auto]
34 id: ID,
35 name: String,
36 status: Status,
37 }
38
39 let mut db = t.setup_db(models!(User, Status)).await;
40 let user_table = table_id(&db, "users");
41
42 t.log().clear();
44
45 let mut user = User::create()
46 .name("Alice")
47 .status(Status::Pending)
48 .exec(&mut db)
49 .await?;
50
51 let sql = t.capability().sql;
57 let status_pos = if driver_test_cfg!(id_u64) { 1 } else { 2 };
58 let status_pat = if sql {
59 ArgOr::Arg(status_pos)
60 } else {
61 ArgOr::Value(1i64)
62 };
63 let op = t.log().pop_op();
64 assert_struct!(op, Operation::QuerySql({
65 stmt: Statement::Insert({
66 source.body: ExprSet::Values({
67 rows: [=~ (Any, Any, status_pat)],
68 }),
69 target: toasty_core::stmt::InsertTarget::Table({
70 table: == user_table,
71 columns: == columns(&db, "users", &["id", "name", "status"]),
72 }),
73 }),
74 }));
75 if sql {
76 assert_struct!(op, Operation::QuerySql({
77 params[status_pos].value: == 1i64,
78 }));
79 }
80
81 let found = User::get_by_id(&mut db, &user.id).await?;
83 assert_eq!(found.status, Status::Pending);
84
85 t.log().clear();
87 user.update().status(Status::Active).exec(&mut db).await?;
88
89 if t.capability().sql {
92 assert_struct!(t.log().pop_op(), Operation::QuerySql({
93 stmt: Statement::Update({
94 target: toasty_core::stmt::UpdateTarget::Table(== user_table),
95 assignments: #{ [2]: Assignment::Set(Expr::Arg({ position: 0 }))},
96 }),
97 params: [{ value: == 2i64 }, ..],
98 }));
99 } else {
100 assert_struct!(t.log().pop_op(), Operation::UpdateByKey({
101 table: == user_table,
102 filter: None,
103 keys: _,
104 assignments: #{ [2]: Assignment::Set(== 2i64)},
105 returning: false,
106 }));
107 }
108
109 let found = User::get_by_id(&mut db, &user.id).await?;
110 assert_eq!(found.status, Status::Active);
111
112 User::filter_by_id(user.id)
114 .update()
115 .status(Status::Done)
116 .exec(&mut db)
117 .await?;
118
119 let found = User::get_by_id(&mut db, &user.id).await?;
120 assert_eq!(found.status, Status::Done);
121
122 let id = user.id;
124 user.delete().exec(&mut db).await?;
125 assert_err!(User::get_by_id(&mut db, &id).await);
126 Ok(())
127}
128
129#[driver_test(requires(sql))]
135pub async fn filter_by_enum_variant(t: &mut Test) -> Result<()> {
136 #[derive(Debug, PartialEq, toasty::Embed)]
137 enum Status {
138 #[column(variant = 1)]
139 Pending,
140 #[column(variant = 2)]
141 Active,
142 #[column(variant = 3)]
143 Done,
144 }
145
146 #[derive(Debug, toasty::Model)]
147 #[allow(dead_code)]
148 struct Task {
149 #[key]
150 #[auto]
151 id: uuid::Uuid,
152 name: String,
153 status: Status,
154 }
155
156 let mut db = t.setup_db(models!(Task, Status)).await;
157
158 for (name, status) in [
160 ("Task A", Status::Pending),
161 ("Task B", Status::Active),
162 ("Task C", Status::Active),
163 ("Task D", Status::Done),
164 ] {
165 Task::create()
166 .name(name)
167 .status(status)
168 .exec(&mut db)
169 .await?;
170 }
171
172 let status_col = column(&db, "tasks", "status");
173 t.log().clear();
174
175 let active = Task::filter(Task::fields().status().eq(Status::Active))
177 .exec(&mut db)
178 .await?;
179 assert_eq!(active.len(), 2);
180 {
181 let (op, _) = t.log().pop();
182 assert_struct!(op, Operation::QuerySql({
183 stmt: Statement::Query({
184 body: ExprSet::Select({
185 filter.expr: Some(Expr::BinaryOp({
186 lhs.as_expr_column_unwrap().column: == status_col.index,
187 op: BinaryOp::Eq,
188 *rhs: Expr::Arg({ position: 0 }),
189 })),
190 }),
191 }),
192 params: [{ value: == 2i64 }],
193 }));
194 }
195
196 let pending = Task::filter(Task::fields().status().eq(Status::Pending))
198 .exec(&mut db)
199 .await?;
200 assert_eq!(pending.len(), 1);
201 assert_eq!(pending[0].name, "Task A");
202 {
203 let (op, _) = t.log().pop();
204 assert_struct!(op, Operation::QuerySql({
205 stmt: Statement::Query({
206 body: ExprSet::Select({
207 filter.expr: Some(Expr::BinaryOp({
208 lhs.as_expr_column_unwrap().column: == status_col.index,
209 op: BinaryOp::Eq,
210 *rhs: Expr::Arg({ position: 0 }),
211 })),
212 }),
213 }),
214 params: [{ value: == 1i64 }],
215 }));
216 }
217
218 let done = Task::filter(Task::fields().status().eq(Status::Done))
220 .exec(&mut db)
221 .await?;
222 assert_eq!(done.len(), 1);
223 assert_eq!(done[0].name, "Task D");
224 {
225 let (op, _) = t.log().pop();
226 assert_struct!(op, Operation::QuerySql({
227 stmt: Statement::Query({
228 body: ExprSet::Select({
229 filter.expr: Some(Expr::BinaryOp({
230 lhs.as_expr_column_unwrap().column: == status_col.index,
231 op: BinaryOp::Eq,
232 *rhs: Expr::Arg({ position: 0 }),
233 })),
234 }),
235 }),
236 params: [{ value: == 3i64 }],
237 }));
238 }
239
240 Ok(())
241}
242
243#[driver_test]
246pub async fn basic_embedded_enum(test: &mut Test) {
247 #[derive(toasty::Embed)]
248 enum Status {
249 #[column(variant = 1)]
250 Pending,
251 #[column(variant = 2)]
252 Active,
253 #[column(variant = 3)]
254 Done,
255 }
256
257 let db = test.setup_db(models!(Status)).await;
258 let schema = db.schema();
259
260 assert_struct!(schema.app.models, #{
262 Status::id(): toasty::schema::app::Model::EmbeddedEnum({
263 name.upper_camel_case(): "Status",
264 variants: [
265 _ { name.upper_camel_case(): "Pending", discriminant: toasty_core::stmt::Value::I64(1), .. },
266 _ { name.upper_camel_case(): "Active", discriminant: toasty_core::stmt::Value::I64(2), .. },
267 _ { name.upper_camel_case(): "Done", discriminant: toasty_core::stmt::Value::I64(3), .. },
268 ],
269 }),
270 });
271
272 assert!(schema.db.tables.is_empty());
274}
275
276#[driver_test]
281pub async fn root_model_with_embedded_enum_field(test: &mut Test) {
282 #[derive(toasty::Embed)]
283 enum Status {
284 #[column(variant = 1)]
285 Pending,
286 #[column(variant = 2)]
287 Active,
288 #[column(variant = 3)]
289 Done,
290 }
291
292 #[derive(toasty::Model)]
293 struct User {
294 #[key]
295 id: String,
296 #[allow(dead_code)]
297 status: Status,
298 }
299
300 let db = test.setup_db(models!(User, Status)).await;
301 let schema = db.schema();
302
303 assert_struct!(schema.app.models, #{
305 Status::id(): toasty::schema::app::Model::EmbeddedEnum({
306 name.upper_camel_case(): "Status",
307 variants.len(): 3,
308 }),
309 User::id(): toasty::schema::app::Model::Root({
310 name.upper_camel_case(): "User",
311 fields: [
312 { name.app: Some("id") },
313 {
314 name.app: Some("status"),
315 ty: FieldTy::Embedded({
316 target: == Status::id(),
317 }),
318 },
319 ],
320 }),
321 });
322
323 assert_struct!(schema.db.tables, [
325 {
326 name: =~ r"users$",
327 columns: [
328 { name: "id" },
329 { name: "status" },
330 ],
331 },
332 ]);
333
334 let user = &schema.app.models[&User::id()];
335 let user_table = schema.table_for(user);
336 let user_mapping = &schema.mapping.models[&User::id()];
337
338 assert_struct!(user_mapping, {
339 columns.len(): 2,
340 fields: [
341 mapping::Field::Primitive(FieldPrimitive {
342 column: == user_table.columns[0].id,
343 lowering: 0,
344 ..
345 }),
346 mapping::Field::Enum(FieldEnum {
347 discriminant: FieldPrimitive {
348 column: == user_table.columns[1].id,
349 lowering: 1,
350 ..
351 },
352 variants.len(): 3,
353 ..
354 }),
355 ],
356 });
357}