use crate::common::TestDatabaseManager;
use anyhow::Result;
use codex_memory::models::{SearchParams, SearchStrategy};
use codex_memory::storage::Storage;
use serial_test::serial;
#[tokio::test]
#[serial]
async fn test_search_fallback_when_no_embeddings() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Storage::new(pool);
let _id1 = storage
.store(
"Rust programming language tutorial",
"Programming tutorial context".to_string(),
"Basic Rust programming concepts".to_string(),
Some(vec!["rust".to_string(), "programming".to_string()]),
)
.await?;
let _id2 = storage
.store(
"Python machine learning guide",
"ML guide context".to_string(),
"Introduction to Python ML".to_string(),
Some(vec!["python".to_string(), "ml".to_string()]),
)
.await?;
let search_params = SearchParams {
query: "programming tutorial".to_string(),
use_tag_embedding: false,
use_content_embedding: false,
similarity_threshold: 0.1,
max_results: 10,
..Default::default()
};
let results = storage.search_memories(search_params).await?;
assert!(
!results.is_empty(),
"Should find results with fallback search"
);
for i in 1..results.len() {
assert!(
results[i - 1].combined_score >= results[i].combined_score,
"Results should be sorted by descending score"
);
}
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_search_with_tag_filter() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Storage::new(pool);
let _id1 = storage
.store(
"Rust web development",
"Web dev context".to_string(),
"Building web apps with Rust".to_string(),
Some(vec!["rust".to_string(), "web".to_string()]),
)
.await?;
let _id2 = storage
.store(
"Python data science",
"Data science context".to_string(),
"Data analysis with Python".to_string(),
Some(vec!["python".to_string(), "data".to_string()]),
)
.await?;
let _id3 = storage
.store(
"Rust system programming",
"Systems context".to_string(),
"Low-level programming in Rust".to_string(),
Some(vec!["rust".to_string(), "systems".to_string()]),
)
.await?;
let search_params = SearchParams {
query: "programming".to_string(),
tag_filter: Some(vec!["rust".to_string()]),
use_tag_embedding: false,
use_content_embedding: false,
similarity_threshold: 0.1,
..Default::default()
};
let results = storage.search_memories(search_params).await?;
for result in &results {
assert!(
result.memory.tags.contains(&"rust".to_string()),
"All results should contain the filtered tag 'rust'"
);
}
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_search_similarity_threshold() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Storage::new(pool);
let _id = storage
.store(
"Machine learning algorithms",
"AI context".to_string(),
"Overview of ML algorithms".to_string(),
Some(vec!["ml".to_string(), "algorithms".to_string()]),
)
.await?;
let high_threshold_params = SearchParams {
query: "machine learning".to_string(),
similarity_threshold: 0.9,
use_tag_embedding: false,
use_content_embedding: false,
..Default::default()
};
let high_results = storage.search_memories(high_threshold_params).await?;
let low_threshold_params = SearchParams {
query: "machine learning".to_string(),
similarity_threshold: 0.1,
use_tag_embedding: false,
use_content_embedding: false,
..Default::default()
};
let low_results = storage.search_memories(low_threshold_params).await?;
assert!(
low_results.len() >= high_results.len(),
"Lower threshold should return more results: {} vs {}",
low_results.len(),
high_results.len()
);
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_search_max_results_limit() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Storage::new(pool);
for i in 1..=20 {
storage
.store(
&format!("Test content number {}", i),
format!("Test context {}", i),
format!("Test summary {}", i),
Some(vec!["test".to_string()]),
)
.await?;
}
let search_params = SearchParams {
query: "test content".to_string(),
max_results: 5,
similarity_threshold: 0.1,
use_tag_embedding: false,
use_content_embedding: false,
..Default::default()
};
let results = storage.search_memories(search_params).await?;
assert_eq!(
results.len(),
5,
"Should respect max_results limit: got {} results",
results.len()
);
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_search_strategy_options() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Storage::new(pool);
let _id = storage
.store(
"Artificial intelligence research",
"AI research context".to_string(),
"Current AI research trends".to_string(),
Some(vec!["ai".to_string(), "research".to_string()]),
)
.await?;
let strategies = vec![
SearchStrategy::TagsFirst,
SearchStrategy::ContentFirst,
SearchStrategy::Hybrid,
];
for strategy in strategies {
let search_params = SearchParams {
query: "artificial intelligence".to_string(),
search_strategy: strategy,
similarity_threshold: 0.1,
use_tag_embedding: false,
use_content_embedding: false,
..Default::default()
};
let results = storage.search_memories(search_params).await;
assert!(results.is_ok(), "Search should succeed for all strategies");
}
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_search_scoring_weights() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Storage::new(pool);
let _id = storage
.store(
"Deep learning neural networks",
"Neural network context".to_string(),
"Introduction to deep learning".to_string(),
Some(vec![
"deep-learning".to_string(),
"neural-networks".to_string(),
]),
)
.await?;
let weight_configs = vec![
(1.0, 0.0), (0.0, 1.0), (0.5, 0.5), (0.3, 0.7), ];
for (tag_weight, content_weight) in weight_configs {
let search_params = SearchParams {
query: "deep learning".to_string(),
tag_weight,
content_weight,
similarity_threshold: 0.1,
use_tag_embedding: false,
use_content_embedding: false,
..Default::default()
};
let results = storage.search_memories(search_params).await?;
if !results.is_empty() {
assert!(
results[0].combined_score >= 0.0,
"Combined score should be non-negative"
);
}
}
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_search_recency_boost() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Storage::new(pool);
let _id1 = storage
.store(
"Quantum computing basics",
"Quantum context".to_string(),
"Introduction to quantum computing".to_string(),
Some(vec!["quantum".to_string(), "computing".to_string()]),
)
.await?;
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
let _id2 = storage
.store(
"Advanced quantum algorithms",
"Advanced quantum context".to_string(),
"Complex quantum computing algorithms".to_string(),
Some(vec!["quantum".to_string(), "algorithms".to_string()]),
)
.await?;
let normal_search = SearchParams {
query: "quantum computing".to_string(),
boost_recent: false,
similarity_threshold: 0.1,
use_tag_embedding: false,
use_content_embedding: false,
..Default::default()
};
let normal_results = storage.search_memories(normal_search).await?;
let boosted_search = SearchParams {
query: "quantum computing".to_string(),
boost_recent: true,
similarity_threshold: 0.1,
use_tag_embedding: false,
use_content_embedding: false,
..Default::default()
};
let boosted_results = storage.search_memories(boosted_search).await?;
assert!(
!normal_results.is_empty(),
"Normal search should find results"
);
assert!(
!boosted_results.is_empty(),
"Boosted search should find results"
);
for result in &boosted_results {
assert!(
result.combined_score >= 0.0,
"Boosted scores should be non-negative"
);
}
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_search_empty_query() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Storage::new(pool);
let search_params = SearchParams {
query: "".to_string(),
..Default::default()
};
let results = storage.search_memories(search_params).await?;
assert!(
results.is_empty() || !results.is_empty(),
"Search with empty query should handle gracefully"
);
manager.cleanup().await?;
Ok(())
}
#[tokio::test]
#[serial]
async fn test_search_no_matches() -> Result<()> {
let mut manager = TestDatabaseManager::new()?;
let pool = manager.setup_test_database().await?;
let storage = Storage::new(pool);
let _id = storage
.store(
"Blockchain technology overview",
"Blockchain context".to_string(),
"Understanding blockchain fundamentals".to_string(),
Some(vec!["blockchain".to_string(), "crypto".to_string()]),
)
.await?;
let search_params = SearchParams {
query: "cooking recipes italian pasta".to_string(),
similarity_threshold: 0.7, use_tag_embedding: false,
use_content_embedding: false,
..Default::default()
};
let results = storage.search_memories(search_params).await?;
assert!(
results.is_empty() || results.len() < 2,
"Should return few or no results for unrelated search"
);
manager.cleanup().await?;
Ok(())
}