use super::*;
#[tokio::test]
async fn memory_manager_add_note_fails_with_no_note_store() {
let mut mgr = MemoryManager::new();
mgr.add_provider(RollingWindow::new(5));
let err = mgr.add_note("fact").unwrap_err().to_string();
assert!(
err.contains("no note-capable memory provider"),
"guidance error expected: {err}"
);
}
#[tokio::test]
async fn rolling_window_add_note_returns_notes_unsupported() {
let mut rw = RollingWindow::new(5);
let err = rw.add_note("fact").unwrap_err();
assert!(err.is::<NotesUnsupported>());
}
#[tokio::test]
async fn rolling_window_replace_and_remove_note_return_notes_unsupported() {
let mut rw = RollingWindow::new(5);
assert!(rw
.replace_note("old", "new")
.unwrap_err()
.is::<NotesUnsupported>());
assert!(rw.remove_note("old").unwrap_err().is::<NotesUnsupported>());
}
#[tokio::test]
async fn memory_manager_replace_and_remove_route_to_note_store() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("NOTES.md");
let mut mgr = MemoryManager::new();
mgr.add_provider(RollingWindow::new(5));
mgr.add_provider(NoteStore::new(path.clone(), 2200));
let ctx = SessionContext {
workspace: "/ws".into(),
session_id: "s".into(),
};
mgr.initialize_all(&ctx).await;
mgr.add_note("model alpha is the fast tier").unwrap();
mgr.add_note("workspace uses just check").unwrap();
mgr.replace_note("alpha", "model beta is the fast tier")
.unwrap();
let raw = std::fs::read_to_string(&path).unwrap();
assert!(raw.contains("model beta"), "{raw}");
assert!(!raw.contains("model alpha"), "{raw}");
mgr.remove_note("just check").unwrap();
let raw = std::fs::read_to_string(&path).unwrap();
assert!(!raw.contains("just check"), "{raw}");
assert!(raw.contains("model beta"), "other entry untouched: {raw}");
let err = mgr.remove_note("nonexistent").unwrap_err().to_string();
assert!(err.contains("no entry contains"), "{err}");
}
#[tokio::test]
async fn memory_manager_replace_and_remove_fail_with_no_note_store() {
let mut mgr = MemoryManager::new();
mgr.add_provider(RollingWindow::new(5));
let err = mgr.replace_note("a", "b").unwrap_err().to_string();
assert!(err.contains("no note-capable memory provider"), "{err}");
let err = mgr.remove_note("a").unwrap_err().to_string();
assert!(err.contains("no note-capable memory provider"), "{err}");
}
#[tokio::test]
async fn memory_manager_add_note_routes_to_note_store() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("NOTES.md");
let mut mgr = MemoryManager::new();
mgr.add_provider(RollingWindow::new(5));
mgr.add_provider(NoteStore::new(path.clone(), 2200));
let ctx = SessionContext {
workspace: "/ws".into(),
session_id: "s".into(),
};
mgr.initialize_all(&ctx).await;
mgr.add_note("the answer is 42").unwrap();
let raw = std::fs::read_to_string(&path).unwrap();
assert!(raw.contains("the answer is 42"));
}
struct CustomNotes {
notes: Vec<String>,
}
#[async_trait]
impl MemoryProvider for CustomNotes {
fn name(&self) -> &str {
"custom_notes"
}
fn build_messages(&self, _system_prompt: &str, _new_task: &str) -> Vec<MemMessage> {
Vec::new()
}
async fn sync_turn(&mut self, _user: &str, _assistant: &str, _metrics: &TurnMetrics) {}
fn add_note(&mut self, fact: &str) -> anyhow::Result<()> {
self.notes.push(fact.to_string());
Ok(())
}
}
#[tokio::test]
async fn memory_manager_add_note_first_ok_wins_regardless_of_name() {
let mut mgr = MemoryManager::new();
mgr.add_provider(RollingWindow::new(5)); mgr.add_provider(CustomNotes { notes: Vec::new() });
mgr.add_note("routed by capability, not by name").unwrap();
}
#[tokio::test]
async fn memory_manager_add_note_surfaces_curator_error() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("NOTES.md");
let mut mgr = MemoryManager::new();
mgr.add_provider(RollingWindow::new(5));
mgr.add_provider(NoteStore::new(path, 40));
let ctx = SessionContext {
workspace: "/ws".into(),
session_id: "s".into(),
};
mgr.initialize_all(&ctx).await;
mgr.add_note("an entry that fits").unwrap();
let err = mgr.add_note(&"x".repeat(80)).unwrap_err().to_string();
assert!(
err.contains("Replace or remove existing entries first"),
"curator error must propagate: {err}"
);
assert!(err.contains("an entry that fits"), "{err}");
}