Codex Handbook
memories/write/src/storage_tests.rs 149 lines
use super::rollout_summary_file_stem;use crate::ensure_layout;use crate::raw_memories_file;use crate::rebuild_raw_memories_file_from_memories;use crate::rollout_summaries_dir;use crate::sync_rollout_summaries_from_memories;use chrono::TimeZone;use chrono::Utc;use codex_config::types::DEFAULT_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION;use codex_protocol::ThreadId;use codex_state::Stage1Output;use pretty_assertions::assert_eq;use std::path::PathBuf;use tempfile::tempdir;const FIXED_PREFIX: &str = "2025-02-11T15-35-19-jqmb";fn stage1_output_with_slug(thread_id: ThreadId, rollout_slug: Option<&str>) -> Stage1Output {    Stage1Output {        thread_id,        source_updated_at: Utc.timestamp_opt(123, 0).single().expect("timestamp"),        raw_memory: "raw memory".to_string(),        rollout_summary: "summary".to_string(),        rollout_slug: rollout_slug.map(ToString::to_string),        rollout_path: PathBuf::from("/tmp/rollout.jsonl"),        cwd: PathBuf::from("/tmp/workspace"),        git_branch: None,        generated_at: Utc.timestamp_opt(124, 0).single().expect("timestamp"),    }}fn fixed_thread_id() -> ThreadId {    ThreadId::try_from("0194f5a6-89ab-7cde-8123-456789abcdef").expect("valid thread id")}#[test]fn rollout_summary_file_stem_uses_uuid_timestamp_and_hash_when_slug_missing() {    let thread_id = fixed_thread_id();    let memory = stage1_output_with_slug(thread_id, /*rollout_slug*/ None);    assert_eq!(rollout_summary_file_stem(&memory), FIXED_PREFIX);}#[test]fn rollout_summary_file_stem_sanitizes_and_truncates_slug() {    let thread_id = fixed_thread_id();    let memory = stage1_output_with_slug(        thread_id,        Some("Unsafe Slug/With Spaces & Symbols + EXTRA_LONG_12345_67890_ABCDE_fghij_klmno"),    );    let stem = rollout_summary_file_stem(&memory);    let slug = stem        .strip_prefix(&format!("{FIXED_PREFIX}-"))        .expect("slug suffix should be present");    assert_eq!(slug.len(), 60);    assert_eq!(        slug,        "unsafe_slug_with_spaces___symbols___extra_long_12345_67890_a"    );}#[test]fn rollout_summary_file_stem_uses_uuid_timestamp_and_hash_when_slug_is_empty() {    let thread_id = fixed_thread_id();    let memory = stage1_output_with_slug(thread_id, Some(""));    assert_eq!(rollout_summary_file_stem(&memory), FIXED_PREFIX);}#[tokio::test]async fn sync_rollout_summaries_and_raw_memories_file_keeps_latest_memories_only() {    let dir = tempdir().expect("tempdir");    let root = dir.path().join("memory");    ensure_layout(&root).await.expect("ensure layout");    let keep_id = ThreadId::default().to_string();    let drop_id = ThreadId::default().to_string();    let keep_path = rollout_summaries_dir(&root).join(format!("{keep_id}.md"));    let drop_path = rollout_summaries_dir(&root).join(format!("{drop_id}.md"));    tokio::fs::write(&keep_path, "keep")        .await        .expect("write keep");    tokio::fs::write(&drop_path, "drop")        .await        .expect("write drop");    let memories = vec![Stage1Output {        thread_id: ThreadId::try_from(keep_id.clone()).expect("thread id"),        source_updated_at: Utc.timestamp_opt(100, 0).single().expect("timestamp"),        raw_memory: "raw memory".to_string(),        rollout_summary: "short summary".to_string(),        rollout_slug: None,        rollout_path: PathBuf::from("/tmp/rollout-100.jsonl"),        cwd: PathBuf::from("/tmp/workspace"),        git_branch: None,        generated_at: Utc.timestamp_opt(101, 0).single().expect("timestamp"),    }];    sync_rollout_summaries_from_memories(        &root,        &memories,        DEFAULT_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION,    )    .await    .expect("sync rollout summaries");    rebuild_raw_memories_file_from_memories(        &root,        &memories,        DEFAULT_MEMORIES_MAX_RAW_MEMORIES_FOR_CONSOLIDATION,    )    .await    .expect("rebuild raw memories");    assert!(        !tokio::fs::try_exists(&keep_path)            .await            .expect("check stale keep path"),        "sync should prune stale filename that used thread id only"    );    assert!(        !tokio::fs::try_exists(&drop_path)            .await            .expect("check stale drop path"),        "sync should prune stale filename for dropped thread"    );    let mut dir = tokio::fs::read_dir(rollout_summaries_dir(&root))        .await        .expect("open rollout summaries dir");    let mut files = Vec::new();    while let Some(entry) = dir.next_entry().await.expect("read dir entry") {        files.push(entry.file_name().to_string_lossy().to_string());    }    files.sort_unstable();    assert_eq!(files.len(), 1);    let canonical_rollout_summary_file = &files[0];    let raw_memories = tokio::fs::read_to_string(raw_memories_file(&root))        .await        .expect("read raw memories");    assert!(raw_memories.contains("raw memory"));    assert!(raw_memories.contains(&keep_id));    assert!(raw_memories.contains("cwd: /tmp/workspace"));    assert!(raw_memories.contains("rollout_path: /tmp/rollout-100.jsonl"));    assert!(raw_memories.contains(&format!(        "rollout_summary_file: {canonical_rollout_summary_file}"    )));}