execsurface 0.1.0-alpha.6

Runtime execution-surface drift detection for Linux CI and developer workflows
#![cfg(all(target_os = "linux", target_arch = "x86_64"))]

use std::fs;
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicU64, Ordering};

use execsurface_baseline::{parse_and_verify, BaselineLock};

static NEXT_ID: AtomicU64 = AtomicU64::new(1);

fn temp_dir(name: &str) -> std::path::PathBuf {
    let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
    let path =
        std::env::temp_dir().join(format!("execsurface-m3-{name}-{}-{id}", std::process::id()));
    fs::create_dir_all(&path).expect("create temp dir");
    path
}

fn cli() -> &'static str {
    env!("CARGO_BIN_EXE_execsurface")
}

#[test]
fn learn_is_byte_reproducible_and_does_not_capture_argv_secret() {
    let dir = temp_dir("repro");
    let first = dir.join("first.lock.json");
    let second = dir.join("second.lock.json");
    let secret = "AX_M3_ARGV_SECRET_7c4300f1";

    for output in [&first, &second] {
        let status = Command::new(cli())
            .current_dir(&dir)
            .args(["learn", "--output"])
            .arg(output)
            .args(["--", "/bin/true", secret])
            .stdout(Stdio::null())
            .status()
            .expect("run learn");
        assert!(status.success());
    }

    let first_bytes = fs::read(&first).expect("first bytes");
    let second_bytes = fs::read(&second).expect("second bytes");
    assert_eq!(first_bytes, second_bytes);
    assert!(!String::from_utf8_lossy(&first_bytes).contains(secret));

    let lock: BaselineLock = parse_and_verify(&first_bytes).expect("verified lock");
    assert_eq!(lock.payload.command.argument_count, 1);
    assert_eq!(lock.payload.command.executable.family, "true");

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

#[test]
fn learn_refuses_silent_overwrite_and_allows_explicit_overwrite() {
    let dir = temp_dir("overwrite");
    let output = dir.join("baseline.json");

    let first = Command::new(cli())
        .current_dir(&dir)
        .args(["learn", "--output"])
        .arg(&output)
        .args(["--", "/bin/true"])
        .stdout(Stdio::null())
        .status()
        .expect("first learn");
    assert!(first.success());

    let second = Command::new(cli())
        .current_dir(&dir)
        .args(["learn", "--output"])
        .arg(&output)
        .args(["--", "/bin/true"])
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
        .expect("second learn");
    assert!(!second.success());

    let overwrite = Command::new(cli())
        .current_dir(&dir)
        .args(["learn", "--overwrite", "--output"])
        .arg(&output)
        .args(["--", "/bin/true"])
        .stdout(Stdio::null())
        .status()
        .expect("overwrite learn");
    assert!(overwrite.success());
    parse_and_verify(&fs::read(&output).expect("read")).expect("verified lock");

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

#[test]
fn failed_target_does_not_create_baseline() {
    let dir = temp_dir("failure");
    let output = dir.join("failed.json");

    let status = Command::new(cli())
        .current_dir(&dir)
        .args(["learn", "--output"])
        .arg(&output)
        .args(["--", "/bin/false"])
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
        .expect("learn false");

    assert!(!status.success());
    assert!(!output.exists());
    let _ = fs::remove_dir_all(dir);
}

#[test]
fn learn_uses_default_lockfile_name() {
    let dir = temp_dir("default-output");
    let output = dir.join("execsurface.lock.json");

    let status = Command::new(cli())
        .current_dir(&dir)
        .args(["learn", "--", "/bin/true"])
        .stdout(Stdio::null())
        .status()
        .expect("learn with default output");

    assert!(status.success());
    assert!(output.exists());
    parse_and_verify(&fs::read(&output).expect("read default lock")).expect("verified lock");

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