use std::sync::Arc;
use std::time::Duration;
use camel_api::body::Body;
use camel_api::{Exchange, Message, ThrottleStrategy};
use camel_builder::{RouteBuilder, StepAccumulator};
use camel_component_direct::DirectComponent;
use camel_test::CamelTestContext;
use tower::ServiceExt;
fn test_rt() -> Arc<dyn camel_component_api::RuntimeObservability> {
Arc::new(camel_component_api::NoOpComponentContext)
}
#[tokio::test(flavor = "multi_thread")]
async fn stop_inside_throttle_halts_route_and_preserves_mutations() {
let direct = DirectComponent::new();
let h = CamelTestContext::builder()
.with_component(direct)
.build()
.await;
let route = RouteBuilder::from("direct:throttle-stop")
.route_id("test-throttle-stop")
.throttle(100, Duration::from_secs(1))
.process(|mut ex: Exchange| async move {
ex.input.body = Body::Text("throttled-mut".to_string());
Ok(ex)
})
.stop()
.end_throttle()
.build()
.unwrap();
h.add_route(route).await.unwrap();
h.start().await;
tokio::time::sleep(Duration::from_millis(50)).await;
let producer = {
let ctx = h.ctx().lock().await;
let producer_ctx = ctx.producer_context();
let registry = ctx.registry();
let component = registry.get("direct").unwrap();
let endpoint = component
.create_endpoint("direct:throttle-stop", &*ctx)
.unwrap();
endpoint.create_producer(test_rt(), &producer_ctx).unwrap()
};
let ex = Exchange::new(Message::new("trigger"));
let result_ex = producer.oneshot(ex).await.unwrap();
assert_eq!(
result_ex.input.body.as_text(),
Some("throttled-mut"),
"BUG: throttle Stop must preserve body mutation (got: {:?})",
result_ex.input.body
);
h.stop().await;
}
#[tokio::test(flavor = "multi_thread")]
async fn throttle_drop_stops_exchange_before_sink() {
let direct = DirectComponent::new();
let h = CamelTestContext::builder()
.with_component(direct)
.with_mock()
.build()
.await;
let route = RouteBuilder::from("direct:throttle-drop")
.route_id("test-throttle-drop")
.throttle(1, Duration::from_secs(1))
.strategy(ThrottleStrategy::Drop)
.to("mock:sink")
.end_throttle()
.build()
.unwrap();
h.add_route(route).await.unwrap();
h.start().await;
tokio::time::sleep(Duration::from_millis(50)).await;
let producer = {
let ctx = h.ctx().lock().await;
let producer_ctx = ctx.producer_context();
let registry = ctx.registry();
let component = registry.get("direct").unwrap();
let endpoint = component
.create_endpoint("direct:throttle-drop", &*ctx)
.unwrap();
endpoint.create_producer(test_rt(), &producer_ctx).unwrap()
};
let ex_pass = Exchange::new(Message::new("passes"));
let _ = producer.clone().oneshot(ex_pass).await.unwrap();
let ex_drop = Exchange::new(Message::new("dropped"));
let result = producer.clone().oneshot(ex_drop).await;
assert!(result.is_ok(), "dropped exchange must return Ok");
if let Some(sink) = h.mock().get_endpoint("sink") {
let received = sink.get_received_exchanges().await;
assert_eq!(
received.len(),
1,
"mock:sink should receive exactly 1 exchange (the passing one); the dropped exchange must not arrive"
);
}
h.stop().await;
}