1use std::sync::Arc;
9
10use crate::{
11 CallerScope, ChunkKind, ChunkSource, MemoryChunk, MemoryProvider, MemoryQuery, MemorySession,
12 MemoryStats, NewMemoryChunk, NewMemorySession, Result, RetrievalProfile, SessionOutcome,
13};
14
15pub async fn assert_write_get_stats(
18 memory: Arc<dyn MemoryProvider>,
19 content: &str,
20) -> Result<(MemoryChunk, MemoryStats)> {
21 let chunk = memory
22 .write(NewMemoryChunk {
23 kind: ChunkKind::Chat,
24 source: ChunkSource::Embedded,
25 session_id: None,
26 project_root: None,
27 caller_id: "conformance".into(),
28 content: content.into(),
29 metadata: serde_json::Value::Null,
30 importance: None,
31 shareable: false,
32 pinned: false,
33 })
34 .await?;
35
36 let fetched = memory
37 .get(&chunk.id)
38 .await?
39 .ok_or_else(|| crate::MemoryError::NotFound(chunk.id.clone()))?;
40 assert_eq!(fetched.content, chunk.content);
41
42 let stats = memory.stats().await?;
43 assert!(stats.total_chunks >= 1);
44
45 Ok((chunk, stats))
46}
47
48pub async fn assert_retrieve_finds_written(
50 memory: Arc<dyn MemoryProvider>,
51 content: &str,
52) -> Result<MemoryChunk> {
53 let chunk = memory
54 .write(NewMemoryChunk {
55 kind: ChunkKind::Chat,
56 source: ChunkSource::Embedded,
57 session_id: None,
58 project_root: None,
59 caller_id: "conformance".into(),
60 content: content.into(),
61 metadata: serde_json::Value::Null,
62 importance: None,
63 shareable: false,
64 pinned: false,
65 })
66 .await?;
67
68 let hits = memory
69 .retrieve(MemoryQuery {
70 text: content.into(),
71 kinds: Some(vec![ChunkKind::Chat]),
72 since: None,
73 until: None,
74 session_id: None,
75 caller_scope: CallerScope::Global,
76 project_root_prefix: None,
77 k: 8,
78 include_rollups: true,
79 min_importance: None,
80 profile: RetrievalProfile::AgentChatTurn,
81 caller_id: "conformance".into(),
82 })
83 .await?;
84
85 assert!(
86 hits.iter().any(|hit| hit.id == chunk.id),
87 "retrieve did not return the written chunk; got {} hits",
88 hits.len()
89 );
90
91 Ok(chunk)
92}
93
94pub async fn assert_session_lifecycle(memory: Arc<dyn MemoryProvider>) -> Result<MemorySession> {
96 let session = memory
97 .open_session(NewMemorySession {
98 caller_id: "conformance".into(),
99 title: Some("conformance session".into()),
100 metadata: serde_json::Value::Null,
101 })
102 .await?;
103
104 memory
105 .write(NewMemoryChunk {
106 kind: ChunkKind::Chat,
107 source: ChunkSource::Embedded,
108 session_id: Some(session.id.clone()),
109 project_root: None,
110 caller_id: session.caller_id.clone(),
111 content: "session-scoped turn".into(),
112 metadata: serde_json::Value::Null,
113 importance: None,
114 shareable: false,
115 pinned: false,
116 })
117 .await?;
118
119 let open = memory
120 .get_session(&session.id)
121 .await?
122 .ok_or_else(|| crate::MemoryError::NotFound(session.id.clone()))?;
123 assert_eq!(open.outcome, SessionOutcome::Open);
124
125 memory
126 .close_session(&session.id, SessionOutcome::Success)
127 .await?;
128
129 let closed = memory
130 .get_session(&session.id)
131 .await?
132 .ok_or_else(|| crate::MemoryError::NotFound(session.id.clone()))?;
133 assert_eq!(closed.outcome, SessionOutcome::Success);
134
135 Ok(closed)
136}
137
138pub async fn assert_summarize_session_roundtrip(
142 memory: Arc<dyn MemoryProvider>,
143) -> Result<MemoryChunk> {
144 let session = memory
145 .open_session(NewMemorySession {
146 caller_id: "conformance".into(),
147 title: Some("summarize me".into()),
148 metadata: serde_json::Value::Null,
149 })
150 .await?;
151
152 memory
153 .write(NewMemoryChunk {
154 kind: ChunkKind::Chat,
155 source: ChunkSource::Embedded,
156 session_id: Some(session.id.clone()),
157 project_root: None,
158 caller_id: session.caller_id.clone(),
159 content: "first turn in session".into(),
160 metadata: serde_json::Value::Null,
161 importance: None,
162 shareable: false,
163 pinned: false,
164 })
165 .await?;
166 memory
167 .write(NewMemoryChunk {
168 kind: ChunkKind::Chat,
169 source: ChunkSource::Embedded,
170 session_id: Some(session.id.clone()),
171 project_root: None,
172 caller_id: session.caller_id.clone(),
173 content: "second turn in session".into(),
174 metadata: serde_json::Value::Null,
175 importance: None,
176 shareable: false,
177 pinned: false,
178 })
179 .await?;
180
181 let summary = memory.summarize_session(&session.id).await?;
182 assert_eq!(summary.kind, ChunkKind::JobSummary);
183 assert!(
184 memory.get(&summary.id).await?.is_some(),
185 "summary chunk was not persisted"
186 );
187
188 Ok(summary)
189}