Skip to main content

toasty_driver_integration_suite/tests/
embed_enum_string_discriminant.rs

1use crate::prelude::*;
2
3/// Tests basic CRUD with a unit enum using explicit string discriminants.
4#[driver_test(id(ID), scenario(crate::scenarios::task_with_string_status))]
5pub async fn string_discriminant_unit_enum(t: &mut Test) -> Result<()> {
6    let mut db = setup(t).await;
7
8    let task = toasty::create!(Task {
9        title: "Ship it",
10        status: Status::Pending,
11    })
12    .exec(&mut db)
13    .await?;
14    assert_eq!(task.status, Status::Pending);
15
16    let found = Task::get_by_id(&mut db, &task.id).await?;
17    assert_eq!(found.status, Status::Pending);
18
19    // Update and re-read
20    let mut task = found;
21    task.update().status(Status::Active).exec(&mut db).await?;
22    let found = Task::get_by_id(&mut db, &task.id).await?;
23    assert_eq!(found.status, Status::Active);
24
25    Ok(())
26}
27
28/// Tests unit enum with default labels (variant ident used as string label).
29#[driver_test(id(ID))]
30pub async fn default_string_labels(t: &mut Test) -> Result<()> {
31    #[derive(Debug, PartialEq, toasty::Embed)]
32    enum Priority {
33        Low,
34        Medium,
35        High,
36    }
37
38    #[derive(Debug, toasty::Model)]
39    struct Task {
40        #[key]
41        #[auto]
42        id: ID,
43        title: String,
44        priority: Priority,
45    }
46
47    let mut db = t.setup_db(models!(Task)).await;
48
49    let task = toasty::create!(Task {
50        title: "Fix bug",
51        priority: Priority::High,
52    })
53    .exec(&mut db)
54    .await?;
55    assert_eq!(task.priority, Priority::High);
56
57    let found = Task::get_by_id(&mut db, &task.id).await?;
58    assert_eq!(found.priority, Priority::High);
59
60    Ok(())
61}
62
63/// Tests mixing explicit string labels with default labels.
64#[driver_test(id(ID))]
65pub async fn mixed_explicit_and_default_labels(t: &mut Test) -> Result<()> {
66    #[derive(Debug, PartialEq, toasty::Embed)]
67    enum Status {
68        #[column(variant = "waiting")]
69        Pending,
70        Active,
71        Done,
72    }
73
74    #[derive(Debug, toasty::Model)]
75    struct Task {
76        #[key]
77        #[auto]
78        id: ID,
79        status: Status,
80    }
81
82    let mut db = t.setup_db(models!(Task)).await;
83
84    // "waiting" is the explicit label for Pending
85    let t1 = toasty::create!(Task {
86        status: Status::Pending
87    })
88    .exec(&mut db)
89    .await?;
90    assert_eq!(t1.status, Status::Pending);
91
92    // "Active" is the default label
93    let t2 = toasty::create!(Task {
94        status: Status::Active
95    })
96    .exec(&mut db)
97    .await?;
98
99    let found1 = Task::get_by_id(&mut db, &t1.id).await?;
100    let found2 = Task::get_by_id(&mut db, &t2.id).await?;
101    assert_eq!(found1.status, Status::Pending);
102    assert_eq!(found2.status, Status::Active);
103
104    Ok(())
105}
106
107/// Tests data-carrying enum with string discriminants.
108#[driver_test(id(ID))]
109pub async fn string_discriminant_data_enum(t: &mut Test) -> Result<()> {
110    #[derive(Debug, PartialEq, toasty::Embed)]
111    enum ContactMethod {
112        #[column(variant = "email")]
113        Email { address: String },
114        #[column(variant = "phone")]
115        Phone { number: String },
116    }
117
118    #[derive(Debug, toasty::Model)]
119    #[allow(dead_code)]
120    struct User {
121        #[key]
122        #[auto]
123        id: ID,
124        name: String,
125        contact: ContactMethod,
126    }
127
128    let mut db = t.setup_db(models!(User)).await;
129
130    let user = toasty::create!(User {
131        name: "Alice",
132        contact: ContactMethod::Email {
133            address: "alice@example.com".into(),
134        },
135    })
136    .exec(&mut db)
137    .await?;
138
139    let found = User::get_by_id(&mut db, &user.id).await?;
140    assert_eq!(
141        found.contact,
142        ContactMethod::Email {
143            address: "alice@example.com".into()
144        }
145    );
146
147    // Update to a different variant
148    let mut user = found;
149    user.update()
150        .contact(ContactMethod::Phone {
151            number: "555-0100".into(),
152        })
153        .exec(&mut db)
154        .await?;
155
156    let found = User::get_by_id(&mut db, &user.id).await?;
157    assert_eq!(
158        found.contact,
159        ContactMethod::Phone {
160            number: "555-0100".into()
161        }
162    );
163
164    Ok(())
165}
166
167/// Tests data-carrying enum with default string labels (variant ident as discriminant).
168#[driver_test(id(ID))]
169pub async fn default_string_labels_data_enum(t: &mut Test) -> Result<()> {
170    #[derive(Debug, PartialEq, toasty::Embed)]
171    enum ContactMethod {
172        Email { address: String },
173        Phone { number: String },
174    }
175
176    #[derive(Debug, toasty::Model)]
177    #[allow(dead_code)]
178    struct User {
179        #[key]
180        #[auto]
181        id: ID,
182        name: String,
183        contact: ContactMethod,
184    }
185
186    let mut db = t.setup_db(models!(User)).await;
187
188    let user = toasty::create!(User {
189        name: "Alice",
190        contact: ContactMethod::Email {
191            address: "alice@example.com".into(),
192        },
193    })
194    .exec(&mut db)
195    .await?;
196
197    let found = User::get_by_id(&mut db, &user.id).await?;
198    assert_eq!(
199        found.contact,
200        ContactMethod::Email {
201            address: "alice@example.com".into()
202        }
203    );
204
205    // Update to a different variant
206    let mut user = found;
207    user.update()
208        .contact(ContactMethod::Phone {
209            number: "555-0100".into(),
210        })
211        .exec(&mut db)
212        .await?;
213
214    let found = User::get_by_id(&mut db, &user.id).await?;
215    assert_eq!(
216        found.contact,
217        ContactMethod::Phone {
218            number: "555-0100".into()
219        }
220    );
221
222    Ok(())
223}
224
225/// Tests data-carrying enum mixing explicit string labels with defaults.
226#[driver_test(id(ID))]
227pub async fn mixed_string_labels_data_enum(t: &mut Test) -> Result<()> {
228    #[derive(Debug, PartialEq, toasty::Embed)]
229    enum ContactMethod {
230        #[column(variant = "mail")]
231        Email {
232            address: String,
233        },
234        Phone {
235            number: String,
236        },
237    }
238
239    #[derive(Debug, toasty::Model)]
240    #[allow(dead_code)]
241    struct User {
242        #[key]
243        #[auto]
244        id: ID,
245        name: String,
246        contact: ContactMethod,
247    }
248
249    let mut db = t.setup_db(models!(User)).await;
250
251    // Create with the explicit-label variant
252    let u1 = toasty::create!(User {
253        name: "Alice",
254        contact: ContactMethod::Email {
255            address: "alice@example.com".into(),
256        },
257    })
258    .exec(&mut db)
259    .await?;
260
261    // Create with the default-label variant
262    let u2 = toasty::create!(User {
263        name: "Bob",
264        contact: ContactMethod::Phone {
265            number: "555-0200".into(),
266        },
267    })
268    .exec(&mut db)
269    .await?;
270
271    let found1 = User::get_by_id(&mut db, &u1.id).await?;
272    assert_eq!(
273        found1.contact,
274        ContactMethod::Email {
275            address: "alice@example.com".into()
276        }
277    );
278
279    let found2 = User::get_by_id(&mut db, &u2.id).await?;
280    assert_eq!(
281        found2.contact,
282        ContactMethod::Phone {
283            number: "555-0200".into()
284        }
285    );
286
287    // Update from explicit-label variant to default-label variant
288    let mut user = found1;
289    user.update()
290        .contact(ContactMethod::Phone {
291            number: "555-0300".into(),
292        })
293        .exec(&mut db)
294        .await?;
295
296    let found = User::get_by_id(&mut db, &user.id).await?;
297    assert_eq!(
298        found.contact,
299        ContactMethod::Phone {
300            number: "555-0300".into()
301        }
302    );
303
304    Ok(())
305}
306
307/// Tests filtering by variant with string discriminants.
308#[driver_test(
309    requires(scan),
310    scenario(crate::scenarios::task_with_string_status::id_uuid)
311)]
312pub async fn filter_by_string_variant(t: &mut Test) -> Result<()> {
313    let mut db = setup(t).await;
314
315    toasty::create!(Task {
316        title: "A",
317        status: Status::Pending
318    })
319    .exec(&mut db)
320    .await?;
321
322    toasty::create!(Task {
323        title: "B",
324        status: Status::Active
325    })
326    .exec(&mut db)
327    .await?;
328
329    let pending = Task::filter(Task::fields().status().is_pending())
330        .exec(&mut db)
331        .await?;
332    assert_eq!(pending.len(), 1);
333    assert_eq!(pending[0].title, "A");
334
335    Ok(())
336}
337
338/// Verifies the schema registers string discriminants with the correct type.
339#[driver_test(scenario(crate::scenarios::task_with_string_status::id_uuid))]
340pub async fn string_discriminant_schema_registration(t: &mut Test) {
341    let db = setup(t).await;
342    let schema = db.schema();
343
344    let status_model = schema.app.model(Status::id()).as_embedded_enum_unwrap();
345    assert_eq!(
346        status_model.discriminant.ty,
347        toasty_core::stmt::Type::String
348    );
349    assert_eq!(status_model.variants.len(), 3);
350    assert_eq!(
351        status_model.variants[0].discriminant,
352        toasty_core::stmt::Value::String("pending".to_string())
353    );
354    assert_eq!(
355        status_model.variants[1].discriminant,
356        toasty_core::stmt::Value::String("active".to_string())
357    );
358    assert_eq!(
359        status_model.variants[2].discriminant,
360        toasty_core::stmt::Value::String("done".to_string())
361    );
362}