toasty_driver_integration_suite/tests/
crud_update_field_order.rs

1use crate::prelude::*;
2
3// Regression tests for https://github.com/tokio-rs/toasty/issues/200
4//
5// The bug: when Assignments used IndexMap keyed by field offset, updating
6// fields in an order different from declaration order could cause values to be
7// assigned to the wrong column, producing type-parsing errors (e.g. writing a
8// String into an i64 column).
9
10// Update only the last declared field, leaving earlier fields untouched.
11// With offset-based IndexMap this could assign the value to field 0 instead.
12#[driver_test(id(ID), scenario(crate::scenarios::widget_mixed_types))]
13pub async fn update_last_field_only(test: &mut Test) -> Result<()> {
14    let mut db = setup(test).await;
15
16    let mut w = Widget::create()
17        .label("w1")
18        .count(10)
19        .active(true)
20        .description("hello")
21        .exec(&mut db)
22        .await?;
23
24    w.update().description("updated").exec(&mut db).await?;
25
26    assert_struct!(w, { label: "w1", count: 10, active: true, description: "updated" });
27
28    let reloaded = Widget::get_by_id(&mut db, &w.id).await?;
29    assert_struct!(reloaded, { label: "w1", count: 10, active: true, description: "updated" });
30
31    Ok(())
32}
33
34// Update fields in reverse declaration order. If offset mapping is wrong, the
35// String value ends up in the i64 column (or vice versa), causing a parse error.
36#[driver_test(id(ID), scenario(crate::scenarios::widget_mixed_types))]
37pub async fn update_fields_reverse_order(test: &mut Test) -> Result<()> {
38    let mut db = setup(test).await;
39
40    let mut w = Widget::create()
41        .label("a")
42        .count(1)
43        .active(false)
44        .description("c")
45        .exec(&mut db)
46        .await?;
47
48    // Update in reverse declaration order
49    w.update()
50        .description("C")
51        .active(true)
52        .count(2)
53        .label("A")
54        .exec(&mut db)
55        .await?;
56
57    assert_struct!(w, { label: "A", count: 2, active: true, description: "C" });
58
59    let reloaded = Widget::get_by_id(&mut db, &w.id).await?;
60    assert_struct!(reloaded, { label: "A", count: 2, active: true, description: "C" });
61
62    Ok(())
63}
64
65// Update a single middle field among many typed fields. This is the simplest
66// trigger for the original bug: a single assignment at a high offset gets
67// misrouted to offset 0.
68#[driver_test(id(ID), scenario(crate::scenarios::widget_mixed_types))]
69pub async fn update_middle_field_mixed_types(test: &mut Test) -> Result<()> {
70    let mut db = setup(test).await;
71
72    let mut w = Widget::create()
73        .label("w1")
74        .count(10)
75        .active(true)
76        .description("original")
77        .exec(&mut db)
78        .await?;
79
80    // Update only the i64 field (offset 2). With the old bug this value could
81    // land in the String column at offset 1, causing a type error.
82    w.update().count(99).exec(&mut db).await?;
83
84    assert_struct!(w, { label: "w1", count: 99, active: true, description: "original" });
85
86    let reloaded = Widget::get_by_id(&mut db, &w.id).await?;
87    assert_struct!(reloaded, { label: "w1", count: 99 });
88
89    Ok(())
90}
91
92// Query-based update with fields in non-declaration order. Exercises the
93// filter_by path rather than the instance-update path.
94#[driver_test(id(ID), scenario(crate::scenarios::widget_mixed_types))]
95pub async fn query_update_non_declaration_order(test: &mut Test) -> Result<()> {
96    let mut db = setup(test).await;
97
98    let w = Widget::create()
99        .label("task")
100        .count(1)
101        .active(false)
102        .description("n/a")
103        .exec(&mut db)
104        .await?;
105
106    // Update via query in reverse order
107    Widget::filter_by_id(w.id)
108        .update()
109        .description("important")
110        .active(true)
111        .count(5)
112        .label("urgent")
113        .exec(&mut db)
114        .await?;
115
116    let reloaded = Widget::get_by_id(&mut db, &w.id).await?;
117    assert_struct!(
118        reloaded,
119        { label: "urgent", count: 5, active: true, description: "important" }
120    );
121
122    Ok(())
123}
124
125// Successive single-field updates targeting different offsets each time.
126// Ensures that each individual update routes to the correct column.
127#[driver_test(id(ID), scenario(crate::scenarios::widget_mixed_types))]
128pub async fn successive_single_field_updates(test: &mut Test) -> Result<()> {
129    let mut db = setup(test).await;
130
131    let mut w = Widget::create()
132        .label("a0")
133        .count(0)
134        .active(false)
135        .description("d0")
136        .exec(&mut db)
137        .await?;
138
139    // Update each field individually, in non-declaration order
140    w.update().description("d1").exec(&mut db).await?;
141    assert_eq!(w.description, "d1");
142
143    w.update().count(2).exec(&mut db).await?;
144    assert_eq!(w.count, 2);
145
146    w.update().active(true).exec(&mut db).await?;
147    assert!(w.active);
148
149    w.update().label("a1").exec(&mut db).await?;
150    assert_eq!(w.label, "a1");
151
152    let reloaded = Widget::get_by_id(&mut db, &w.id).await?;
153    assert_struct!(reloaded, { label: "a1", count: 2, active: true, description: "d1" });
154
155    Ok(())
156}