pi_agent_rust 0.6.0

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

#[test]
fn actual_native_files_roundtrip_without_reinterpreting_delimiters() {
    let temp = tempfile::tempdir().unwrap();
    for name in [
        "ordinary.rs",
        "space name.rs",
        "hash#name.rs",
        "percent%20literal.rs",
        "plus+name.rs",
        "日本語😀.rs",
    ] {
        let path = temp.path().join(name);
        std::fs::write(&path, name).unwrap();
        let path = path.canonicalize().unwrap();
        let uri = try_path_to_uri(&path).unwrap();
        assert_eq!(path_to_uri(&path), uri);
        let recovered = uri_to_path(&uri).unwrap();
        assert!(recovered.is_absolute());
        assert_eq!(std::fs::read(&recovered).unwrap(), name.as_bytes());
        assert_eq!(try_path_to_uri(&recovered).unwrap(), uri);
        assert!(!uri.contains(' '));
        assert!(url::Url::parse(&uri).unwrap().query().is_none());
        assert!(url::Url::parse(&uri).unwrap().fragment().is_none());
    }
}

#[test]
fn malformed_uris_never_name_a_relative_or_different_file() {
    for uri in [
        "",
        "relative.rs",
        "https://example.com/a",
        "file:relative",
        "file:/tmp/a",
        "file://",
        "file://localhost",
        "file://C:/file",
        "file://user:password@host/file",
        "file://localhost:99/tmp/file",
        "file://local%68ost/tmp/file",
        "file:///tmp/%",
        "file:///tmp/%0",
        "file:///tmp/%GG",
        "file:///tmp/%00",
        "file:///tmp/a%2fb",
        "file:///tmp/a%2Fb",
        "file:////host/share/file",
        "file:///tmp/../outside",
        "file:///tmp/%2e%2e/outside",
        "file:///tmp/.%2E/outside",
        "file:///tmp/./file",
        "file:///tmp/%2e/file",
        "file:///tmp/file?query",
        "file:///tmp/file#fragment",
        "file:///tmp/raw space",
        "file:///tmp/raw\nnewline",
        "file:///tmp/raw\0nul",
        "file:///tmp/raw\\separator",
        " file:///tmp/file",
        "file://./share/name",
        "file://../share/name",
    ] {
        assert!(uri_to_path(uri).is_none(), "accepted {uri:?}");
        assert!(normalize_uri(uri).is_none(), "normalized {uri:?}");
    }
}

#[test]
fn unrepresentable_native_paths_return_errors_before_dispatch() {
    for path in [
        Path::new("relative.rs"),
        Path::new(""),
        Path::new("/tmp/../elsewhere"),
    ] {
        assert!(try_path_to_uri(path).is_err());
        assert!(path_to_uri(path).is_empty());
    }
}

#[test]
fn pure_windows_decoding_supports_drive_roots_and_unc_paths() {
    for (authority, input, expected) in [
        ("", "/C:/Users/me/code.rs", "C:\\Users\\me\\code.rs"),
        (
            "localhost",
            "/c:/Program Files/日本語.rs",
            "C:\\Program Files\\日本語.rs",
        ),
        ("LOCALHOST", "/d:/", "D:\\"),
        (
            "build-server",
            "/share/my project/file.rs",
            "\\\\build-server\\share\\my project\\file.rs",
        ),
        ("BUILD-SERVER", "/share", "\\\\build-server\\share"),
    ] {
        assert_eq!(
            windows_path(authority, input.as_bytes()).as_deref(),
            Some(expected)
        );
    }
    let path = decode_path("/c%3a/Users/me/space%20name.rs").unwrap();
    assert_eq!(
        windows_path("", &path).as_deref(),
        Some("C:\\Users\\me\\space name.rs")
    );
}

#[test]
fn pure_windows_decoding_rejects_lossy_names_devices_and_path_aliases() {
    for input in [
        b"/C:/bad\xff".as_slice(),
        b"/C:/bad\0",
        b"/C:/bad\x01",
        b"/not/a/drive",
        b"/C:relative",
        b"/C:",
        b"/1:/file",
        b"/C|/file",
        b"/C:/file:stream",
        b"/C:/trailing.",
        b"/C:/trailing ",
        b"/C:/NUL.txt",
        b"/C:/nul .txt",
        b"/C:/COM1",
        b"/C:/lpt9.log",
        b"/C:/CONIN$",
        b"/C:/bad\\component",
        b"/C:/bad?name",
        b"/C:/bad*name",
        b"/C:/bad|name",
    ] {
        assert!(windows_path("", input).is_none(), "accepted {input:?}");
    }
    assert!(windows_path("server", b"/").is_none());
    for device in ["COM¹", "LPT².log", "COM³.txt"] {
        assert!(windows_path("", format!("/C:/{device}").as_bytes()).is_none());
    }
    for regular in ["COM10", "NUL-file", "company.rs"] {
        assert!(windows_path("", format!("/C:/{regular}").as_bytes()).is_some());
    }
}

