rdsh 0.10.0

Rust port/shim for dsh: fast launcher, token saver, zero behavior breakage via passthrough
use super::*;
use std::io::{Read, Write};

const LIMIT: u64 = 128 * 1024 * 1024;

struct Fixture(PathBuf);

impl Fixture {
    fn new() -> Self {
        let path = std::env::temp_dir().join(format!(
            "rdsh-cloud-json-{}-{}",
            std::process::id(),
            getrandom::u64().unwrap()
        ));
        std::fs::create_dir(&path).unwrap();
        Self(path)
    }

    fn padded_json(&self, length: u64) -> PathBuf {
        let path = self.0.join("input.json");
        let mut file = std::fs::File::create(&path).unwrap();
        file.write_all(b"{}").unwrap();
        std::io::copy(&mut std::io::repeat(b' ').take(length - 2), &mut file).unwrap();
        path
    }
}

impl Drop for Fixture {
    fn drop(&mut self) {
        std::fs::remove_dir_all(&self.0).unwrap();
    }
}

#[test]
fn rejects_valid_json_larger_than_128_mib() {
    let fixture = Fixture::new();
    let path = fixture.padded_json(LIMIT + 1);
    let error = read_json::<serde_json::Value>(&path).unwrap_err();
    assert!(error.to_string().contains("128 MiB"), "{error}");
}

#[test]
fn accepts_valid_json_exactly_at_the_limit() {
    let fixture = Fixture::new();
    let path = fixture.padded_json(LIMIT);
    let value: serde_json::Value = read_json(&path).unwrap();
    assert_eq!(value, serde_json::json!({}));
}

#[test]
fn bounds_reads_when_input_grows_after_metadata() {
    struct GrowingInput {
        read: usize,
    }
    impl Read for GrowingInput {
        fn read(&mut self, bytes: &mut [u8]) -> std::io::Result<usize> {
            bytes.fill(b' ');
            self.read += bytes.len();
            assert!(self.read <= LIMIT as usize + 1, "read past the limit probe");
            Ok(bytes.len())
        }
    }
    let mut reader = GrowingInput { read: 0 };
    let error = read_json_from::<serde_json::Value>(&mut reader, 0).unwrap_err();
    assert_eq!(error.to_string(), "Input JSON exceeds 128 MiB");
    assert_eq!(reader.read, LIMIT as usize + 1);
}

#[test]
fn preserves_plan_request_and_lease_deserialization() {
    fn round_trip<T: for<'de> Deserialize<'de> + Serialize>(
        fixture: &Fixture,
        value: serde_json::Value,
    ) {
        let path = fixture.0.join("document.json");
        std::fs::write(&path, serde_json::to_vec(&value).unwrap()).unwrap();
        let decoded: T = read_json(&path).unwrap();
        assert_eq!(serde_json::to_value(decoded).unwrap(), value);
    }
    let fixture = Fixture::new();
    let request = serde_json::json!({
        "run_id":"run-1", "task_id":"task-1", "attempt_id":"attempt-1",
        "worker_id":"worker-1", "idempotency_key":"key-1", "container_name":"worker",
        "image_digest":format!("example.invalid/worker@sha256:{}", "0".repeat(64))
    });
    round_trip::<SandboxRequest>(&fixture, request.clone());
    round_trip::<PoolPlan>(
        &fixture,
        serde_json::json!({"namespace":"ns", "name":"pool", "template":"worker",
            "replicas":2, "expected_resource_version":null}),
    );
    round_trip::<SandboxLease>(
        &fixture,
        serde_json::json!({"namespace":"ns", "pool":"pool", "request":request,
            "sandbox_id":"sandbox", "lease_id":"lease", "generation":1,
            "fencing_token":null, "phase":"ready", "allocation":"warm"}),
    );
}

#[test]
fn rejects_invalid_json_missing_files_and_directories() {
    let fixture = Fixture::new();
    let path = fixture.0.join("invalid.json");
    for bytes in [b"{".as_slice(), &[0xff]] {
        std::fs::write(&path, bytes).unwrap();
        assert!(read_json::<serde_json::Value>(&path).is_err());
    }
    assert!(read_json::<serde_json::Value>(&fixture.0.join("missing")).is_err());
    assert!(read_json::<serde_json::Value>(&fixture.0).is_err());
}

#[cfg(unix)]
#[test]
fn rejects_fifo_without_waiting_for_a_writer() {
    use std::ffi::CString;
    use std::os::unix::ffi::OsStrExt;

    let fixture = Fixture::new();
    let path = fixture.0.join("input.fifo");
    let c_path = CString::new(path.as_os_str().as_bytes()).unwrap();
    assert_eq!(unsafe { libc::mkfifo(c_path.as_ptr(), 0o600) }, 0);
    let error = read_json::<serde_json::Value>(&path).unwrap_err();
    assert!(error.to_string().contains("regular file"), "{error}");
}