toasty_driver_integration_suite/tests/
embed_enum_index.rs

1use crate::prelude::*;
2
3/// Tests that `#[unique]` and `#[index]` on embedded enum variant fields produce
4/// physical DB indices on the flattened columns.
5#[driver_test]
6pub async fn embedded_enum_index_schema(test: &mut Test) {
7    #[derive(Debug, toasty::Embed)]
8    enum ContactInfo {
9        #[column(variant = 1)]
10        Email {
11            #[unique]
12            address: String,
13        },
14        #[column(variant = 2)]
15        Phone {
16            #[index]
17            number: String,
18        },
19    }
20
21    #[derive(Debug, toasty::Model)]
22    struct User {
23        #[key]
24        id: String,
25        name: String,
26        #[allow(dead_code)]
27        contact: ContactInfo,
28    }
29
30    let db = test.setup_db(models!(User, ContactInfo)).await;
31    let schema = db.schema();
32
33    // The embedded enum should carry its indices in the app schema
34    assert_struct!(schema.app.models, #{
35        ContactInfo::id(): toasty::schema::app::Model::EmbeddedEnum({
36            indices.len(): 2,
37        }),
38        ..
39    });
40
41    // The DB table should have indices on the flattened variant field columns.
42    // Index 0: primary key (id)
43    // Index 1: unique on contact_address
44    // Index 2: non-unique on contact_number
45    let table = &schema.db.tables[0];
46    let address_col = columns(&db, "users", &["contact_address"])[0];
47    let number_col = columns(&db, "users", &["contact_number"])[0];
48
49    assert_struct!(table.indices, [
50        // PK
51        { primary_key: true },
52        // Unique index on contact_address
53        { unique: true, primary_key: false, columns: [{ column: == address_col }] },
54        // Non-unique index on contact_number
55        { unique: false, primary_key: false, columns: [{ column: == number_col }] },
56    ]);
57}
58
59/// Tests that unique constraint on embedded enum variant field is enforced at
60/// the database level.
61#[driver_test]
62pub async fn embedded_enum_unique_index_enforced(test: &mut Test) -> Result<()> {
63    #[derive(Debug, toasty::Embed)]
64    enum ContactInfo {
65        #[column(variant = 1)]
66        Email {
67            #[unique]
68            address: String,
69        },
70        #[column(variant = 2)]
71        Phone { number: String },
72    }
73
74    #[derive(Debug, toasty::Model)]
75    struct User {
76        #[key]
77        id: String,
78        name: String,
79        contact: ContactInfo,
80    }
81
82    let mut db = test.setup_db(models!(User, ContactInfo)).await;
83
84    // Create a user with an email contact
85    User::create()
86        .id("1")
87        .name("Alice")
88        .contact(ContactInfo::Email {
89            address: "alice@example.com".to_string(),
90        })
91        .exec(&mut db)
92        .await?;
93
94    // Creating another user with the same email address should fail
95    assert_err!(
96        User::create()
97            .id("2")
98            .name("Bob")
99            .contact(ContactInfo::Email {
100                address: "alice@example.com".to_string(),
101            })
102            .exec(&mut db)
103            .await
104    );
105
106    // Creating a user with a different email works
107    User::create()
108        .id("3")
109        .name("Charlie")
110        .contact(ContactInfo::Email {
111            address: "charlie@example.com".to_string(),
112        })
113        .exec(&mut db)
114        .await?;
115
116    // Creating a user with a phone contact works (different variant, no unique on number)
117    User::create()
118        .id("4")
119        .name("Dave")
120        .contact(ContactInfo::Phone {
121            number: "555-1234".to_string(),
122        })
123        .exec(&mut db)
124        .await?;
125
126    // Filter by the indexed variant field
127    let users = User::filter(
128        User::fields()
129            .contact()
130            .email()
131            .matches(|e| e.address().eq("alice@example.com")),
132    )
133    .exec(&mut db)
134    .await?;
135
136    assert_struct!(users, [{ name: "Alice" }]);
137
138    Ok(())
139}