#[cfg(test)]
#[allow(clippy::module_inception)]
mod tests {
use super::super::client::MemoryClient;
use super::super::parsers::{
creator_label, parse_drawers, parse_dream_stats, parse_memory_details, parse_memory_event,
parse_palaces, parse_recall_hits,
};
use super::super::types::{DEFAULT_MEMORY_URL, normalize_url, resolve_memory_url};
use super::super::types::{
DRAWER_SNIPPET_FALLBACK_MAX, DreamStats, MemoryEvent, NO_CREATOR_LABEL,
};
#[test]
fn default_memory_url_is_local() {
assert!(DEFAULT_MEMORY_URL.starts_with("http://127.0.0.1"));
}
#[test]
fn normalize_url_adds_scheme() {
assert_eq!(normalize_url("127.0.0.1:7070"), "http://127.0.0.1:7070");
assert_eq!(
normalize_url("http://127.0.0.1:7070"),
"http://127.0.0.1:7070"
);
}
#[test]
fn memory_client_stores_base_url() {
let client = MemoryClient::new("http://127.0.0.1:7070");
assert_eq!(client.base_url(), "http://127.0.0.1:7070");
}
#[test]
fn memory_client_repoints() {
let mut client = MemoryClient::new("http://127.0.0.1:7070");
client.set_base_url("http://127.0.0.1:8080");
assert_eq!(client.base_url(), "http://127.0.0.1:8080");
}
#[test]
fn resolve_memory_url_returns_http_url() {
let url = resolve_memory_url();
assert!(url.starts_with("http://") || url.starts_with("https://"));
}
#[test]
fn palace_list_accepts_array_and_object_shapes() {
let arr = serde_json::json!([
{"id": "p1", "name": "default", "vector_count": 8400},
{"id": "p2", "name": "work", "vectors": 0},
]);
let rows = parse_palaces(&arr);
assert_eq!(rows.len(), 2);
assert_eq!(rows[0].id, "p1");
assert_eq!(rows[0].vector_count, 8400);
assert_eq!(rows[1].name, "work");
let obj = serde_json::json!({
"palaces": [{"id": "p3", "name": "notes", "total_vectors": 12}],
});
let rows = parse_palaces(&obj);
assert_eq!(rows.len(), 1);
assert_eq!(rows[0].vector_count, 12);
assert!(parse_palaces(&serde_json::json!("nonsense")).is_empty());
}
#[test]
fn parse_recall_hits_projects_fields() {
let raw = serde_json::json!([
{
"palace_id": "default",
"content": "JWT middleware added to auth flow\nmore detail",
"score": 0.83,
},
{
"palace_id": "work",
"content": " single line ",
"score": 0.5,
},
]);
let hits = parse_recall_hits(&raw);
assert_eq!(hits.len(), 2);
assert_eq!(hits[0].palace_id, "default");
assert_eq!(hits[0].snippet, "JWT middleware added to auth flow");
assert!((hits[0].score - 0.83).abs() < 1e-6);
assert_eq!(hits[1].snippet, "single line");
assert!(parse_recall_hits(&serde_json::json!({})).is_empty());
}
#[test]
fn parse_dream_stats_reads_counts() {
let raw = serde_json::json!({
"merged": 3, "pruned": 1, "compacted": 0,
"closets_updated": 5, "duration_ms": 42,
});
assert_eq!(
parse_dream_stats(&raw),
DreamStats {
merged: 3,
pruned: 1,
compacted: 0,
}
);
assert_eq!(
parse_dream_stats(&serde_json::json!({})),
DreamStats::default()
);
}
#[test]
fn parse_memory_event_maps_type_tag() {
assert_eq!(
parse_memory_event(&serde_json::json!({
"type": "palace_created", "id": "p1", "name": "notes",
})),
Some(MemoryEvent::PalaceCreated {
name: "notes".into(),
})
);
assert_eq!(
parse_memory_event(&serde_json::json!({
"type": "drawer_added",
"palace_id": "default",
"drawer_count": 14,
"content_preview": "How the migration system handles…",
})),
Some(MemoryEvent::DrawerAdded {
palace_id: "default".into(),
drawer_count: 14,
content_preview: "How the migration system handles…".into(),
})
);
assert_eq!(
parse_memory_event(&serde_json::json!({
"type": "drawer_added", "palace_id": "default", "drawer_count": 14,
})),
Some(MemoryEvent::DrawerAdded {
palace_id: "default".into(),
drawer_count: 14,
content_preview: String::new(),
})
);
assert_eq!(
parse_memory_event(&serde_json::json!({
"type": "dream_completed", "merged": 3, "pruned": 1, "compacted": 0,
})),
Some(MemoryEvent::DreamCompleted {
merged: 3,
pruned: 1,
compacted: 0,
})
);
assert!(parse_memory_event(&serde_json::json!({"type": "connected"})).is_none());
assert!(parse_memory_event(&serde_json::json!({"type": "lag", "skipped": 2})).is_none());
assert!(parse_memory_event(&serde_json::json!({"no": "type"})).is_none());
}
#[test]
fn parse_drawers_projects_fields() {
let raw = serde_json::json!([
{
"id": "11111111-1111-1111-1111-111111111111",
"created_at": "2026-05-20T12:34:56Z",
"tags": ["msg:from=cto", "user-tag"],
"content": "ignored when snippet is present",
"snippet": "JWT middleware added",
},
{
"id": "22222222-2222-2222-2222-222222222222",
"created_at": "2026-05-19T08:00:00Z",
"tags": ["creator:client=mpm", "creator:source=http"],
"content": "Plain content for the legacy fallback path",
},
{
"id": "33333333-3333-3333-3333-333333333333",
"created_at": "bad-timestamp",
"tags": [],
},
]);
let drawers = parse_drawers(&raw);
assert_eq!(drawers.len(), 3);
assert_eq!(drawers[0].id, "11111111-1111-1111-1111-111111111111");
assert_eq!(drawers[0].creator, "msg:from=cto");
assert_eq!(drawers[0].tags.len(), 2);
assert!(drawers[0].created_at.is_some());
assert_eq!(drawers[0].snippet.as_deref(), Some("JWT middleware added"));
assert_eq!(drawers[1].creator, "creator:client=mpm");
assert_eq!(
drawers[1].snippet.as_deref(),
Some("Plain content for the legacy fallback path"),
);
assert!(drawers[2].created_at.is_none());
assert_eq!(drawers[2].creator, NO_CREATOR_LABEL);
assert!(drawers[2].snippet.is_none());
let obj = serde_json::json!({
"drawers": [{"id": "abc", "tags": []}],
});
let drawers = parse_drawers(&obj);
assert_eq!(drawers.len(), 1);
assert_eq!(drawers[0].id, "abc");
assert!(parse_drawers(&serde_json::json!("nope")).is_empty());
let null_snippet = serde_json::json!([{
"id": "44444444-4444-4444-4444-444444444444",
"snippet": serde_json::Value::Null,
"tags": [],
}]);
let drawers = parse_drawers(&null_snippet);
assert!(drawers[0].snippet.is_none());
let long_content = "x".repeat(200);
let long = serde_json::json!([{
"id": "55555555-5555-5555-5555-555555555555",
"content": long_content,
"tags": [],
}]);
let drawers = parse_drawers(&long);
let snippet = drawers[0].snippet.as_deref().expect("fallback snippet");
assert_eq!(snippet.chars().count(), DRAWER_SNIPPET_FALLBACK_MAX);
assert!(
snippet.ends_with('…'),
"long fallback snippet must be truncated with ellipsis",
);
}
#[test]
fn parse_memory_details_projects_full_content() {
let raw = serde_json::json!([
{
"id": "11111111-1111-1111-1111-111111111111",
"created_at": "2026-05-20T12:34:56Z",
"tags": ["msg:from=cto"],
"content": "Full memory body the modal renders verbatim.",
},
{
"id": "22222222-2222-2222-2222-222222222222",
"created_at": "bad-timestamp",
"tags": [],
"content": "",
},
]);
let details = parse_memory_details(&raw);
assert_eq!(details.len(), 2);
assert_eq!(details[0].id, "11111111-1111-1111-1111-111111111111");
assert_eq!(
details[0].content,
"Full memory body the modal renders verbatim."
);
assert_eq!(details[0].tags, vec!["msg:from=cto".to_string()]);
assert!(details[0].created_at.is_some());
assert!(details[1].created_at.is_none());
assert!(details[1].content.is_empty());
let obj = serde_json::json!({
"drawers": [{"id": "abc", "content": "wrapped", "tags": []}],
});
let details = parse_memory_details(&obj);
assert_eq!(details.len(), 1);
assert_eq!(details[0].content, "wrapped");
assert!(parse_memory_details(&serde_json::json!("nope")).is_empty());
}
#[test]
fn creator_label_picks_first_match() {
let label = creator_label(&[
"user-tag".into(),
"msg:from=cto".into(),
"creator:client=mpm".into(),
]);
assert_eq!(label, "msg:from=cto");
let label = creator_label(&["tag:creator:client=mpm".into()]);
assert_eq!(label, "tag:creator:client=mpm");
let label = creator_label(&["creator:source=http".into()]);
assert_eq!(label, "creator:source=http");
assert_eq!(
creator_label(&["user-tag".into(), "kind:note".into()]),
NO_CREATOR_LABEL,
);
assert_eq!(creator_label(&[]), NO_CREATOR_LABEL);
}
}