ralph-coder 0.2.1

An agentic code generation CLI powered by multiple LLM backends
Documentation
/// Session persistence tests — all state in TempDir.
use ralph::config::{Config, SessionConfig};
use ralph::providers::{Message, Role};
use ralph::session::{Session, SessionStatus};
use tempfile::TempDir;

fn test_config(sessions_dir: &str) -> Config {
    let mut config = Config::default();
    config.session = SessionConfig {
        auto_resume: true,
        sessions_dir: Some(sessions_dir.to_string()),
        clean_after_days: 30,
    };
    config
}

#[test]
fn create_and_load_session() {
    let dir = TempDir::new().unwrap();
    let workspace = dir.path().join("workspace");
    std::fs::create_dir_all(&workspace).unwrap();

    let sessions_dir = dir.path().join("sessions");
    let config = test_config(&sessions_dir.display().to_string());

    let session = Session::create(&workspace, "anthropic", "claude-sonnet-4-6", &config).unwrap();
    let session_id = session.meta.session_id.clone();

    // Reload
    let loaded = Session::load(&session_id, &config).unwrap();
    assert_eq!(loaded.meta.session_id, session_id);
    assert_eq!(loaded.meta.provider, "anthropic");
    assert_eq!(loaded.meta.model, "claude-sonnet-4-6");
}

#[test]
fn save_turn_persists_messages() {
    let dir = TempDir::new().unwrap();
    let workspace = dir.path().join("workspace");
    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, "openai", "gpt-4o", &config).unwrap();
    let session_id = session.meta.session_id.clone();

    let msgs = vec![Message::user("hello"), Message::assistant("hi there")];
    session.save_turn(&msgs, 1).unwrap();

    let loaded = Session::load(&session_id, &config).unwrap();
    assert_eq!(loaded.messages.len(), 2);
    assert_eq!(loaded.meta.turn_count, 1);
}

#[test]
fn set_status_persists() {
    let dir = TempDir::new().unwrap();
    let workspace = dir.path().join("workspace");
    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, "deepseek", "deepseek-chat", &config).unwrap();
    let id = session.meta.session_id.clone();

    session.set_status(SessionStatus::Done).unwrap();
    let loaded = Session::load(&id, &config).unwrap();
    assert_eq!(loaded.meta.status, SessionStatus::Done);
}

#[test]
fn find_for_workspace_returns_most_recent() {
    let dir = TempDir::new().unwrap();
    let workspace = dir.path().join("workspace");
    std::fs::create_dir_all(&workspace).unwrap();

    let sessions_dir = dir.path().join("sessions");
    let config = test_config(&sessions_dir.display().to_string());

    let s1 = Session::create(&workspace, "anthropic", "claude-sonnet-4-6", &config).unwrap();
    let id1 = s1.meta.session_id.clone();

    // Brief pause to ensure different timestamps
    std::thread::sleep(std::time::Duration::from_millis(10));

    let s2 = Session::create(&workspace, "openai", "gpt-4o", &config).unwrap();
    let id2 = s2.meta.session_id.clone();

    let found = Session::find_for_workspace(&workspace, &config).unwrap();
    // Should find the most recently created (s2)
    assert_eq!(found.meta.session_id, id2);
    let _ = id1; // suppress warning
}

#[test]
fn list_all_returns_sessions() {
    let dir = TempDir::new().unwrap();
    let workspace = dir.path().join("workspace");
    std::fs::create_dir_all(&workspace).unwrap();

    let sessions_dir = dir.path().join("sessions");
    let config = test_config(&sessions_dir.display().to_string());

    Session::create(&workspace, "anthropic", "claude-sonnet-4-6", &config).unwrap();
    Session::create(&workspace, "openai", "gpt-4o", &config).unwrap();

    let all = Session::list_all(&config);
    assert_eq!(all.len(), 2);
}

#[test]
fn load_nonexistent_session_errors() {
    let dir = TempDir::new().unwrap();
    let sessions_dir = dir.path().join("sessions");
    let config = test_config(&sessions_dir.display().to_string());

    assert!(Session::load("nonexistent-session-id", &config).is_err());
}