use std::sync::Arc;
use camel_api::{BoxProcessor, CamelError, Exchange, Message};
use camel_component_direct::DirectComponent;
use camel_component_mock::MockComponent;
use camel_component_seda::SedaComponent;
use camel_core::intercept::{InterceptAction, InterceptRule, InterceptRules};
use camel_core::route::BuilderStep;
use camel_core::{CamelContext, RouteDefinition};
use tower::ServiceExt;
pub(crate) fn test_rt() -> Arc<dyn camel_component_api::RuntimeObservability> {
Arc::new(camel_component_api::NoOpComponentContext)
}
pub(crate) fn skip_to_mock_z() -> InterceptRules {
InterceptRules::new(vec![InterceptRule {
uri: "seda:out".into(),
action: InterceptAction::SkipTo {
uri: "mock:z".into(),
},
}])
.expect("valid mock targets")
}
pub(crate) async fn boot_context_with_intercept(
rules: Option<InterceptRules>,
) -> (CamelContext, MockComponent) {
let mut builder = CamelContext::builder();
if let Some(rules) = rules {
builder = builder.with_intercept_rules(rules);
}
let mut ctx = builder.build().await.expect("build context");
let mock = MockComponent::new();
ctx.register_component(mock.clone());
ctx.register_component(DirectComponent::new());
ctx.register_component(SedaComponent::new());
(ctx, mock)
}
pub(crate) async fn boot_context() -> (CamelContext, MockComponent) {
boot_context_with_intercept(None).await
}
pub(crate) const TEST_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
fn is_seda_no_active_consumers(err: &CamelError) -> bool {
matches!(err, CamelError::EndpointCreationFailed(msg) if msg.contains("has no active consumers"))
}
pub(crate) async fn send_awaiting_consumers<F, Fut>(what: &str, attempt: F) -> Exchange
where
F: Fn() -> Fut,
Fut: std::future::Future<Output = Result<Exchange, CamelError>>,
{
let deadline = tokio::time::Instant::now() + TEST_TIMEOUT;
loop {
match attempt().await {
Err(err) if is_seda_no_active_consumers(&err) => {
assert!(
tokio::time::Instant::now() < deadline,
"SEDA consumers did not activate within {TEST_TIMEOUT:?}: {err}"
);
tokio::time::sleep(std::time::Duration::from_millis(5)).await;
}
result => {
return result.unwrap_or_else(|err| panic!("{what} should succeed: {err}"));
}
}
}
}
pub(crate) async fn send_to_direct_result(
ctx: &CamelContext,
endpoint_uri: &str,
exchange: Exchange,
) -> Result<Exchange, CamelError> {
let component = ctx
.registry()
.get("direct")
.expect("direct component not registered");
let producer_ctx = ctx.producer_context();
let endpoint = component
.create_endpoint(endpoint_uri, ctx)
.expect("failed to create direct endpoint");
let producer = endpoint
.create_producer(test_rt(), &producer_ctx)
.expect("failed to create direct producer");
producer.oneshot(exchange).await
}
pub(crate) async fn send_to_direct(
ctx: &CamelContext,
endpoint_uri: &str,
exchange: Exchange,
) -> Exchange {
send_to_direct_result(ctx, endpoint_uri, exchange)
.await
.expect("direct call should succeed")
}
pub(crate) fn raw_seda_producer(ctx: &CamelContext, endpoint_uri: &str) -> BoxProcessor {
let component = ctx
.registry()
.get("seda")
.expect("seda component not registered");
let producer_ctx = ctx.producer_context();
let endpoint = component
.create_endpoint(endpoint_uri, ctx)
.expect("failed to create seda endpoint");
endpoint
.create_producer(test_rt(), &producer_ctx)
.expect("failed to create seda producer")
}
pub(crate) async fn probe_seda_until_active(
ctx: &CamelContext,
endpoint_uri: &str,
probe_body: &str,
) {
let producer = raw_seda_producer(ctx, endpoint_uri);
send_awaiting_consumers("seda readiness probe", || {
producer
.clone()
.oneshot(Exchange::new(Message::new(probe_body.to_string())))
})
.await;
}
pub(crate) fn direct_to_mock_route() -> RouteDefinition {
RouteDefinition::new("direct:in", vec![BuilderStep::To("mock:out".into())])
.with_route_id("freeze-after-add")
}