//! Memory chain seam tests //! //! Verifies the integration seams in the memory pipeline: //! 1. Extract & store: experience → FTS5 write //! 2. Retrieve & inject: FTS5 search → memory found //! 3. Dedup: same experience not duplicated (reuse_count incremented) use std::sync::Arc; use zclaw_growth::{ ExperienceStore, Experience, VikingAdapter, storage::SqliteStorage, }; async fn test_store() -> ExperienceStore { let sqlite = SqliteStorage::in_memory().await; let viking = Arc::new(VikingAdapter::new(Arc::new(sqlite))); ExperienceStore::new(viking) } // --------------------------------------------------------------------------- // Seam 1: Extract & Store — experience written to FTS5 // --------------------------------------------------------------------------- #[tokio::test] async fn seam_experience_store_and_retrieve() { let store = test_store().await; let exp = Experience::new( "agent-001", "高 CPU 使用率告警频繁", "生产环境 CPU 使用率告警", vec!["检查进程列表".to_string(), "重启服务".to_string()], "已解决", ); store.store_experience(&exp).await.expect("store experience"); // Retrieve by agent let found = store.find_by_agent("agent-001").await.expect("find"); assert_eq!(found.len(), 1, "should find exactly one experience"); assert_eq!(found[0].pain_pattern, "高 CPU 使用率告警频繁"); } // --------------------------------------------------------------------------- // Seam 2: Retrieve by pattern — FTS5 search finds relevant experiences // --------------------------------------------------------------------------- #[tokio::test] async fn seam_experience_pattern_search() { let store = test_store().await; // Store multiple experiences let exp1 = Experience::new( "agent-001", "数据库连接超时", "PostgreSQL 连接池耗尽", vec!["增加连接池大小".to_string()], "已解决", ); let exp2 = Experience::new( "agent-001", "前端白屏问题", "React 渲染错误", vec!["检查错误边界".to_string()], "已修复", ); store.store_experience(&exp1).await.expect("store exp1"); store.store_experience(&exp2).await.expect("store exp2"); // Search for database-related experience let results = store.find_by_pattern("agent-001", "数据库 连接").await.expect("search"); assert!(!results.is_empty(), "FTS5 should find database experience"); assert!( results.iter().any(|e| e.pain_pattern.contains("数据库")), "should match database experience, got: {:?}", results ); } // --------------------------------------------------------------------------- // Seam 3: Dedup — same pain_pattern increments reuse_count // --------------------------------------------------------------------------- #[tokio::test] async fn seam_experience_dedup() { let store = test_store().await; let exp = Experience::new( "agent-001", "内存泄漏检测", "服务运行一段时间后内存持续增长", vec!["分析 heap dump".to_string()], "已修复", ); // Store twice with same agent_id and pain_pattern store.store_experience(&exp).await.expect("first store"); store.store_experience(&exp).await.expect("second store (dedup)"); let all = store.find_by_agent("agent-001").await.expect("find"); assert_eq!(all.len(), 1, "dedup should keep only one experience"); assert!( all[0].reuse_count >= 1, "reuse_count should be incremented, got: {}", all[0].reuse_count ); }