Luigit
repositories / smith

smith

There are many coding harnesses - but this one is fast

owned by admin

xtask/tests/e2e_dependency.rs

Raw
//! Dependency report CLI behavior (`SMH-SPEC-SPEC0001`).

use std::{fs, path::PathBuf, time::Duration};

fn workspace_root() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..")
}

#[test]
fn e2e_deps_rejects_invalid_arguments() {
    let mut command = assert_cmd::Command::new(env!("CARGO_BIN_EXE_xtask"));
    let output = command
        .current_dir(workspace_root())
        .args(["deps", "--unknown"])
        .timeout(Duration::from_secs(10))
        .output()
        .unwrap();
    assert_eq!(output.status.code(), Some(1));
    assert!(String::from_utf8_lossy(&output.stderr).contains("usage: cargo x deps [--offline]"));
}

#[test]
fn e2e_dependency_resolution_rejects_invalid_mode_without_mutating_lockfile() {
    let root = workspace_root();
    let lockfile = root.join("Cargo.lock");
    let before = fs::read(&lockfile).unwrap();
    let output = assert_cmd::Command::new(env!("CARGO_BIN_EXE_xtask"))
        .current_dir(root)
        .args(["deps", "--verify", "unknown", "-p", "smith-core"])
        .timeout(Duration::from_secs(10))
        .output()
        .unwrap();
    assert_eq!(output.status.code(), Some(1));
    assert!(String::from_utf8_lossy(&output.stderr).contains("usage: cargo x deps --verify"));
    assert_eq!(fs::read(&lockfile).unwrap(), before);
}

#[test]
fn e2e_dependency_resolution_rejects_missing_or_invalid_target_without_mutating_lockfile() {
    let root = workspace_root();
    let lockfile = root.join("Cargo.lock");
    let before = fs::read(&lockfile).unwrap();
    for arguments in [
        vec!["deps", "--verify", "locked", "--target"],
        vec!["deps", "--verify", "locked", "--target", ""],
        vec![
            "deps",
            "--verify",
            "locked",
            "--target",
            "x86_64-unknown-linux-gnu",
            "--unknown",
        ],
    ] {
        let output = assert_cmd::Command::new(env!("CARGO_BIN_EXE_xtask"))
            .current_dir(&root)
            .args(arguments)
            .timeout(Duration::from_secs(10))
            .output()
            .unwrap();
        assert_eq!(output.status.code(), Some(1));
        assert!(String::from_utf8_lossy(&output.stderr).contains("usage: cargo x deps --verify"));
    }
    let invalid_target = assert_cmd::Command::new(env!("CARGO_BIN_EXE_xtask"))
        .current_dir(&root)
        .args([
            "deps",
            "--verify",
            "locked",
            "--target",
            "not-a-rust-target",
        ])
        .timeout(Duration::from_secs(120))
        .output()
        .unwrap();
    assert_eq!(invalid_target.status.code(), Some(1));
    assert!(String::from_utf8_lossy(&invalid_target.stderr).contains("not-a-rust-target"));
    assert_eq!(fs::read(&lockfile).unwrap(), before);
}

#[test]
fn e2e_default_resolution_checks_host_despite_ambient_build_target() {
    let root = workspace_root();
    let before = fs::read(root.join("Cargo.lock")).unwrap();
    assert_cmd::Command::new(env!("CARGO_BIN_EXE_xtask"))
        .current_dir(&root)
        .env("CARGO_BUILD_TARGET", "not-a-rust-target")
        .args(["deps", "--verify", "locked", "-p", "smith-core"])
        .timeout(Duration::from_secs(120))
        .assert()
        .success();
    assert_eq!(fs::read(root.join("Cargo.lock")).unwrap(), before);
}

#[test]
fn e2e_dependency_resolution_scopes_locked_check_without_rewriting_lockfile() {
    let root = workspace_root();
    let lockfile = root.join("Cargo.lock");
    let before = fs::read(&lockfile).unwrap();
    let native_target = String::from_utf8(
        assert_cmd::Command::new("rustc")
            .arg("-vV")
            .timeout(Duration::from_secs(10))
            .output()
            .unwrap()
            .stdout,
    )
    .unwrap()
    .lines()
    .find_map(|line| line.strip_prefix("host: "))
    .unwrap()
    .to_owned();
    for arguments in [
        vec![
            "deps",
            "--verify",
            "locked",
            "-p",
            "smith-core",
            "--target",
            &native_target,
        ],
        vec![
            "deps",
            "--verify",
            "locked",
            "--target",
            &native_target,
            "--package",
            "smith-core",
        ],
    ] {
        let output = assert_cmd::Command::new(env!("CARGO_BIN_EXE_xtask"))
            .current_dir(&root)
            .args(arguments)
            .timeout(Duration::from_secs(120))
            .assert()
            .success()
            .get_output()
            .stdout
            .clone();
        assert!(String::from_utf8_lossy(&output).contains(&format!("target {native_target}")));
    }
    assert_eq!(fs::read(&lockfile).unwrap(), before);
}

