pi_agent_rust 0.8.0

Native AI coding agent CLI - Rust port of Pi Agent
use super::*;

mod protocol;

#[test]
fn wire_arrays_null_and_completion_lists_are_distinct_valid_results() {
    assert_eq!(list_parts(&Value::Null).unwrap().0.len(), 0);
    assert_eq!(list_parts(&json!([{"label":"x"}])).unwrap().0.len(), 1);
    let raw = json!({"isIncomplete":true,"items":[{"label":"x"}],"itemDefaults":{"data":null}});
    let (items, defaults, incomplete) = list_parts(&raw).unwrap();
    assert_eq!(items.len(), 1);
    assert!(defaults.is_some());
    assert!(incomplete);
}

#[test]
fn malformed_and_oversized_results_are_not_successful_empty_lists() {
    for raw in [
        json!(1),
        json!({}),
        json!({"items":[]}),
        json!({"isIncomplete":false,"items":null}),
        json!([{}]),
        json!([{"label":""}]),
        json!([{"label":"x","sortText":42}]),
        json!({"isIncomplete":false,"items":[],"itemDefaults":false}),
        json!({"isIncomplete":false,"items":[],"applyKind":{}}),
    ] {
        assert!(list_parts(&raw).is_err(), "{raw}");
    }
    assert!(list_parts(&json!(vec![json!({"label":"x"}); MAX_SERVER_ITEMS + 1])).is_err());
    assert!(
        list_parts(&json!({"isIncomplete":false,"items":[],"junk":"x".repeat(MAX_RESPONSE_BYTES)}))
            .is_err()
    );
}

#[test]
fn cache_generations_never_reuse_an_old_listing_identity() {
    let cache = CompletionCache::default();
    assert_eq!(cache.start().unwrap(), 1);
    cache.clear();
    assert_eq!(cache.start().unwrap(), 2);
    lock(&cache.0).serial = u64::MAX;
    assert!(cache.start().is_err());
    assert!(lock(&cache.0).items.is_empty());
}

#[test]
fn listing_and_selection_inputs_do_not_silently_ignore_conflicting_selectors() {
    for raw in [
        json!({"action":"completion"}),
        json!({"action":"completion","file":"a.rs","position":{"line":0,"character":0},"apply":true}),
        json!({"action":"completion","file":"a.rs","position":{"line":0,"character":0},"limit":0}),
        json!({"action":"completion","completionId":"id","file":"a.rs"}),
        json!({"action":"completion","completionId":"id","range":{"start":{"line":0,"character":0},"end":{"line":0,"character":0}}}),
        json!({"action":"completion","completionId":"id","symbol":"x"}),
        json!({"action":"completion","completionId":"id","actionId":"action"}),
    ] {
        assert!(validate_input(&serde_json::from_value(raw).unwrap()).is_err());
    }
    for raw in [
        json!({"action":"completion","file":"a.rs","position":{"line":0,"character":0}}),
        json!({"action":"completion","completionId":"id","apply":true}),
    ] {
        validate_input(&serde_json::from_value(raw).unwrap()).unwrap();
    }
}

#[test]
fn source_admission_is_bounded_and_preserves_exact_utf8() {
    let temp = tempfile::tempdir().unwrap();
    let path = temp.path().join("source");
    std::fs::write(&path, "😀\r\nhello").unwrap();
    assert_eq!(read_source(&path).unwrap(), "😀\r\nhello");
    std::fs::write(&path, [255, 254]).unwrap();
    assert!(read_source(&path).is_err());
    std::fs::write(&path, vec![b'x'; MAX_SOURCE_BYTES + 1]).unwrap();
    assert!(read_source(&path).is_err());
    assert!(read_source(temp.path()).is_err());
}