use super::*;
#[test]
fn test_cascade_config_default() {
let config = CascadeConfig::default();
assert_eq!(config.top_k, 10);
assert!(config.bm25_threshold > 0.0);
assert!(config.hdc_threshold > 0.0);
assert!(config.concept_graph_threshold > 0.0);
assert!(config.merge_results);
assert_eq!(config.min_results, 3);
assert!(config.enable_concept_expansion);
}
#[test]
fn test_cascade_retriever_creation() {
let config = CascadeConfig::default();
let retriever = CascadeRetriever::new(config);
assert_eq!(retriever.config().top_k, 10);
assert!(retriever.is_empty());
}
#[test]
fn test_default_config_creation() {
let retriever = CascadeRetriever::default_config();
assert_eq!(retriever.config().top_k, 10);
}
#[test]
fn test_add_episode() {
let mut retriever = CascadeRetriever::default_config();
retriever.add_episode("ep-1", "Implement authentication in Rust using JWT tokens");
assert_eq!(retriever.len(), 1);
assert!(!retriever.is_empty());
retriever.add_episode("ep-2", "Fix bug in database connection pool");
assert_eq!(retriever.len(), 2);
}
#[test]
fn test_clear_episodes() {
let mut retriever = CascadeRetriever::default_config();
retriever.add_episode("ep-1", "Test episode 1");
retriever.add_episode("ep-2", "Test episode 2");
assert_eq!(retriever.len(), 2);
retriever.clear();
assert_eq!(retriever.len(), 0);
assert!(retriever.is_empty());
}
#[test]
fn test_placeholder_retrieve_without_csm() {
#[cfg(not(feature = "csm"))]
{
let mut retriever = CascadeRetriever::default_config();
retriever.add_episode("ep-1", "Test episode");
let result = retriever.retrieve("test query");
assert!(result.episode_ids.is_empty());
assert!(result.scores.is_empty());
assert_eq!(result.api_calls, 0);
}
}
#[test]
fn test_estimate_api_call_probability() {
let retriever = CascadeRetriever::new(CascadeConfig::default());
let prob = retriever.estimate_api_call_probability("test");
assert!((0.0..=1.0).contains(&prob));
}
#[test]
fn test_estimate_api_short_query_low_probability() {
let retriever = CascadeRetriever::new(CascadeConfig::default());
let prob = retriever.estimate_api_call_probability("rust error");
assert!(
prob < 0.3,
"short keyword query should have low prob: {prob}"
);
}
#[test]
fn test_estimate_api_long_query_higher_probability() {
let retriever = CascadeRetriever::new(CascadeConfig::default());
let prob = retriever.estimate_api_call_probability(
"explain the theoretical foundations of memory-augmented neural networks in detail",
);
assert!(
prob > 0.3,
"long abstract query should have higher prob: {prob}"
);
}
#[test]
fn test_estimate_api_code_tokens_lower_probability() {
let retriever = CascadeRetriever::new(CascadeConfig::default());
let prose_prob = retriever.estimate_api_call_probability(
"explain the theoretical foundations of memory augmented neural networks in detail",
);
let code_prob = retriever
.estimate_api_call_probability("fix memory::storage::cache::wrapper::CacheStats::hit_rate");
assert!(
code_prob <= prose_prob,
"code tokens should not increase prob: code={code_prob} prose={prose_prob}"
);
}
#[test]
fn test_estimate_api_empty_query() {
let retriever = CascadeRetriever::new(CascadeConfig::default());
let prob = retriever.estimate_api_call_probability("");
assert!((0.0..=1.0).contains(&prob));
}
#[test]
fn test_config_custom_values() {
let config = CascadeConfig {
top_k: 5,
bm25_threshold: 0.4,
hdc_threshold: 0.6,
concept_graph_threshold: 0.5,
merge_results: false,
min_results: 2,
enable_concept_expansion: false,
};
let retriever = CascadeRetriever::new(config);
assert_eq!(retriever.config().top_k, 5);
assert!(!retriever.config().merge_results);
assert!(!retriever.config().enable_concept_expansion);
}
#[test]
fn test_tier_result_helpers() {
let tier_result = TierResult {
tier: "test".to_string(),
results: vec![("id1".to_string(), 0.9), ("id2".to_string(), 0.7)],
sufficient: true,
};
assert_eq!(tier_result.ids(), vec!["id1", "id2"]);
assert_eq!(tier_result.scores(), vec![0.9, 0.7]);
assert_eq!(tier_result.len(), 2);
assert!(!tier_result.is_empty());
}
#[cfg(feature = "csm")]
mod csm_tests {
use super::*;
#[test]
fn test_bm25_exact_match_zero_api_calls() {
let mut retriever = CascadeRetriever::default_config();
retriever.add_episode("ep-1", "authentication JWT token Rust implementation");
retriever.add_episode("ep-2", "database connection pool timeout fix");
retriever.add_episode("ep-3", "API rate limiting middleware design");
retriever.add_episode("ep-4", "memory cache optimization performance");
retriever.add_episode("ep-5", "error handling patterns in async code");
let result = retriever.retrieve("authentication JWT token");
assert!(!result.episode_ids.is_empty());
assert!(result.contributing_tiers.contains(&"bm25".to_string()));
assert_eq!(result.api_calls, 0);
}
#[test]
fn test_hdc_similarity_zero_api_calls() {
let mut retriever = CascadeRetriever::default_config();
retriever.add_episode(
"ep-1",
"implement user login system with secure password storage",
);
retriever.add_episode("ep-2", "create authentication flow for web application");
retriever.add_episode("ep-3", "setup database migration scripts");
retriever.add_episode("ep-4", "optimize query performance with indexes");
retriever.add_episode("ep-5", "fix memory leak in cache implementation");
let result = retriever.retrieve("user authentication and login security");
assert!(!result.episode_ids.is_empty());
assert!(
result.contributing_tiers.contains(&"bm25".to_string())
|| result.contributing_tiers.contains(&"hdc".to_string())
);
assert_eq!(result.api_calls, 0);
}
#[test]
fn test_empty_index_returns_api_needed() {
let retriever = CascadeRetriever::default_config();
let result = retriever.retrieve("any query");
assert!(result.episode_ids.is_empty());
assert!(result.api_calls > 0);
}
#[test]
fn test_cascade_tier_escalation() {
let mut retriever = CascadeRetriever::default_config();
retriever.add_episode("ep-1", "unique_keyword_alpha implementation");
retriever.add_episode("ep-2", "unique_keyword_beta configuration");
retriever.add_episode("ep-3", "unique_keyword_gamma optimization");
let result = retriever.retrieve("unique_keyword_alpha");
assert!(result.contributing_tiers.contains(&"bm25".to_string()));
let result = retriever.retrieve("implement alpha feature");
assert!(!result.episode_ids.is_empty() || result.api_calls > 0);
}
#[test]
fn test_merged_results_bm25_hdc() {
let config = CascadeConfig {
top_k: 10,
bm25_threshold: 0.2,
hdc_threshold: 0.3,
concept_graph_threshold: 0.4,
merge_results: true,
min_results: 3,
enable_concept_expansion: true,
};
let mut retriever = CascadeRetriever::new(config);
retriever.add_episode("ep-1", "Rust async programming patterns");
retriever.add_episode("ep-2", "Tokio runtime configuration best practices");
retriever.add_episode("ep-3", "async error handling strategies");
retriever.add_episode("ep-4", "Rust concurrent programming guide");
retriever.add_episode("ep-5", "async task spawning performance tips");
let result = retriever.retrieve("Rust async programming");
assert!(!result.episode_ids.is_empty());
assert!(!result.contributing_tiers.is_empty());
assert_eq!(result.api_calls, 0);
}
#[test]
fn test_disable_merge_results() {
let config = CascadeConfig {
top_k: 5,
bm25_threshold: 0.3,
hdc_threshold: 0.5,
concept_graph_threshold: 0.4,
merge_results: false,
min_results: 1,
enable_concept_expansion: false,
};
let mut retriever = CascadeRetriever::new(config);
retriever.add_episode("ep-1", "authentication implementation");
retriever.add_episode("ep-2", "database connection setup");
let result = retriever.retrieve("authentication");
assert!(result.contributing_tiers.len() <= 1);
}
#[test]
fn test_compute_tier_weights_short_query() {
let weights = compute_tier_weights("short");
assert!(weights.0 > weights.1); }
#[test]
fn test_compute_tier_weights_medium_query() {
let weights = compute_tier_weights("this is a medium length query with more words");
assert!(weights.0 > 0.3 && weights.1 > 0.3);
}
#[test]
fn test_compute_tier_weights_long_query() {
let long_query = "this is a very long query that contains many words and should favor semantic matching over keyword matching in the cascade retrieval pipeline";
let weights = compute_tier_weights(long_query);
assert!(weights.1 > weights.0); }
#[test]
fn test_scores_normalized() {
let mut retriever = CascadeRetriever::default_config();
retriever.add_episode("ep-1", "authentication token JWT");
retriever.add_episode("ep-2", "database pool connection");
retriever.add_episode("ep-3", "rate limiting API");
let result = retriever.retrieve("authentication");
for score in &result.scores {
assert!((0.0..=1.0).contains(score));
}
}
#[test]
fn test_top_k_limit() {
let config = CascadeConfig {
top_k: 3,
..CascadeConfig::default()
};
let mut retriever = CascadeRetriever::new(config);
for i in 1..=10 {
retriever.add_episode(&format!("ep-{i}"), &format!("episode {i} content"));
}
let result = retriever.retrieve("episode");
assert!(result.episode_ids.len() <= 3);
}
#[test]
fn test_tokenize_works() {
let tokens = CascadeRetriever::tokenize("Hello World Test");
assert!(!tokens.is_empty());
}
#[test]
fn test_hdc_encoder_similarity() {
use crate::retrieval::HdcEncoder;
let encoder = HdcEncoder::new();
let vec1 = encoder.encode("authentication login");
let vec2 = encoder.encode("authentication login");
let vec3 = encoder.encode("database connection");
let sim_same = vec1.cosine_similarity(&vec2);
assert!(sim_same > 0.9);
let sim_diff = vec1.cosine_similarity(&vec3);
assert!(sim_diff < sim_same);
}
#[test]
fn test_concept_graph_e2e_cascade_tier3() {
let config = CascadeConfig {
top_k: 5,
bm25_threshold: 0.5,
hdc_threshold: 0.6,
concept_graph_threshold: 0.1,
merge_results: true,
min_results: 1,
enable_concept_expansion: true,
};
let mut retriever = CascadeRetriever::new(config);
retriever.add_episode("ep-1", "implement JWT authentication flow");
retriever.add_episode("ep-2", "fix database connection pool timeout");
retriever.add_episode("ep-3", "add rate limiting middleware");
retriever.add_episode("ep-4", "optimize build artifact caching");
retriever.add_episode("ep-5", "refactor error handling patterns");
let result = retriever.retrieve("fix auth bug");
assert!(!result.episode_ids.is_empty());
assert!(!result.contributing_tiers.is_empty());
assert_eq!(result.api_calls, 0);
}
}