use fluxion_core::{FluxionError, StreamItem};
use fluxion_stream::ScanOrderedExt;
use fluxion_test_utils::{
assert_stream_ended,
helpers::unwrap_stream,
test_channel_with_errors,
test_data::{person_alice, person_bob, person_charlie, TestData},
Sequenced,
};
#[tokio::test]
async fn test_scan_ordered_propagates_errors() -> anyhow::Result<()> {
let (tx, stream) = test_channel_with_errors::<Sequenced<TestData>>();
let mut result = stream.scan_ordered(0, |count, _| {
*count += 1;
*count
});
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_alice())))?;
assert!(matches!(
unwrap_stream::<Sequenced<i32>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == 1
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Test error")))?;
assert!(matches!(
unwrap_stream(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_bob())))?;
assert!(matches!(
unwrap_stream::<Sequenced<i32>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == 2
));
drop(tx);
assert_stream_ended(&mut result, 100).await;
Ok(())
}
#[tokio::test]
async fn test_scan_ordered_error_at_start() -> anyhow::Result<()> {
let (tx, stream) = test_channel_with_errors::<Sequenced<TestData>>();
let mut result = stream.scan_ordered(100, |count, _| {
*count += 1;
*count
});
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error(
"Initial error",
)))?;
assert!(matches!(
unwrap_stream::<Sequenced<i32>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_alice())))?;
assert!(matches!(
unwrap_stream::<Sequenced<i32>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == 101
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_bob())))?;
assert!(matches!(
unwrap_stream::<Sequenced<i32>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == 102
));
drop(tx);
assert_stream_ended(&mut result, 100).await;
Ok(())
}
#[tokio::test]
async fn test_scan_ordered_multiple_consecutive_errors() -> anyhow::Result<()> {
let (tx, stream) = test_channel_with_errors::<Sequenced<TestData>>();
let mut result = stream.scan_ordered(Vec::<String>::new(), |names, data| {
if let TestData::Person(p) = data {
names.push(p.name.clone());
}
names.clone()
});
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_alice())))?;
assert!(matches!(
unwrap_stream::<Sequenced<Vec<String>>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == vec!["Alice"]
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error 1")))?;
assert!(matches!(
unwrap_stream::<Sequenced<Vec<String>>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error 2")))?;
assert!(matches!(
unwrap_stream::<Sequenced<Vec<String>>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error 3")))?;
assert!(matches!(
unwrap_stream::<Sequenced<Vec<String>>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_bob())))?;
assert!(matches!(
unwrap_stream::<Sequenced<Vec<String>>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == vec!["Alice", "Bob"]
));
drop(tx);
assert_stream_ended(&mut result, 100).await;
Ok(())
}
#[tokio::test]
async fn test_scan_ordered_error_between_values() -> anyhow::Result<()> {
let (tx, stream) = test_channel_with_errors::<Sequenced<TestData>>();
let mut result = stream.scan_ordered(Vec::<TestData>::new(), |list, data| {
list.push(data.clone());
list.clone()
});
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_alice())))?;
assert!(matches!(
unwrap_stream::<Sequenced<Vec<TestData>>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == vec![person_alice()]
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error")))?;
assert!(matches!(
unwrap_stream::<Sequenced<Vec<TestData>>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_bob())))?;
assert!(matches!(
unwrap_stream::<Sequenced<Vec<TestData>>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == vec![person_alice(), person_bob()]
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error 2")))?;
assert!(matches!(
unwrap_stream::<Sequenced<Vec<TestData>>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_charlie())))?;
assert!(matches!(
unwrap_stream::<Sequenced<Vec<TestData>>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == vec![person_alice(), person_bob(), person_charlie()]
));
drop(tx);
assert_stream_ended(&mut result, 100).await;
Ok(())
}
#[tokio::test]
async fn test_scan_ordered_error_doesnt_reset_complex_state() -> anyhow::Result<()> {
let (tx, stream) = test_channel_with_errors::<Sequenced<TestData>>();
let mut result = stream.scan_ordered(
(u32::MAX, u32::MIN),
|state: &mut (u32, u32), data: &TestData| {
if let TestData::Person(p) = data {
state.0 = state.0.min(p.age);
state.1 = state.1.max(p.age);
}
*state
},
);
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_alice())))?;
assert!(matches!(
unwrap_stream::<Sequenced<(u32, u32)>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == (25, 25)
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_bob())))?;
assert!(matches!(
unwrap_stream::<Sequenced<(u32, u32)>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == (25, 30)
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error")))?;
assert!(matches!(
unwrap_stream::<Sequenced<(u32, u32)>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_charlie())))?;
assert!(matches!(
unwrap_stream::<Sequenced<(u32, u32)>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == (25, 35)
));
drop(tx);
assert_stream_ended(&mut result, 100).await;
Ok(())
}
#[tokio::test]
async fn test_scan_ordered_only_errors() -> anyhow::Result<()> {
let (tx, stream) = test_channel_with_errors::<Sequenced<TestData>>();
let mut result = stream.scan_ordered(0, |count, _| {
*count += 1;
*count
});
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error 1")))?;
assert!(matches!(
unwrap_stream::<Sequenced<i32>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error 2")))?;
assert!(matches!(
unwrap_stream::<Sequenced<i32>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error 3")))?;
assert!(matches!(
unwrap_stream::<Sequenced<i32>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
drop(tx);
assert_stream_ended(&mut result, 100).await;
Ok(())
}
#[tokio::test]
async fn test_scan_ordered_error_with_type_transformation() -> anyhow::Result<()> {
let (tx, stream) = test_channel_with_errors::<Sequenced<TestData>>();
let mut result = stream.scan_ordered(0, |count: &mut i32, data: &TestData| {
*count += 1;
format!("Item #{}: {}", count, data)
});
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_alice())))?;
assert!(matches!(
unwrap_stream::<Sequenced<String>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == "Item #1: Person[name=Alice, age=25]"
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error")))?;
assert!(matches!(
unwrap_stream::<Sequenced<String>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_bob())))?;
assert!(matches!(
unwrap_stream::<Sequenced<String>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == "Item #2: Person[name=Bob, age=30]"
));
drop(tx);
assert_stream_ended(&mut result, 100).await;
Ok(())
}
#[tokio::test]
async fn test_scan_ordered_alternating_values_and_errors() -> anyhow::Result<()> {
let (tx, stream) = test_channel_with_errors::<Sequenced<TestData>>();
let mut result = stream.scan_ordered(0u32, |total, data| {
if let TestData::Person(p) = data {
*total += p.age;
}
*total
});
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_alice())))?; assert!(matches!(
unwrap_stream::<Sequenced<u32>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == 25
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error 1")))?;
assert!(matches!(
unwrap_stream::<Sequenced<u32>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_bob())))?; assert!(matches!(
unwrap_stream::<Sequenced<u32>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == 55
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error 2")))?;
assert!(matches!(
unwrap_stream::<Sequenced<u32>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_charlie())))?; assert!(matches!(
unwrap_stream::<Sequenced<u32>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == 90
));
drop(tx);
assert_stream_ended(&mut result, 100).await;
Ok(())
}
#[tokio::test]
async fn test_scan_ordered_error_with_string_accumulation() -> anyhow::Result<()> {
let (tx, stream) = test_channel_with_errors::<Sequenced<TestData>>();
let mut result = stream.scan_ordered(String::new(), |acc, data| {
if !acc.is_empty() {
acc.push_str(", ");
}
acc.push_str(&data.to_string());
acc.clone()
});
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_alice())))?;
assert!(matches!(
unwrap_stream::<Sequenced<String>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == "Person[name=Alice, age=25]"
));
tx.unbounded_send(StreamItem::Error(FluxionError::stream_error("Error")))?;
assert!(matches!(
unwrap_stream::<Sequenced<String>, _>(&mut result, 100).await,
StreamItem::Error(_)
));
tx.unbounded_send(StreamItem::Value(Sequenced::new(person_bob())))?;
assert!(matches!(
unwrap_stream::<Sequenced<String>, _>(&mut result, 100).await,
StreamItem::Value(ref v) if v.value == "Person[name=Alice, age=25], Person[name=Bob, age=30]"
));
drop(tx);
assert_stream_ended(&mut result, 100).await;
Ok(())
}