toasty_driver_integration_suite/tests/
starts_with.rs1use crate::prelude::*;
2
3#[derive(Debug, toasty::Model)]
6#[key(partition = partition_id, local = sort_key)]
7struct Item {
8 partition_id: i64,
9 sort_key: String,
10 name: String,
11}
12
13async fn setup(test: &mut Test) -> toasty::Db {
14 let mut db = test.setup_db(models!(Item)).await;
15
16 toasty::create!(Item::[
17 { partition_id: 1_i64, sort_key: "alpha-1", name: "Alice" },
18 { partition_id: 1_i64, sort_key: "alpha-2", name: "Alicia" },
19 { partition_id: 1_i64, sort_key: "beta-1", name: "Bob" },
20 { partition_id: 1_i64, sort_key: "beta-2", name: "Barry" },
21 { partition_id: 2_i64, sort_key: "alpha-1", name: "Carol" },
22 ])
23 .exec(&mut db)
24 .await
25 .unwrap();
26
27 db
28}
29
30#[driver_test]
33pub async fn starts_with_sort_key(test: &mut Test) -> Result<()> {
34 let mut db = setup(test).await;
35
36 let mut items: Vec<Item> = Item::filter(
37 Item::fields()
38 .partition_id()
39 .eq(1_i64)
40 .and(Item::fields().sort_key().starts_with("alpha".to_string())),
41 )
42 .exec(&mut db)
43 .await?;
44
45 items.sort_by(|a, b| a.sort_key.cmp(&b.sort_key));
46
47 assert_eq!(items.len(), 2);
48 assert_eq!(items[0].sort_key, "alpha-1");
49 assert_eq!(items[1].sort_key, "alpha-2");
50
51 Ok(())
52}
53
54#[driver_test]
57pub async fn starts_with_non_key_attr(test: &mut Test) -> Result<()> {
58 let mut db = setup(test).await;
59
60 let mut items: Vec<Item> = Item::filter(
61 Item::fields()
62 .partition_id()
63 .eq(1_i64)
64 .and(Item::fields().name().starts_with("Al".to_string())),
65 )
66 .exec(&mut db)
67 .await?;
68
69 items.sort_by(|a, b| a.name.cmp(&b.name));
70
71 assert_eq!(items.len(), 2);
72 assert_eq!(items[0].name, "Alice");
73 assert_eq!(items[1].name, "Alicia");
74
75 Ok(())
76}
77
78#[driver_test]
80pub async fn starts_with_no_match(test: &mut Test) -> Result<()> {
81 let mut db = setup(test).await;
82
83 let items: Vec<Item> = Item::filter(
84 Item::fields()
85 .partition_id()
86 .eq(1_i64)
87 .and(Item::fields().sort_key().starts_with("gamma".to_string())),
88 )
89 .exec(&mut db)
90 .await?;
91
92 assert_eq!(items.len(), 0);
93
94 Ok(())
95}
96
97#[driver_test(requires(not(sql)))]
99pub async fn starts_with_empty_prefix(test: &mut Test) -> Result<()> {
100 let mut db = setup(test).await;
101
102 let result: toasty::Result<Vec<Item>> = Item::filter(
103 Item::fields()
104 .partition_id()
105 .eq(1_i64)
106 .and(Item::fields().sort_key().starts_with("".to_string())),
107 )
108 .exec(&mut db)
109 .await;
110
111 assert!(
112 result.is_err(),
113 "expected error when using starts_with with empty prefix on DynamoDB"
114 );
115
116 Ok(())
117}
118
119#[driver_test(requires(sql))]
121pub async fn starts_with_empty_prefix_sql(test: &mut Test) -> Result<()> {
122 let mut db = setup(test).await;
123
124 let items: Vec<Item> = Item::filter(
125 Item::fields()
126 .partition_id()
127 .eq(1_i64)
128 .and(Item::fields().sort_key().starts_with("".to_string())),
129 )
130 .exec(&mut db)
131 .await?;
132
133 assert_eq!(items.len(), 4, "empty prefix should match all rows on SQL");
134
135 Ok(())
136}
137
138#[driver_test]
142pub async fn starts_with_special_chars(test: &mut Test) -> Result<()> {
143 #[derive(Debug, toasty::Model)]
144 #[key(partition = partition_id, local = sort_key)]
145 struct StringItem {
146 partition_id: i64,
147 sort_key: String,
148 }
149
150 let mut db = test.setup_db(models!(StringItem)).await;
151
152 toasty::create!(StringItem::[
153 { partition_id: 1_i64, sort_key: "100%-discount" },
154 { partition_id: 1_i64, sort_key: "100xdiscount" },
155 { partition_id: 1_i64, sort_key: "1009" },
156 { partition_id: 1_i64, sort_key: "a_b-literal" },
157 { partition_id: 1_i64, sort_key: "axb-wildcard" },
158 { partition_id: 1_i64, sort_key: "!bang-literal" },
159 { partition_id: 1_i64, sort_key: "x!bang" },
160 ])
161 .exec(&mut db)
162 .await
163 .unwrap();
164
165 let mut items: Vec<StringItem> = StringItem::filter(
167 StringItem::fields().partition_id().eq(1_i64).and(
168 StringItem::fields()
169 .sort_key()
170 .starts_with("100%".to_string()),
171 ),
172 )
173 .exec(&mut db)
174 .await?;
175 items.sort_by(|a, b| a.sort_key.cmp(&b.sort_key));
176 assert_eq!(items.len(), 1);
177 assert_eq!(items[0].sort_key, "100%-discount");
178
179 let mut items: Vec<StringItem> = StringItem::filter(
181 StringItem::fields().partition_id().eq(1_i64).and(
182 StringItem::fields()
183 .sort_key()
184 .starts_with("a_b".to_string()),
185 ),
186 )
187 .exec(&mut db)
188 .await?;
189 items.sort_by(|a, b| a.sort_key.cmp(&b.sort_key));
190 assert_eq!(items.len(), 1);
191 assert_eq!(items[0].sort_key, "a_b-literal");
192
193 let mut items: Vec<StringItem> = StringItem::filter(
196 StringItem::fields().partition_id().eq(1_i64).and(
197 StringItem::fields()
198 .sort_key()
199 .starts_with("!bang".to_string()),
200 ),
201 )
202 .exec(&mut db)
203 .await?;
204 items.sort_by(|a, b| a.sort_key.cmp(&b.sort_key));
205 assert_eq!(items.len(), 1);
206 assert_eq!(items[0].sort_key, "!bang-literal");
207
208 Ok(())
209}
210
211#[driver_test]
214pub async fn starts_with_optional_field(test: &mut Test) -> Result<()> {
215 #[derive(Debug, toasty::Model)]
216 #[key(partition = partition_id, local = id)]
217 struct OptItem {
218 partition_id: i64,
219 id: i64,
220 nickname: Option<String>,
221 }
222
223 let mut db = test.setup_db(models!(OptItem)).await;
224
225 toasty::create!(OptItem::[
226 { partition_id: 1_i64, id: 1_i64, nickname: Some("Ali".to_string()) },
227 { partition_id: 1_i64, id: 2_i64, nickname: Some("Alicia".to_string()) },
228 { partition_id: 1_i64, id: 3_i64, nickname: Some("Bob".to_string()) },
229 { partition_id: 1_i64, id: 4_i64, nickname: None },
230 ])
231 .exec(&mut db)
232 .await?;
233
234 let mut items: Vec<OptItem> = OptItem::filter(
235 OptItem::fields()
236 .partition_id()
237 .eq(1_i64)
238 .and(OptItem::fields().nickname().starts_with("Al".to_string())),
239 )
240 .exec(&mut db)
241 .await?;
242
243 items.sort_by_key(|i| i.id);
244
245 assert_eq!(items.len(), 2);
246 assert_eq!(items[0].nickname.as_deref(), Some("Ali"));
247 assert_eq!(items[1].nickname.as_deref(), Some("Alicia"));
248
249 Ok(())
250}
251
252#[driver_test(requires(not(sql)))]
255pub async fn starts_with_partition_key_error(test: &mut Test) -> Result<()> {
256 #[derive(Debug, toasty::Model)]
257 #[key(partition = partition_id, local = sort_key)]
258 struct StringKeyItem {
259 partition_id: String,
260 sort_key: String,
261 }
262
263 let mut db = test.setup_db(models!(StringKeyItem)).await;
264
265 StringKeyItem::create()
266 .partition_id("hello")
267 .sort_key("world")
268 .exec(&mut db)
269 .await?;
270
271 let result = StringKeyItem::filter(
272 StringKeyItem::fields()
273 .partition_id()
274 .starts_with("hel".to_string()),
275 )
276 .exec(&mut db)
277 .await;
278
279 assert!(
280 result.is_err(),
281 "expected error when using starts_with on partition key"
282 );
283
284 Ok(())
285}