diff --git a/desktop/src-tauri/src/intelligence/reflection.rs b/desktop/src-tauri/src/intelligence/reflection.rs index 06796b5..fe7a5c0 100644 --- a/desktop/src-tauri/src/intelligence/reflection.rs +++ b/desktop/src-tauri/src/intelligence/reflection.rs @@ -884,6 +884,97 @@ mod tests { // Should not trigger (only 2 tasks, threshold is 5) assert!(!patterns.iter().any(|p| p.observation.contains("待办任务"))); } + + #[tokio::test] + async fn test_reflection_cycle_full() { + // Use config with use_llm=false to test rules-based path without driver + let config = ReflectionConfig { + use_llm: false, + trigger_after_conversations: 5, + ..Default::default() + }; + let mut engine = ReflectionEngine::new(Some(config)); + + // Record 5 conversations + for _ in 0..5 { + engine.record_conversation(); + } + assert!(engine.should_reflect(), "Should trigger after 5 conversations"); + + // Provide memories with enough task entries to exceed threshold (5) + let mut memories = Vec::new(); + for i in 0..6 { + memories.push(MemoryEntryForAnalysis { + memory_type: "task".to_string(), + content: format!("待办任务 {}", i), + importance: 7, + access_count: 1, + tags: vec!["任务".to_string()], + }); + } + // Add some preferences and knowledge + memories.push(MemoryEntryForAnalysis { + memory_type: "preferences".to_string(), + content: "用户偏好简洁回复".to_string(), + importance: 8, + access_count: 3, + tags: vec!["偏好".to_string()], + }); + memories.push(MemoryEntryForAnalysis { + memory_type: "knowledge".to_string(), + content: "用户熟悉 Rust 编程".to_string(), + importance: 6, + access_count: 2, + tags: vec!["知识".to_string()], + }); + + // Run reflection (no LLM driver, rules-based) + let result = engine.reflect("agent-test", &memories, None).await; + + // Verify result structure + assert!(!result.patterns.is_empty(), "Should detect patterns from memories"); + assert!(!result.improvements.is_empty(), "Should generate improvements"); + assert!(!result.timestamp.is_empty(), "Should have timestamp"); + assert!(result.used_fallback, "Should use rules-based fallback when no LLM driver"); + + // Verify state reset after reflection + assert!(!engine.should_reflect(), "Counter should reset after reflection"); + + // Verify history stored + assert_eq!(engine.history.len(), 1, "History should contain 1 reflection result"); + } + + #[tokio::test] + async fn test_reflection_generates_identity_proposals() { + let config = ReflectionConfig { + use_llm: false, + allow_soul_modification: true, + trigger_after_conversations: 3, + ..Default::default() + }; + let mut engine = ReflectionEngine::new(Some(config)); + + for _ in 0..3 { + engine.record_conversation(); + } + + // High-frequency preference pattern should trigger identity proposal + let memories: Vec = (0..7) + .map(|i| MemoryEntryForAnalysis { + memory_type: "preferences".to_string(), + content: format!("偏好项目 {}", i), + importance: 8, + access_count: 5, + tags: vec!["偏好".to_string()], + }) + .collect(); + + let result = engine.reflect("agent-identity-test", &memories, None).await; + + // Identity proposals are generated when patterns are strong enough + // (rules-based may not always produce proposals, but result structure should be valid) + assert!(result.identity_proposals.len() <= 5, "Identity proposals should be bounded"); + } } // === Helpers ===