use super::*;
use std::net::{IpAddr, Ipv4Addr};
fn args(items: &[&str]) -> Vec<String> {
items.iter().map(|s| (*s).to_string()).collect()
}
fn complete(workspace: &std::path::Path) -> Vec<String> {
args(&[
"--workspace",
&workspace.display().to_string(),
"--port",
"3000",
])
}
#[test]
fn test9862_a_complete_command_line_is_the_configuration() {
let dir = tempfile::tempdir().expect("a scratch directory");
let config = Config::from_args(complete(dir.path())).expect("a complete command line parses");
assert_eq!(config.workspace_root.as_deref(), Some(dir.path()));
assert_eq!(config.http_port, 3000);
assert!(!config.debug, "debug is off unless it is asked for");
assert_eq!(config.parent_pid, None, "an ordinary daemon dies with nobody");
assert_eq!(
config.listen,
IpAddr::V4(Ipv4Addr::LOCALHOST),
"a daemon nobody told otherwise is reachable only from this machine"
);
}
#[test]
fn test9897_the_listen_address_is_loopback_unless_it_is_asked_for() {
let dir = tempfile::tempdir().expect("a scratch directory");
let default = Config::from_args(complete(dir.path())).expect("parses");
assert!(
default.listen.is_loopback(),
"the default must not be reachable from another machine"
);
let mut argv = complete(dir.path());
argv.extend(args(&["--listen", "0.0.0.0"]));
let everywhere = Config::from_args(argv).expect("an explicit address parses");
assert_eq!(everywhere.listen, IpAddr::V4(Ipv4Addr::UNSPECIFIED));
assert!(!everywhere.listen.is_loopback());
let mut argv = complete(dir.path());
argv.extend(args(&["--listen", "192.168.1.10"]));
let one_interface = Config::from_args(argv).expect("a specific address parses");
assert_eq!(one_interface.listen, "192.168.1.10".parse::<IpAddr>().unwrap());
}
#[test]
fn test9898_a_listen_address_that_is_not_an_address_is_refused() {
let dir = tempfile::tempdir().expect("a scratch directory");
let mut argv = complete(dir.path());
argv.extend(args(&["--listen", "everywhere"]));
let error = Config::from_args(argv).expect_err("that is not an address");
assert!(
error.contains("--listen must be an IP address"),
"the refusal must say what was wanted: {error}"
);
}
#[test]
fn test9863_the_optional_flags_are_read() {
let dir = tempfile::tempdir().expect("a scratch directory");
let mut argv = complete(dir.path());
argv.extend(args(&["--debug", "--parent-pid", "4242"]));
let config = Config::from_args(argv).expect("the optional flags parse");
assert!(config.debug);
assert_eq!(config.parent_pid, Some(4242));
}
#[test]
fn test9864_every_required_argument_is_required() {
let dir = tempfile::tempdir().expect("a scratch directory");
let workspace = dir.path().display().to_string();
let cases: [(&str, Vec<String>); 1] = [("--port", args(&["--workspace", &workspace]))];
for (missing, argv) in cases {
let error = Config::from_args(argv)
.unwrap_err_or_else(|| format!("{missing} was not required"));
assert!(
error.contains(missing),
"the refusal must name what is missing ({missing}): {error}"
);
}
let without = Config::from_args(args(&["--port", "3000"]))
.expect("a daemon may start without a workspace");
assert_eq!(without.workspace_root, None);
}
#[test]
fn test9997_the_workbench_stays_local_and_peers_can_reach_the_machine() {
let dir = tempfile::tempdir().expect("a scratch directory");
let config = Config::from_args(complete(dir.path())).expect("parses");
assert!(
config.listen.is_loopback(),
"the workbench must not be reachable from another machine by default"
);
assert_eq!(
config.peer_listen,
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
"peers must be able to reach this machine without anybody configuring it"
);
let narrowed = Config::from_args({
let mut argv = complete(dir.path());
argv.extend(args(&["--peer-listen", "192.168.1.10"]));
argv
})
.expect("parses");
assert_eq!(narrowed.peer_listen.to_string(), "192.168.1.10");
assert!(narrowed.listen.is_loopback(), "narrowing one must not move the other");
let exposed = Config::from_args({
let mut argv = complete(dir.path());
argv.extend(args(&["--listen", "0.0.0.0"]));
argv
})
.expect("parses");
assert_eq!(exposed.listen, IpAddr::V4(Ipv4Addr::UNSPECIFIED));
assert_eq!(
exposed.peer_listen,
IpAddr::V4(Ipv4Addr::UNSPECIFIED),
"exposing the workbench says nothing about the peer listener"
);
let error = Config::from_args({
let mut argv = complete(dir.path());
argv.extend(args(&["--peer-listen", "everywhere"]));
argv
})
.unwrap_err_or_else(|| "a non-address was accepted".to_string());
assert!(error.contains("--peer-listen"), "{error}");
}
#[test]
fn test9865_a_flag_without_its_value_is_refused() {
let error = Config::from_args(args(&["--workspace"]))
.expect_err("a flag with no value must be refused");
assert!(
error.contains("--workspace requires a value"),
"the refusal must name the flag: {error}"
);
}
#[test]
fn test9866_the_workspace_must_be_an_absolute_directory() {
let relative = Config::from_args(args(&[
"--workspace",
"wsb",
"--port",
"3000",
]))
.expect_err("a relative workspace must be refused");
assert!(
relative.contains("must be absolute"),
"the refusal must say why: {relative}"
);
let dir = tempfile::tempdir().expect("a scratch directory");
let file = dir.path().join("not-a-directory");
std::fs::write(&file, b"").expect("the fixture is written");
let not_a_dir = Config::from_args(args(&[
"--workspace",
&file.display().to_string(),
"--port",
"3000",
]))
.expect_err("a workspace that is not a directory must be refused");
assert!(
not_a_dir.contains("not a directory"),
"the refusal must say why: {not_a_dir}"
);
}
#[test]
fn test9867_a_port_of_zero_is_refused() {
let dir = tempfile::tempdir().expect("a scratch directory");
let error = Config::from_args(args(&[
"--workspace",
&dir.path().display().to_string(),
"--port",
"0",
]))
.expect_err("port 0 must be refused");
assert!(error.contains("must not be 0"), "{error}");
}
#[test]
fn test9869_an_unknown_argument_is_refused() {
let dir = tempfile::tempdir().expect("a scratch directory");
let mut argv = complete(dir.path());
argv.push("--serve-everything".to_string());
let error = Config::from_args(argv).expect_err("an unknown flag must be refused");
assert!(
error.contains("--serve-everything"),
"the refusal must name it: {error}"
);
}
#[test]
fn test9870_a_parent_pid_of_zero_is_refused() {
let dir = tempfile::tempdir().expect("a scratch directory");
let mut argv = complete(dir.path());
argv.extend(args(&["--parent-pid", "0"]));
let error = Config::from_args(argv).expect_err("pid 0 must be refused");
assert!(error.contains("must not be 0"), "{error}");
}
trait UnwrapErrOrElse<T> {
fn unwrap_err_or_else(self, message: impl FnOnce() -> String) -> String;
}
impl<T: std::fmt::Debug> UnwrapErrOrElse<T> for Result<T, String> {
fn unwrap_err_or_else(self, message: impl FnOnce() -> String) -> String {
match self {
Ok(value) => panic!("{}: parsed as {value:?}", message()),
Err(error) => error,
}
}
}