codex-memory 3.0.15

A simple memory storage service with MCP interface for Claude Desktop
Documentation
use chrono::Utc;
use codex_memory::{Memory, StorageStats};
use uuid::Uuid;

#[test]
fn test_memory_new() {
    let content = "Test content".to_string();
    let context = "Test context".to_string();
    let summary = "Test summary".to_string();
    let tags = Some(vec!["tag1".to_string(), "tag2".to_string()]);

    let memory = Memory::new(
        content.clone(),
        context.clone(),
        summary.clone(),
        tags.clone(),
    );

    // Verify basic fields
    assert_eq!(memory.content, content);
    assert_eq!(memory.context, context);
    assert_eq!(memory.summary, summary);
    assert_eq!(memory.tags, vec!["tag1", "tag2"]);

    // Verify auto-generated fields
    assert!(!memory.id.is_nil());
    assert!(!memory.content_hash.is_empty());
    assert!(memory.chunk_index.is_none());
    assert!(memory.total_chunks.is_none());
    assert!(memory.parent_id.is_none());

    // Timestamps should be recent
    let now = Utc::now();
    let diff = now.signed_duration_since(memory.created_at);
    assert!(diff.num_seconds() < 1); // Created within last second
}

#[test]
fn test_memory_new_without_tags() {
    let memory = Memory::new(
        "Content".to_string(),
        "Context".to_string(),
        "Summary".to_string(),
        None,
    );

    assert!(memory.tags.is_empty());
}

#[test]
fn test_memory_new_chunk() {
    let parent_id = Uuid::new_v4();
    let content = "Chunk content".to_string();
    let context = "Chunk context".to_string();
    let summary = "Chunk summary".to_string();
    let tags = Some(vec!["chunk".to_string()]);

    let memory = Memory::new_chunk(
        content.clone(),
        context.clone(),
        summary.clone(),
        tags,
        2, // chunk_index
        5, // total_chunks
        parent_id,
    );

    // Verify chunk-specific fields
    assert_eq!(memory.chunk_index, Some(2));
    assert_eq!(memory.total_chunks, Some(5));
    assert_eq!(memory.parent_id, Some(parent_id));

    // Verify other fields still work
    assert_eq!(memory.content, content);
    assert_eq!(memory.context, context);
    assert_eq!(memory.summary, summary);
    assert_eq!(memory.tags, vec!["chunk"]);
}

#[test]
fn test_memory_content_hash_uniqueness() {
    let memory1 = Memory::new(
        "Content 1".to_string(),
        "Context".to_string(),
        "Summary".to_string(),
        None,
    );

    let memory2 = Memory::new(
        "Content 2".to_string(),
        "Context".to_string(),
        "Summary".to_string(),
        None,
    );

    // Different content should produce different hashes
    assert_ne!(memory1.content_hash, memory2.content_hash);
}

#[test]
fn test_memory_content_hash_consistency() {
    let content = "Same content".to_string();

    let memory1 = Memory::new(
        content.clone(),
        "Context 1".to_string(),
        "Summary 1".to_string(),
        Some(vec!["tag1".to_string()]),
    );

    let memory2 = Memory::new(
        content.clone(),
        "Context 2".to_string(),
        "Summary 2".to_string(),
        Some(vec!["tag2".to_string()]),
    );

    // Same content should produce same hash (for deduplication)
    assert_eq!(memory1.content_hash, memory2.content_hash);
}

#[test]
fn test_memory_hash_format() {
    let memory = Memory::new(
        "Test".to_string(),
        "Context".to_string(),
        "Summary".to_string(),
        None,
    );

    // SHA-256 produces 64 hex characters
    assert_eq!(memory.content_hash.len(), 64);

    // Should be valid hex
    assert!(memory.content_hash.chars().all(|c| c.is_ascii_hexdigit()));
}

#[test]
fn test_storage_stats_fields() {
    let now = Utc::now();
    let stats = StorageStats {
        total_memories: 42,
        table_size: "1.5 MB".to_string(),
        last_memory_created: Some(now),
    };

    assert_eq!(stats.total_memories, 42);
    assert_eq!(stats.table_size, "1.5 MB");
    assert_eq!(stats.last_memory_created, Some(now));
}

#[test]
fn test_storage_stats_serialization() {
    use serde_json;

    let stats = StorageStats {
        total_memories: 100,
        table_size: "2 MB".to_string(),
        last_memory_created: None,
    };

    // Should serialize to JSON
    let json = serde_json::to_string(&stats).expect("Should serialize");
    assert!(json.contains("\"total_memories\":100"));
    assert!(json.contains("\"table_size\":\"2 MB\""));
    assert!(json.contains("\"last_memory_created\":null"));

    // Should deserialize back
    let deserialized: StorageStats = serde_json::from_str(&json).expect("Should deserialize");
    assert_eq!(deserialized.total_memories, stats.total_memories);
    assert_eq!(deserialized.table_size, stats.table_size);
    assert_eq!(deserialized.last_memory_created, stats.last_memory_created);
}