asar-rust 0.1.0

Rust port of @electron/asar — create and extract Electron ASAR archives
Documentation
use asar_rust::asar::*;
use std::fs;

fn setup_dir(name: &str) -> std::path::PathBuf {
    let dir = std::env::temp_dir().join(format!("asar-hv-{}", name));
    let _ = fs::remove_dir_all(&dir);
    fs::create_dir_all(&dir).unwrap();
    dir
}

#[test]
fn test_parse_valid_header() {
    let dir = setup_dir("valid-header");
    let src = dir.join("src");
    fs::create_dir_all(&src).unwrap();
    fs::write(src.join("only.txt"), b"x").unwrap();
    let dest = dir.join("test.asar");
    create_package(&src, &dest).unwrap();

    let header = get_raw_header(&dest).unwrap();
    assert!(header.header_size > 0);
    assert!(!header.header_string.is_empty());
    let _ = fs::remove_dir_all(&dir);
}

#[test]
fn test_header_contains_integrity_for_every_file() {
    let dir = setup_dir("integrity-header");
    let src = dir.join("src");
    fs::create_dir_all(src.join("dir")).unwrap();
    fs::write(src.join("a.txt"), b"hello").unwrap();
    fs::write(src.join("b.txt"), b"world").unwrap();
    fs::write(src.join("dir/c.txt"), b"nested").unwrap();
    let dest = dir.join("test.asar");
    create_package(&src, &dest).unwrap();

    let header = get_raw_header(&dest).unwrap();
    let header_str = header.header_string;
    assert!(header_str.contains("SHA256"));
    assert!(header_str.contains("blocks"));
    let _ = fs::remove_dir_all(&dir);
}

#[test]
fn test_header_has_correct_file_offsets() {
    let dir = setup_dir("offsets");
    let src = dir.join("src");
    fs::create_dir_all(&src).unwrap();
    fs::write(src.join("first.txt"), b"aaaa").unwrap(); // 4 bytes
    fs::write(src.join("second.txt"), b"bb").unwrap(); // 2 bytes
    fs::write(src.join("third.txt"), b"ccccc").unwrap(); // 5 bytes
    let dest = dir.join("test.asar");
    create_package(&src, &dest).unwrap();

    let header = get_raw_header(&dest).unwrap();
    let header_str = header.header_string;

    // offsets should be sequential: 0, 4, 6
    let first_offset = header_str.find("\"offset\":\"0\"").is_some()
        || header_str.find("\"offset\": \"0\"").is_some();
    assert!(first_offset);

    let _ = fs::remove_dir_all(&dir);
}

#[test]
fn test_valid_pickle_structure() {
    let dir = setup_dir("pickle-validate");
    let src = dir.join("src");
    fs::create_dir_all(&src).unwrap();
    fs::write(src.join("test.txt"), b"hello").unwrap();
    let dest = dir.join("test.asar");
    create_package(&src, &dest).unwrap();

    let raw = fs::read(&dest).unwrap();
    // First 4 bytes: payload size of size pickle (should be 4 for UInt32)
    let payload_size = u32::from_le_bytes(raw[0..4].try_into().unwrap());
    assert_eq!(payload_size, 4);
    // Bytes 4-7: the header size value
    let header_size = u32::from_le_bytes(raw[4..8].try_into().unwrap());
    assert!(header_size > 0);
    assert!(raw.len() > 8 + header_size as usize);
    let _ = fs::remove_dir_all(&dir);
}

#[test]
fn test_valid_json_in_header() {
    let dir = setup_dir("json-validate");
    let src = dir.join("src");
    fs::create_dir_all(src.join("b")).unwrap();
    fs::write(src.join("a.txt"), b"x").unwrap();
    fs::write(src.join("b/c.txt"), b"y").unwrap();
    let dest = dir.join("test.asar");
    create_package(&src, &dest).unwrap();

    let header = get_raw_header(&dest).unwrap();
    // Should parse as valid JSON
    let _: serde_json::Value = serde_json::from_str(&header.header_string).unwrap();
    assert!(header.header_string.contains("files"));
    assert!(header.header_string.contains("b"));
    let _ = fs::remove_dir_all(&dir);
}