magi-code 0.63.1

Repository-aware CLI coding agent for terminal work
Documentation
use super::*;

#[test]
fn context_cache_key_is_provider_aware() {
    let messages = vec![ChatMessage::user("hello")];
    let openai = ContextCache::key("openai", "m", "sys", &messages, &[]);
    let codex = ContextCache::key("openai-codex", "m", "sys", &messages, &[]);
    assert_ne!(openai, codex);
}

#[test]
fn context_cache_material_key_matches_conversation_key_for_multi_tool_conversation() {
    let temp = TempDir::new().unwrap();
    let file = temp.path().join("a.txt");
    fs::write(&file, "one").unwrap();
    let fingerprint = FileFingerprint::from_path(&file).unwrap();
    let items = vec![
        ProviderConversationItem::Message(ChatMessage::user("inspect files")),
        ProviderConversationItem::ResponseItem(json!({
            "type": "function_call",
            "call_id": "call_1",
            "name": "read",
            "arguments": "{\"path\":\"a.txt\"}",
        })),
        ProviderConversationItem::ToolResult(ProviderToolResult {
            call_id: "call_1".to_string(),
            tool_name: "read".to_string(),
            success: true,
            output: "one".to_string(),
        }),
        ProviderConversationItem::ResponseItem(json!({
            "type": "function_call",
            "call_id": "call_2",
            "name": "read",
            "arguments": "{\"path\":\"b.txt\"}",
        })),
        ProviderConversationItem::ToolResult(ProviderToolResult {
            call_id: "call_2".to_string(),
            tool_name: "read".to_string(),
            success: false,
            output: "missing".to_string(),
        }),
    ];
    let material = conversation_cache_material("p", "m", "sys", &items);

    assert_eq!(
        ContextCache::key_for_material(&material, std::slice::from_ref(&fingerprint)),
        ContextCache::key_for_conversation("p", "m", "sys", &items, &[fingerprint])
    );
}

#[test]
fn cache_key_is_stable_and_invalidates_on_file_change() {
    let temp = TempDir::new().unwrap();
    let file = temp.path().join("a.txt");
    fs::write(&file, "one").unwrap();
    let fp1 = FileFingerprint::from_path(&file).unwrap();
    assert_eq!(
        fp1.content_hash,
        "7692c3ad3540bb803c020b3aee66cd8887123234ea0c6e7143c0add73ff431ed"
    );
    assert_eq!(fp1.content_hash.len(), 64);
    assert!(
        fp1.content_hash
            .chars()
            .all(|ch| { ch.is_ascii_hexdigit() && !ch.is_ascii_uppercase() })
    );
    let messages = vec![ChatMessage::user("hello")];
    let key1 = ContextCache::key("p", "m", "sys", &messages, std::slice::from_ref(&fp1));
    let key2 = ContextCache::key("p", "m", "sys", &messages, &[fp1]);
    assert_eq!(key1, key2);
    fs::write(&file, "two").unwrap();
    let fp2 = FileFingerprint::from_path(&file).unwrap();
    let key3 = ContextCache::key("p", "m", "sys", &messages, &[fp2]);
    assert_ne!(key1, key3);
}

#[test]
fn cache_read_write_round_trip() {
    let temp = TempDir::new().unwrap();
    let cache = ContextCache::new(temp.path().join("cache"));
    let key = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
    let entry = ContextCacheEntry {
        key: key.to_string(),
        token_estimate: 3,
        messages: vec![ChatMessage::user("hi")],
        input_material: String::new(),
    };
    assert!(cache.read(key).unwrap().is_none());
    cache.write(&entry).unwrap();
    assert_eq!(cache.read(key).unwrap(), Some(entry));
}

#[test]
fn context_cache_key_is_sha256_hex() {
    let key = ContextCache::key("p", "m", "sys", &[ChatMessage::user("hi")], &[]);
    assert_eq!(key.len(), 64);
    assert!(
        key.chars()
            .all(|ch| ch.is_ascii_hexdigit() && !ch.is_ascii_uppercase())
    );
}

#[test]
fn cache_accepts_valid_generated_key_round_trip() {
    let temp = TempDir::new().unwrap();
    let cache = ContextCache::new(temp.path().join("cache"));
    let key = ContextCache::key("p", "m", "sys", &[ChatMessage::user("hi")], &[]);
    let entry = ContextCacheEntry {
        key: key.clone(),
        token_estimate: 1,
        messages: vec![ChatMessage::user("hi")],
        input_material: String::new(),
    };
    cache.write(&entry).unwrap();
    assert_eq!(cache.read(&key).unwrap(), Some(entry));
}

#[test]
fn cache_rejects_traversal_keys() {
    let temp = TempDir::new().unwrap();
    let cache = ContextCache::new(temp.path().join("cache"));
    for key in ["", "..", "../escape", "abc/def", "not-hex-value"] {
        assert!(cache.read(key).is_err(), "accepted {key}");
    }
}

