use std::sync::{Arc, Mutex};
use camel_api::{AsyncHealthCheck, HealthStatus};
use camel_component_api::test_support::NoopRuntimeObservability;
use camel_component_api::{Component, ComponentContext, NoOpComponentContext};
use crate::{HttpComponent, HttpsComponent};
#[derive(Default)]
struct RecordingContext {
checks: Mutex<Vec<Arc<dyn AsyncHealthCheck>>>,
}
impl ComponentContext for RecordingContext {
fn resolve_component(&self, _scheme: &str) -> Option<Arc<dyn Component>> {
None
}
fn resolve_language(&self, name: &str) -> Option<Arc<dyn camel_language_api::Language>> {
NoOpComponentContext.resolve_language(name)
}
fn metrics(&self) -> Arc<dyn camel_api::MetricsCollector> {
Arc::new(camel_api::NoOpMetrics)
}
fn platform_service(&self) -> Arc<dyn camel_api::PlatformService> {
Arc::new(camel_api::NoopPlatformService::default())
}
fn register_route_health_check(&self, _route_id: &str, _check: Arc<dyn AsyncHealthCheck>) {}
fn unregister_route_health_check(&self, _route_id: &str) {}
fn register_current_route_health_check(&self, check: Arc<dyn AsyncHealthCheck>) {
self.checks.lock().expect("recording lock").push(check); }
}
async fn closed_port() -> u16 {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("bind"); listener.local_addr().expect("addr").port() }
async fn assert_gate_follows_consumer(
component: &dyn Component,
scheme: &str,
consumer_uri_extra: &str,
) {
let port = closed_port().await;
let uri = format!("{scheme}://127.0.0.1:{port}/x{consumer_uri_extra}");
let producer_ctx = RecordingContext::default();
let _producer_endpoint = component
.create_endpoint(&uri, &producer_ctx)
.expect("endpoint builds"); let producer_check = producer_ctx.checks.lock().expect("lock").remove(0); assert_eq!(
producer_check.check().await.status,
HealthStatus::Healthy,
"a producer-only endpoint must not probe its remote target"
);
let consumer_ctx = RecordingContext::default();
let consumer_endpoint = component
.create_endpoint(&uri, &consumer_ctx)
.expect("endpoint builds"); let consumer_check = consumer_ctx.checks.lock().expect("lock").remove(0); assert_eq!(
consumer_check.check().await.status,
HealthStatus::Healthy,
"inert until the consumer exists"
);
let _consumer = consumer_endpoint
.create_consumer(Arc::new(NoopRuntimeObservability))
.expect("consumer builds"); assert_eq!(
consumer_check.check().await.status,
HealthStatus::Unhealthy,
"a consumer whose own listener is down must still fail readiness"
);
}
#[tokio::test]
async fn http_listener_check_is_inert_for_producers_only() {
assert_gate_follows_consumer(&HttpComponent::new(), "http", "").await;
}
#[tokio::test]
async fn https_listener_check_is_inert_for_producers_only() {
assert_gate_follows_consumer(
&HttpsComponent::new(),
"https",
"?tlsCert=/x.pem&tlsKey=/y.pem",
)
.await;
}