use std::path::{Path, PathBuf};
#[path = "boundary.rs"]
mod implementations;
pub use implementations::{MemoryConfigEnvironment, RealConfigEnvironment};
pub trait ConfigEnvironment: Send + Sync {
fn unified_config_path(&self) -> Option<PathBuf>;
fn local_config_path(&self) -> Option<PathBuf> {
Some(PathBuf::from(".agent/ralph-workflow.toml"))
}
fn prompt_path(&self) -> PathBuf {
PathBuf::from("PROMPT.md")
}
fn file_exists(&self, path: &Path) -> bool;
fn read_file(&self, path: &Path) -> std::io::Result<String>;
fn write_file(&self, path: &Path, content: &str) -> std::io::Result<()>;
fn create_dir_all(&self, path: &Path) -> std::io::Result<()>;
fn worktree_root(&self) -> Option<PathBuf> {
None }
fn get_env_var(&self, _key: &str) -> Option<String> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_real_environment_returns_path() {
let env = RealConfigEnvironment;
let path = env.unified_config_path();
if let Some(p) = path {
assert!(p.to_string_lossy().contains("ralph-workflow.toml"));
}
}
#[test]
fn test_memory_environment_with_custom_paths() {
let env = MemoryConfigEnvironment::new()
.with_unified_config_path("/custom/config.toml")
.with_prompt_path("/custom/PROMPT.md");
assert_eq!(
env.unified_config_path(),
Some(PathBuf::from("/custom/config.toml"))
);
assert_eq!(env.prompt_path(), PathBuf::from("/custom/PROMPT.md"));
}
#[test]
fn test_memory_environment_default_prompt_path() {
let env = MemoryConfigEnvironment::new();
assert_eq!(env.prompt_path(), PathBuf::from("PROMPT.md"));
}
#[test]
fn test_memory_environment_no_unified_config() {
let env = MemoryConfigEnvironment::new();
assert_eq!(env.unified_config_path(), None);
}
#[test]
fn test_memory_environment_file_operations() {
let env = MemoryConfigEnvironment::new();
let path = Path::new("/test/file.txt");
assert!(!env.file_exists(path));
env.write_file(path, "test content").unwrap();
assert!(env.file_exists(path));
assert_eq!(env.read_file(path).unwrap(), "test content");
assert!(env.was_written(path));
}
#[test]
fn test_memory_environment_with_prepopulated_file() {
let env =
MemoryConfigEnvironment::new().with_file("/test/existing.txt", "existing content");
assert!(env.file_exists(Path::new("/test/existing.txt")));
assert_eq!(
env.read_file(Path::new("/test/existing.txt")).unwrap(),
"existing content"
);
}
#[test]
fn test_memory_environment_read_nonexistent_file() {
let env = MemoryConfigEnvironment::new();
let result = env.read_file(Path::new("/nonexistent"));
assert!(result.is_err());
assert_eq!(result.unwrap_err().kind(), std::io::ErrorKind::NotFound);
}
#[test]
fn test_memory_environment_with_worktree_root() {
let env = MemoryConfigEnvironment::new().with_worktree_root("/test/worktree");
assert_eq!(env.worktree_root(), Some(PathBuf::from("/test/worktree")));
assert_eq!(
env.local_config_path(),
Some(PathBuf::from("/test/worktree/.agent/ralph-workflow.toml"))
);
}
#[test]
fn test_memory_environment_without_worktree_root() {
let env = MemoryConfigEnvironment::new();
assert_eq!(env.worktree_root(), None);
assert_eq!(
env.local_config_path(),
Some(PathBuf::from(".agent/ralph-workflow.toml"))
);
}
#[test]
fn test_memory_environment_explicit_local_path_overrides_worktree() {
let env = MemoryConfigEnvironment::new()
.with_worktree_root("/test/worktree")
.with_local_config_path("/custom/path/config.toml");
assert_eq!(
env.local_config_path(),
Some(PathBuf::from("/custom/path/config.toml"))
);
}
#[test]
fn test_canonical_repo_root_used_for_local_config_path() {
let canonical_root = "/home/user/my-repo";
let env = MemoryConfigEnvironment::new().with_worktree_root(canonical_root);
assert_eq!(env.worktree_root(), Some(PathBuf::from(canonical_root)));
assert_eq!(
env.local_config_path(),
Some(PathBuf::from(
"/home/user/my-repo/.agent/ralph-workflow.toml"
))
);
}
#[test]
fn test_worktree_root_and_local_config_path_consistency() {
let env = MemoryConfigEnvironment::new().with_worktree_root("/repos/main-repo");
let root = env.worktree_root().unwrap();
let local_path = env.local_config_path().unwrap();
assert_eq!(local_path, root.join(".agent/ralph-workflow.toml"));
}
}