#[test]
fn cache_rejects_absolute_or_prefixed_keys() {
    let temp = TempDir::new().unwrap();
    let cache = ContextCache::new(temp.path().join("cache"));
    for key in [
        "/tmp/escape",
        "C:\\escape",
        "0123456789abcdeg0123456789abcdef0123456789abcdef0123456789abcdef",
        "ABCDEF0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
    ] {
        assert!(cache.read(key).is_err(), "accepted {key}");
    }
}

#[test]
fn conversation_replay_rejects_oversized_jsonl_without_materializing_all_events() {
    let temp = TempDir::new().unwrap();
    let manager = crate::sessions::SessionManager::new(temp.path().join("sessions"));
    let session = manager.open("safe").unwrap();
    let event = SessionEvent::new(
        "user_input",
        session.id().to_string(),
        temp.path().to_path_buf(),
        json!({"text":"x".repeat(512)}),
    );
    fs::create_dir_all(session.path().parent().unwrap()).unwrap();
    fs::write(
        session.path(),
        format!("{}\n", serde_json::to_string(&event).unwrap()),
    )
    .unwrap();
    crate::sessions::secure_test_session_root(session.path().parent().unwrap());

    let error = build_conversation_replay_with_limits(
        Some(&session),
        ConversationReplayLimits {
            max_lines: 100,
            max_bytes: 128,
        },
    )
    .unwrap_err()
    .to_string();

    assert!(
        error.contains("session replay exceeded bounded history budget"),
        "{error}"
    );
    assert!(error.contains("start /new"), "{error}");
}
#[test]
fn context_cache_key_uses_ordered_conversation_material() {
    let first = vec![
        ProviderConversationItem::Message(ChatMessage::user("one")),
        ProviderConversationItem::Message(ChatMessage::assistant("two")),
    ];
    let reversed = vec![
        ProviderConversationItem::Message(ChatMessage::assistant("two")),
        ProviderConversationItem::Message(ChatMessage::user("one")),
    ];
    assert_ne!(
        ContextCache::key_for_conversation("p", "m", "sys", &first, &[]),
        ContextCache::key_for_conversation("p", "m", "sys", &reversed, &[])
    );
}

#[test]
fn context_cache_material_has_stable_prefix_when_history_appends() {
    let prefix_items = vec![ProviderConversationItem::Message(ChatMessage::user("one"))];
    let mut appended_items = prefix_items.clone();
    appended_items.push(ProviderConversationItem::Message(ChatMessage::assistant(
        "two",
    )));
    let prefix = conversation_cache_material("p", "m", "sys", &prefix_items);
    let appended = conversation_cache_material("p", "m", "sys", &appended_items);
    assert!(appended.starts_with(&prefix));
    assert_ne!(prefix, appended);
}

#[test]
#[ignore = "diagnostic-only optimization measurement; run explicitly with --ignored --nocapture"]
fn optimization_harness_measures_large_replay_and_cache_material() {
    // Large count is fixture data for optimization measurement, not a runtime ceiling.
    let temp = TempDir::new().unwrap();
    let manager = crate::sessions::SessionManager::new(temp.path().join("sessions"));
    let session = manager.create().unwrap();
    let tool_pair_count = 1_500;
    for index in 0..tool_pair_count {
        session
            .append(&SessionEvent::new(
                "tool_call",
                session.id().to_string(),
                temp.path().to_path_buf(),
                json!({
                    "id": format!("call_{index}"),
                    "name": "read",
                    "arguments": {"path": format!("fixtures/{index}.txt")}
                }),
            ))
            .unwrap();
        session
            .append(&SessionEvent::new(
                "tool_result",
                session.id().to_string(),
                temp.path().to_path_buf(),
                json!({
                    "call_id": format!("call_{index}"),
                    "result": {
                        "tool_name": "read",
                        "success": true,
                        "content": format!("file contents {index}")
                    }
                }),
            ))
            .unwrap();
    }

    let replay_started = std::time::Instant::now();
    let replay = build_conversation_replay(Some(&session)).unwrap();
    let replay_elapsed = replay_started.elapsed();
    let material_started = std::time::Instant::now();
    let material = conversation_cache_material("p", "m", "sys", &replay.items);
    let material_elapsed = material_started.elapsed();

    println!(
        "optimization_harness context_replay tool_pairs={} items={} material_bytes={} replay_elapsed={replay_elapsed:?} material_elapsed={material_elapsed:?}",
        tool_pair_count,
        replay.items.len(),
        material.len()
    );
    assert_eq!(replay.items.len(), tool_pair_count * 2);
    assert!(material.contains(&format!("call_{}", tool_pair_count - 1)));
}