opencrabs 0.3.56

The autonomous, self-improving AI agent. Single Rust binary. Every channel. Install with: cargo install opencrabs
Documentation
use crate::utils::string::*;

#[test]
fn test_strip_ctx_footer() {
    assert_eq!(
        strip_ctx_footer("Hello\n\nctx: 84K/200K 42% | 406 tok/s"),
        "Hello"
    );
    assert_eq!(strip_ctx_footer("ctx: 8K/200K 4%"), "");
    assert_eq!(
        strip_ctx_footer("Some text\nctx: 1M/2M 50% | 123 tok/s\nMore text"),
        "Some text\nMore text"
    );
    // Don't strip lines that just happen to contain a colon
    assert_eq!(
        strip_ctx_footer("Note: something here"),
        "Note: something here"
    );
    // Multi-line without footer is unchanged
    let multi = "line1\nline2\nline3";
    assert_eq!(strip_ctx_footer(multi), multi);
}

#[test]
fn test_truncate_str_ascii() {
    assert_eq!(truncate_str("hello world", 5), "hello");
    assert_eq!(truncate_str("hello", 10), "hello");
    assert_eq!(truncate_str("hello", 5), "hello");
}

#[test]
fn test_truncate_str_multibyte_boundary() {
    // █ is U+2588, 3 bytes in UTF-8
    let s = "abc█def";
    // "abc" = 3 bytes, "█" = bytes 3..6, "def" = bytes 6..9
    assert_eq!(truncate_str(s, 3), "abc"); // exact boundary before █
    assert_eq!(truncate_str(s, 4), "abc"); // inside █, backs up to 3
    assert_eq!(truncate_str(s, 5), "abc"); // inside █, backs up to 3
    assert_eq!(truncate_str(s, 6), "abc█"); // exact boundary after █
}

#[test]
fn test_truncate_str_emoji() {
    // 🦀 is U+1F980, 4 bytes in UTF-8
    let s = "hi🦀bye";
    // "hi" = 2 bytes, "🦀" = bytes 2..6, "bye" = bytes 6..9
    assert_eq!(truncate_str(s, 2), "hi");
    assert_eq!(truncate_str(s, 3), "hi"); // inside 🦀
    assert_eq!(truncate_str(s, 5), "hi"); // inside 🦀
    assert_eq!(truncate_str(s, 6), "hi🦀");
}

#[test]
fn test_truncate_str_zero() {
    assert_eq!(truncate_str("hello", 0), "");
    assert_eq!(truncate_str("🦀", 0), "");
}

#[test]
fn test_truncate_str_empty() {
    assert_eq!(truncate_str("", 5), "");
    assert_eq!(truncate_str("", 0), "");
}

#[test]
fn test_truncate_str_all_multibyte() {
    // Each char is 3 bytes
    let s = "███"; // 9 bytes
    assert_eq!(truncate_str(s, 1), ""); // inside first █
    assert_eq!(truncate_str(s, 3), "");
    assert_eq!(truncate_str(s, 7), "██"); // inside third █
    assert_eq!(truncate_str(s, 9), "███");
}

#[test]
fn test_truncate_middle_preserves_tail() {
    let s = "/Users/alice/srv/myapp/lib/presentation/pages/some_really_long_widget_name.dart";
    let out = truncate_middle(s, 40);
    // The filename must survive — that's the whole point of middle-elide.
    assert!(
        out.ends_with("some_really_long_widget_name.dart")
            || out.ends_with("_name.dart")
            || out.ends_with(".dart"),
        "expected filename tail preserved, got: {}",
        out
    );
    assert!(out.contains(''), "expected ellipsis marker in middle");
    assert!(out.len() <= 40, "expected <= 40 bytes, got {}", out.len());
}

#[test]
fn test_truncate_middle_short_string_unchanged() {
    assert_eq!(truncate_middle("short.rs", 80), "short.rs");
}

#[test]
fn test_truncate_middle_tiny_budget_falls_back() {
    let out = truncate_middle("hello world", 4);
    // Budget too small for head+…+tail — falls back to head-only truncation.
    assert!(out.len() <= 4);
}

#[test]
fn test_tilde_home_collapses_prefix() {
    if let Some(h) = dirs::home_dir() {
        let home_str = h.to_string_lossy();
        let full = format!("{}/srv/project/file.rs", home_str);
        assert_eq!(tilde_home(&full), "~/srv/project/file.rs");
        // Non-home paths untouched.
        assert_eq!(tilde_home("/tmp/foo"), "/tmp/foo");
        // Bash commands with multiple occurrences all collapsed.
        let cmd = format!("cp {}/a {}/b", home_str, home_str);
        assert_eq!(tilde_home(&cmd), "cp ~/a ~/b");
    }
}

// ── looks_like_file_path ───────────────────────────────────────────────

#[test]
fn test_looks_like_file_path_absolute_with_segments() {
    assert!(looks_like_file_path("/Users/alice/Downloads/report.pdf"));
    assert!(looks_like_file_path("/tmp/foo.txt"));
    assert!(looks_like_file_path("/etc/hosts"));
}

#[test]
fn test_looks_like_file_path_with_extension_and_text() {
    // Drag-and-drop pattern: path followed by a message
    assert!(looks_like_file_path("/report.pdf check this"));
    assert!(looks_like_file_path("/data.csv analyze"));
}

#[test]
fn test_looks_like_file_path_slash_commands_are_not_paths() {
    assert!(!looks_like_file_path("/help"));
    assert!(!looks_like_file_path("/models"));
    assert!(!looks_like_file_path("/deploy staging"));
    assert!(!looks_like_file_path("/credits"));
}

#[test]
fn test_looks_like_file_path_no_slash_prefix() {
    assert!(!looks_like_file_path("hello world"));
    assert!(!looks_like_file_path("report.pdf"));
}

// ── format_ctx_footer ───────────────────────────────────────────────────

#[test]
fn test_format_ctx_footer_k_values() {
    assert_eq!(format_ctx_footer(8000, 200000, None), "ctx: 8K/200K 4%");
}

#[test]
fn test_format_ctx_footer_small_values() {
    assert_eq!(format_ctx_footer(500, 200000, None), "ctx: 500/200K 0%");
}

#[test]
fn test_format_ctx_footer_m_values() {
    assert_eq!(
        format_ctx_footer(1200000, 2000000, None),
        "ctx: 1.2M/2.0M 60%"
    );
}

#[test]
fn test_format_ctx_footer_zero_used() {
    assert_eq!(format_ctx_footer(0, 200000, None), "ctx: 0/200K 0%");
}

#[test]
fn test_format_ctx_footer_zero_max() {
    assert_eq!(format_ctx_footer(5000, 0, None), "ctx: 5K/0 0%");
}

#[test]
fn test_format_ctx_footer_full() {
    assert_eq!(
        format_ctx_footer(200000, 200000, None),
        "ctx: 200K/200K 100%"
    );
}

#[test]
fn test_format_ctx_footer_with_tps() {
    assert_eq!(
        format_ctx_footer(8000, 200000, Some(45.7)),
        "ctx: 8K/200K 4% | 46 tok/s"
    );
    assert_eq!(
        format_ctx_footer(500, 200000, Some(123.4)),
        "ctx: 500/200K 0% | 123 tok/s"
    );
}