git-comma 1.0.9

AI-powered git commit message generator using OpenRouter API
Documentation
use tempfile::TempDir;

#[test]
fn test_config_load_preserves_api_key_field() {
    let tmp = TempDir::new().unwrap();
    let path = tmp.path().join("comma.json");

    let cfg = git_comma::config::Config {
        api_key: "sk-or-v1-test".to_string(),
        model_id: "anthropic/claude-3-haiku".to_string(),
        max_chars: 15_000,
    };
    cfg.save(&path).unwrap();

    let loaded = git_comma::config::Config::load_from_path(&path).unwrap();
    assert_eq!(loaded.api_key, "sk-or-v1-test");
    assert_eq!(loaded.model_id, "anthropic/claude-3-haiku");
}

#[test]
fn test_load_config_malformed_json() {
    let tmp = TempDir::new().unwrap();
    let path = tmp.path().join("comma.json");
    std::fs::write(&path, "{invalid json}").unwrap();

    let result = git_comma::config::Config::load_from_path(&path);
    assert!(matches!(result, Err(git_comma::config::ConfigError::MalformedJson)));
}

#[test]
fn test_load_config_missing_file() {
    let tmp = TempDir::new().unwrap();
    let path = tmp.path().join("nonexistent.json");

    let result = git_comma::config::Config::load_from_path(&path);
    assert!(matches!(result, Err(git_comma::config::ConfigError::IoError(_))));
}

#[test]
fn test_load_config_success() {
    let tmp = TempDir::new().unwrap();
    let config_path = tmp.path().join("comma.json");
    let content = r#"{"api_key": "sk-or-v1-test", "model_id": "test/model"}"#;
    std::fs::write(&config_path, content).unwrap();

    let result = git_comma::config::Config::load_from_path(&config_path);
    assert!(result.is_ok());
    let cfg = result.unwrap();
    assert_eq!(cfg.api_key, "sk-or-v1-test");
    assert_eq!(cfg.model_id, "test/model");
}

#[test]
fn test_save_config_atomic() {
    let tmp = TempDir::new().unwrap();
    let config_path = tmp.path().join("comma.json");

    let cfg = git_comma::config::Config {
        api_key: "sk-or-v1-test".into(),
        model_id: "test/model".into(),
        max_chars: 15_000,
    };

    cfg.save(&config_path).unwrap();

    let loaded = std::fs::read_to_string(&config_path).unwrap();
    assert!(loaded.contains("sk-or-v1-test"));
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let meta = std::fs::metadata(&config_path).unwrap();
        let perm = meta.permissions().mode();
        assert_eq!(perm & 0o777, 0o600);
    }
}

#[test]
fn test_config_max_chars_defaults_to_15000() {
    let tmp = TempDir::new().unwrap();
    let path = tmp.path().join("comma.json");
    // Write JSON without max_chars field (simulates existing config)
    std::fs::write(&path, r#"{"api_key":"sk-or-v1-test","model_id":"test/model"}"#).unwrap();
    let loaded = git_comma::config::Config::load_from_path(&path).unwrap();
    assert_eq!(loaded.max_chars, 15_000);
}

#[test]
fn test_config_max_chars_round_trip() {
    let tmp = TempDir::new().unwrap();
    let path = tmp.path().join("comma.json");
    let cfg = git_comma::config::Config {
        api_key: "sk-or-v1-test".to_string(),
        model_id: "test/model".to_string(),
        max_chars: 20_000,
    };
    cfg.save(&path).unwrap();
    let loaded = git_comma::config::Config::load_from_path(&path).unwrap();
    assert_eq!(loaded.max_chars, 20_000);
}

#[test]
fn test_config_max_chars_appears_in_saved_json() {
    let tmp = TempDir::new().unwrap();
    let path = tmp.path().join("comma.json");
    let cfg = git_comma::config::Config {
        api_key: "sk-or-v1-test".to_string(),
        model_id: "test/model".to_string(),
        max_chars: 15_000,
    };
    cfg.save(&path).unwrap();
    let json = std::fs::read_to_string(&path).unwrap();
    assert!(json.contains("\"max_chars\""), "saved JSON should contain max_chars field");
    assert!(json.contains("15000"), "saved JSON should contain max_chars value");
}

#[test]
fn test_validate_max_chars_rejects_zero() {
    let result = git_comma::setup::validate_max_chars_input("0");
    assert!(result.is_err());
    assert_eq!(result.unwrap_err(), "Must be at least 1");
}

#[test]
fn test_validate_max_chars_accepts_one() {
    let result = git_comma::setup::validate_max_chars_input("1");
    assert_eq!(result.unwrap(), 1);
}

#[test]
fn test_validate_max_chars_accepts_empty_as_default() {
    let result = git_comma::setup::validate_max_chars_input("");
    assert_eq!(result.unwrap(), 15_000); // keeps default
}

#[test]
fn test_validate_max_chars_rejects_non_numeric() {
    let result = git_comma::setup::validate_max_chars_input("abc");
    assert!(result.is_err());
}

#[test]
fn test_check_diff_size_within_limit() {
    let diff = "a".repeat(25_000);
    let result = git_comma::preflight::check_diff_size(&diff, 30_000);
    assert!(result.is_ok());
}

#[test]
fn test_check_diff_size_exceeds_limit() {
    let diff = "a".repeat(15_000);
    let result = git_comma::preflight::check_diff_size(&diff, 10_000);
    assert!(result.is_err());
    match result.unwrap_err() {
        git_comma::preflight::PreflightError::DiffTooLarge { size } => {
            assert_eq!(size, 15_000);
        }
        other => panic!("Expected DiffTooLarge, got: {:?}", other),
    }
}

#[test]
fn test_check_diff_size_exact_limit() {
    let diff = "a".repeat(10_000);
    let result = git_comma::preflight::check_diff_size(&diff, 10_000);
    assert!(result.is_ok()); // exactly at limit passes
}