#[test]
#[cfg(unix)]
fn e2e_deps_offline_report_uses_cargo_protocol_without_registry_cache() {
    use std::{
        os::unix::fs::PermissionsExt,
        sync::atomic::{AtomicUsize, Ordering},
    };

    static SCRATCH_COUNTER: AtomicUsize = AtomicUsize::new(0);

    let root = workspace_root();
    let lockfile = root.join("Cargo.lock");
    let before = fs::read(&lockfile).unwrap();
    let scratch = Scratch(std::env::temp_dir().join(format!(
        "smith-dependency-e2e-{}-{}",
        std::process::id(),
        SCRATCH_COUNTER.fetch_add(1, Ordering::Relaxed)
    )));
    fs::create_dir(&scratch.0).unwrap();
    let bin = scratch.0.join("bin");
    let fixtures = scratch.0.join("fixtures");
    let cargo_home = scratch.0.join("cargo-home");
    fs::create_dir(&bin).unwrap();
    fs::create_dir(&fixtures).unwrap();
    fs::create_dir(&cargo_home).unwrap();

    let helper = bin.join("cargo");
    fs::copy(
        PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/cargo-offline.sh"),
        &helper,
    )
    .unwrap();
    fs::set_permissions(&helper, fs::Permissions::from_mode(0o755)).unwrap();
    write_metadata_fixtures(&fixtures);

    let mut paths = vec![bin];
    paths.extend(std::env::split_paths(&std::env::var_os("PATH").unwrap()));
    let output = assert_cmd::Command::new(env!("CARGO_BIN_EXE_xtask"))
        .current_dir(&root)
        .env("PATH", std::env::join_paths(paths).unwrap())
        .env("CARGO_HOME", &cargo_home)
        .env("SMITH_METADATA", fixtures.join("workspace.json"))
        .env("SMITH_FIXTURES", &fixtures)
        .args(["deps", "--offline"])
        .timeout(Duration::from_secs(120))
        .assert()
        .success()
        .get_output()
        .stdout
        .clone();
    let report = String::from_utf8(output).unwrap();
    assert_eq!(
        report,
        concat!(
            "Offline registry-cache evidence only; upstream freshness is unverified.\n",
            "Crates.io only: git, path, and alternate registries excluded; compatibility resolves with default features disabled.\n",
            "member | scope | target | dependency | requirement | locked | latest compatible | latest stable | constraint reason\n",
            "smith-fixture | normal | all-targets | ureq | ~3.4 | 3.4.1 | 3.4.1 | 3.5.0 | smith-harness, http stack; tilde pin: the chunk-gap timeout uses ureq::unversioned\n",
            "smith-fixture | normal | all-targets | wasmtime | ^49.0.2 | 49.0.2 | 49.0.2 | 50.0.0 | smith-harness: Wasmtime hosts components; guest wit-bindgen must align with its component tooling release, not independently chase latest. Wasmi or Wasmer remain alternatives only after component compatibility and Smith workload benchmarks justify replacing Wasmtime. smith-harness, wasm plugin engine\n",
            "smith-fixture | normal | all-targets | wit-bindgen | =0.62.0 | 0.62.0 | 0.62.0 | 0.63.0 | plugins/echo: guest component bindings aligned with Wasmtime's release set.\n",
        )
    );
    assert_eq!(fs::read(&lockfile).unwrap(), before);
}

fn write_metadata_fixtures(directory: &std::path::Path) {
    let registry = "registry+https://github.com/rust-lang/crates.io-index";
    let workspace_id = "smith-fixture 0.0.0 (path+file:///fixture)";
    let dependencies = [
        ("ureq", "~3.4", "3.4.1", "ureq"),
        ("wasmtime", "^49.0.2", "49.0.2", "wasmtime"),
        ("wit-bindgen", "=0.62.0", "0.62.0", "wit_bindgen"),
    ];
    let workspace_dependencies = dependencies
        .iter()
        .map(|(name, requirement, _, _)| {
            serde_json::json!({
                "name": name,
                "source": registry,
                "req": requirement,
                "kind": null,
                "rename": null,
                "optional": false,
                "uses_default_features": false,
                "features": []
            })
        })
        .collect::<Vec<_>>();
    let packages = std::iter::once(serde_json::json!({
        "id": workspace_id,
        "name": "smith-fixture",
        "version": "0.0.0",
        "source": null,
        "dependencies": workspace_dependencies
    }))
    .chain(dependencies.iter().map(|(name, _, locked, _)| {
        serde_json::json!({
            "id": format!("{name} {locked} ({registry})"),
            "name": name,
            "version": locked,
            "source": registry,
            "dependencies": []
        })
    }))
    .collect::<Vec<_>>();
    let edges = dependencies
        .iter()
        .map(|(name, _, locked, edge)| {
            serde_json::json!({
                "name": edge,
                "pkg": format!("{name} {locked} ({registry})"),
                "dep_kinds": [{"kind": null, "target": null}]
            })
        })
        .collect::<Vec<_>>();
    let metadata = serde_json::json!({
        "packages": packages,
        "workspace_members": [workspace_id],
        "resolve": {"nodes": [{"id": workspace_id, "deps": edges}]}
    });
    fs::write(
        directory.join("workspace.json"),
        serde_json::to_vec(&metadata).unwrap(),
    )
    .unwrap();

    for (name, _, compatible, _) in dependencies {
        let latest = match name {
            "ureq" => "3.5.0",
            "wasmtime" => "50.0.0",
            "wit-bindgen" => "0.63.0",
            _ => unreachable!(),
        };
        for (label, version) in [("compatible", compatible), ("latest", latest)] {
            let package = serde_json::json!({
                "id": format!("{name} {version} ({registry})"),
                "name": name,
                "version": version,
                "source": registry,
                "dependencies": []
            });
            let metadata = serde_json::json!({
                "packages": [package],
                "workspace_members": [],
                "resolve": {"nodes": []}
            });
            fs::write(
                directory.join(format!("{name}-{label}.json")),
                serde_json::to_vec(&metadata).unwrap(),
            )
            .unwrap();
        }
    }
}

struct Scratch(PathBuf);

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