use futures_util::StreamExt;
use serial_test::serial;
use std::collections::HashMap;
mod helpers;
use dynamo_table::table::query_items_stream;
use helpers::*;
#[tokio::test]
#[serial]
async fn test_query_with_pagination() {
let _ = setup::table::<TestObject>().await;
let partition_key = "pagination_test_1".to_string();
let count = 50;
for i in 0..(count + 10) {
let obj = TestObject {
game: partition_key.clone(),
age: format!("{:03}", i), ux: "test".into(),
number2: i,
};
obj.add_item().await.unwrap();
}
let first_page = TestObject::query_items(&partition_key, None, Some(count as u16), None)
.await
.unwrap();
assert_eq!(first_page.items.len(), count);
if let Some(cursor) = first_page.start_cursor() {
let second_page = TestObject::query_items(
&partition_key,
None,
Some(count as u16),
cursor.exclusive_start_key(),
)
.await
.unwrap();
assert_eq!(second_page.items.len(), 10);
} else {
panic!("Should have pagination cursor");
}
}
#[tokio::test]
#[serial]
async fn test_query_with_filter() {
let _ = setup::table::<TestObject>().await;
let partition_key = "filter_test_1".to_string();
for i in 0..10 {
let obj = TestObject {
game: partition_key.clone(),
age: format!("item{}", i),
ux: format!("value{}", i),
number2: i,
};
obj.add_item().await.unwrap();
}
let expression = "number2 > :threshold".to_string();
let mut filter_values: HashMap<String, u8> = HashMap::new();
filter_values.insert(":threshold".into(), 5);
let results = TestObject::query_items_with_filter(
&partition_key,
None,
Some(100),
None,
expression,
filter_values,
)
.await
.unwrap();
assert_eq!(
results.items.len(),
4,
"Should have 4 items with number2 > 5"
);
for item in &results.items {
assert!(item.number2 > 5);
}
}
#[tokio::test]
#[serial]
async fn test_reverse_query() {
let _ = setup::table::<TestObject>().await;
let partition_key = "reverse_test_1".to_string();
for i in 0..5 {
let obj = TestObject {
game: partition_key.clone(),
age: format!("item{}", i),
ux: "test".into(),
number2: i,
};
obj.add_item().await.unwrap();
}
let results = TestObject::reverse_query_items(&partition_key, None, Some(10), None)
.await
.unwrap();
assert_eq!(results.items.len(), 5);
assert_eq!(results.items[0].age, "item4");
assert_eq!(results.items[4].age, "item0");
}
#[tokio::test]
#[serial]
async fn test_query_stream() {
let _ = setup::table::<TestObject>().await;
let partition_key = "stream_test_1".to_string();
let count = 50;
for i in 0..count {
let obj = TestObject {
game: partition_key.clone(),
age: format!("{:03}", i),
ux: "stream".into(),
number2: i,
};
obj.add_item().await.unwrap();
}
let items = query_items_stream::<TestObject>(&partition_key, None, None, Some(10), None, true)
.await
.filter_map(|item| async { item.ok() })
.collect::<Vec<TestObject>>()
.await;
assert_eq!(items.len(), count, "Should stream all items");
}
#[tokio::test]
#[serial]
async fn test_query_between() {
let _ = setup::table::<TestObject>().await;
let partition_key = "between_test_1".to_string();
for i in 0..10 {
let obj = TestObject {
game: partition_key.clone(),
age: format!("2024-01-{:02}", i + 1),
ux: "date_test".into(),
number2: i,
};
obj.add_item().await.unwrap();
}
let results = TestObject::query_items_between(
&partition_key,
None,
Some(100),
true,
"2024-01-03".to_string(),
"2024-01-07".to_string(),
)
.await
.unwrap();
assert_eq!(results.items.len(), 5, "Should get items from 03 to 07");
}
#[tokio::test]
#[serial]
async fn test_query_begins_with() {
let _ = setup::table::<TestObject>().await;
let partition_key = "prefix_test_1".to_string();
for prefix in &["user_", "admin_", "guest_"] {
for i in 0..3 {
let obj = TestObject {
game: partition_key.clone(),
age: format!("{}{}", prefix, i),
ux: "prefix_test".into(),
number2: i,
};
obj.add_item().await.unwrap();
}
}
let results = TestObject::query_items_begins_with(
&partition_key,
None,
Some(100),
true,
"user_".to_string(),
)
.await
.unwrap();
assert_eq!(results.items.len(), 3);
for item in &results.items {
assert!(item.age.starts_with("user_"));
}
}
#[tokio::test]
#[serial]
async fn test_query_contains() {
let _ = setup::table::<TestObject>().await;
let partition_key = "contains_test_1".to_string();
for value in &["alpha_mid_beta", "midway", "nomatch", "prefix_mid"] {
let obj = TestObject {
game: partition_key.clone(),
age: value.to_string(),
ux: value.to_string(),
number2: value.len(),
};
obj.add_item().await.unwrap();
}
let results = TestObject::query_items_contains(
&partition_key,
None,
Some(100),
true,
"ux".to_string(),
"mid".to_string(),
)
.await
.unwrap();
assert_eq!(results.items.len(), 3);
for item in &results.items {
assert!(item.age.contains("mid"));
}
let results = TestObject::query_items_contains(
&partition_key,
None,
Some(100),
true,
"ux".to_string(),
"1mid".to_string(),
)
.await
.unwrap();
assert_eq!(results.items.len(), 0);
}
#[tokio::test]
#[serial]
async fn test_query_single_item() {
let _ = setup::table::<TestObject>().await;
let partition_key = "single_query_test_1".to_string();
let obj = TestObject {
game: partition_key.clone(),
age: "unique".into(),
ux: "single".into(),
number2: 42,
};
obj.add_item().await.unwrap();
let result = TestObject::query_item(&partition_key).await.unwrap();
assert!(result.is_some());
assert_eq!(result.unwrap(), obj);
}