use rash::cli::{self, Invocation, ParseError};
use std::ffi::OsString;
fn split(args: &[&str]) -> Invocation {
cli::parse(args.iter().map(OsString::from)).expect("should parse")
}
fn ssh(args: &[&str]) -> Vec<String> {
split(args)
.ssh_args
.iter()
.map(|a| a.to_string_lossy().into_owned())
.collect()
}
fn monitor(args: &[&str]) -> Option<String> {
split(args)
.monitor
.map(|m| m.to_string_lossy().into_owned())
}
#[test]
fn monitor_port_forms() {
assert_eq!(
monitor(&["-M", "20000", "-N", "host"]).as_deref(),
Some("20000")
);
assert_eq!(
monitor(&["-M20000", "-N", "host"]).as_deref(),
Some("20000")
);
assert_eq!(
monitor(&["-M", "20000:7", "host"]).as_deref(),
Some("20000:7")
);
assert_eq!(monitor(&["-M", "0", "host"]).as_deref(), Some("0"));
assert_eq!(monitor(&["-N", "host"]), None);
assert_eq!(ssh(&["-M", "20000", "-N", "host"]), ["-N", "host"]);
assert_eq!(ssh(&["-M20000", "-N", "host"]), ["-N", "host"]);
assert_eq!(
monitor(&["-M", "20000", "-M", "30000", "host"]).as_deref(),
Some("30000")
);
}
#[test]
fn forwards_are_injected_where_dash_m_stood() {
assert_eq!(split(&["-M", "20000", "-N", "host"]).inject_at, 0);
assert_eq!(split(&["-N", "-M", "20000", "host"]).inject_at, 1);
assert_eq!(split(&["-N", "-q", "-M", "20000", "host"]).inject_at, 2);
let mut inv = split(&["-N", "-M", "20000", "host"]);
cli::splice_forwards(
&mut inv.ssh_args,
inv.inject_at,
["-L", "20000:127.0.0.1:20000", "-R", "20000:127.0.0.1:20001"]
.iter()
.map(OsString::from)
.collect(),
);
let got: Vec<String> = inv
.ssh_args
.iter()
.map(|a| a.to_string_lossy().into_owned())
.collect();
assert_eq!(
got,
[
"-N",
"-L",
"20000:127.0.0.1:20000",
"-R",
"20000:127.0.0.1:20001",
"host"
]
);
}
#[test]
fn dash_f_is_rash_s_and_is_stripped_from_clusters() {
assert!(split(&["-f", "-M", "0", "host"]).background);
assert_eq!(ssh(&["-f", "-M", "0", "host"]), ["host"]);
assert!(split(&["-fN", "-M", "0", "host"]).background);
assert_eq!(ssh(&["-fN", "-M", "0", "host"]), ["-N", "host"]);
assert!(split(&["-Nf", "-M", "0", "host"]).background);
assert_eq!(ssh(&["-Nf", "-M", "0", "host"]), ["-N", "host"]);
assert_eq!(ssh(&["-M", "0", "-f", "host"]), ["host"]);
}
#[test]
fn d4_f_inside_an_option_value_is_never_stripped() {
assert_eq!(
ssh(&["-i", "/home/me/.ssh/id_for_work", "-M", "0", "host"]),
["-i", "/home/me/.ssh/id_for_work", "host"]
);
assert_eq!(
ssh(&["-i/home/me/.ssh/id_for_work", "-M", "0", "host"]),
["-i/home/me/.ssh/id_for_work", "host"]
);
assert_eq!(
ssh(&["-o", "ExitOnForwardFailure=yes", "-M", "0", "host"]),
["-o", "ExitOnForwardFailure=yes", "host"]
);
}
#[test]
fn d1_unknown_options_are_forwarded_not_rejected() {
assert_eq!(
ssh(&["-B", "en0", "-M", "0", "host"]),
["-B", "en0", "host"]
);
assert_eq!(ssh(&["-Zq", "-M", "0", "host"]), ["-Zq", "host"]);
let inv = split(&["-fNZ", "-M", "0", "host"]);
assert!(inv.background);
assert_eq!(ssh(&["-fNZ", "-M", "0", "host"]), ["-NZ", "host"]);
}
#[test]
fn d2_p_takes_a_value_on_modern_openssh() {
assert_eq!(
ssh(&["-P", "mytag", "-M", "0", "host"]),
["-P", "mytag", "host"]
);
assert_eq!(
ssh(&["-p", "2222", "-M", "0", "host"]),
["-p", "2222", "host"]
);
}
#[test]
fn d3_nothing_after_the_separator_is_rewritten() {
assert_eq!(
ssh(&["-M", "0", "host", "--", "cmd", "-flag"]),
["host", "cmd", "-flag"]
);
assert_eq!(
ssh(&["-M", "0", "host", "--", "cmd", "--", "-f"]),
["host", "cmd", "--", "-f"]
);
let inv = split(&["-M", "0", "host", "--", "-M", "1"]);
assert_eq!(
inv.monitor
.map(|m| m.to_string_lossy().into_owned())
.as_deref(),
Some("0")
);
}
#[test]
fn passthrough_shapes() {
assert_eq!(ssh(&["-4", "-M", "0", "host"]), ["-4", "host"]);
assert_eq!(
ssh(&["-L", "8080:localhost:80", "-M", "0", "host"]),
["-L", "8080:localhost:80", "host"]
);
assert_eq!(ssh(&["-M", "0", "host", "-"]), ["host", "-"]);
assert_eq!(
ssh(&["-M", "0", "-t", "host", "screen", "-D", "-R"]),
["-t", "host", "screen", "-D", "-R"]
);
}
#[test]
fn dash_m_inside_a_cluster() {
let inv = split(&["-NM20000", "host"]);
assert_eq!(
inv.monitor
.map(|m| m.to_string_lossy().into_owned())
.as_deref(),
Some("20000")
);
assert_eq!(ssh(&["-NM20000", "host"]), ["-N", "host"]);
assert_eq!(inv.inject_at, 0);
let inv = split(&["-NM", "20000", "host"]);
assert_eq!(
inv.monitor
.map(|m| m.to_string_lossy().into_owned())
.as_deref(),
Some("20000")
);
assert_eq!(ssh(&["-NM", "20000", "host"]), ["-N", "host"]);
}
#[test]
fn version_is_rash_s_not_ssh_s() {
assert!(split(&["-V"]).version);
assert!(split(&["-M", "0", "-V", "host"]).version);
assert_eq!(ssh(&["-M", "0", "-V", "host"]), ["host"]);
}
#[test]
fn long_options_belong_to_rash() {
assert!(split(&["--dry-run", "-M", "0", "host"]).dry_run);
assert!(split(&["--help"]).help);
assert!(split(&["--version"]).version);
let inv = split(&["--monitor", "unix", "-N", "host"]);
assert_eq!(
inv.monitor_long
.map(|m| m.to_string_lossy().into_owned())
.as_deref(),
Some("unix")
);
let inv = split(&["--monitor=unix", "-N", "host"]);
assert_eq!(
inv.monitor_long
.map(|m| m.to_string_lossy().into_owned())
.as_deref(),
Some("unix")
);
assert_eq!(
ssh(&["-M", "0", "host", "--", "--dry-run"]),
["host", "--dry-run"]
);
assert!(!split(&["-M", "0", "host", "--", "--dry-run"]).dry_run);
}
#[test]
fn errors() {
assert_eq!(
cli::parse(["-M"].iter().map(OsString::from)),
Err(ParseError::MissingValue("-M".into()))
);
assert_eq!(
cli::parse(["--nope"].iter().map(OsString::from)),
Err(ParseError::UnknownLongOption("--nope".into()))
);
assert_eq!(
cli::parse(["--monitor"].iter().map(OsString::from)),
Err(ParseError::MissingValue("--monitor".into()))
);
}