diff --git a/.github/workflows/pr_validation.yaml b/.github/workflows/pr_validation.yaml index c0a473d..21679b1 100644 --- a/.github/workflows/pr_validation.yaml +++ b/.github/workflows/pr_validation.yaml @@ -83,6 +83,7 @@ jobs: - 6333:6333 - 6334:6334 env: + REQUIRE_QDRANT_TESTS: 1 QDRANT_CONNECTION_STRING: ${{ vars.QDRANT_CONNECTION_STRING }} OXIGRAPH_PATH: ./data/oxigraph OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} diff --git a/tests/initialization_tests.rs b/tests/initialization_tests.rs index 41f4368..7ddc89c 100644 --- a/tests/initialization_tests.rs +++ b/tests/initialization_tests.rs @@ -2,14 +2,17 @@ use character_memory::{CustomError, VectorDatabaseError}; #[path = "support/mod.rs"] pub mod test_support; -use test_support::{cleanup_collection, is_qdrant_unavailable_error, try_setup_character_memory}; +use test_support::{ + cleanup_collection, is_qdrant_unavailable_error, should_skip_qdrant_unavailable, + try_setup_character_memory, +}; #[tokio::test] async fn test_character_memory_initialization() { // Setup let (_character_memory, collection_name) = match try_setup_character_memory().await { Ok(setup) => setup, - Err(CustomError::VectorDatabaseError(error)) if is_qdrant_unavailable_error(&error) => { + Err(CustomError::VectorDatabaseError(error)) if should_skip_qdrant_unavailable(&error) => { println!("skipping live initialization test because Qdrant is unavailable: {error}"); return; } diff --git a/tests/public_facade_tests.rs b/tests/public_facade_tests.rs index dbe0715..44925cb 100644 --- a/tests/public_facade_tests.rs +++ b/tests/public_facade_tests.rs @@ -13,7 +13,7 @@ async fn public_remember_and_retrieve_use_graph_authoritative_path() { let (memory, collection_name) = match test_support::try_setup_character_memory().await { Ok(setup) => setup, Err(CustomError::VectorDatabaseError(error)) - if test_support::is_qdrant_unavailable_error(&error) => + if test_support::should_skip_qdrant_unavailable(&error) => { println!("skipping live public facade test because Qdrant is unavailable: {error}"); return; @@ -106,7 +106,7 @@ async fn public_correct_and_forget_hide_stale_memories_from_normal_retrieval() { let (memory, collection_name) = match test_support::try_setup_character_memory().await { Ok(setup) => setup, Err(CustomError::VectorDatabaseError(error)) - if test_support::is_qdrant_unavailable_error(&error) => + if test_support::should_skip_qdrant_unavailable(&error) => { println!( "skipping live public lifecycle facade test because Qdrant is unavailable: {error}" diff --git a/tests/retrieval_guardrails_tests.rs b/tests/retrieval_guardrails_tests.rs index eb9ae97..30295d4 100644 --- a/tests/retrieval_guardrails_tests.rs +++ b/tests/retrieval_guardrails_tests.rs @@ -23,7 +23,7 @@ async fn stats_persist_across_facade_reopen() { let memory = match setup(&collection_name, &fixture, None).await { Ok(memory) => memory, Err(CustomError::VectorDatabaseError(error)) - if test_support::is_qdrant_unavailable_error(&error) => + if test_support::should_skip_qdrant_unavailable(&error) => { println!("skipping stats persistence test because Qdrant is unavailable: {error}"); return; @@ -126,7 +126,7 @@ async fn restart_safe_retrieval_excludes_suppressed_and_superseded_memories() { let memory = match setup(&collection_name, &fixture, None).await { Ok(memory) => memory, Err(CustomError::VectorDatabaseError(error)) - if test_support::is_qdrant_unavailable_error(&error) => + if test_support::should_skip_qdrant_unavailable(&error) => { println!("skipping restart-safe retrieval test because Qdrant is unavailable: {error}"); return; @@ -259,7 +259,7 @@ async fn selectivity_telemetry_and_fanout_override_bound_entity_root_expansion() let memory = match setup(&collection_name, &fixture, None).await { Ok(memory) => memory, Err(CustomError::VectorDatabaseError(error)) - if test_support::is_qdrant_unavailable_error(&error) => + if test_support::should_skip_qdrant_unavailable(&error) => { println!("skipping selectivity fanout test because Qdrant is unavailable: {error}"); return; diff --git a/tests/support/base.rs b/tests/support/base.rs index 1b8c59f..a50c294 100644 --- a/tests/support/base.rs +++ b/tests/support/base.rs @@ -156,6 +156,14 @@ pub fn is_qdrant_unavailable_error(error: &VectorDatabaseError) -> bool { ) } +pub fn should_skip_qdrant_unavailable(error: &VectorDatabaseError) -> bool { + let unavailable = is_qdrant_unavailable_error(error); + if unavailable && std::env::var_os("REQUIRE_QDRANT_TESTS").is_some() { + panic!("Qdrant is required for this test but is unavailable: {error}"); + } + unavailable +} + pub async fn cleanup_collection(collection_name: &str) { let settings = load_test_settings().expect("Failed to load settings from environment"); diff --git a/tests/support/mod.rs b/tests/support/mod.rs index 642554e..e9810c4 100644 --- a/tests/support/mod.rs +++ b/tests/support/mod.rs @@ -4,7 +4,7 @@ mod persistent; pub use base::{ cleanup_collection, config_error, is_qdrant_unavailable_error, load_test_settings, - unique_collection_name, DeterministicEmbeddingProvider, + should_skip_qdrant_unavailable, unique_collection_name, DeterministicEmbeddingProvider, }; pub use basic::try_setup_character_memory; pub use persistent::try_setup_persistent_character_memory; diff --git a/tests/write_planning_tests.rs b/tests/write_planning_tests.rs index 70e231b..a35c7bf 100644 --- a/tests/write_planning_tests.rs +++ b/tests/write_planning_tests.rs @@ -768,17 +768,11 @@ async fn setup_basic() -> Option<(CharacterMemory, String)> { match try_setup_in_memory_character_memory().await { Ok(fixture) => Some(fixture), Err(CustomError::VectorDatabaseError(error)) - if base::is_qdrant_unavailable_error(&error) => + if base::should_skip_qdrant_unavailable(&error) => { println!("skipping write-planning test because Qdrant is unavailable: {error}"); None } - Err(error) if is_qdrant_timeout_signature(&error) => { - println!( - "skipping write-planning test because local Qdrant gRPC mutation stalled: {error}" - ); - None - } Err(error) => panic!("unexpected basic setup failure: {error}"), } } @@ -796,17 +790,13 @@ async fn setup_persistent( { Ok(memory) => Some(memory), Err(CustomError::VectorDatabaseError(error)) - if base::is_qdrant_unavailable_error(&error) => + if base::should_skip_qdrant_unavailable(&error) => { println!( "skipping persistent write-planning test because Qdrant is unavailable: {error}" ); None } - Err(error) if is_qdrant_timeout_signature(&error) => { - println!("skipping persistent write-planning test because local Qdrant gRPC mutation stalled: {error}"); - None - } Err(error) => panic!("unexpected persistent setup failure: {error}"), } } @@ -1173,11 +1163,6 @@ fn graph_only_commit_options() -> CommitOptions { } } -fn is_qdrant_timeout_signature(error: &CustomError) -> bool { - let message = error.to_string(); - message.contains("Vector database error") && message.contains("Timeout expired") -} - fn has_candidate_kind( plan: &RememberWritePlan, predicate: impl Fn(&MemoryCandidate) -> bool,