1use crate::prelude::*;
19use crate::scenarios::composite_chain_relations::Category;
20
21async fn make_category(db: &mut toasty::Db, name: &str, revision: i64) -> Result<Category> {
26 toasty::create!(Category {
27 id: uuid::Uuid::new_v4(),
28 revision,
29 name,
30 })
31 .exec(db)
32 .await
33}
34
35#[driver_test(scenario(crate::scenarios::composite_chain_relations))]
43pub async fn user_todos_category_composite(test: &mut Test) -> Result<()> {
44 let mut db = setup(test).await;
45
46 let user = toasty::create!(User { name: "Anchovy" })
47 .exec(&mut db)
48 .await?;
49 let other_user = toasty::create!(User { name: "Other" })
50 .exec(&mut db)
51 .await?;
52
53 let food = make_category(&mut db, "Food", 1).await?;
54 let drink = make_category(&mut db, "Drink", 1).await?;
55 let _unused = make_category(&mut db, "Unused", 1).await?;
56
57 toasty::create!(Todo::[
58 { title: "salad", user: &user, category: &food },
59 { title: "tea", user: &user, category: &drink },
60 { title: "sushi", user: &user, category: &food },
61 { title: "wine", user: &other_user, category: &drink },
62 ])
63 .exec(&mut db)
64 .await?;
65
66 let mut categories = user.todos().category().exec(&mut db).await?;
67 categories.sort_by_key(|c| c.name.clone());
68
69 let ids: Vec<_> = categories.iter().map(|c| (c.id, c.revision)).collect();
70 assert_unique!(ids);
71 assert_eq!(categories.len(), 2);
72 assert_eq!(
73 (categories[0].id, categories[0].revision),
74 (drink.id, drink.revision)
75 );
76 assert_eq!(
77 (categories[1].id, categories[1].revision),
78 (food.id, food.revision)
79 );
80 Ok(())
81}
82
83#[driver_test(scenario(crate::scenarios::composite_chain_relations))]
88pub async fn user_todos_category_distinguishes_by_revision(test: &mut Test) -> Result<()> {
89 let mut db = setup(test).await;
90
91 let user = toasty::create!(User { name: "Owner" })
92 .exec(&mut db)
93 .await?;
94
95 let shared_id = uuid::Uuid::new_v4();
98 let v1 = toasty::create!(Category {
99 id: shared_id,
100 revision: 1,
101 name: "v1",
102 })
103 .exec(&mut db)
104 .await?;
105 let v2 = toasty::create!(Category {
106 id: shared_id,
107 revision: 2,
108 name: "v2",
109 })
110 .exec(&mut db)
111 .await?;
112
113 toasty::create!(Todo::[
114 { title: "old", user: &user, category: &v1 },
115 { title: "new", user: &user, category: &v2 },
116 ])
117 .exec(&mut db)
118 .await?;
119
120 let mut categories = user.todos().category().exec(&mut db).await?;
121 categories.sort_by_key(|c| c.revision);
122
123 assert_eq!(categories.len(), 2);
124 assert_eq!(
125 (categories[0].id, categories[0].revision),
126 (v1.id, v1.revision)
127 );
128 assert_eq!(
129 (categories[1].id, categories[1].revision),
130 (v2.id, v2.revision)
131 );
132 Ok(())
133}
134
135#[driver_test(scenario(crate::scenarios::composite_chain_relations))]
139pub async fn composite_chain_from_empty_source_is_empty(test: &mut Test) -> Result<()> {
140 let mut db = setup(test).await;
141
142 let lonely = toasty::create!(User { name: "Lonely" })
143 .exec(&mut db)
144 .await?;
145
146 let busy = toasty::create!(User { name: "Busy" }).exec(&mut db).await?;
147 let cat = make_category(&mut db, "Solo", 1).await?;
148 toasty::create!(Todo {
149 title: "salad",
150 user: &busy,
151 category: &cat,
152 })
153 .exec(&mut db)
154 .await?;
155
156 let categories = lonely.todos().category().exec(&mut db).await?;
157 assert!(categories.is_empty());
158 Ok(())
159}
160
161#[driver_test(scenario(crate::scenarios::composite_chain_relations))]
165pub async fn composite_chain_then_filter(test: &mut Test) -> Result<()> {
166 let mut db = setup(test).await;
167
168 let user = toasty::create!(User { name: "Filty" })
169 .exec(&mut db)
170 .await?;
171 let food = make_category(&mut db, "Food", 1).await?;
172 let drink = make_category(&mut db, "Drink", 1).await?;
173
174 toasty::create!(Todo::[
175 { title: "salad", user: &user, category: &food },
176 { title: "tea", user: &user, category: &drink },
177 ])
178 .exec(&mut db)
179 .await?;
180
181 let only_food = user
182 .todos()
183 .category()
184 .filter(Category::fields().name().eq("Food"))
185 .exec(&mut db)
186 .await?;
187 assert_eq!(only_food.len(), 1);
188 assert_eq!(
189 (only_food[0].id, only_food[0].revision),
190 (food.id, food.revision)
191 );
192 Ok(())
193}
194
195#[driver_test(scenario(crate::scenarios::composite_chain_relations))]
204pub async fn category_todos_user_composite_first_hop(test: &mut Test) -> Result<()> {
205 let mut db = setup(test).await;
206
207 let alice = toasty::create!(User { name: "Alice" })
208 .exec(&mut db)
209 .await?;
210 let bob = toasty::create!(User { name: "Bob" }).exec(&mut db).await?;
211
212 let food = make_category(&mut db, "Food", 1).await?;
213 let other = make_category(&mut db, "Other", 1).await?;
214
215 toasty::create!(Todo::[
216 { title: "salad", user: &alice, category: &food },
217 { title: "tea", user: &bob, category: &food },
218 { title: "wine", user: &alice, category: &other },
219 ])
220 .exec(&mut db)
221 .await?;
222
223 let mut users = food.todos().user().exec(&mut db).await?;
224 users.sort_by_key(|u| u.name.clone());
225
226 let ids: Vec<_> = users.iter().map(|u| u.id).collect();
227 assert_unique!(ids);
228 assert_eq!(users.len(), 2);
229 assert_eq!(users[0].name, "Alice");
230 assert_eq!(users[1].name, "Bob");
231 Ok(())
232}
233
234#[driver_test(scenario(crate::scenarios::composite_chain_relations))]
238pub async fn category_todos_respects_revision(test: &mut Test) -> Result<()> {
239 let mut db = setup(test).await;
240
241 let alice = toasty::create!(User { name: "Alice" })
242 .exec(&mut db)
243 .await?;
244 let bob = toasty::create!(User { name: "Bob" }).exec(&mut db).await?;
245
246 let shared_id = uuid::Uuid::new_v4();
248 let v1 = toasty::create!(Category {
249 id: shared_id,
250 revision: 1,
251 name: "v1",
252 })
253 .exec(&mut db)
254 .await?;
255 let v2 = toasty::create!(Category {
256 id: shared_id,
257 revision: 2,
258 name: "v2",
259 })
260 .exec(&mut db)
261 .await?;
262
263 toasty::create!(Todo::[
264 { title: "for-v1", user: &alice, category: &v1 },
265 { title: "for-v2", user: &bob, category: &v2 },
266 ])
267 .exec(&mut db)
268 .await?;
269
270 let v1_users = v1.todos().user().exec(&mut db).await?;
271 assert_eq!(v1_users.len(), 1);
272 assert_eq!(v1_users[0].name, "Alice");
273
274 let v2_users = v2.todos().user().exec(&mut db).await?;
275 assert_eq!(v2_users.len(), 1);
276 assert_eq!(v2_users[0].name, "Bob");
277 Ok(())
278}
279
280#[driver_test(scenario(crate::scenarios::composite_chain_relations))]
285pub async fn category_todos_filter(test: &mut Test) -> Result<()> {
286 let mut db = setup(test).await;
287
288 let alice = toasty::create!(User { name: "Alice" })
289 .exec(&mut db)
290 .await?;
291 let bob = toasty::create!(User { name: "Bob" }).exec(&mut db).await?;
292
293 let food = make_category(&mut db, "Food", 1).await?;
294
295 toasty::create!(Todo::[
296 { title: "salad", user: &alice, category: &food },
297 { title: "sushi", user: &bob, category: &food },
298 ])
299 .exec(&mut db)
300 .await?;
301
302 let just_salad = food
303 .todos()
304 .filter(Todo::fields().title().eq("salad"))
305 .exec(&mut db)
306 .await?;
307 assert_eq!(just_salad.len(), 1);
308 assert_eq!(just_salad[0].title, "salad");
309 Ok(())
310}
311
312#[driver_test(scenario(crate::scenarios::composite_chain_relations))]
318pub async fn filtered_category_todos_user_composite(test: &mut Test) -> Result<()> {
319 let mut db = setup(test).await;
320
321 let alice = toasty::create!(User { name: "Alice" })
322 .exec(&mut db)
323 .await?;
324 let bob = toasty::create!(User { name: "Bob" }).exec(&mut db).await?;
325
326 let food = make_category(&mut db, "Food", 1).await?;
327 let drink = make_category(&mut db, "Drink", 1).await?;
328
329 toasty::create!(Todo::[
330 { title: "salad", user: &alice, category: &food },
331 { title: "tea", user: &bob, category: &drink },
332 ])
333 .exec(&mut db)
334 .await?;
335
336 let mut users = Category::filter(Category::fields().name().eq("Food"))
337 .todos()
338 .user()
339 .exec(&mut db)
340 .await?;
341 users.sort_by_key(|u| u.name.clone());
342
343 assert_eq!(users.len(), 1);
344 assert_eq!(users[0].name, "Alice");
345 Ok(())
346}
347
348#[driver_test]
356pub async fn composite_has_many_through_has_many(test: &mut Test) -> Result<()> {
357 #[derive(Debug, toasty::Model)]
358 #[key(id, revision)]
359 struct Author {
360 id: uuid::Uuid,
361 revision: i64,
362 name: String,
363 #[has_many]
364 posts: toasty::HasMany<Post>,
365 }
366
367 #[derive(Debug, toasty::Model)]
368 #[index(author_id, author_revision)]
369 struct Post {
370 #[key]
371 #[auto]
372 id: uuid::Uuid,
373 author_id: uuid::Uuid,
374 author_revision: i64,
375 #[belongs_to(key = [author_id, author_revision], references = [id, revision])]
376 author: toasty::BelongsTo<Author>,
377 title: String,
378 #[has_many]
379 comments: toasty::HasMany<Comment>,
380 }
381
382 #[derive(Debug, toasty::Model)]
383 struct Comment {
384 #[key]
385 #[auto]
386 id: uuid::Uuid,
387 #[index]
388 post_id: uuid::Uuid,
389 #[belongs_to(key = post_id, references = id)]
390 post: toasty::BelongsTo<Post>,
391 body: String,
392 }
393
394 let mut db = test.setup_db(models!(Author, Post, Comment)).await;
395
396 let alice = toasty::create!(Author {
397 id: uuid::Uuid::new_v4(),
398 revision: 1,
399 name: "Alice",
400 })
401 .exec(&mut db)
402 .await?;
403 let bob = toasty::create!(Author {
404 id: uuid::Uuid::new_v4(),
405 revision: 1,
406 name: "Bob",
407 })
408 .exec(&mut db)
409 .await?;
410
411 let p1 = toasty::create!(Post {
412 title: "p1",
413 author: &alice
414 })
415 .exec(&mut db)
416 .await?;
417 let p2 = toasty::create!(Post {
418 title: "p2",
419 author: &alice
420 })
421 .exec(&mut db)
422 .await?;
423 let _p3 = toasty::create!(Post {
424 title: "p3",
425 author: &bob
426 })
427 .exec(&mut db)
428 .await?;
429
430 toasty::create!(Comment::[
431 { body: "c1", post: &p1 },
432 { body: "c2", post: &p1 },
433 { body: "c3", post: &p2 },
434 ])
435 .exec(&mut db)
436 .await?;
437
438 let mut alice_comments = alice.posts().comments().exec(&mut db).await?;
439 alice_comments.sort_by_key(|c| c.body.clone());
440 let bodies: Vec<_> = alice_comments.into_iter().map(|c| c.body).collect();
441 assert_eq!(bodies, vec!["c1", "c2", "c3"]);
442
443 let bob_comments = bob.posts().comments().exec(&mut db).await?;
444 assert!(bob_comments.is_empty());
445 Ok(())
446}