Skip to main content

toasty_driver_integration_suite/tests/
embed_enum_native_ops.rs

1use crate::prelude::*;
2
3/// Full CRUD lifecycle for native database enums (string discriminant enums that
4/// map to PostgreSQL CREATE TYPE, MySQL ENUM(), SQLite TEXT+CHECK).
5///
6/// Exercises INSERT, SELECT, UPDATE (instance and query-based), and DELETE
7/// through the driver — the paths affected by parameter type inference.
8#[driver_test(id(ID))]
9pub async fn native_enum_crud_lifecycle(t: &mut Test) -> Result<()> {
10    #[derive(Debug, PartialEq, toasty::Embed)]
11    enum Priority {
12        Low,
13        Medium,
14        High,
15    }
16
17    #[derive(Debug, toasty::Model)]
18    struct Task {
19        #[key]
20        #[auto]
21        id: ID,
22        title: String,
23        priority: Priority,
24    }
25
26    let mut db = t.setup_db(models!(Task, Priority)).await;
27
28    // -- Create with each variant --
29    let t1 = toasty::create!(Task {
30        title: "A",
31        priority: Priority::Low
32    })
33    .exec(&mut db)
34    .await?;
35    let t2 = toasty::create!(Task {
36        title: "B",
37        priority: Priority::Medium
38    })
39    .exec(&mut db)
40    .await?;
41    let t3 = toasty::create!(Task {
42        title: "C",
43        priority: Priority::High
44    })
45    .exec(&mut db)
46    .await?;
47
48    assert_eq!(t1.priority, Priority::Low);
49    assert_eq!(t2.priority, Priority::Medium);
50    assert_eq!(t3.priority, Priority::High);
51
52    // -- Get by ID roundtrips each variant --
53    assert_eq!(
54        Task::get_by_id(&mut db, &t1.id).await?.priority,
55        Priority::Low
56    );
57    assert_eq!(
58        Task::get_by_id(&mut db, &t2.id).await?.priority,
59        Priority::Medium
60    );
61    assert_eq!(
62        Task::get_by_id(&mut db, &t3.id).await?.priority,
63        Priority::High
64    );
65
66    // -- Update via instance method --
67    let mut t1 = t1;
68    t1.update().priority(Priority::High).exec(&mut db).await?;
69    assert_eq!(
70        Task::get_by_id(&mut db, &t1.id).await?.priority,
71        Priority::High
72    );
73
74    // -- Update via query --
75    Task::filter_by_id(t2.id)
76        .update()
77        .priority(Priority::Low)
78        .exec(&mut db)
79        .await?;
80    assert_eq!(
81        Task::get_by_id(&mut db, &t2.id).await?.priority,
82        Priority::Low
83    );
84
85    // -- Delete and verify --
86    let t3_id = t3.id;
87    t3.delete().exec(&mut db).await?;
88    assert!(Task::get_by_id(&mut db, &t3_id).await.is_err());
89
90    Ok(())
91}
92
93/// Filter operations on native database enums: eq, ne, in_list.
94///
95/// Gated on `requires(sql)` until [#855] is fixed — scan over a native enum
96/// panics in the engine with `not yet implemented: ty=Unit`.
97///
98/// [#855]: https://github.com/tokio-rs/toasty/issues/855
99#[driver_test(requires(sql))]
100pub async fn native_enum_filter_operations(t: &mut Test) -> Result<()> {
101    #[derive(Debug, PartialEq, toasty::Embed)]
102    enum Status {
103        Pending,
104        Active,
105        Done,
106    }
107
108    #[derive(Debug, toasty::Model)]
109    #[allow(dead_code)]
110    struct Task {
111        #[key]
112        #[auto]
113        id: uuid::Uuid,
114        name: String,
115        status: Status,
116    }
117
118    let mut db = t.setup_db(models!(Task, Status)).await;
119
120    for (name, status) in [
121        ("A", Status::Pending),
122        ("B", Status::Active),
123        ("C", Status::Active),
124        ("D", Status::Done),
125    ] {
126        toasty::create!(Task { name, status }).exec(&mut db).await?;
127    }
128
129    // -- Filter eq (via is_variant) --
130    let active = Task::filter(Task::fields().status().is_active())
131        .exec(&mut db)
132        .await?;
133    assert_eq!(active.len(), 2);
134
135    // -- Filter ne --
136    let not_active = Task::filter(Task::fields().status().ne(Status::Active))
137        .exec(&mut db)
138        .await?;
139    assert_eq!(not_active.len(), 2);
140    assert!(not_active.iter().all(|t| t.status != Status::Active));
141
142    // -- Filter in_list --
143    let pending_or_done = Task::filter(
144        Task::fields()
145            .status()
146            .in_list([Status::Pending, Status::Done]),
147    )
148    .exec(&mut db)
149    .await?;
150    assert_eq!(pending_or_done.len(), 2);
151    assert!(pending_or_done.iter().all(|t| t.status != Status::Active));
152
153    // -- Delete filtered by enum value --
154    Task::filter(Task::fields().status().is_done())
155        .delete()
156        .exec(&mut db)
157        .await?;
158    let remaining = Task::all().exec(&mut db).await?;
159    assert_eq!(remaining.len(), 3);
160
161    Ok(())
162}
163
164/// Multiple native enum fields on the same model, each creating its own
165/// database enum type.
166#[driver_test(id(ID))]
167pub async fn native_enum_multiple_fields(t: &mut Test) -> Result<()> {
168    #[derive(Debug, PartialEq, toasty::Embed)]
169    enum Priority {
170        Low,
171        High,
172    }
173
174    #[derive(Debug, PartialEq, toasty::Embed)]
175    enum Status {
176        Open,
177        Closed,
178    }
179
180    #[derive(Debug, toasty::Model)]
181    struct Ticket {
182        #[key]
183        #[auto]
184        id: ID,
185        priority: Priority,
186        status: Status,
187    }
188
189    let mut db = t.setup_db(models!(Ticket, Priority, Status)).await;
190
191    let ticket = toasty::create!(Ticket {
192        priority: Priority::High,
193        status: Status::Open,
194    })
195    .exec(&mut db)
196    .await?;
197
198    let found = Ticket::get_by_id(&mut db, &ticket.id).await?;
199    assert_eq!(found.priority, Priority::High);
200    assert_eq!(found.status, Status::Open);
201
202    // Update both enum fields
203    let mut ticket = found;
204    ticket
205        .update()
206        .priority(Priority::Low)
207        .status(Status::Closed)
208        .exec(&mut db)
209        .await?;
210
211    let found = Ticket::get_by_id(&mut db, &ticket.id).await?;
212    assert_eq!(found.priority, Priority::Low);
213    assert_eq!(found.status, Status::Closed);
214
215    Ok(())
216}
217
218/// Native enum with explicit custom type name via `#[column(type = enum("custom_name"))]`.
219#[driver_test(id(ID))]
220pub async fn native_enum_custom_type_name(t: &mut Test) -> Result<()> {
221    #[derive(Debug, PartialEq, toasty::Embed)]
222    #[column(type = enum("task_priority"))]
223    enum Priority {
224        Low,
225        Medium,
226        High,
227    }
228
229    #[derive(Debug, toasty::Model)]
230    struct Task {
231        #[key]
232        #[auto]
233        id: ID,
234        priority: Priority,
235    }
236
237    let mut db = t.setup_db(models!(Task, Priority)).await;
238
239    let task = toasty::create!(Task {
240        priority: Priority::Medium
241    })
242    .exec(&mut db)
243    .await?;
244
245    assert_eq!(
246        Task::get_by_id(&mut db, &task.id).await?.priority,
247        Priority::Medium
248    );
249
250    // Update roundtrip
251    let mut task = task;
252    task.update().priority(Priority::High).exec(&mut db).await?;
253    assert_eq!(
254        Task::get_by_id(&mut db, &task.id).await?.priority,
255        Priority::High
256    );
257
258    Ok(())
259}