mod mock_source;
use anyhow::Result;
use async_trait::async_trait;
use drasi_lib::bootstrap::{
BootstrapContext, BootstrapProvider, BootstrapRequest, BootstrapResult,
};
use drasi_lib::channels::{BootstrapEventSender, ComponentStatus};
use drasi_lib::config::SourceSubscriptionSettings;
use drasi_lib::{DrasiLib, Query, Source};
use mock_source::MockSource;
use std::sync::Arc;
use std::time::Duration;
struct FailingBootstrapProvider;
#[async_trait]
impl BootstrapProvider for FailingBootstrapProvider {
async fn bootstrap(
&self,
_request: BootstrapRequest,
_context: &BootstrapContext,
_event_tx: BootstrapEventSender,
_settings: Option<&SourceSubscriptionSettings>,
) -> Result<BootstrapResult> {
Err(anyhow::anyhow!("simulated bootstrap failure"))
}
}
async fn wait_for_error_status(core: &DrasiLib, query_id: &str) -> Result<ComponentStatus> {
let deadline = tokio::time::Instant::now() + Duration::from_secs(5);
let mut status = core.get_query_status(query_id).await?;
while tokio::time::Instant::now() < deadline {
if status == ComponentStatus::Error {
break;
}
tokio::time::sleep(Duration::from_millis(50)).await;
status = core.get_query_status(query_id).await?;
}
Ok(status)
}
#[tokio::test]
async fn query_enters_error_state_when_bootstrap_fails() -> Result<()> {
let (mock_source, _handle) = MockSource::new("test-source")?;
mock_source
.set_bootstrap_provider(Box::new(FailingBootstrapProvider))
.await;
let query = Query::cypher("q1")
.query("MATCH (p:Person) RETURN p.name AS name")
.from_source("test-source")
.auto_start(true)
.build();
let core = Arc::new(
DrasiLib::builder()
.with_id("bootstrap-failure-test")
.with_source(mock_source)
.with_query(query)
.build()
.await?,
);
core.start().await?;
let status = wait_for_error_status(&core, "q1").await?;
assert_eq!(
status,
ComponentStatus::Error,
"Query should transition to Error state when bootstrap fails, got {status:?}"
);
Ok(())
}
#[tokio::test]
async fn query_enters_error_state_when_multiple_bootstraps_fail() -> Result<()> {
let (source_a, _handle_a) = MockSource::new("source-a")?;
source_a
.set_bootstrap_provider(Box::new(FailingBootstrapProvider))
.await;
let (source_b, _handle_b) = MockSource::new("source-b")?;
source_b
.set_bootstrap_provider(Box::new(FailingBootstrapProvider))
.await;
let query = Query::cypher("q-multi")
.query("MATCH (p:Person) RETURN p.name AS name")
.from_source("source-a")
.from_source("source-b")
.auto_start(true)
.build();
let core = Arc::new(
DrasiLib::builder()
.with_id("bootstrap-multi-failure-test")
.with_source(source_a)
.with_source(source_b)
.with_query(query)
.build()
.await?,
);
core.start().await?;
let status = wait_for_error_status(&core, "q-multi").await?;
assert_eq!(
status,
ComponentStatus::Error,
"Query should transition to Error state when multiple bootstraps fail, got {status:?}"
);
Ok(())
}