toasty_driver_integration_suite/tests/
embedded_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::{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 assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
53 stmt: Statement::Insert(_ {
54 source.body: ExprSet::Values(_ {
55 rows: [== (Any, Any, 1i64)],
56 ..
57 }),
58 target: toasty_core::stmt::InsertTarget::Table(_ {
59 table: == user_table,
60 columns: == columns(&db, "users", &["id", "name", "status"]),
61 ..
62 }),
63 ..
64 }),
65 ..
66 }));
67
68 let found = User::get_by_id(&mut db, &user.id).await?;
70 assert_eq!(found.status, Status::Pending);
71
72 t.log().clear();
74 user.update().status(Status::Active).exec(&mut db).await?;
75
76 if t.capability().sql {
79 assert_struct!(t.log().pop_op(), Operation::QuerySql(_ {
80 stmt: Statement::Update(_ {
81 target: toasty_core::stmt::UpdateTarget::Table(== user_table),
82 assignments: #{ 2: _ { expr: == 2i64, .. }},
83 ..
84 }),
85 ..
86 }));
87 } else {
88 assert_struct!(t.log().pop_op(), Operation::UpdateByKey(_ {
89 table: == user_table,
90 filter: None,
91 keys: _,
92 assignments: #{ 2: _ { expr: == 2i64, .. }},
93 returning: false,
94 ..
95 }));
96 }
97
98 let found = User::get_by_id(&mut db, &user.id).await?;
99 assert_eq!(found.status, Status::Active);
100
101 User::filter_by_id(user.id)
103 .update()
104 .status(Status::Done)
105 .exec(&mut db)
106 .await?;
107
108 let found = User::get_by_id(&mut db, &user.id).await?;
109 assert_eq!(found.status, Status::Done);
110
111 let id = user.id;
113 user.delete().exec(&mut db).await?;
114 assert_err!(User::get_by_id(&mut db, &id).await);
115 Ok(())
116}
117
118#[driver_test(requires(sql))]
124pub async fn filter_by_enum_variant(t: &mut Test) -> Result<()> {
125 #[derive(Debug, PartialEq, toasty::Embed)]
126 enum Status {
127 #[column(variant = 1)]
128 Pending,
129 #[column(variant = 2)]
130 Active,
131 #[column(variant = 3)]
132 Done,
133 }
134
135 #[derive(Debug, toasty::Model)]
136 #[allow(dead_code)]
137 struct Task {
138 #[key]
139 #[auto]
140 id: uuid::Uuid,
141 name: String,
142 status: Status,
143 }
144
145 let mut db = t.setup_db(models!(Task, Status)).await;
146
147 for (name, status) in [
149 ("Task A", Status::Pending),
150 ("Task B", Status::Active),
151 ("Task C", Status::Active),
152 ("Task D", Status::Done),
153 ] {
154 Task::create()
155 .name(name)
156 .status(status)
157 .exec(&mut db)
158 .await?;
159 }
160
161 let status_col = column(&db, "tasks", "status");
162 t.log().clear();
163
164 let active = Task::filter(Task::fields().status().eq(Status::Active))
166 .exec(&mut db)
167 .await?;
168 assert_eq!(active.len(), 2);
169 {
170 let (op, _) = t.log().pop();
171 assert_struct!(op, Operation::QuerySql(_ {
172 stmt: Statement::Query(_ {
173 body: ExprSet::Select(_ {
174 filter.expr: Some(Expr::BinaryOp(_ {
175 lhs.as_expr_column_unwrap().column: == status_col.index,
176 op: BinaryOp::Eq,
177 *rhs: == 2i64,
178 ..
179 })),
180 ..
181 }),
182 ..
183 }),
184 ..
185 }));
186 }
187
188 let pending = Task::filter(Task::fields().status().eq(Status::Pending))
190 .exec(&mut db)
191 .await?;
192 assert_eq!(pending.len(), 1);
193 assert_eq!(pending[0].name, "Task A");
194 {
195 let (op, _) = t.log().pop();
196 assert_struct!(op, Operation::QuerySql(_ {
197 stmt: Statement::Query(_ {
198 body: ExprSet::Select(_ {
199 filter.expr: Some(Expr::BinaryOp(_ {
200 lhs.as_expr_column_unwrap().column: == status_col.index,
201 op: BinaryOp::Eq,
202 *rhs: == 1i64,
203 ..
204 })),
205 ..
206 }),
207 ..
208 }),
209 ..
210 }));
211 }
212
213 let done = Task::filter(Task::fields().status().eq(Status::Done))
215 .exec(&mut db)
216 .await?;
217 assert_eq!(done.len(), 1);
218 assert_eq!(done[0].name, "Task D");
219 {
220 let (op, _) = t.log().pop();
221 assert_struct!(op, Operation::QuerySql(_ {
222 stmt: Statement::Query(_ {
223 body: ExprSet::Select(_ {
224 filter.expr: Some(Expr::BinaryOp(_ {
225 lhs.as_expr_column_unwrap().column: == status_col.index,
226 op: BinaryOp::Eq,
227 *rhs: == 3i64,
228 ..
229 })),
230 ..
231 }),
232 ..
233 }),
234 ..
235 }));
236 }
237
238 Ok(())
239}
240
241#[driver_test]
244pub async fn basic_embedded_enum(test: &mut Test) {
245 #[derive(toasty::Embed)]
246 enum Status {
247 #[column(variant = 1)]
248 Pending,
249 #[column(variant = 2)]
250 Active,
251 #[column(variant = 3)]
252 Done,
253 }
254
255 let db = test.setup_db(models!(Status)).await;
256 let schema = db.schema();
257
258 assert_struct!(schema.app.models, #{
260 Status::id(): toasty::schema::app::Model::EmbeddedEnum(_ {
261 name.upper_camel_case(): "Status",
262 variants: [
263 _ { name.upper_camel_case(): "Pending", discriminant: 1, .. },
264 _ { name.upper_camel_case(): "Active", discriminant: 2, .. },
265 _ { name.upper_camel_case(): "Done", discriminant: 3, .. },
266 ],
267 ..
268 }),
269 });
270
271 assert!(schema.db.tables.is_empty());
273}
274
275#[driver_test]
280pub async fn root_model_with_embedded_enum_field(test: &mut Test) {
281 #[derive(toasty::Embed)]
282 enum Status {
283 #[column(variant = 1)]
284 Pending,
285 #[column(variant = 2)]
286 Active,
287 #[column(variant = 3)]
288 Done,
289 }
290
291 #[derive(toasty::Model)]
292 struct User {
293 #[key]
294 id: String,
295 #[allow(dead_code)]
296 status: Status,
297 }
298
299 let db = test.setup_db(models!(User, Status)).await;
300 let schema = db.schema();
301
302 assert_struct!(schema.app.models, #{
304 Status::id(): toasty::schema::app::Model::EmbeddedEnum(_ {
305 name.upper_camel_case(): "Status",
306 variants.len(): 3,
307 ..
308 }),
309 User::id(): toasty::schema::app::Model::Root(_ {
310 name.upper_camel_case(): "User",
311 fields: [
312 _ { name.app_name: "id", .. },
313 _ {
314 name.app_name: "status",
315 ty: FieldTy::Embedded(_ {
316 target: == Status::id(),
317 ..
318 }),
319 ..
320 },
321 ],
322 ..
323 }),
324 });
325
326 assert_struct!(schema.db.tables, [
328 _ {
329 name: =~ r"users$",
330 columns: [
331 _ { name: "id", .. },
332 _ { name: "status", .. },
333 ],
334 ..
335 },
336 ]);
337
338 let user = &schema.app.models[&User::id()];
339 let user_table = schema.table_for(user);
340 let user_mapping = &schema.mapping.models[&User::id()];
341
342 assert_struct!(user_mapping, _ {
343 columns.len(): 2,
344 fields: [
345 mapping::Field::Primitive(FieldPrimitive {
346 column: == user_table.columns[0].id,
347 lowering: 0,
348 ..
349 }),
350 mapping::Field::Enum(FieldEnum {
351 discriminant: FieldPrimitive {
352 column: == user_table.columns[1].id,
353 lowering: 1,
354 ..
355 },
356 variants.len(): 3,
357 ..
358 }),
359 ],
360 ..
361 });
362}