use super::*;
use crate::cli::Cli;
use clap::Parser as _;
fn command_of(args: &[&str]) -> Command {
Cli::try_parse_from(args)
.expect("the command line must parse")
.command
.expect("a subcommand was given")
}
#[test]
fn every_state_touching_family_accepts_a_target() {
for args in [
&["router", "tokens", "list"][..],
&["router", "accounts", "list"][..],
&["router", "providers", "list"][..],
&["router", "logs", "anomalies"][..],
&["router", "doctor"][..],
] {
assert!(
target_of(&command_of(args)).is_some(),
"{args:?} must carry a target"
);
let with_server = [args, &["--server", "http://router.example:8080"]].concat();
let command = command_of(&with_server);
let target = target_of(&command).expect("a target");
assert_eq!(
target.server.as_deref(),
Some("http://router.example:8080"),
"{args:?} must accept --server"
);
}
}
#[test]
fn only_an_explicit_local_target_skips_resolution() {
for args in [
&["router", "tokens", "list"][..],
&["router", "logs", "anomalies"][..],
] {
assert!(
may_be_remote(&command_of(args)),
"{args:?} must resolve: a persisted selection counts"
);
for flag in ["--local", "--managed"] {
let explicit = [args, &[flag]].concat();
assert!(
!may_be_remote(&command_of(&explicit)),
"{args:?} {flag} asks for this machine and must not contact a server"
);
}
}
}
#[test]
fn commands_without_a_target_are_left_alone() {
for args in [
&["router", "serve"][..],
&["router", "auth", "status"][..],
&["router", "clients", "list"][..],
] {
let command = command_of(args);
assert!(target_of(&command).is_none(), "{args:?}");
assert!(!may_be_remote(&command), "{args:?}");
}
}
#[test]
fn a_remote_command_does_not_need_the_local_signing_secret() {
let cli = Cli::try_parse_from(["router", "tokens", "list"]).expect("parses");
assert!(
cli.token_secret.as_deref().is_none_or(str::is_empty),
"the fixture must start without a secret"
);
let relaxed = relax_token_secret_for_remote(cli);
assert!(
relaxed
.token_secret
.as_deref()
.is_some_and(|secret| !secret.is_empty()),
"a remote command must be able to start without one"
);
}
#[test]
fn a_supplied_secret_survives_the_relaxation() {
let cli = Cli::try_parse_from(["router", "tokens", "list", "--token-secret", "real-secret"])
.expect("parses");
let relaxed = relax_token_secret_for_remote(cli);
assert_eq!(relaxed.token_secret.as_deref(), Some("real-secret"));
}
#[test]
fn an_explicitly_local_command_still_needs_its_secret() {
let cli = Cli::try_parse_from(["router", "tokens", "list", "--local"]).expect("parses");
let relaxed = relax_token_secret_for_remote(cli);
assert!(
relaxed.token_secret.as_deref().is_none_or(str::is_empty),
"signing happens here, so the secret is genuinely required"
);
}
#[test]
fn a_refusal_names_the_target_and_an_alternative() {
let server = ResolvedServer::at(
"http://router.example:8080".to_string(),
Some("token".to_string()),
"test",
);
let lines = no_remote_form("doctor", &server, "run `router doctor` on that deployment");
let all = lines.join("\n");
assert!(
all.contains("http://router.example:8080"),
"describing local state as though it were the target is the bug: {all}"
);
assert!(
all.contains("run `router doctor` on that deployment"),
"{all}"
);
assert!(
all.contains("--local"),
"the local action stays reachable: {all}"
);
assert!(all.starts_with("error:"), "{all}");
}
#[tokio::test]
async fn an_explicit_local_target_resolves_without_a_server() {
let target = AuthTarget {
local: true,
server: None,
managed: false,
};
match resolve(&target).await {
Ok(Target::Local) => {}
Ok(Target::Remote(_)) => panic!("--local must not contact a server"),
Err(code) => panic!("--local must always resolve, got {code:?}"),
}
}
#[tokio::test]
async fn an_unreachable_named_target_is_an_error() {
let target = AuthTarget {
local: false,
server: Some("http://127.0.0.1:1".to_string()),
managed: false,
};
match resolve(&target).await {
Ok(_) => panic!("an unreachable target must not resolve to the local path"),
Err(code) => assert_eq!(format!("{code:?}"), format!("{:?}", ExitCode::from(1))),
}
}
#[test]
fn a_refusal_exits_non_zero() {
let code = refuse(vec!["error: nope".to_string()]);
assert_eq!(format!("{code:?}"), format!("{:?}", ExitCode::from(1)));
}
#[test]
fn every_subcommand_in_every_family_reports_its_target() {
for args in [
&["router", "tokens", "issue"][..],
&["router", "tokens", "rotate", "tok-1"][..],
&["router", "tokens", "list"][..],
&["router", "tokens", "revoke", "tok-1"][..],
&["router", "tokens", "expire", "tok-1"][..],
&["router", "tokens", "show", "tok-1"][..],
&["router", "accounts", "list"][..],
&["router", "providers", "list"][..],
&[
"router",
"providers",
"add",
"--name",
"d",
"--base-url",
"https://d/v1",
][..],
&["router", "providers", "show", "d"][..],
&["router", "providers", "remove", "d"][..],
&["router", "providers", "import", "/tmp/m.lenv"][..],
&["router", "logs", "summary"][..],
&["router", "logs", "anomalies"][..],
&["router", "logs", "show", "cid-1"][..],
] {
let command = command_of(args);
assert!(
target_of(&command).is_some(),
"{args:?} must report a target, or it silently stays local"
);
let local = [args, &["--local"]].concat();
assert!(
!may_be_remote(&command_of(&local)),
"{args:?} must honour --local"
);
}
}