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#[driver_test(requires(sql))]
95pub async fn native_enum_filter_operations(t: &mut Test) -> Result<()> {
96    #[derive(Debug, PartialEq, toasty::Embed)]
97    enum Status {
98        Pending,
99        Active,
100        Done,
101    }
102
103    #[derive(Debug, toasty::Model)]
104    #[allow(dead_code)]
105    struct Task {
106        #[key]
107        #[auto]
108        id: uuid::Uuid,
109        name: String,
110        status: Status,
111    }
112
113    let mut db = t.setup_db(models!(Task, Status)).await;
114
115    for (name, status) in [
116        ("A", Status::Pending),
117        ("B", Status::Active),
118        ("C", Status::Active),
119        ("D", Status::Done),
120    ] {
121        toasty::create!(Task { name, status }).exec(&mut db).await?;
122    }
123
124    // -- Filter eq (via is_variant) --
125    let active = Task::filter(Task::fields().status().is_active())
126        .exec(&mut db)
127        .await?;
128    assert_eq!(active.len(), 2);
129
130    // -- Filter ne --
131    let not_active = Task::filter(Task::fields().status().ne(Status::Active))
132        .exec(&mut db)
133        .await?;
134    assert_eq!(not_active.len(), 2);
135    assert!(not_active.iter().all(|t| t.status != Status::Active));
136
137    // -- Filter in_list --
138    let pending_or_done = Task::filter(
139        Task::fields()
140            .status()
141            .in_list([Status::Pending, Status::Done]),
142    )
143    .exec(&mut db)
144    .await?;
145    assert_eq!(pending_or_done.len(), 2);
146    assert!(pending_or_done.iter().all(|t| t.status != Status::Active));
147
148    // -- Delete filtered by enum value --
149    Task::filter(Task::fields().status().is_done())
150        .delete()
151        .exec(&mut db)
152        .await?;
153    let remaining = Task::all().exec(&mut db).await?;
154    assert_eq!(remaining.len(), 3);
155
156    Ok(())
157}
158
159/// Multiple native enum fields on the same model, each creating its own
160/// database enum type.
161#[driver_test(id(ID))]
162pub async fn native_enum_multiple_fields(t: &mut Test) -> Result<()> {
163    #[derive(Debug, PartialEq, toasty::Embed)]
164    enum Priority {
165        Low,
166        High,
167    }
168
169    #[derive(Debug, PartialEq, toasty::Embed)]
170    enum Status {
171        Open,
172        Closed,
173    }
174
175    #[derive(Debug, toasty::Model)]
176    struct Ticket {
177        #[key]
178        #[auto]
179        id: ID,
180        priority: Priority,
181        status: Status,
182    }
183
184    let mut db = t.setup_db(models!(Ticket, Priority, Status)).await;
185
186    let ticket = toasty::create!(Ticket {
187        priority: Priority::High,
188        status: Status::Open,
189    })
190    .exec(&mut db)
191    .await?;
192
193    let found = Ticket::get_by_id(&mut db, &ticket.id).await?;
194    assert_eq!(found.priority, Priority::High);
195    assert_eq!(found.status, Status::Open);
196
197    // Update both enum fields
198    let mut ticket = found;
199    ticket
200        .update()
201        .priority(Priority::Low)
202        .status(Status::Closed)
203        .exec(&mut db)
204        .await?;
205
206    let found = Ticket::get_by_id(&mut db, &ticket.id).await?;
207    assert_eq!(found.priority, Priority::Low);
208    assert_eq!(found.status, Status::Closed);
209
210    Ok(())
211}
212
213/// Native enum with explicit custom type name via `#[column(type = enum("custom_name"))]`.
214#[driver_test(id(ID))]
215pub async fn native_enum_custom_type_name(t: &mut Test) -> Result<()> {
216    #[derive(Debug, PartialEq, toasty::Embed)]
217    #[column(type = enum("task_priority"))]
218    enum Priority {
219        Low,
220        Medium,
221        High,
222    }
223
224    #[derive(Debug, toasty::Model)]
225    struct Task {
226        #[key]
227        #[auto]
228        id: ID,
229        priority: Priority,
230    }
231
232    let mut db = t.setup_db(models!(Task, Priority)).await;
233
234    let task = toasty::create!(Task {
235        priority: Priority::Medium
236    })
237    .exec(&mut db)
238    .await?;
239
240    assert_eq!(
241        Task::get_by_id(&mut db, &task.id).await?.priority,
242        Priority::Medium
243    );
244
245    // Update roundtrip
246    let mut task = task;
247    task.update().priority(Priority::High).exec(&mut db).await?;
248    assert_eq!(
249        Task::get_by_id(&mut db, &task.id).await?.priority,
250        Priority::High
251    );
252
253    Ok(())
254}