use std::path::PathBuf;
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
use serde_json::Value;
const FINGERPRINT: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
#[test]
fn version_json_mode_emits_one_newline_delimited_event() {
let output = ferry_command()
.args(["--json", "version"])
.output()
.expect("ferry version runs");
assert!(
output.status.success(),
"ferry version failed: {}",
String::from_utf8_lossy(&output.stderr)
);
let stdout = String::from_utf8(output.stdout).expect("stdout is utf8");
let lines = stdout.lines().collect::<Vec<_>>();
assert_eq!(lines.len(), 1);
let event: Value = serde_json::from_str(lines[0]).expect("version event is json");
assert_eq!(event["event"], "version");
assert!(event["version"].as_str().is_some());
}
#[test]
fn peers_json_mode_persists_and_lists_trusted_peer() {
let config = IsolatedConfig::new("peers-json");
let trust = ferry_command()
.envs(config.envs())
.args(["--json", "peers", "trust", FINGERPRINT])
.output()
.expect("ferry peers trust runs");
assert!(
trust.status.success(),
"ferry peers trust failed: {}",
String::from_utf8_lossy(&trust.stderr)
);
let trusted: Value = serde_json::from_slice(&trust.stdout).expect("peer trusted event is json");
assert_eq!(trusted["event"], "peer_trusted");
assert_eq!(trusted["fingerprint"], FINGERPRINT);
let list = ferry_command()
.envs(config.envs())
.args(["--json", "--no-discovery", "peers"])
.output()
.expect("ferry peers runs");
assert!(
list.status.success(),
"ferry peers failed: {}",
String::from_utf8_lossy(&list.stderr)
);
let listed: Value = serde_json::from_slice(&list.stdout).expect("peer list event is json");
assert_eq!(listed["event"], "peers");
assert_eq!(listed["peers"][0]["fingerprint"], FINGERPRINT);
assert_eq!(listed["peers"][0]["trust_state"], "trusted");
}
fn ferry_command() -> Command {
Command::new(env!("CARGO_BIN_EXE_ferry"))
}
struct IsolatedConfig {
root: PathBuf,
}
impl IsolatedConfig {
fn new(name: &str) -> Self {
let nonce = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("system clock is after unix epoch")
.as_nanos();
let root =
std::env::temp_dir().join(format!("fileferry-{name}-{}-{nonce}", std::process::id()));
std::fs::create_dir_all(root.join("home")).expect("home dir created");
std::fs::create_dir_all(root.join("xdg-config")).expect("xdg config dir created");
std::fs::create_dir_all(root.join("appdata")).expect("appdata dir created");
Self { root }
}
fn envs(&self) -> Vec<(&'static str, PathBuf)> {
vec![
("HOME", self.root.join("home")),
("USERPROFILE", self.root.join("home")),
("XDG_CONFIG_HOME", self.root.join("xdg-config")),
("APPDATA", self.root.join("appdata")),
]
}
}
impl Drop for IsolatedConfig {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.root);
}
}