use super::super::promote_summary_to_memories;
use super::super::slug::content_hash;
use crate::memory::tests_helper::setup_memory_schema;
#[test]
fn test_promote_multi_decisions() {
let conn = rusqlite::Connection::open_in_memory().unwrap();
setup_memory_schema(&conn);
let decisions = "• Use RwLock instead of Mutex for concurrent read support\n\
• Switch to trigram tokenizer for CJK text search\n\
• Set compression threshold to 100 observations";
let count = promote_summary_to_memories(
&conn,
"session-1",
"test/proj",
Some("Optimize search and concurrency"),
Some(decisions),
None,
None,
)
.unwrap();
assert_eq!(count, 3);
let memories = crate::memory::get_recent_memories(&conn, "test/proj", 10).unwrap();
let titles: Vec<&str> = memories
.iter()
.map(|memory| memory.title.as_str())
.collect();
assert!(
titles.iter().any(|title| title.contains("RwLock")),
"title should contain keyword from content: {:?}",
titles
);
assert!(
titles.iter().any(|title| title.contains("trigram")),
"title should contain keyword from content: {:?}",
titles
);
}
#[test]
fn test_promote_multi_learned() {
let conn = rusqlite::Connection::open_in_memory().unwrap();
setup_memory_schema(&conn);
let learned = "- FTS5 trigram tokenizer handles CJK without word boundaries\n\
- WAL mode allows concurrent reads with single writer";
let count = promote_summary_to_memories(
&conn,
"session-1",
"test/proj",
Some("Research storage"),
None,
Some(learned),
None,
)
.unwrap();
assert_eq!(count, 2);
let memories = crate::memory::get_recent_memories(&conn, "test/proj", 10).unwrap();
assert!(memories
.iter()
.all(|memory| memory.memory_type == "discovery"));
}
#[test]
fn test_promote_actionable_learned_as_lesson() {
let conn = rusqlite::Connection::open_in_memory().unwrap();
setup_memory_schema(&conn);
let learned = "Root cause: warning-only fallback hid missing data; avoid silent degradation.";
let count = promote_summary_to_memories(
&conn,
"session-1",
"test/proj",
Some("Fix missing memory output"),
None,
Some(learned),
None,
)
.unwrap();
assert_eq!(count, 1);
let memories = crate::memory::get_recent_memories(&conn, "test/proj", 10).unwrap();
assert_eq!(memories.len(), 1);
assert_eq!(memories[0].memory_type, "lesson");
let metadata = crate::memory::lesson::get_lesson_metadata(&conn, memories[0].id)
.unwrap()
.expect("lesson metadata should exist");
assert_eq!(metadata.reinforcement_count, 1);
assert!(metadata.confidence >= 0.8);
}
#[test]
fn test_promote_duplicate_lesson_reinforces_existing() {
let conn = rusqlite::Connection::open_in_memory().unwrap();
setup_memory_schema(&conn);
let learned = "Lesson: after three failed fixes, stop and challenge the hypothesis.";
promote_summary_to_memories(
&conn,
"session-1",
"test/proj",
Some("Fix build loop"),
None,
Some(learned),
None,
)
.unwrap();
promote_summary_to_memories(
&conn,
"session-2",
"test/proj",
Some("Fix build loop again"),
None,
Some(learned),
None,
)
.unwrap();
let memories = crate::memory::get_recent_memories(&conn, "test/proj", 10).unwrap();
assert_eq!(memories.len(), 1);
assert_eq!(memories[0].memory_type, "lesson");
let metadata = crate::memory::lesson::get_lesson_metadata(&conn, memories[0].id)
.unwrap()
.expect("lesson metadata should exist");
assert_eq!(metadata.reinforcement_count, 2);
}
#[test]
fn test_promote_preference_defaults_to_project_scope() {
let conn = rusqlite::Connection::open_in_memory().unwrap();
setup_memory_schema(&conn);
let count = promote_summary_to_memories(
&conn,
"session-1",
"test/proj",
Some("Capture local preference"),
None,
None,
Some("Always run project-specific smoke tests"),
)
.unwrap();
assert_eq!(count, 1);
let memories = crate::memory::get_recent_memories(&conn, "test/proj", 10).unwrap();
assert_eq!(memories.len(), 1);
assert_eq!(memories[0].memory_type, "preference");
assert_eq!(memories[0].scope, "project");
}
#[test]
fn test_promote_content_format() {
let conn = rusqlite::Connection::open_in_memory().unwrap();
setup_memory_schema(&conn);
let decisions = "Switched from unicode61 to trigram tokenizer for better CJK support";
promote_summary_to_memories(
&conn,
"session-1",
"test/proj",
Some("Fix search"),
Some(decisions),
None,
None,
)
.unwrap();
let memories = crate::memory::get_recent_memories(&conn, "test/proj", 10).unwrap();
assert_eq!(memories.len(), 1);
assert!(
!memories[0].text.contains("**Request**"),
"content should not have boilerplate: {}",
memories[0].text
);
assert!(
memories[0].text.contains("[Context:"),
"content should have compact context: {}",
memories[0].text
);
}
#[test]
fn test_content_hash_dedup() {
let hash1 = content_hash("Use FTS5 trigram tokenizer for CJK support");
let hash2 = content_hash("Use FTS5 trigram tokenizer for CJK support");
assert_eq!(hash1, hash2);
let hash3 = content_hash("Switch to WAL mode for concurrent reads");
assert_ne!(hash1, hash3);
}
#[test]
fn test_cross_session_dedup() {
let conn = rusqlite::Connection::open_in_memory().unwrap();
setup_memory_schema(&conn);
let decision = "Use FTS5 trigram tokenizer for CJK text search support";
promote_summary_to_memories(
&conn,
"session-1",
"test/proj",
Some("Optimize search"),
Some(decision),
None,
None,
)
.unwrap();
promote_summary_to_memories(
&conn,
"session-2",
"test/proj",
Some("Database performance tuning"),
Some(decision),
None,
None,
)
.unwrap();
let memories = crate::memory::get_recent_memories(&conn, "test/proj", 10).unwrap();
assert_eq!(
memories.len(),
1,
"same decision should dedup across sessions"
);
}