mirror of
https://github.com/openai/codex.git
synced 2026-11-13 21:30:47 +00:00
## Why Memory baseline snapshots need bounded file reads, and recovery must keep existing memory files visible to consolidation rather than treating them as already processed. ## What changed - Limit baseline files to 10 MiB, skip oversized working files, and check stored blob sizes before reading them. - Version baselines and replace missing, outdated, or unusable metadata with an empty baseline, preserving working files as additions. Also invalidate the baseline when a tracked file grows beyond the limit. - Serialize baseline initialization, resets, and diffs with a cross-process lock. Isolate repository access and pin index writes to the baseline worktree. ## Testing Add coverage for oversized files, legacy baseline migration, interrupted resets, cross-process locking, and consolidation with unchanged inputs after baseline recovery. GitOrigin-RevId: 34ecc9e5700a88ed37eff83a90f08d8a9b0d9f29
167 lines
5.5 KiB
Rust
167 lines
5.5 KiB
Rust
use super::*;
|
|
use codex_git_utils::GitBaselineChange;
|
|
use codex_git_utils::GitBaselineChangeStatus;
|
|
use pretty_assertions::assert_eq;
|
|
use std::fs;
|
|
use tempfile::TempDir;
|
|
|
|
#[test]
|
|
fn render_workspace_diff_file_bounds_large_diff() {
|
|
let diff = GitBaselineDiff {
|
|
changes: vec![GitBaselineChange {
|
|
status: GitBaselineChangeStatus::Modified,
|
|
path: "MEMORY.md".to_string(),
|
|
}],
|
|
unified_diff: "a".repeat(crate::workspace_diff::MAX_BYTES + 128),
|
|
};
|
|
|
|
let rendered = render_workspace_diff_file(&diff);
|
|
|
|
assert!(rendered.contains("- M MEMORY.md"));
|
|
assert!(rendered.contains("[workspace diff truncated at 4194304 bytes]"));
|
|
assert!(rendered.ends_with("```\n"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn reset_memory_workspace_baseline_removes_generated_diff() {
|
|
let home = TempDir::new().expect("tempdir");
|
|
let root = home.path().join("memories");
|
|
prepare_memory_workspace(&root)
|
|
.await
|
|
.expect("prepare memory workspace");
|
|
fs::write(root.join("MEMORY.md"), "memory").expect("write memory");
|
|
write_workspace_diff(
|
|
&root,
|
|
&GitBaselineDiff {
|
|
changes: vec![GitBaselineChange {
|
|
status: GitBaselineChangeStatus::Added,
|
|
path: "MEMORY.md".to_string(),
|
|
}],
|
|
unified_diff: "+memory\n".to_string(),
|
|
},
|
|
)
|
|
.await
|
|
.expect("write workspace diff");
|
|
|
|
reset_memory_workspace_baseline(&root)
|
|
.await
|
|
.expect("reset baseline");
|
|
|
|
assert!(!root.join(crate::workspace_diff::FILENAME).exists());
|
|
assert_eq!(memory_storage_bytes(&root).await.expect("storage size"), 6);
|
|
let diff = memory_workspace_diff(&root)
|
|
.await
|
|
.expect("load workspace diff");
|
|
assert_eq!(diff.changes, Vec::new());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn memory_storage_size_counts_nested_files_but_not_git_or_symlinks() -> anyhow::Result<()> {
|
|
let home = TempDir::new()?;
|
|
for version in [MemoryVersion::V1, MemoryVersion::V2] {
|
|
let root = home.path().join(version.directory_name());
|
|
fs::create_dir_all(root.join("rollout_summaries"))?;
|
|
fs::create_dir_all(root.join("extensions/notes"))?;
|
|
fs::create_dir_all(root.join(".git/objects"))?;
|
|
fs::write(
|
|
root.join(".git/objects/baseline"),
|
|
"git metadata is not memory",
|
|
)?;
|
|
let files = [
|
|
("memory_summary.md", "summary"),
|
|
("rollout_summaries/thread.md", "rollout"),
|
|
("extensions/notes/note.md", "café"),
|
|
];
|
|
for (path, content) in files {
|
|
fs::write(root.join(path), content)?;
|
|
}
|
|
#[cfg(unix)]
|
|
{
|
|
use std::os::unix::fs::symlink;
|
|
symlink(root.join("memory_summary.md"), root.join("file-link"))?;
|
|
symlink(&root, root.join("directory-loop"))?;
|
|
symlink(root.join("missing"), root.join("broken-link"))?;
|
|
}
|
|
assert_eq!(
|
|
memory_storage_bytes(&root).await?,
|
|
files
|
|
.iter()
|
|
.map(|(_, content)| content.len() as u64)
|
|
.sum::<u64>(),
|
|
);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn prepare_memory_workspace_recovers_unusable_git_dir() {
|
|
let home = TempDir::new().expect("tempdir");
|
|
let root = home.path().join("memories");
|
|
fs::create_dir_all(root.join(".git")).expect("create unusable git dir");
|
|
fs::write(root.join("MEMORY.md"), "memory").expect("write memory");
|
|
|
|
prepare_memory_workspace(&root)
|
|
.await
|
|
.expect("prepare memory workspace");
|
|
|
|
let diff = memory_workspace_diff(&root)
|
|
.await
|
|
.expect("load workspace diff");
|
|
assert_eq!(
|
|
diff.changes,
|
|
vec![GitBaselineChange {
|
|
status: GitBaselineChangeStatus::Added,
|
|
path: "MEMORY.md".to_string(),
|
|
}]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn previous_char_boundary_handles_multibyte_text() {
|
|
let text = "aé";
|
|
assert_eq!(previous_char_boundary(text, /*max_bytes*/ 2), 1);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn validate_consolidation_artifacts_rejects_invalid_summary() {
|
|
let home = TempDir::new().expect("tempdir");
|
|
let root = home.path().join("memories");
|
|
fs::create_dir_all(&root).expect("create memory root");
|
|
fs::write(root.join("MEMORY.md"), "memory").expect("write memory");
|
|
fs::write(root.join("memory_summary.md"), "outdated\n").expect("write summary");
|
|
|
|
let err = validate_consolidation_artifacts(&root)
|
|
.await
|
|
.expect_err("invalid summary should fail validation");
|
|
|
|
assert!(err.to_string().contains("does not start with v1"));
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn v2_accepts_summary_only_and_checks_its_format_and_size() -> anyhow::Result<()> {
|
|
let home = TempDir::new()?;
|
|
let root = home.path();
|
|
fs::write(
|
|
root.join("memory_summary.md"),
|
|
"v1\n\n## User Profile\nProfile\n## User preferences\nPreferences\n## General Tips\nTips\n## What's in Memory\nIndex\n",
|
|
)?;
|
|
validate_consolidation_artifacts_for_version(root, MemoryVersion::V2).await?;
|
|
assert!(validate_consolidation_artifacts(root).await.is_err());
|
|
fs::write(root.join("memory_summary.md"), "v2\nsummary")?;
|
|
assert!(
|
|
validate_consolidation_artifacts_for_version(root, MemoryVersion::V2)
|
|
.await
|
|
.is_err()
|
|
);
|
|
fs::write(
|
|
root.join("memory_summary.md"),
|
|
format!("v1\n{}", "x".repeat(10_000)),
|
|
)?;
|
|
assert!(
|
|
validate_consolidation_artifacts_for_version(root, MemoryVersion::V2)
|
|
.await
|
|
.is_err()
|
|
);
|
|
Ok(())
|
|
}
|