toasty_driver_integration_suite/tests/
crud_upsert.rs1use crate::prelude::*;
2
3#[driver_test(requires(upsert_primary_key), scenario(crate::scenarios::two_models))]
4pub async fn upsert_by_primary_key_creates_then_updates(test: &mut Test) -> Result<()> {
5 let mut db = setup(test).await;
6 let seed = toasty::create!(User { name: "seed" }).exec(&mut db).await?;
7 let id = seed.id;
8 seed.delete().exec(&mut db).await?;
9
10 let created = User::upsert_by_id(id).name("created").exec(&mut db).await?;
11 assert_eq!(created.id, id);
12 assert_eq!(created.name, "created");
13
14 let updated = User::upsert_by_id(id).name("updated").exec(&mut db).await?;
15 assert_eq!(updated.id, id);
16 assert_eq!(updated.name, "updated");
17 assert_eq!(User::get_by_id(&mut db, id).await?.name, "updated");
18 Ok(())
19}
20
21#[driver_test(
24 requires(upsert_primary_key),
25 scenario(crate::scenarios::versioned_item)
26)]
27pub async fn upsert_update_increments_version(test: &mut Test) -> Result<()> {
28 let mut db = setup(test).await;
29 let seed = toasty::create!(Item { name: "seed" }).exec(&mut db).await?;
30 let id = seed.id;
31 seed.delete().exec(&mut db).await?;
32
33 let mut stale = Item::upsert_by_id(id).name("first").exec(&mut db).await?;
34 assert_struct!(stale, { name: "first", version: 1 });
35
36 let updated = Item::upsert_by_id(id).name("second").exec(&mut db).await?;
37 assert_struct!(updated, { name: "second", version: 2 });
38
39 let result: Result<()> = stale.update().name("stale").exec(&mut db).await;
40 assert!(result.is_err(), "expected stale update to fail");
41 Ok(())
42}
43
44#[driver_test(
45 requires(upsert_branch_assignments),
46 scenario(crate::scenarios::two_models)
47)]
48pub async fn upsert_branch_overrides(test: &mut Test) -> Result<()> {
49 let mut db = setup(test).await;
50 let seed = toasty::create!(User { name: "seed" }).exec(&mut db).await?;
51 let id = seed.id;
52 seed.delete().exec(&mut db).await?;
53
54 let created = User::upsert_by_id(id)
55 .name("shared")
56 .on_create(|create| create.name("created"))
57 .on_update(|update| update.name("updated"))
58 .exec(&mut db)
59 .await?;
60 assert_eq!(created.name, "created");
61
62 let updated = User::upsert_by_id(id)
63 .name("shared again")
64 .on_create(|create| create.name("created again"))
65 .on_update(|update| update.name("updated"))
66 .exec(&mut db)
67 .await?;
68 assert_eq!(updated.name, "updated");
69 Ok(())
70}
71
72#[driver_test(
73 requires(upsert_branch_assignments),
74 scenario(crate::scenarios::two_models)
75)]
76pub async fn upsert_branch_overrides_are_order_independent(test: &mut Test) -> Result<()> {
77 let mut db = setup(test).await;
78 let seed = toasty::create!(User { name: "seed" }).exec(&mut db).await?;
79 let id = seed.id;
80 seed.delete().exec(&mut db).await?;
81
82 let created = User::upsert_by_id(id)
83 .on_create(|create| create.name("created"))
84 .on_update(|update| update.name("updated"))
85 .name("shared")
86 .exec(&mut db)
87 .await?;
88 assert_eq!(created.name, "created");
89
90 let updated = User::upsert_by_id(id)
91 .on_create(|create| create.name("created again"))
92 .on_update(|update| update.name("updated"))
93 .name("shared again")
94 .exec(&mut db)
95 .await?;
96 assert_eq!(updated.name, "updated");
97 Ok(())
98}
99
100#[driver_test(
101 requires(upsert_branch_assignments),
102 scenario(crate::scenarios::two_models)
103)]
104pub async fn upsert_single_branch_override_keeps_shared_other_branch(
105 test: &mut Test,
106) -> Result<()> {
107 let mut db = setup(test).await;
108 let create_seed = toasty::create!(User { name: "seed" }).exec(&mut db).await?;
109 let create_id = create_seed.id;
110 create_seed.delete().exec(&mut db).await?;
111
112 let created = User::upsert_by_id(create_id)
113 .on_create(|create| create.name("created"))
114 .name("shared")
115 .exec(&mut db)
116 .await?;
117 assert_eq!(created.name, "created");
118
119 let updated = User::upsert_by_id(create_id)
120 .on_create(|create| create.name("created again"))
121 .name("shared")
122 .exec(&mut db)
123 .await?;
124 assert_eq!(updated.name, "shared");
125
126 let update_seed = toasty::create!(User { name: "seed" }).exec(&mut db).await?;
127 let update_id = update_seed.id;
128 update_seed.delete().exec(&mut db).await?;
129
130 let created = User::upsert_by_id(update_id)
131 .on_update(|update| update.name("updated"))
132 .name("shared")
133 .exec(&mut db)
134 .await?;
135 assert_eq!(created.name, "shared");
136
137 let updated = User::upsert_by_id(update_id)
138 .on_update(|update| update.name("updated"))
139 .name("shared again")
140 .exec(&mut db)
141 .await?;
142 assert_eq!(updated.name, "updated");
143 Ok(())
144}
145
146#[driver_test(
147 requires(upsert_targeted_ignore),
148 scenario(crate::scenarios::two_models)
149)]
150pub async fn upsert_or_ignore_returns_option(test: &mut Test) -> Result<()> {
151 let mut db = setup(test).await;
152 let seed = toasty::create!(User { name: "seed" }).exec(&mut db).await?;
153 let id = seed.id;
154 seed.delete().exec(&mut db).await?;
155
156 let created = User::upsert_by_id(id)
157 .name("first")
158 .or_ignore()
159 .exec(&mut db)
160 .await?;
161 assert_eq!(created.unwrap().name, "first");
162
163 let ignored = User::upsert_by_id(id)
164 .name("second")
165 .or_ignore()
166 .exec(&mut db)
167 .await?;
168 assert!(ignored.is_none());
169 assert_eq!(User::get_by_id(&mut db, id).await?.name, "first");
170 Ok(())
171}
172
173#[driver_test(
174 requires(and(upsert_primary_key, upsert_targeted_ignore)),
175 scenario(crate::scenarios::user_unique_email_with_name)
176)]
177pub async fn upsert_or_ignore_suppresses_only_the_selected_conflict(test: &mut Test) -> Result<()> {
178 let mut db = setup(test).await;
179 let first_seed = toasty::create!(User {
180 email: "first-seed@example.com",
181 name: "seed",
182 })
183 .exec(&mut db)
184 .await?;
185 let first_id = first_seed.id;
186 first_seed.delete().exec(&mut db).await?;
187
188 let second_seed = toasty::create!(User {
189 email: "second-seed@example.com",
190 name: "seed",
191 })
192 .exec(&mut db)
193 .await?;
194 let second_id = second_seed.id;
195 second_seed.delete().exec(&mut db).await?;
196
197 let created = User::upsert_by_id(first_id)
198 .email("alice@example.com")
199 .name("Alice")
200 .or_ignore()
201 .exec(&mut db)
202 .await?;
203 assert_eq!(created.unwrap().email, "alice@example.com");
204
205 let ignored = User::upsert_by_id(first_id)
206 .email("other@example.com")
207 .name("Other")
208 .or_ignore()
209 .exec(&mut db)
210 .await?;
211 assert!(ignored.is_none());
212 assert_eq!(
213 User::get_by_id(&mut db, first_id).await?.email,
214 "alice@example.com"
215 );
216
217 let other_conflict = User::upsert_by_id(second_id)
218 .email("alice@example.com")
219 .name("Duplicate")
220 .or_ignore()
221 .exec(&mut db)
222 .await;
223 assert!(other_conflict.is_err());
224 Ok(())
225}
226
227#[driver_test(
228 requires(upsert_unique),
229 scenario(crate::scenarios::user_unique_email_with_name)
230)]
231pub async fn upsert_by_unique_field(test: &mut Test) -> Result<()> {
232 let mut db = setup(test).await;
233 let created = User::upsert_by_email("alice@example.com")
234 .name("Alice")
235 .exec(&mut db)
236 .await?;
237 let updated = User::upsert_by_email("alice@example.com")
238 .name("Alicia")
239 .exec(&mut db)
240 .await?;
241
242 assert_eq!(updated.id, created.id);
243 assert_eq!(updated.email, "alice@example.com");
244 assert_eq!(updated.name, "Alicia");
245 Ok(())
246}
247
248#[driver_test(requires(upsert_unique), scenario(crate::scenarios::upsert_models))]
249pub async fn upsert_by_composite_unique_constraint(test: &mut Test) -> Result<()> {
250 let mut db = setup_entry(test).await;
251 let created = Entry::upsert_by_tenant_and_slug("acme", "home")
252 .value("one")
253 .exec(&mut db)
254 .await?;
255 let updated = Entry::upsert_by_tenant_and_slug("acme", "home")
256 .value("two")
257 .exec(&mut db)
258 .await?;
259
260 assert_eq!(updated.id, created.id);
261 assert_eq!(updated.value, "two");
262 Ok(())
263}
264
265#[driver_test(
266 requires(upsert_primary_key),
267 scenario(crate::scenarios::upsert_models)
268)]
269pub async fn upsert_applies_model_defaults(test: &mut Test) -> Result<()> {
270 let mut db = setup_defaulted_item(test).await;
271 let seed = toasty::create!(DefaultedItem { value: "seed" })
272 .exec(&mut db)
273 .await?;
274 let id = seed.id;
275 seed.delete().exec(&mut db).await?;
276
277 let created = DefaultedItem::upsert_by_id(id)
278 .value("one")
279 .exec(&mut db)
280 .await?;
281 assert_eq!(created.created_only, "created");
282 assert_eq!(created.updated_always, "always");
283
284 let updated = DefaultedItem::upsert_by_id(id)
285 .value("two")
286 .exec(&mut db)
287 .await?;
288 assert_eq!(updated.created_only, "created");
289 assert_eq!(updated.updated_always, "always");
290 Ok(())
291}
292
293#[driver_test(requires(upsert_primary_key))]
294pub async fn upsert_explicit_assignments_override_model_defaults(test: &mut Test) -> Result<()> {
295 #[derive(Debug, toasty::Model)]
296 struct Item {
297 #[key]
298 #[auto]
299 id: uuid::Uuid,
300
301 #[default("create default".to_string())]
302 default_only: String,
303
304 #[update("update default".to_string())]
305 update_only: String,
306
307 #[default("create branch default".to_string())]
308 #[update("update branch default".to_string())]
309 branch_defaults: String,
310 }
311
312 let mut db = test.setup_db(models!(Item)).await;
313 let seed = toasty::create!(Item {}).exec(&mut db).await?;
314 let id = seed.id;
315 seed.delete().exec(&mut db).await?;
316
317 let created = Item::upsert_by_id(id)
318 .default_only("created explicitly")
319 .update_only("created explicitly")
320 .branch_defaults("created explicitly")
321 .exec(&mut db)
322 .await?;
323 assert_struct!(created, {
324 default_only: "created explicitly",
325 update_only: "created explicitly",
326 branch_defaults: "created explicitly",
327 });
328
329 let updated = Item::upsert_by_id(id)
330 .default_only("updated explicitly")
331 .update_only("updated explicitly")
332 .branch_defaults("updated explicitly")
333 .exec(&mut db)
334 .await?;
335 assert_struct!(updated, {
336 default_only: "updated explicitly",
337 update_only: "updated explicitly",
338 branch_defaults: "updated explicitly",
339 });
340
341 assert_none!(Item::upsert_by_id(id).or_ignore().exec(&mut db).await?);
342 Ok(())
343}
344
345#[driver_test(
346 requires(upsert_primary_key),
347 scenario(crate::scenarios::upsert_models)
348)]
349pub async fn upsert_create_assignment_initializes_only_new_record(test: &mut Test) -> Result<()> {
350 let mut db = setup_defaulted_item(test).await;
351 let seed = toasty::create!(DefaultedItem { value: "seed" })
352 .exec(&mut db)
353 .await?;
354 let id = seed.id;
355 seed.delete().exec(&mut db).await?;
356
357 let created = DefaultedItem::upsert_by_id(id)
358 .value("one")
359 .on_create(|create| create.created_only("initial"))
360 .exec(&mut db)
361 .await?;
362 assert_eq!(created.created_only, "initial");
363
364 let updated = DefaultedItem::upsert_by_id(id)
365 .value("two")
366 .on_create(|create| create.created_only("replacement"))
367 .exec(&mut db)
368 .await?;
369 assert_eq!(updated.created_only, "initial");
370 Ok(())
371}
372
373#[driver_test(
374 requires(upsert_branch_assignments),
375 scenario(crate::scenarios::two_models)
376)]
377pub async fn upsert_update_can_reference_incoming_value(test: &mut Test) -> Result<()> {
378 let mut db = setup(test).await;
379 let seed = toasty::create!(User { name: "seed" }).exec(&mut db).await?;
380 let id = seed.id;
381
382 let updated = User::upsert_by_id(id)
383 .name("shared")
384 .on_create(|create| create.name("created"))
385 .on_update(|update| {
386 let incoming = update.incoming();
387 update.name(incoming.name())
388 })
389 .exec(&mut db)
390 .await?;
391 assert_eq!(updated.name, "created");
392 Ok(())
393}
394
395#[driver_test(
396 requires(upsert_branch_assignments),
397 scenario(crate::scenarios::two_models)
398)]
399pub async fn upsert_update_can_reference_stored_value(test: &mut Test) -> Result<()> {
400 let mut db = setup(test).await;
401 let seed = toasty::create!(User { name: "stored" })
402 .exec(&mut db)
403 .await?;
404
405 let updated = User::upsert_by_id(seed.id)
406 .name("incoming")
407 .on_update(|update| update.name(toasty::stmt::set(User::fields().name())))
408 .exec(&mut db)
409 .await?;
410 assert_eq!(updated.name, "stored");
411 Ok(())
412}
413
414#[driver_test(
415 requires(upsert_primary_key),
416 scenario(crate::scenarios::upsert_models)
417)]
418pub async fn upsert_shared_assignment_operators(test: &mut Test) -> Result<()> {
419 let mut db = setup_assigned_item(test).await;
420 let seed = toasty::create!(AssignedItem {
421 count: 0,
422 tags: Vec::<String>::new(),
423 })
424 .exec(&mut db)
425 .await?;
426 let id = seed.id;
427 seed.delete().exec(&mut db).await?;
428
429 let created = AssignedItem::upsert_by_id(id)
430 .count(toasty::stmt::increment())
431 .tags(toasty::stmt::push("a"))
432 .note(Some("present".to_string()))
433 .exec(&mut db)
434 .await?;
435 assert_eq!(created.count, 1);
436 assert_eq!(created.tags, ["a"]);
437 assert_eq!(created.note.as_deref(), Some("present"));
438
439 let incremented = AssignedItem::upsert_by_id(id)
440 .count(toasty::stmt::increment())
441 .tags(toasty::stmt::push("b"))
442 .note(None::<String>)
443 .exec(&mut db)
444 .await?;
445 assert_eq!(incremented.count, 2);
446 assert_eq!(incremented.tags, ["a", "b"]);
447 assert_eq!(incremented.note, None);
448
449 let decremented = AssignedItem::upsert_by_id(id)
450 .count(toasty::stmt::subtract(1_i64))
451 .tags(toasty::stmt::extend(Vec::<String>::new()))
452 .exec(&mut db)
453 .await?;
454 assert_eq!(decremented.count, 1);
455 assert_eq!(decremented.tags, ["a", "b"]);
456 Ok(())
457}
458
459#[driver_test(requires(upsert_primary_key))]
460pub async fn upsert_shared_mutations_apply_declared_defaults(test: &mut Test) -> Result<()> {
461 #[derive(Debug, toasty::Model)]
462 struct Item {
463 #[key]
464 id: String,
465
466 #[default(10)]
467 count: i64,
468
469 #[default(vec!["base".to_string()])]
470 tags: Vec<String>,
471 }
472
473 let mut db = test.setup_db(models!(Item)).await;
474
475 let created = Item::upsert_by_id("item")
476 .count(toasty::stmt::subtract(3))
477 .tags(toasty::stmt::push("a"))
478 .exec(&mut db)
479 .await?;
480 assert_struct!(created, { count: 7, tags: ["base", "a"] });
481
482 let updated = Item::upsert_by_id("item")
483 .count(toasty::stmt::subtract(3))
484 .tags(toasty::stmt::push("b"))
485 .exec(&mut db)
486 .await?;
487 assert_struct!(updated, { count: 4, tags: ["base", "a", "b"] });
488 Ok(())
489}
490
491#[driver_test(requires(upsert_primary_key))]
492pub async fn upsert_shared_mutation_requires_declared_default(test: &mut Test) -> Result<()> {
493 #[derive(Debug, toasty::Model)]
494 struct Item {
495 #[key]
496 id: String,
497
498 count: i64,
499 }
500
501 let mut db = test.setup_db(models!(Item)).await;
502 let error = Item::upsert_by_id("item")
503 .count(toasty::stmt::increment())
504 .exec(&mut db)
505 .await
506 .unwrap_err();
507 assert!(error.is_invalid_statement(), "unexpected error: {error}");
508 assert!(error.to_string().contains("#[default]"));
509 Ok(())
510}
511
512#[driver_test(requires(and(upsert_primary_key, vec_scalar)))]
513pub async fn upsert_shared_replacement_does_not_require_default(test: &mut Test) -> Result<()> {
514 #[derive(Debug, toasty::Model)]
515 struct Item {
516 #[key]
517 id: String,
518
519 tags: Vec<String>,
520 }
521
522 let mut db = test.setup_db(models!(Item)).await;
523 let created = Item::upsert_by_id("item")
524 .tags(toasty::stmt::clear())
525 .exec(&mut db)
526 .await?;
527 assert!(created.tags.is_empty());
528 Ok(())
529}
530
531#[driver_test(requires(upsert_branch_assignments))]
532pub async fn upsert_update_mutation_does_not_require_default(test: &mut Test) -> Result<()> {
533 #[derive(Debug, toasty::Model)]
534 struct Item {
535 #[key]
536 id: String,
537
538 count: i64,
539 }
540
541 let mut db = test.setup_db(models!(Item)).await;
542
543 let created = Item::upsert_by_id("item")
544 .on_create(|item| item.count(10))
545 .on_update(|item| item.count(toasty::stmt::subtract(3)))
546 .exec(&mut db)
547 .await?;
548 assert_eq!(created.count, 10);
549
550 let updated = Item::upsert_by_id("item")
551 .on_create(|item| item.count(10))
552 .on_update(|item| item.count(toasty::stmt::subtract(3)))
553 .exec(&mut db)
554 .await?;
555 assert_eq!(updated.count, 7);
556 Ok(())
557}
558
559#[driver_test(requires(and(upsert_primary_key, vec_pop)))]
560pub async fn upsert_pop_applies_declared_default_on_create(test: &mut Test) -> Result<()> {
561 #[derive(Debug, toasty::Model)]
562 struct Item {
563 #[key]
564 id: String,
565
566 #[default(vec!["a".to_string(), "b".to_string()])]
567 tags: Vec<String>,
568 }
569
570 let mut db = test.setup_db(models!(Item)).await;
571
572 let created = Item::upsert_by_id("item")
573 .tags(toasty::stmt::pop())
574 .exec(&mut db)
575 .await?;
576 assert_eq!(created.tags, ["a"]);
577
578 let updated = Item::upsert_by_id("item")
579 .tags(toasty::stmt::pop())
580 .exec(&mut db)
581 .await?;
582 assert!(updated.tags.is_empty());
583 Ok(())
584}
585
586#[driver_test(
587 requires(upsert_primary_key),
588 scenario(crate::scenarios::has_many_nullable_fk)
589)]
590pub async fn upsert_without_update_assignments_is_invalid(test: &mut Test) -> Result<()> {
591 let mut db = setup(test).await;
592 let seed = toasty::create!(User {}).exec(&mut db).await?;
593 let error = User::upsert_by_id(seed.id).exec(&mut db).await.unwrap_err();
594 assert!(error.is_invalid_statement(), "unexpected error: {error}");
595 Ok(())
596}
597
598#[driver_test(
599 requires(and(upsert_primary_key, not(upsert_branch_assignments))),
600 scenario(crate::scenarios::two_models)
601)]
602pub async fn unsupported_upsert_branches_are_reported_before_dispatch(
603 test: &mut Test,
604) -> Result<()> {
605 let mut db = setup(test).await;
606 let seed = toasty::create!(User { name: "seed" }).exec(&mut db).await?;
607 let error = User::upsert_by_id(seed.id)
608 .name("shared")
609 .on_update(|update| update.name("updated"))
610 .exec(&mut db)
611 .await
612 .unwrap_err();
613 assert!(error.is_unsupported_feature(), "unexpected error: {error}");
614 assert!(
615 error.to_string().contains(test.capability().driver_name),
616 "error does not identify the driver: {error}"
617 );
618 Ok(())
619}
620
621#[driver_test(
622 requires(and(upsert_primary_key, not(upsert_unique))),
623 scenario(crate::scenarios::user_unique_email_with_name)
624)]
625pub async fn unsupported_unique_upsert_is_reported_before_dispatch(test: &mut Test) -> Result<()> {
626 let mut db = setup(test).await;
627 let error = User::upsert_by_email("alice@example.com")
628 .name("Alice")
629 .exec(&mut db)
630 .await
631 .unwrap_err();
632 assert!(error.is_unsupported_feature(), "unexpected error: {error}");
633 assert!(
634 error.to_string().contains(test.capability().driver_name),
635 "error does not identify the driver: {error}"
636 );
637 Ok(())
638}
639
640#[driver_test(
641 requires(not(upsert_primary_key)),
642 scenario(crate::scenarios::two_models)
643)]
644pub async fn unsupported_upsert_is_reported_before_dispatch(test: &mut Test) -> Result<()> {
645 let mut db = setup(test).await;
646 let seed = toasty::create!(User { name: "seed" }).exec(&mut db).await?;
647 let error = User::upsert_by_id(seed.id)
648 .name("updated")
649 .exec(&mut db)
650 .await
651 .unwrap_err();
652 assert!(error.is_unsupported_feature(), "unexpected error: {error}");
653 assert!(
654 error.to_string().contains(test.capability().driver_name),
655 "error does not identify the driver: {error}"
656 );
657 Ok(())
658}