|
| 1 | +use codex_memory::recall::RecallContext; |
| 2 | +use codex_memory::recall::recall; |
| 3 | +use codex_memory::store::MemoryStore; |
| 4 | +use codex_memory::types::Counters; |
| 5 | +use codex_memory::types::Kind; |
| 6 | +use codex_memory::types::MemoryItem; |
| 7 | +use codex_memory::types::RelevanceHints; |
| 8 | +use codex_memory::types::Scope; |
| 9 | +use codex_memory::types::Status; |
| 10 | +use std::collections::HashMap; |
| 11 | +use std::sync::Mutex; |
| 12 | + |
| 13 | +#[derive(Default)] |
| 14 | +struct TestStore { |
| 15 | + items: Mutex<HashMap<String, MemoryItem>>, |
| 16 | +} |
| 17 | + |
| 18 | +impl TestStore { |
| 19 | + fn new(items: Vec<MemoryItem>) -> Self { |
| 20 | + let map = items.into_iter().map(|i| (i.id.clone(), i)).collect(); |
| 21 | + Self { |
| 22 | + items: Mutex::new(map), |
| 23 | + } |
| 24 | + } |
| 25 | +} |
| 26 | + |
| 27 | +impl MemoryStore for TestStore { |
| 28 | + fn add(&self, item: MemoryItem) -> anyhow::Result<()> { |
| 29 | + self.items.lock().unwrap().insert(item.id.clone(), item); |
| 30 | + Ok(()) |
| 31 | + } |
| 32 | + |
| 33 | + fn update(&self, item: &MemoryItem) -> anyhow::Result<()> { |
| 34 | + self.items |
| 35 | + .lock() |
| 36 | + .unwrap() |
| 37 | + .insert(item.id.clone(), item.clone()); |
| 38 | + Ok(()) |
| 39 | + } |
| 40 | + |
| 41 | + fn delete(&self, _id: &str) -> anyhow::Result<()> { |
| 42 | + Ok(()) |
| 43 | + } |
| 44 | + |
| 45 | + fn get(&self, id: &str) -> anyhow::Result<Option<MemoryItem>> { |
| 46 | + Ok(self.items.lock().unwrap().get(id).cloned()) |
| 47 | + } |
| 48 | + |
| 49 | + fn list( |
| 50 | + &self, |
| 51 | + _scope: Option<Scope>, |
| 52 | + status: Option<Status>, |
| 53 | + ) -> anyhow::Result<Vec<MemoryItem>> { |
| 54 | + let items = self.items.lock().unwrap(); |
| 55 | + Ok(items |
| 56 | + .values() |
| 57 | + .filter(|i| match status.as_ref() { |
| 58 | + Some(s) => i.status == *s, |
| 59 | + None => true, |
| 60 | + }) |
| 61 | + .cloned() |
| 62 | + .collect()) |
| 63 | + } |
| 64 | + |
| 65 | + fn archive(&self, _id: &str, _archived: bool) -> anyhow::Result<()> { |
| 66 | + Ok(()) |
| 67 | + } |
| 68 | + |
| 69 | + fn export(&self, _out: &mut dyn std::io::Write) -> anyhow::Result<()> { |
| 70 | + Ok(()) |
| 71 | + } |
| 72 | + |
| 73 | + fn import(&self, _input: &mut dyn std::io::Read) -> anyhow::Result<usize> { |
| 74 | + Ok(0) |
| 75 | + } |
| 76 | + |
| 77 | + fn stats(&self) -> anyhow::Result<serde_json::Value> { |
| 78 | + Ok(serde_json::json!({})) |
| 79 | + } |
| 80 | +} |
| 81 | + |
| 82 | +fn item(id: &str, content: &str, lang: &str) -> MemoryItem { |
| 83 | + MemoryItem { |
| 84 | + id: id.to_string(), |
| 85 | + created_at: "2024-01-01T00:00:00Z".into(), |
| 86 | + updated_at: "2024-01-01T00:00:00Z".into(), |
| 87 | + schema_version: 1, |
| 88 | + source: "test".into(), |
| 89 | + scope: Scope::Global, |
| 90 | + status: Status::Active, |
| 91 | + kind: Kind::Fact, |
| 92 | + content: content.into(), |
| 93 | + tags: vec![], |
| 94 | + relevance_hints: RelevanceHints { |
| 95 | + files: vec![], |
| 96 | + crates: vec![], |
| 97 | + languages: vec![lang.into()], |
| 98 | + commands: vec![], |
| 99 | + }, |
| 100 | + counters: Counters { |
| 101 | + seen_count: 0, |
| 102 | + used_count: 0, |
| 103 | + last_used_at: None, |
| 104 | + }, |
| 105 | + expiry: None, |
| 106 | + } |
| 107 | +} |
| 108 | + |
1 | 109 | #[test] |
2 | | -fn placeholder() { |
3 | | - // placeholder test |
| 110 | +fn ranks_and_updates_counters() { |
| 111 | + let a = item("1", "use cargo build for rust", "rust"); |
| 112 | + let b = item("2", "cargo test runs tests", "rust"); |
| 113 | + let c = item("3", "npm install packages", "javascript"); |
| 114 | + let store = TestStore::new(vec![a.clone(), b.clone(), c.clone()]); |
| 115 | + let now = "2024-01-10T00:00:00Z".to_string(); |
| 116 | + let ctx = RecallContext { |
| 117 | + repo_root: None, |
| 118 | + dir: None, |
| 119 | + current_file: None, |
| 120 | + crate_name: None, |
| 121 | + language: Some("rust".into()), |
| 122 | + command: None, |
| 123 | + now_rfc3339: now.clone(), |
| 124 | + item_cap: 2, |
| 125 | + token_cap: 50, |
| 126 | + }; |
| 127 | + let out = recall(&store, "cargo build rust", &ctx).unwrap(); |
| 128 | + assert_eq!(out.len(), 2); |
| 129 | + assert_eq!(out[0].id, "1"); |
| 130 | + assert_eq!(out[1].id, "2"); |
| 131 | + let a_upd = store.get("1").unwrap().unwrap(); |
| 132 | + assert_eq!(a_upd.counters.used_count, 1); |
| 133 | + assert_eq!(a_upd.counters.last_used_at.as_deref(), Some(now.as_str())); |
| 134 | + let b_upd = store.get("2").unwrap().unwrap(); |
| 135 | + assert_eq!(b_upd.counters.used_count, 1); |
| 136 | + assert_eq!(b_upd.counters.last_used_at.as_deref(), Some(now.as_str())); |
| 137 | + let c_upd = store.get("3").unwrap().unwrap(); |
| 138 | + assert_eq!(c_upd.counters.used_count, 0); |
| 139 | + assert_eq!(c_upd.counters.last_used_at, None); |
4 | 140 | } |
0 commit comments