innisfree 0.4.3

Exposes local services on public IPv4 address, via cloud server.
Documentation
//! End-to-end test for the `innisfree systemd-service` subcommand.
//!
//! Builds (or re-uses the cached) `innisfree` binary via `escargot`,
//! invokes `innisfree systemd-service --executable-path=<binary>` to
//! render the unit pointing at that exact path, writes the result to
//! a `<...>@.service` tempfile, and asserts that `systemd-analyze
//! verify` accepts it without warnings.
//!
//! `--executable-path` is the load-bearing knob here: pointing
//! `ExecStart=` at the cargo-built binary (which exists and is
//! executable on the test host) means `systemd-analyze` doesn't bail
//! on the missing-binary check that used to require a filter hack.
//!
//! Gated behind the `systemd` feature so day-to-day `cargo test` runs
//! aren't held to it (the verify step requires `systemd-analyze` on
//! PATH). Run with:
//!
//!     cargo test --features systemd --test systemd_unit
#![cfg(feature = "systemd")]

use std::io::Write;
use std::process::Command;

#[test]
fn rendered_unit_passes_systemd_analyze() {
    // escargot returns the on-disk path of the built `innisfree`
    // binary; if it isn't built (or is stale) it builds it for us.
    // We need a real, executable path so `systemd-analyze verify`
    // doesn't fail the ExecStart= existence check.
    let runner = escargot::CargoBuild::new()
        .bin("innisfree")
        .run()
        .expect("failed to build innisfree binary via escargot");
    let bin = runner.path().to_path_buf();

    // Render the unit with ExecStart= pointed at the binary above.
    let render = Command::new(&bin)
        .arg("systemd-service")
        .arg("--executable-path")
        .arg(&bin)
        .output()
        .expect("invoking innisfree systemd-service");
    assert!(
        render.status.success(),
        "innisfree systemd-service failed (status={}):\nstderr:\n{}",
        render.status,
        String::from_utf8_lossy(&render.stderr),
    );

    // Persist to a `@.service` path so systemd-analyze recognizes the
    // file as a template unit and instantiates `%i` for verification.
    let mut unit_file = tempfile::Builder::new()
        .prefix("innisfree-")
        .suffix("@.service")
        .tempfile()
        .expect("creating tempfile");
    unit_file
        .write_all(&render.stdout)
        .expect("writing rendered unit to tempfile");
    unit_file.flush().expect("flushing tempfile");

    let verify = Command::new("systemd-analyze")
        .arg("verify")
        .arg(unit_file.path())
        .output()
        .expect("invoking systemd-analyze; is it installed and on PATH?");

    let stderr = String::from_utf8_lossy(&verify.stderr);
    let stdout = String::from_utf8_lossy(&verify.stdout);
    assert!(
        verify.status.success() && stderr.trim().is_empty() && stdout.trim().is_empty(),
        "systemd-analyze verify failed or emitted warnings:\nstatus: {}\nstdout:\n{}\nstderr:\n{}",
        verify.status,
        stdout,
        stderr,
    );
}