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, Value},
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)).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: None,
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(scan), scenario(crate::scenarios::task_name_status))]
136pub async fn filter_by_enum_variant(t: &mut Test) -> Result<()> {
137 let mut db = setup(t).await;
138
139 for (name, status) in [
141 ("Task A", Status::Pending),
142 ("Task B", Status::Active),
143 ("Task C", Status::Active),
144 ("Task D", Status::Done),
145 ] {
146 Task::create()
147 .name(name)
148 .status(status)
149 .exec(&mut db)
150 .await?;
151 }
152
153 let status_col = column(&db, "tasks", "status");
154 t.log().clear();
155
156 let active = Task::filter(Task::fields().status().eq(Status::Active))
158 .exec(&mut db)
159 .await?;
160 assert_eq!(active.len(), 2);
161 {
162 let (op, _) = t.log().pop();
163 if t.capability().sql {
164 assert_struct!(op, Operation::QuerySql({
165 stmt: Statement::Query({
166 body: ExprSet::Select({
167 filter.expr: Some(Expr::BinaryOp({
168 lhs.as_expr_column_unwrap().column: == status_col.index,
169 op: BinaryOp::Eq,
170 *rhs: Expr::Arg({ position: 0 }),
171 })),
172 }),
173 }),
174 params: [{ value: == 2i64 }],
175 }));
176 } else {
177 assert_struct!(op, Operation::Scan({
178 filter: Some(Expr::BinaryOp({
179 lhs.as_expr_column_unwrap().column: == status_col.index,
180 op: BinaryOp::Eq,
181 *rhs: Expr::Value(== Value::I64(2)),
182 })),
183 }));
184 }
185 }
186
187 let pending = Task::filter(Task::fields().status().eq(Status::Pending))
189 .exec(&mut db)
190 .await?;
191 assert_eq!(pending.len(), 1);
192 assert_eq!(pending[0].name, "Task A");
193 {
194 let (op, _) = t.log().pop();
195 if t.capability().sql {
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: Expr::Arg({ position: 0 }),
203 })),
204 }),
205 }),
206 params: [{ value: == 1i64 }],
207 }));
208 } else {
209 assert_struct!(op, Operation::Scan({
210 filter: Some(Expr::BinaryOp({
211 lhs.as_expr_column_unwrap().column: == status_col.index,
212 op: BinaryOp::Eq,
213 *rhs: Expr::Value(== Value::I64(1)),
214 })),
215 }));
216 }
217 }
218
219 let done = Task::filter(Task::fields().status().eq(Status::Done))
221 .exec(&mut db)
222 .await?;
223 assert_eq!(done.len(), 1);
224 assert_eq!(done[0].name, "Task D");
225 {
226 let (op, _) = t.log().pop();
227 if t.capability().sql {
228 assert_struct!(op, Operation::QuerySql({
229 stmt: Statement::Query({
230 body: ExprSet::Select({
231 filter.expr: Some(Expr::BinaryOp({
232 lhs.as_expr_column_unwrap().column: == status_col.index,
233 op: BinaryOp::Eq,
234 *rhs: Expr::Arg({ position: 0 }),
235 })),
236 }),
237 }),
238 params: [{ value: == 3i64 }],
239 }));
240 } else {
241 assert_struct!(op, Operation::Scan({
242 filter: Some(Expr::BinaryOp({
243 lhs.as_expr_column_unwrap().column: == status_col.index,
244 op: BinaryOp::Eq,
245 *rhs: Expr::Value(== Value::I64(3)),
246 })),
247 }));
248 }
249 }
250
251 Ok(())
252}
253
254#[driver_test(scenario(crate::scenarios::user_with_status))]
257pub async fn basic_embedded_enum(test: &mut Test) {
258 let db = setup(test).await;
259 let schema = db.schema();
260
261 let status = &schema.app.models[&Status::id()];
263 assert_struct!(status, toasty::schema::app::Model::EmbeddedEnum({
264 name.upper_camel_case(): "Status",
265 variants: [
266 _ { name.upper_camel_case(): "Pending", discriminant: toasty_core::stmt::Value::I64(1), .. },
267 _ { name.upper_camel_case(): "Active", discriminant: toasty_core::stmt::Value::I64(2), .. },
268 _ { name.upper_camel_case(): "Done", discriminant: toasty_core::stmt::Value::I64(3), .. },
269 ],
270 }));
271}
272
273#[driver_test(scenario(crate::scenarios::user_with_status))]
278pub async fn root_model_with_embedded_enum_field(test: &mut Test) {
279 let db = setup(test).await;
280 let schema = db.schema();
281
282 assert_struct!(schema.app.models, #{
284 Status::id(): toasty::schema::app::Model::EmbeddedEnum({
285 name.upper_camel_case(): "Status",
286 variants.len(): 3,
287 }),
288 User::id(): toasty::schema::app::Model::Root({
289 name.upper_camel_case(): "User",
290 fields: [
291 { name.app: Some("id") },
292 {
293 name.app: Some("status"),
294 ty: FieldTy::Embedded({
295 target: == Status::id(),
296 }),
297 },
298 ],
299 }),
300 });
301
302 assert_struct!(schema.db.tables, [
304 {
305 name: =~ r"users$",
306 columns: [
307 { name: "id" },
308 { name: "status" },
309 ],
310 },
311 ]);
312
313 let user = &schema.app.models[&User::id()];
314 let user_table = schema.table_for(user);
315 let user_mapping = &schema.mapping.models[&User::id()];
316
317 assert_struct!(user_mapping, {
318 columns.len(): 2,
319 fields: [
320 mapping::Field::Primitive(FieldPrimitive {
321 column: == user_table.columns[0].id,
322 lowering: 0,
323 ..
324 }),
325 mapping::Field::Enum(FieldEnum {
326 discriminant: FieldPrimitive {
327 column: == user_table.columns[1].id,
328 lowering: 1,
329 ..
330 },
331 variants.len(): 3,
332 ..
333 }),
334 ],
335 });
336}