#[cfg(unix)]
#[test]
fn unix_non_utf8_names_do_not_alias_replacement_characters() {
    use std::os::unix::ffi::OsStringExt;

    let temp = tempfile::tempdir().unwrap();
    let name = std::ffi::OsString::from_vec(b"invalid-\xff.rs".to_vec());
    let raw = temp.path().join(name);
    let replacement = temp.path().join("invalid-�.rs");
    // The aliasing guarded here is a property of the encoder, so assert it
    // without a filesystem; neither path has to exist for these to hold.
    let uri = try_path_to_uri(&raw).unwrap();
    assert!(uri.contains("%FF"));
    assert_eq!(uri_to_path(&uri), Some(raw.clone()));
    assert_ne!(uri, try_path_to_uri(&replacement).unwrap());

    // Reading back through the round-tripped path does need a filesystem that
    // will hold a name which is not UTF-8, and APFS and HFS+ will not: they
    // refuse it with EILSEQ, which failed this test on macOS instead of
    // skipping the one half that cannot run there. Creating the
    // replacement-character name first keeps the skip honest — if that write
    // fails the directory is at fault, and the unwrap says so.
    std::fs::write(&replacement, "different filename").unwrap();
    if std::fs::write(&raw, "raw filename").is_err() {
        return;
    }
    assert_eq!(
        std::fs::read(uri_to_path(&uri).unwrap()).unwrap(),
        b"raw filename"
    );
}

#[cfg(unix)]
#[test]
fn unix_backslashes_queries_and_percent_escapes_remain_literal_names() {
    for (name, encoded) in [
        ("a\\b", "a%5Cb"),
        ("a?b", "a%3Fb"),
        ("a%2Fb", "a%252Fb"),
        ("a+b", "a+b"),
    ] {
        let path = PathBuf::from("/tmp").join(name);
        let uri = try_path_to_uri(&path).unwrap();
        assert!(uri.ends_with(encoded), "{uri}");
        assert_eq!(uri_to_path(&uri), Some(path));
    }
}

#[cfg(unix)]
#[test]
fn local_authorities_and_encoded_unreserved_characters_share_an_identity() {
    for alias in [
        "file://localhost/tmp/a%20b.rs",
        "FILE://LOCALHOST/tmp/%61%20b.rs",
        "file:///tmp/a%20b%2ers",
    ] {
        assert_eq!(
            normalize_uri(alias).as_deref(),
            Some("file:///tmp/a%20b.rs")
        );
    }
    assert!(uri_to_path("file://remote-server/share/file.rs").is_none());
    let nul = PathBuf::from("/tmp/embedded\0nul");
    assert!(try_path_to_uri(&nul).is_err());
}

#[cfg(windows)]
#[test]
fn native_windows_drives_verbatim_roots_and_unc_uris_roundtrip() {
    for (native, expected) in [
        (
            r"C:\Users\me\space name.rs",
            "file:///C:/Users/me/space%20name.rs",
        ),
        (
            r"\\?\C:\Users\me\space name.rs",
            "file:///C:/Users/me/space%20name.rs",
        ),
        (
            r"\\server\share\space name.rs",
            "file://server/share/space%20name.rs",
        ),
        (
            r"\\?\UNC\server\share\space name.rs",
            "file://server/share/space%20name.rs",
        ),
    ] {
        assert_eq!(try_path_to_uri(Path::new(native)).unwrap(), expected);
        let path = uri_to_path(expected).unwrap();
        assert!(path.is_absolute());
        assert_eq!(try_path_to_uri(&path).unwrap(), expected);
    }
    assert_eq!(
        normalize_uri("file:///c%3a/Users/me/code.rs").as_deref(),
        Some("file:///C:/Users/me/code.rs")
    );
    assert!(uri_to_path("file:///C:/bad%5Ccomponent").is_none());
    assert!(try_path_to_uri(Path::new(r"\\.\pipe\fixture")).is_err());
}

#[cfg(windows)]
#[test]
fn native_windows_unpaired_surrogates_are_never_replaced() {
    use std::os::windows::ffi::OsStringExt;

    let mut name: Vec<_> = "C:\\work\\bad".encode_utf16().collect();
    name.push(0xd800);
    let path = PathBuf::from(std::ffi::OsString::from_wide(&name));
    assert!(try_path_to_uri(&path).is_err());
}