use intent_engine::db::models::SearchResult;
use intent_engine::db::{create_pool, run_migrations};
use intent_engine::search::SearchManager;
use intent_engine::tasks::TaskManager;
use tempfile::TempDir;
async fn setup_test_db() -> (TempDir, sqlx::SqlitePool) {
let temp_dir = TempDir::new().unwrap();
let intent_dir = temp_dir.path().join(".intent-engine");
std::fs::create_dir_all(&intent_dir).unwrap();
let db_path = intent_dir.join("project.db");
let pool = create_pool(&db_path).await.unwrap();
run_migrations(&pool).await.unwrap();
(temp_dir, pool)
}
#[tokio::test]
async fn test_chinese_single_char_search() {
let (_temp_dir, pool) = setup_test_db().await;
let task_mgr = TaskManager::new(&pool);
let search_mgr = SearchManager::new(&pool);
task_mgr
.add_task(
"实现用户认证功能".to_string(),
Some("使用JWT实现登录".to_string()),
None,
None,
None,
None,
)
.await
.unwrap();
let results = search_mgr
.search("用", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1, "应该找到1个包含'用'的任务");
assert!(match &results[0] {
SearchResult::Task { task, .. } => &task.name,
_ => panic!("Expected task result"),
}
.contains("用户"));
let results = search_mgr
.search("认", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1, "应该找到1个包含'认'的任务");
let results = search_mgr
.search("证", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1, "应该找到1个包含'证'的任务");
let results = search_mgr
.search("功", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1, "应该找到1个包含'功'的任务");
}
#[tokio::test]
async fn test_chinese_two_char_search() {
let (_temp_dir, pool) = setup_test_db().await;
let task_mgr = TaskManager::new(&pool);
let search_mgr = SearchManager::new(&pool);
task_mgr
.add_task("实现用户认证".to_string(), None, None, None, None, None)
.await
.unwrap();
task_mgr
.add_task("添加数据库索引".to_string(), None, None, None, None, None)
.await
.unwrap();
task_mgr
.add_task("优化查询性能".to_string(), None, None, None, None, None)
.await
.unwrap();
let test_cases = vec![
("用户", "实现用户认证"),
("认证", "实现用户认证"),
("数据", "添加数据库索引"),
("索引", "添加数据库索引"),
("查询", "优化查询性能"),
("性能", "优化查询性能"),
];
for (query, expected_name) in test_cases {
let results = search_mgr
.search(query, true, false, None, None, false)
.await
.unwrap()
.results;
assert!(
results
.iter()
.any(|r| if let SearchResult::Task { task, .. } = r {
&task.name
} else {
""
}
.contains(expected_name)),
"搜索'{}'应该找到'{}'",
query,
expected_name
);
}
}
#[tokio::test]
async fn test_chinese_multi_char_search() {
let (_temp_dir, pool) = setup_test_db().await;
let task_mgr = TaskManager::new(&pool);
let search_mgr = SearchManager::new(&pool);
task_mgr
.add_task(
"实现JWT用户认证功能".to_string(),
Some("基于Token的认证机制".to_string()),
None,
None,
None,
None,
)
.await
.unwrap();
task_mgr
.add_task(
"优化数据库查询性能".to_string(),
Some("添加索引提升查询速度".to_string()),
None,
None,
None,
None,
)
.await
.unwrap();
let results = search_mgr
.search("用户认", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1, "三字词搜索应该使用FTS5");
let results = search_mgr
.search("用户认证", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
assert!(match &results[0] {
SearchResult::Task { task, .. } => &task.name,
_ => panic!("Expected task result"),
}
.contains("认证"));
let results = search_mgr
.search("用户认证功能", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
let results = search_mgr
.search("数据库查询", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
let results = search_mgr
.search("查询性能", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
}
#[tokio::test]
async fn test_mixed_language_search() {
let (_temp_dir, pool) = setup_test_db().await;
let task_mgr = TaskManager::new(&pool);
let search_mgr = SearchManager::new(&pool);
task_mgr
.add_task(
"实现JWT认证".to_string(),
Some("JSON Web Token认证".to_string()),
None,
None,
None,
None,
)
.await
.unwrap();
task_mgr
.add_task(
"添加API接口".to_string(),
Some("RESTful API设计".to_string()),
None,
None,
None,
None,
)
.await
.unwrap();
task_mgr
.add_task("配置OAuth2流程".to_string(), None, None, None, None, None)
.await
.unwrap();
let results = search_mgr
.search("JWT", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
assert!(match &results[0] {
SearchResult::Task { task, .. } => &task.name,
_ => panic!("Expected task result"),
}
.contains("JWT"));
let results = search_mgr
.search("API", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
assert!(match &results[0] {
SearchResult::Task { task, .. } => &task.name,
_ => panic!("Expected task result"),
}
.contains("API"));
let results = search_mgr
.search("OAuth2", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
let results = search_mgr
.search("认证", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
let results = search_mgr
.search("接口", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
let results = search_mgr
.search("流程", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
}
#[tokio::test]
async fn test_japanese_search() {
let (_temp_dir, pool) = setup_test_db().await;
let task_mgr = TaskManager::new(&pool);
let search_mgr = SearchManager::new(&pool);
task_mgr
.add_task(
"ユーザー認証を実装".to_string(),
Some("JWTトークンを使用".to_string()),
None,
None,
None,
None,
)
.await
.unwrap();
task_mgr
.add_task(
"データベース索引".to_string(),
Some("性能向上".to_string()),
None,
None,
None,
None,
)
.await
.unwrap();
let results = search_mgr
.search("ユ", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1, "应该找到包含'ユ'的任务");
let results = search_mgr
.search("認証", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
let results = search_mgr
.search("ユーザー", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
let results = search_mgr
.search("データベース", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
let results = search_mgr
.search("を", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
}
#[tokio::test]
async fn test_edge_cases() {
let (_temp_dir, pool) = setup_test_db().await;
let task_mgr = TaskManager::new(&pool);
let search_mgr = SearchManager::new(&pool);
task_mgr
.add_task("实现:用户认证".to_string(), None, None, None, None, None)
.await
.unwrap();
let results = search_mgr
.search("用户", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1, "应该忽略标点符号");
task_mgr
.add_task("实现OAuth2认证".to_string(), None, None, None, None, None)
.await
.unwrap();
let results = search_mgr
.search("认证", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 2, "应该找到2个包含'认证'的任务");
task_mgr
.add_task("实现 用户 认证".to_string(), None, None, None, None, None)
.await
.unwrap();
let results = search_mgr
.search("用户", true, false, None, None, false)
.await
.unwrap()
.results;
assert!(!results.is_empty(), "应该能处理空格");
task_mgr
.add_task(
"任务标题".to_string(),
Some("描述中包含用户信息".to_string()),
None,
None,
None,
None,
)
.await
.unwrap();
let results = search_mgr
.search("用户", true, false, None, None, false)
.await
.unwrap()
.results;
assert!(!results.is_empty(), "应该能搜索spec字段");
}
#[tokio::test]
async fn test_search_performance() {
let (_temp_dir, pool) = setup_test_db().await;
let task_mgr = TaskManager::new(&pool);
let search_mgr = SearchManager::new(&pool);
for i in 0..1000 {
task_mgr
.add_task(
format!("任务{}: 实现功能{}", i, i % 10),
Some(format!("这是任务{}的详细说明,包含各种关键词", i)),
None,
None,
None,
None,
)
.await
.unwrap();
}
let start = std::time::Instant::now();
let _results = search_mgr
.search("功能", true, false, None, None, false)
.await
.unwrap()
.results;
let duration = start.elapsed();
assert!(
duration.as_millis() < 100,
"FTS5搜索耗时{}ms,超过100ms阈值",
duration.as_millis()
);
let start = std::time::Instant::now();
let _results = search_mgr
.search("任", true, false, None, None, false)
.await
.unwrap()
.results; let duration = start.elapsed();
assert!(
duration.as_millis() < 500,
"LIKE搜索耗时{}ms,超过500ms阈值",
duration.as_millis()
);
}
#[tokio::test]
async fn test_empty_and_special_queries() {
let (_temp_dir, pool) = setup_test_db().await;
let task_mgr = TaskManager::new(&pool);
let search_mgr = SearchManager::new(&pool);
task_mgr
.add_task("测试任务".to_string(), None, None, None, None, None)
.await
.unwrap();
let results = search_mgr.search("", true, false, None, None, false).await;
assert!(results.is_ok(), "空查询应该返回空结果而不是错误");
let results = search_mgr
.search(" ", true, false, None, None, false)
.await;
assert!(results.is_ok());
let results = search_mgr
.search("@#$%", true, false, None, None, false)
.await;
assert!(results.is_ok());
}
#[tokio::test]
async fn test_case_sensitivity() {
let (_temp_dir, pool) = setup_test_db().await;
let task_mgr = TaskManager::new(&pool);
let search_mgr = SearchManager::new(&pool);
task_mgr
.add_task("Implement API".to_string(), None, None, None, None, None)
.await
.unwrap();
let results_upper = search_mgr
.search("API", true, false, None, None, false)
.await
.unwrap()
.results;
let results_lower = search_mgr
.search("api", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results_upper.len(), results_lower.len());
}
#[tokio::test]
async fn test_korean_search() {
let (_temp_dir, pool) = setup_test_db().await;
let task_mgr = TaskManager::new(&pool);
let search_mgr = SearchManager::new(&pool);
task_mgr
.add_task(
"사용자 인증 구현".to_string(),
Some("JWT 토큰 사용".to_string()),
None,
None,
None,
None,
)
.await
.unwrap();
let results = search_mgr
.search("사", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1, "应该找到包含韩文的任务");
let results = search_mgr
.search("사용", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
let results = search_mgr
.search("사용자", true, false, None, None, false)
.await
.unwrap()
.results;
assert_eq!(results.len(), 1);
}