use rinq::{QueryBuilder, RinqError};
#[test]
fn test_from_creates_query_builder() {
let data = vec![1, 2, 3];
let result: Vec<_> = QueryBuilder::from(data).collect();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_from_vec_creates_query_builder() {
let data = vec![1, 2, 3, 4, 5];
let result: Vec<_> = QueryBuilder::from(data).collect();
assert_eq!(result, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_from_empty_vec() {
let data: Vec<i32> = vec![];
let result: Vec<_> = QueryBuilder::from(data).collect();
assert_eq!(result, Vec::<i32>::new());
}
#[test]
fn test_from_vec_with_strings() {
let data = vec!["hello".to_string(), "world".to_string()];
let result: Vec<_> = QueryBuilder::from(data).collect();
assert_eq!(result, vec!["hello".to_string(), "world".to_string()]);
}
#[test]
fn test_from_slice_cloned() {
let data = vec![1, 2, 3, 4, 5];
let result: Vec<_> = QueryBuilder::from(data).collect();
assert_eq!(result, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_from_slice_copied() {
let data = vec![10, 20, 30];
let result: Vec<_> = QueryBuilder::from(data).collect();
assert_eq!(result, vec![10, 20, 30]);
}
#[test]
fn test_from_array_into_iter() {
let data = [1, 2, 3];
let result: Vec<_> = QueryBuilder::from(data).collect();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_from_range() {
let result: Vec<_> = QueryBuilder::from(1..6).collect();
assert_eq!(result, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_from_vec_of_tuples() {
let data = vec![(1, "a"), (2, "b"), (3, "c")];
let result: Vec<_> = QueryBuilder::from(data).collect();
assert_eq!(result, vec![(1, "a"), (2, "b"), (3, "c")]);
}
#[test]
fn test_where_filters_correctly() {
let data = vec![1, 2, 3, 4, 5];
let result: Vec<_> = QueryBuilder::from(data).where_(|x| x % 2 == 0).collect();
assert_eq!(result, vec![2, 4]);
}
#[test]
fn test_count_returns_correct_length() {
let data = vec![1, 2, 3, 4, 5];
let count = QueryBuilder::from(data).count();
assert_eq!(count, 5);
}
#[test]
fn test_first_returns_first_element() {
let data = vec![1, 2, 3];
let first = QueryBuilder::from(data).first();
assert_eq!(first, Some(1));
}
#[test]
fn test_first_returns_none_for_empty() {
let data: Vec<i32> = vec![];
let first = QueryBuilder::from(data).first();
assert_eq!(first, None);
}
#[test]
fn test_select_transforms_to_same_type() {
let data = vec![1, 2, 3, 4, 5];
let result: Vec<_> = QueryBuilder::from(data)
.where_(|x| x % 2 == 0)
.select(|x| x * 2)
.collect();
assert_eq!(result, vec![4, 8]);
}
#[test]
fn test_select_transforms_to_string() {
let data = vec![1, 2, 3];
let result: Vec<String> = QueryBuilder::from(data)
.where_(|x| *x > 1)
.select(|x| format!("number: {}", x))
.collect();
assert_eq!(
result,
vec!["number: 2".to_string(), "number: 3".to_string()]
);
}
#[test]
fn test_select_transforms_to_tuple() {
let data = vec![1, 2, 3, 4];
let result: Vec<(i32, i32)> = QueryBuilder::from(data)
.where_(|x| *x <= 3)
.select(|x| (x, x * x))
.collect();
assert_eq!(result, vec![(1, 1), (2, 4), (3, 9)]);
}
#[test]
fn test_select_transforms_to_bool() {
let data = vec![1, 2, 3, 4, 5];
let result: Vec<bool> = QueryBuilder::from(data)
.where_(|x| *x > 0)
.select(|x| x % 2 == 0)
.collect();
assert_eq!(result, vec![false, true, false, true, false]);
}
#[test]
fn test_select_transforms_to_option() {
let data = vec![0, 1, 2, 3];
let result: Vec<Option<i32>> = QueryBuilder::from(data)
.where_(|x| *x >= 0)
.select(|x| if x > 0 { Some(x * 10) } else { None })
.collect();
assert_eq!(result, vec![None, Some(10), Some(20), Some(30)]);
}
#[test]
fn test_select_transforms_string_to_length() {
let data = vec!["hello".to_string(), "world".to_string(), "rust".to_string()];
let result: Vec<usize> = QueryBuilder::from(data)
.where_(|s| s.len() > 3)
.select(|s| s.len())
.collect();
assert_eq!(result, vec![5, 5, 4]);
}
#[test]
fn test_select_transforms_tuple_to_sum() {
let data = vec![(1, 2), (3, 4), (5, 6)];
let result: Vec<i32> = QueryBuilder::from(data)
.where_(|(a, b)| a + b < 10)
.select(|(a, b)| a + b)
.collect();
assert_eq!(result, vec![3, 7]);
}
#[test]
fn test_select_with_complex_type_conversion() {
#[derive(Debug, PartialEq)]
struct Person {
name: String,
age: i32,
}
let data = vec![1, 2, 3];
let result: Vec<Person> = QueryBuilder::from(data)
.where_(|x| *x > 0)
.select(|x| Person {
name: format!("Person{}", x),
age: x * 10,
})
.collect();
assert_eq!(
result,
vec![
Person {
name: "Person1".to_string(),
age: 10
},
Person {
name: "Person2".to_string(),
age: 20
},
Person {
name: "Person3".to_string(),
age: 30
},
]
);
}
#[test]
fn test_select_preserves_element_count() {
let data = vec![1, 2, 3, 4, 5];
let filtered_count = QueryBuilder::from(data.clone())
.where_(|x| x % 2 == 0)
.count();
let selected_count = QueryBuilder::from(data)
.where_(|x| x % 2 == 0)
.select(|x| x * 2)
.count();
assert_eq!(filtered_count, selected_count);
}
#[test]
fn test_select_on_empty_collection() {
let data: Vec<i32> = vec![];
let result: Vec<String> = QueryBuilder::from(data)
.where_(|x| *x > 0)
.select(|x| format!("{}", x))
.collect();
assert_eq!(result, Vec::<String>::new());
}
#[test]
fn test_collect_to_vec() {
let data = vec![1, 2, 3, 4, 5];
let result: Vec<_> = QueryBuilder::from(data.clone())
.where_(|x| *x % 2 == 0)
.collect();
assert_eq!(result, vec![2, 4]);
}
#[test]
fn test_collect_to_hashset() {
use std::collections::HashSet;
let data = vec![1, 2, 3, 2, 1];
let result: HashSet<_> = QueryBuilder::from(data).where_(|x| *x > 0).collect();
let expected: HashSet<_> = vec![1, 2, 3].into_iter().collect();
assert_eq!(result, expected);
}
#[test]
fn test_collect_to_btreeset() {
use std::collections::BTreeSet;
let data = vec![3, 1, 4, 1, 5, 9, 2, 6];
let result: BTreeSet<_> = QueryBuilder::from(data).where_(|x| *x < 7).collect();
let expected: BTreeSet<_> = vec![1, 2, 3, 4, 5, 6].into_iter().collect();
assert_eq!(result, expected);
}
#[test]
fn test_collect_to_vec_after_sort() {
let data = vec![5, 3, 1, 4, 2];
let result: Vec<_> = QueryBuilder::from(data)
.where_(|_| true)
.order_by(|x| *x)
.collect();
assert_eq!(result, vec![1, 2, 3, 4, 5]);
}
#[test]
fn test_collect_to_vec_after_projection() {
let data = vec![1, 2, 3];
let result: Vec<String> = QueryBuilder::from(data)
.where_(|x| *x > 0)
.select(|x| format!("num_{}", x))
.collect();
assert_eq!(result, vec!["num_1", "num_2", "num_3"]);
}
#[test]
fn test_collect_to_vec_with_pagination() {
let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result: Vec<_> = QueryBuilder::from(data)
.where_(|_| true)
.skip(3)
.take(4)
.collect();
assert_eq!(result, vec![4, 5, 6, 7]);
}
#[test]
fn test_collect_to_string() {
let data = vec!['h', 'e', 'l', 'l', 'o'];
let result: String = QueryBuilder::from(data).where_(|_| true).collect();
assert_eq!(result, "hello");
}
#[test]
fn test_collect_empty_to_vec() {
let data: Vec<i32> = vec![];
let result: Vec<_> = QueryBuilder::from(data).collect();
assert_eq!(result, Vec::<i32>::new());
}
#[test]
fn test_collect_with_complex_chain() {
let data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result: Vec<_> = QueryBuilder::from(data)
.where_(|x| *x % 2 == 0)
.order_by(|x| -*x)
.skip(1)
.take(2)
.collect();
assert_eq!(result, vec![8, 6]);
}
use proptest::prelude::*;
#[cfg(test)]
mod property_tests {
use super::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_from_preserves_original_collection(
data in prop::collection::vec(any::<i32>(), 0..100)
) {
let original = data.clone();
let _query = QueryBuilder::from(data.clone());
prop_assert_eq!(data, original);
}
#[test]
fn prop_query_execution_preserves_original(
data in prop::collection::vec(any::<i32>(), 0..100)
) {
let original = data.clone();
let _result: Vec<_> = QueryBuilder::from(data.clone())
.where_(|x| x % 2 == 0)
.select(|x| x.saturating_mul(2))
.collect();
prop_assert_eq!(data, original);
}
#[test]
fn prop_complex_query_preserves_original(
data in prop::collection::vec(any::<i32>(), 0..100)
) {
let original = data.clone();
let _result: Vec<_> = QueryBuilder::from(data.clone())
.where_(|x| x % 2 == 0)
.where_(|x| *x > 10)
.take(5)
.skip(1)
.collect();
prop_assert_eq!(data, original);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_type_state_enforces_valid_query_construction(
data in prop::collection::vec(any::<i32>(), 0..100)
) {
let query = QueryBuilder::from(data.clone());
let _result: Vec<_> = query.collect();
let query = QueryBuilder::from(data.clone())
.where_(|x| x % 2 == 0);
let _result: Vec<_> = query.collect();
let query = QueryBuilder::from(data.clone())
.where_(|x| x % 2 == 0)
.where_(|x| *x > 0);
let _result: Vec<_> = query.collect();
let query = QueryBuilder::from(data.clone())
.where_(|x| x % 2 == 0)
.order_by(|x| *x);
let _result: Vec<_> = query.collect();
let query = QueryBuilder::from(data.clone())
.where_(|x| x % 2 == 0)
.select(|x| x.saturating_mul(2));
let _result: Vec<_> = query.collect();
let query = QueryBuilder::from(data.clone())
.where_(|x| x % 2 == 0)
.take(5)
.skip(2);
let _result: Vec<_> = query.collect();
let query = QueryBuilder::from(data.clone())
.where_(|x| x % 2 == 0)
.order_by(|x| *x)
.then_by(|x| -*x);
let _result: Vec<_> = query.collect();
let count = QueryBuilder::from(data.clone()).count();
prop_assert!(count <= data.len());
let _first = QueryBuilder::from(data.clone()).first();
let _last = QueryBuilder::from(data.clone()).last();
let _any_result = QueryBuilder::from(data.clone())
.any(|x| *x > 0);
let _all_result = QueryBuilder::from(data.clone())
.all(|x| *x < 1000);
let without_inspect: Vec<_> = QueryBuilder::from(data.clone())
.where_(|x| x % 2 == 0)
.collect();
let with_inspect: Vec<_> = QueryBuilder::from(data.clone())
.where_(|x| x % 2 == 0)
.inspect(|_x| {
})
.collect();
prop_assert_eq!(without_inspect, with_inspect);
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_where_all_elements_satisfy_predicate(
data in prop::collection::vec(any::<i32>(), 0..100)
) {
use std::sync::Arc;
let predicates: Vec<Arc<dyn Fn(&i32) -> bool>> = vec![
Arc::new(|x: &i32| *x % 2 == 0), Arc::new(|x: &i32| *x > 0), Arc::new(|x: &i32| *x < 50), Arc::new(|x: &i32| *x % 3 == 0), ];
for predicate in predicates {
let p = predicate.clone();
let result: Vec<_> = QueryBuilder::from(data.clone())
.where_(move |x| p(x))
.collect();
prop_assert!(result.iter().all(|x| predicate(x)));
prop_assert!(result.len() <= data.len());
}
}
}
proptest! {
#![proptest_config(ProptestConfig::with_cases(100))]
#[test]
fn prop_multiple_where_chains_correctly(
data in prop::collection::vec(any::<i32>(), 0..100)
) {
let result: Vec<_> = QueryBuilder::from(data.clone())
.where_(|x| *x % 2 == 0) .where_(|x| *x > 0) .where_(|x| *x < 100) .collect();
prop_assert!(result.iter().all(|x| *x % 2 == 0));
prop_assert!(result.iter().all(|x| *x > 0));
prop_assert!(result.iter().all(|x| *x < 100));
prop_assert!(result.len() <= data.len());
let manual_result: Vec<_> = data.iter()
.filter(|x| **x % 2 == 0)
.filter(|x| **x > 0)
.filter(|x| **x < 100)
.copied()
.collect();
prop_assert_eq!(result, manual_result);
}
#[test]
fn prop_chained_where_order_independent(
data in prop::collection::vec(any::<i32>(), 0..100)
) {
let result1: Vec<_> = QueryBuilder::from(data.clone())
.where_(|x| *x % 2 == 0)
.where_(|x| *x > 10)
.collect();
let result2: Vec<_> = QueryBuilder::from(data.clone())
.where_(|x| *x > 10)
.where_(|x| *x % 2 == 0)
.collect();
prop_assert_eq!(result1.len(), result2.len());
prop_assert!(result1.iter().all(|x| *x % 2 == 0 && *x > 10));
prop_assert!(result2.iter().all(|x| *x % 2 == 0 && *x > 10));
}
}
}
#[test]
fn test_then_by_descending_basic() {
#[derive(Debug, Clone, PartialEq)]
struct Item {
category: i32,
value: i32,
}
let data = vec![
Item {
category: 1,
value: 10,
},
Item {
category: 2,
value: 30,
},
Item {
category: 1,
value: 20,
},
Item {
category: 2,
value: 10,
},
];
let result: Vec<_> = QueryBuilder::from(data)
.where_(|_| true)
.order_by(|x| x.category)
.then_by_descending(|x| x.value)
.collect();
assert_eq!(result[0].category, 1);
assert_eq!(result[0].value, 20);
assert_eq!(result[1].category, 1);
assert_eq!(result[1].value, 10);
assert_eq!(result[2].category, 2);
assert_eq!(result[2].value, 30);
assert_eq!(result[3].category, 2);
assert_eq!(result[3].value, 10);
}
#[test]
fn test_then_by_descending_all_equal_primary() {
let data = vec![(1, 5), (1, 3), (1, 8), (1, 1)];
let result: Vec<_> = QueryBuilder::from(data)
.where_(|_| true)
.order_by(|x| x.0)
.then_by_descending(|x| x.1)
.collect();
assert_eq!(result, vec![(1, 8), (1, 5), (1, 3), (1, 1)]);
}
#[test]
fn test_then_by_descending_single_element() {
let data = vec![(42, 99)];
let result: Vec<_> = QueryBuilder::from(data)
.where_(|_| true)
.order_by(|x| x.0)
.then_by_descending(|x| x.1)
.collect();
assert_eq!(result, vec![(42, 99)]);
}
#[test]
fn test_take_while_basic() {
let data = vec![1, 2, 3, 4, 5];
let result: Vec<_> = QueryBuilder::from(data).take_while(|x| *x < 4).collect();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_take_while_all_match() {
let data = vec![1, 2, 3];
let result: Vec<_> = QueryBuilder::from(data).take_while(|x| *x < 100).collect();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_take_while_none_match() {
let data = vec![10, 20, 30];
let result: Vec<_> = QueryBuilder::from(data).take_while(|x| *x < 5).collect();
assert!(result.is_empty());
}
#[test]
fn test_take_while_empty_collection() {
let data: Vec<i32> = vec![];
let result: Vec<_> = QueryBuilder::from(data).take_while(|x| *x < 100).collect();
assert!(result.is_empty());
}
#[test]
fn test_take_while_after_filter() {
let data = vec![1, 2, 3, 4, 5, 6];
let result: Vec<_> = QueryBuilder::from(data)
.where_(|x| *x % 2 == 0)
.take_while(|x| *x < 5)
.collect();
assert_eq!(result, vec![2, 4]);
}
#[test]
fn test_take_while_after_sort() {
let data = vec![5, 1, 3, 2, 4];
let result: Vec<_> = QueryBuilder::from(data)
.where_(|_| true)
.order_by(|x| *x)
.take_while(|x| *x <= 3)
.collect();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_skip_while_basic() {
let data = vec![1, 2, 3, 4, 5];
let result: Vec<_> = QueryBuilder::from(data).skip_while(|x| *x < 3).collect();
assert_eq!(result, vec![3, 4, 5]);
}
#[test]
fn test_skip_while_all_match() {
let data = vec![1, 2, 3];
let result: Vec<_> = QueryBuilder::from(data).skip_while(|x| *x < 100).collect();
assert!(result.is_empty());
}
#[test]
fn test_skip_while_none_match() {
let data = vec![10, 20, 30];
let result: Vec<_> = QueryBuilder::from(data).skip_while(|x| *x < 5).collect();
assert_eq!(result, vec![10, 20, 30]);
}
#[test]
fn test_skip_while_empty_collection() {
let data: Vec<i32> = vec![];
let result: Vec<_> = QueryBuilder::from(data).skip_while(|x| *x < 100).collect();
assert!(result.is_empty());
}
#[test]
fn test_skip_while_after_filter() {
let data = vec![1, 2, 3, 4, 5, 6];
let result: Vec<_> = QueryBuilder::from(data)
.where_(|x| *x % 2 == 0)
.skip_while(|x| *x < 4)
.collect();
assert_eq!(result, vec![4, 6]);
}
#[test]
fn test_skip_while_after_sort() {
let data = vec![5, 1, 3, 2, 4];
let result: Vec<_> = QueryBuilder::from(data)
.where_(|_| true)
.order_by(|x| *x)
.skip_while(|x| *x <= 3)
.collect();
assert_eq!(result, vec![4, 5]);
}
#[test]
fn test_repeat_basic() {
let result: Vec<i32> = QueryBuilder::repeat(7, 4).collect();
assert_eq!(result, vec![7, 7, 7, 7]);
}
#[test]
fn test_repeat_zero_count() {
let result: Vec<i32> = QueryBuilder::repeat(42, 0).collect();
assert!(result.is_empty());
}
#[test]
fn test_repeat_count_one() {
let result: Vec<&str> = QueryBuilder::repeat("hello", 1).collect();
assert_eq!(result, vec!["hello"]);
}
#[test]
fn test_empty_collects_to_empty_vec() {
let result: Vec<i32> = QueryBuilder::empty().collect();
assert!(result.is_empty());
}
#[test]
fn test_empty_count_is_zero() {
let count: usize = QueryBuilder::<i32, _>::empty().count();
assert_eq!(count, 0);
}
#[test]
fn test_empty_chained() {
let result: Vec<i32> = QueryBuilder::empty().where_(|x| *x > 0).collect();
assert!(result.is_empty());
}
#[test]
fn test_flat_map_empty_outer() {
let data: Vec<Vec<i32>> = vec![];
let result: Vec<i32> = QueryBuilder::from(data)
.where_(|_| true)
.flat_map(|v| v)
.collect();
assert!(result.is_empty());
}
#[test]
fn test_flat_map_empty_inner() {
let data: Vec<Vec<i32>> = vec![vec![], vec![], vec![]];
let result: Vec<i32> = QueryBuilder::from(data)
.where_(|_| true)
.flat_map(|v| v)
.collect();
assert!(result.is_empty());
}
#[test]
fn test_flat_map_mixed_empty_and_nonempty() {
let data: Vec<Vec<i32>> = vec![vec![1, 2], vec![], vec![3]];
let result: Vec<i32> = QueryBuilder::from(data)
.where_(|_| true)
.flat_map(|v| v)
.collect();
assert_eq!(result, vec![1, 2, 3]);
}
#[test]
fn test_flat_map_type_transformation() {
let data = vec![1, 2, 3];
let result: Vec<String> = QueryBuilder::from(data)
.where_(|_| true)
.flat_map(|x| vec![format!("{}", x), format!("{}!", x)])
.collect();
assert_eq!(result, vec!["1", "1!", "2", "2!", "3", "3!"]);
}
#[test]
fn test_flat_map_after_filter() {
let data = vec![1, 2, 3, 4];
let result: Vec<i32> = QueryBuilder::from(data)
.where_(|x| *x % 2 == 0)
.flat_map(|x| vec![x, x * 10])
.collect();
assert_eq!(result, vec![2, 20, 4, 40]);
}
#[test]
fn test_flat_map_preserves_order() {
let data = vec![vec![3, 1], vec![4, 1, 5]];
let result: Vec<i32> = QueryBuilder::from(data)
.where_(|_| true)
.flat_map(|v| v)
.collect();
assert_eq!(result, vec![3, 1, 4, 1, 5]);
}
#[test]
fn test_contains_existing_element() {
let data = vec![1, 2, 3, 4, 5];
assert!(QueryBuilder::from(data).contains(&3));
}
#[test]
fn test_contains_missing_element() {
let data = vec![1, 2, 3];
assert!(!QueryBuilder::from(data).contains(&99));
}
#[test]
fn test_contains_empty_collection() {
let data: Vec<i32> = vec![];
assert!(!QueryBuilder::from(data).contains(&0));
}
#[test]
fn test_first_or_default_empty() {
let data: Vec<i32> = vec![];
assert_eq!(QueryBuilder::from(data).first_or_default(), 0);
}
#[test]
fn test_first_or_default_nonempty() {
let data = vec![42, 1, 2];
assert_eq!(QueryBuilder::from(data).first_or_default(), 42);
}
#[test]
fn test_last_or_default_empty() {
let data: Vec<i32> = vec![];
assert_eq!(QueryBuilder::from(data).last_or_default(), 0);
}
#[test]
fn test_last_or_default_nonempty() {
let data = vec![1, 2, 99];
assert_eq!(QueryBuilder::from(data).last_or_default(), 99);
}
#[test]
fn test_single_zero_elements() {
let data: Vec<i32> = vec![];
let result = QueryBuilder::from(data).single();
assert!(matches!(result, Err(RinqError::IteratorExhausted)));
}
#[test]
fn test_single_one_element() {
let data = vec![42];
assert_eq!(QueryBuilder::from(data).single(), Ok(42));
}
#[test]
fn test_single_multiple_elements() {
let data = vec![1, 2];
let result = QueryBuilder::from(data).single();
assert!(matches!(result, Err(RinqError::ExecutionError { .. })));
}
#[test]
fn test_single_or_default_empty() {
let data: Vec<i32> = vec![];
assert_eq!(QueryBuilder::from(data).single_or_default(), Ok(0));
}