Skip to main content

tank_tests/
user.rs

1#![allow(unused_imports)]
2use std::{
3    collections::{BTreeMap, HashSet},
4    sync::LazyLock,
5};
6use tank::{
7    Entity, Executor, expr,
8    stream::{StreamExt, TryStreamExt},
9};
10use time::macros::datetime;
11use tokio::sync::Mutex;
12use uuid::Uuid;
13
14static MUTEX: LazyLock<Mutex<()>> = LazyLock::new(|| Mutex::new(()));
15
16#[derive(Entity, Debug, Clone)]
17// follower_count is PK to allow ordering for ScyllaDB/Cassandra
18#[tank(schema = "testing", name = "user_profiles", primary_key = (id, follower_count))]
19pub struct UserProfile {
20    #[tank(name = "user_id")]
21    pub id: Uuid,
22    #[tank(unique, column_type = (mysql = "VARCHAR(128)"))]
23    pub username: String,
24    #[tank(unique, column_type = (mysql = "VARCHAR(128)"))]
25    pub email: String,
26    pub full_name: Option<String>,
27    #[tank(default = 0)]
28    pub follower_count: u32,
29    pub is_active: bool,
30    pub last_login: Option<time::PrimitiveDateTime>,
31    #[cfg(not(feature = "disable-maps"))]
32    pub preferences: Option<BTreeMap<String, String>>,
33}
34
35pub async fn users(executor: &mut impl Executor) {
36    let _lock = MUTEX.lock().await;
37
38    // Cleanup
39    let result = UserProfile::drop_table(executor, true, false).await;
40    assert!(
41        result.is_ok(),
42        "Failed to UserProfile::drop_table: {:?}",
43        result.unwrap_err()
44    );
45
46    // Setup
47    let result = UserProfile::create_table(executor, false, true).await;
48    assert!(
49        result.is_ok(),
50        "Failed to UserProfile::create_table: {:?}",
51        result.unwrap_err()
52    );
53
54    // Insert
55    let users_to_create = vec![
56        UserProfile {
57            id: Uuid::parse_str("a1a1a1a1-a1a1-a1a1-a1a1-a1a1a1a1a1a1")
58                .unwrap()
59                .into(),
60            username: "alice".into(),
61            email: "alice@example.com".into(),
62            full_name: Some("Alice Wonderland".into()),
63            follower_count: 56,
64            is_active: true,
65            last_login: Some(datetime!(2025-07-15 10:00:00)),
66            #[cfg(not(feature = "disable-maps"))]
67            preferences: Some(BTreeMap::from_iter([("theme".into(), "dark".into())])),
68        },
69        UserProfile {
70            id: Uuid::parse_str("b2b2b2b2-b2b2-b2b2-b2b2-b2b2b2b2b2b2")
71                .unwrap()
72                .into(),
73            username: "bob".into(),
74            email: "bob@example.com".into(),
75            full_name: Some("Bob Builder".into()),
76            follower_count: 99,
77            is_active: false,
78            last_login: None,
79            #[cfg(not(feature = "disable-maps"))]
80            preferences: Some(BTreeMap::from_iter([("theme".into(), "light".into())])),
81        },
82        UserProfile {
83            id: Uuid::parse_str("c3c3c3c3-c3c3-c3c3-c3c3-c3c3c3c3c3c3")
84                .unwrap()
85                .into(),
86            username: "charlie".into(),
87            email: "charlie@example.com".into(),
88            full_name: None,
89            follower_count: 5000,
90            is_active: true,
91            last_login: Some(datetime!(2025-07-16 11:30:00)),
92            #[cfg(not(feature = "disable-maps"))]
93            preferences: None,
94        },
95        UserProfile {
96            id: Uuid::parse_str("d4d4d4d4-d4d4-d4d4-d4d4-d4d4d4d4d4d4")
97                .unwrap()
98                .into(),
99            username: "dean".into(),
100            email: "dean@example.com".into(),
101            full_name: Some("Dean Martin".into()),
102            follower_count: 15000,
103            is_active: true,
104            last_login: None,
105            #[cfg(not(feature = "disable-maps"))]
106            preferences: Some(BTreeMap::from_iter([(
107                "notifications".into(),
108                "off".into(),
109            )])),
110        },
111        UserProfile {
112            id: Uuid::parse_str("e5e5e5e5-e5e5-e5e5-e5e5-e5e5e5e5e5e5")
113                .unwrap()
114                .into(),
115            username: "eve".into(),
116            email: "eve@example.com".into(),
117            full_name: Some("Eve".into()),
118            follower_count: 1,
119            is_active: false,
120            last_login: Some(datetime!(2024-01-01 00:00:00)),
121            #[cfg(not(feature = "disable-maps"))]
122            preferences: None,
123        },
124    ];
125
126    let result = UserProfile::insert_many(executor, users_to_create.iter()).await;
127    assert!(
128        result.is_ok(),
129        "Failed to insert users: {:?}",
130        result.unwrap_err()
131    );
132    if let Some(affected) = result.unwrap().rows_affected {
133        assert_eq!(affected, 5);
134    }
135
136    // Find users with more than 1000 followers (should be 2: charlie, dean)
137    let popular_users = UserProfile::find_many(executor, expr!(follower_count > 1000), None)
138        .try_collect::<Vec<_>>()
139        .await
140        .unwrap();
141    assert_eq!(popular_users.len(), 2);
142
143    // Find active users (should be 3: alice, charlie, dean)
144    let active_users = UserProfile::find_many(executor, expr!(is_active == true), None)
145        .try_collect::<Vec<_>>()
146        .await
147        .unwrap();
148    assert_eq!(active_users.len(), 3);
149    let active_users = active_users
150        .into_iter()
151        .map(|u| u.username)
152        .collect::<HashSet<_>>();
153    assert_eq!(
154        active_users,
155        HashSet::from_iter(["alice".into(), "charlie".into(), "dean".into()])
156    );
157
158    // 4. Update a Bob
159    let mut bob = UserProfile::find_one(executor, expr!(username == "bob"))
160        .await
161        .expect("Expected query to succeed")
162        .expect("Could not find bob ");
163    bob.is_active = true;
164    bob.full_name = Some("Robert Builder".into());
165    bob.last_login = Some(datetime!(2025-07-17 20:00:00));
166    let result = bob.save(executor).await;
167    assert!(
168        result.is_ok(),
169        "Failed to save Bob: {:?}",
170        result.unwrap_err()
171    );
172    let updated_bob = UserProfile::find_one(executor, bob.primary_key_expr())
173        .await
174        .expect("Expected query to succeed")
175        .expect("Could not find bob ");
176    assert_eq!(updated_bob.is_active, true);
177    assert_eq!(updated_bob.full_name, Some("Robert Builder".into()));
178    assert!(updated_bob.last_login.is_some());
179
180    // There must be 4 active users
181    let active_users_after_update =
182        UserProfile::find_many(executor, expr!(is_active == true), None)
183            .try_collect::<Vec<_>>()
184            .await
185            .unwrap();
186    assert_eq!(active_users_after_update.len(), 4);
187
188    // Find eve user and delete it.
189    let eve = UserProfile::find_one(executor, expr!(username == "eve"))
190        .await
191        .expect("Expected query to succeed")
192        .expect("Could not find eve ");
193    let result = eve.delete(executor).await;
194    assert!(
195        result.is_ok(),
196        "Failed to delete Eve: {:?}",
197        result.unwrap_err()
198    );
199    let maybe_eve = UserProfile::find_one(executor, eve.primary_key_expr())
200        .await
201        .expect("Expected query to succeed");
202    assert!(maybe_eve.is_none(), "Eve should have been deleted");
203
204    // There must be 5 total users
205    let total_users = UserProfile::find_many(executor, true, None).count().await;
206    assert_eq!(total_users, 4, "There should be 4 users remaining");
207
208    // Delete all users who never logged in (only Dean)
209    let result = UserProfile::delete_many(executor, expr!(last_login == NULL))
210        .await
211        .expect("Expected query to succeed");
212    if let Some(affected) = result.rows_affected {
213        assert_eq!(affected, 1, "Should have removed 1 rows");
214    }
215
216    // There must be 3 users left (alice, bob, charlie)
217    let final_users = UserProfile::find_many(executor, true, None)
218        .try_collect::<Vec<_>>()
219        .await
220        .expect("Expected query to succeed");
221    assert_eq!(final_users.len(), 3);
222    let final_usernames = final_users
223        .into_iter()
224        .map(|u| u.username)
225        .collect::<HashSet<_>>();
226    assert_eq!(
227        final_usernames,
228        HashSet::from_iter(["alice".into(), "bob".into(), "charlie".into()])
229    );
230}