ralph-coder 0.2.1

An agentic code generation CLI powered by multiple LLM backends
Documentation
use ralph::config::{infer_test_cmd, TestingConfig};
use std::fs;
use tempfile::TempDir;

// ── infer_test_cmd ────────────────────────────────────────────────────────────

#[test]
fn infer_detects_rust_project() {
    let dir = TempDir::new().unwrap();
    fs::write(dir.path().join("Cargo.toml"), "[package]").unwrap();
    assert_eq!(infer_test_cmd(dir.path()), Some("cargo test".to_string()));
}

#[test]
fn infer_detects_go_project() {
    let dir = TempDir::new().unwrap();
    fs::write(dir.path().join("go.mod"), "module example.com/foo").unwrap();
    assert_eq!(
        infer_test_cmd(dir.path()),
        Some("go test ./...".to_string())
    );
}

#[test]
fn infer_detects_python_pytest_ini() {
    let dir = TempDir::new().unwrap();
    fs::write(dir.path().join("pytest.ini"), "[pytest]").unwrap();
    assert_eq!(infer_test_cmd(dir.path()), Some("pytest".to_string()));
}

#[test]
fn infer_detects_python_pyproject() {
    let dir = TempDir::new().unwrap();
    fs::write(dir.path().join("pyproject.toml"), "[tool.pytest]").unwrap();
    assert_eq!(infer_test_cmd(dir.path()), Some("pytest".to_string()));
}

#[test]
fn infer_detects_node_project() {
    let dir = TempDir::new().unwrap();
    fs::write(dir.path().join("package.json"), "{}").unwrap();
    assert_eq!(infer_test_cmd(dir.path()), Some("npm test".to_string()));
}

#[test]
fn infer_returns_none_for_unknown_project() {
    let dir = TempDir::new().unwrap();
    assert!(infer_test_cmd(dir.path()).is_none());
}

#[test]
fn rust_takes_priority_over_node() {
    // If both Cargo.toml and package.json exist, Rust wins (checked first).
    let dir = TempDir::new().unwrap();
    fs::write(dir.path().join("Cargo.toml"), "[package]").unwrap();
    fs::write(dir.path().join("package.json"), "{}").unwrap();
    assert_eq!(infer_test_cmd(dir.path()), Some("cargo test".to_string()));
}

// ── TestingConfig::resolved_cmd ───────────────────────────────────────────────

#[test]
fn resolved_cmd_returns_none_when_auto_test_disabled() {
    let dir = TempDir::new().unwrap();
    fs::write(dir.path().join("Cargo.toml"), "[package]").unwrap();
    let cfg = TestingConfig {
        auto_test: false,
        cmd: String::new(),
        max_retries: 3,
    };
    assert!(cfg.resolved_cmd(dir.path()).is_none());
}

#[test]
fn resolved_cmd_returns_explicit_cmd_when_set() {
    let dir = TempDir::new().unwrap();
    let cfg = TestingConfig {
        auto_test: true,
        cmd: "make test".to_string(),
        max_retries: 3,
    };
    assert_eq!(cfg.resolved_cmd(dir.path()), Some("make test".to_string()));
}

#[test]
fn resolved_cmd_falls_back_to_infer_when_cmd_empty() {
    let dir = TempDir::new().unwrap();
    fs::write(dir.path().join("Cargo.toml"), "[package]").unwrap();
    let cfg = TestingConfig {
        auto_test: true,
        cmd: String::new(),
        max_retries: 3,
    };
    assert_eq!(cfg.resolved_cmd(dir.path()), Some("cargo test".to_string()));
}

#[test]
fn resolved_cmd_returns_none_when_auto_test_enabled_but_undetectable() {
    let dir = TempDir::new().unwrap();
    let cfg = TestingConfig {
        auto_test: true,
        cmd: String::new(),
        max_retries: 3,
    };
    assert!(cfg.resolved_cmd(dir.path()).is_none());
}

// ── TestingConfig defaults ────────────────────────────────────────────────────

#[test]
fn testing_config_default_is_disabled() {
    let cfg = TestingConfig::default();
    assert!(!cfg.auto_test);
    assert!(cfg.cmd.is_empty());
    assert_eq!(cfg.max_retries, 3);
}

// ── test_passed detection (via shell output format) ───────────────────────────

#[test]
fn test_passed_detects_exit_zero() {
    // shell_tools appends "[exit: 0]" on success
    let output = "running 5 tests\ntest foo ... ok\n[exit: 0]";
    assert!(output.trim_end().ends_with("[exit: 0]"));
}

#[test]
fn test_failed_detects_nonzero_exit() {
    let output = "FAILED tests/foo.rs\n[exit: 1]";
    assert!(!output.trim_end().ends_with("[exit: 0]"));
}

// ── system_prompt auto-test section ──────────────────────────────────────────

#[test]
fn system_prompt_includes_auto_test_note_when_cmd_set() {
    let prompt = ralph::prompts::system_prompt("/workspace", false, None, Some("cargo test"), None);
    assert!(prompt.contains("cargo test"));
    assert!(prompt.contains("automatically"));
}

#[test]
fn system_prompt_uses_generic_test_line_when_no_cmd() {
    let prompt = ralph::prompts::system_prompt("/workspace", false, None, None, None);
    assert!(prompt.contains("run_test"));
    assert!(!prompt.contains("automatically"));
}