nornir 0.4.21

Companion to cargo: dependency tracking, release gating, deploy, benchmarks, and documentation assembly. Project-agnostic.
Documentation
//! The nornir data root is derived from the **running user's home** — ONE rule
//! for everyone, no path env-vars. This test proves it two ways:
//!
//! 1. the in-process resolver returns `$HOME/.nornir` for whoever runs the test;
//! 2. (gated on `sudo -n -H -u nornir true` succeeding) the built `nornir root`
//!    binary, run *as the server's system user* via `sudo -H -u nornir`, prints
//!    `/home/nornir/.nornir` — identical home-derived behavior for "me or
//!    nornir", which is the whole point of the refactor.

use std::path::PathBuf;
use std::process::Command;

#[test]
fn root_is_home_derived_and_same_for_any_user() {
    // --- (1) In-process: the running user's resolution is exactly $HOME/.nornir.
    let home = std::env::var("HOME").expect("HOME set for the test runner");
    let expected = PathBuf::from(&home).join(".nornir");
    assert_eq!(
        nornir::config::nornir_home(),
        expected,
        "nornir_home() must be $HOME/.nornir for the running user",
    );

    // The binary agrees with the library when run as the current user.
    let out = Command::new(env!("CARGO_BIN_EXE_nornir"))
        .arg("root")
        .output()
        .expect("run `nornir root`");
    assert!(out.status.success(), "`nornir root` exited non-zero");
    let printed = String::from_utf8(out.stdout).unwrap();
    assert_eq!(
        printed.trim(),
        expected.display().to_string(),
        "`nornir root` must print the running user's $HOME/.nornir",
    );

    // --- (2) Cross-user proof: same code, run as the `nornir` service user via
    // `sudo -H -u nornir`, must derive THAT user's home → /home/nornir/.nornir.
    // Gated on passwordless sudo to the nornir user being available; otherwise
    // skipped with a logged note (so CI without sudo still passes).
    let sudo_ok = Command::new("sudo")
        .args(["-n", "-H", "-u", "nornir", "true"])
        .status()
        .map(|s| s.success())
        .unwrap_or(false);
    if !sudo_ok {
        eprintln!(
            "note: skipping the `sudo -H -u nornir nornir root` cross-user check \
             — passwordless `sudo -n -H -u nornir` is not available here"
        );
        return;
    }

    // The build output lives under the test runner's home (here a removable
    // drive) which the `nornir` user can't traverse/execute. Stage the binary
    // into a world-executable location (`/tmp`) so this proves the home RULE,
    // not filesystem permissions. If staging or the run fails for an
    // environment reason, skip with a note rather than fail.
    let bin = env!("CARGO_BIN_EXE_nornir");
    let staged = std::env::temp_dir().join("nornir-root-test-bin");
    if std::fs::copy(bin, &staged).is_err() {
        eprintln!("note: skipping cross-user check — could not stage the binary into /tmp");
        return;
    }
    #[cfg(unix)]
    {
        use std::os::unix::fs::PermissionsExt;
        let _ = std::fs::set_permissions(&staged, std::fs::Permissions::from_mode(0o755));
    }

    let out = Command::new("sudo")
        .args(["-n", "-H", "-u", "nornir"])
        .arg(&staged)
        .arg("root")
        .output()
        .expect("run `sudo -H -u nornir <staged nornir> root`");
    let _ = std::fs::remove_file(&staged);
    assert!(
        out.status.success(),
        "`sudo -H -u nornir nornir root` exited non-zero: {}",
        String::from_utf8_lossy(&out.stderr),
    );
    let printed = String::from_utf8(out.stdout).unwrap();
    assert_eq!(
        printed.trim(),
        "/home/nornir/.nornir",
        "running as the server user must derive /home/nornir/.nornir — \
         identical home-derived rule, no path env-var",
    );
}