use ralph::checkpoint::Checkpoint;
use ralph::config::{Config, SessionConfig};
use ralph::session::Session;
use ralph::tools::file_tools;
use tempfile::TempDir;
fn test_config(sessions_dir: &str) -> Config {
let mut config = Config::default();
config.session = SessionConfig {
auto_resume: false,
sessions_dir: Some(sessions_dir.to_string()),
clean_after_days: 30,
};
config
}
#[tokio::test]
async fn revert_restores_and_deletes() {
let dir = TempDir::new().unwrap();
let workspace = dir.path().join("ws");
std::fs::create_dir_all(&workspace).unwrap();
let sessions_dir = dir.path().join("sessions");
let config = test_config(&sessions_dir.display().to_string());
let existing_file = workspace.join("existing.rs");
file_tools::write_file(&existing_file, "original content").unwrap();
let mut session =
Session::create(&workspace, "anthropic", "claude-sonnet-4-6", &config).unwrap();
session.modified_files.push(existing_file.clone());
session.flush().unwrap();
let _cp = Checkpoint::create(&session, "baseline", &Default::default())
.await
.unwrap();
file_tools::write_file(&existing_file, "modified content").unwrap();
let new_file = workspace.join("new_file.rs");
file_tools::write_file(&new_file, "new file content").unwrap();
session.modified_files.push(new_file.clone());
session.flush().unwrap();
assert_eq!(
std::fs::read_to_string(&existing_file).unwrap(),
"modified content"
);
assert!(new_file.exists());
let loaded_cp = Checkpoint::find(&session, "baseline").unwrap();
let cp_files: std::collections::HashSet<String> =
loaded_cp.meta.modified_files.iter().cloned().collect();
for file_path in &session.modified_files {
let rel = file_path
.strip_prefix(&workspace)
.unwrap_or(file_path)
.to_string_lossy()
.to_string();
if !cp_files.contains(&rel) && file_path.exists() {
std::fs::remove_file(file_path).unwrap();
}
}
let files_dir = loaded_cp.dir.join("files");
if files_dir.exists() {
for entry in walkdir_flat(&files_dir) {
let rel = entry.strip_prefix(&files_dir).unwrap_or(&entry);
let dest = workspace.join(rel);
if let Some(parent) = dest.parent() {
std::fs::create_dir_all(parent).unwrap();
}
std::fs::copy(&entry, &dest).unwrap();
}
}
assert_eq!(
std::fs::read_to_string(&existing_file).unwrap(),
"original content"
);
assert!(
!new_file.exists(),
"post-checkpoint file should have been deleted"
);
}
#[tokio::test]
async fn revert_files_only_leaves_messages() {
let dir = TempDir::new().unwrap();
let workspace = dir.path().join("ws");
std::fs::create_dir_all(&workspace).unwrap();
let sessions_dir = dir.path().join("sessions");
let config = test_config(&sessions_dir.display().to_string());
let mut session =
Session::create(&workspace, "anthropic", "claude-sonnet-4-6", &config).unwrap();
session
.messages
.push(ralph::providers::Message::user("task 1"));
session
.messages
.push(ralph::providers::Message::assistant("done 1"));
session.flush().unwrap();
let cp = Checkpoint::create(&session, "msg-checkpoint", &Default::default())
.await
.unwrap();
assert_eq!(cp.messages.len(), 2);
session
.messages
.push(ralph::providers::Message::user("task 2"));
session.flush().unwrap();
assert_eq!(session.messages.len(), 3);
let loaded = Checkpoint::find(&session, "msg-checkpoint").unwrap();
assert_eq!(loaded.messages.len(), 2);
}
fn walkdir_flat(dir: &std::path::Path) -> Vec<std::path::PathBuf> {
let mut files = Vec::new();
if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
files.extend(walkdir_flat(&path));
} else {
files.push(path);
}
}
}
files
}