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} must not contact a server"
);
}
}
}
#[test]
fn managed_is_refused_where_no_container_can_be_started() {
for args in [
&["router", "tokens", "list"][..],
&["router", "accounts", "list"],
&["router", "providers", "list"],
&["router", "logs", "anomalies"],
&["router", "doctor"],
&["router", "tls", "ca"],
] {
let managed = [args, &["--managed"]].concat();
assert!(
refuse_managed(&command_of(&managed)).is_some(),
"{args:?} --managed must be refused rather than silently meaning --local"
);
assert!(
refuse_managed(&command_of(args)).is_none(),
"{args:?} without the flag is unaffected"
);
}
}
#[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_command_that_signs_nothing_does_not_need_the_signing_secret() {
for argv in [
&["router", "tokens", "list"][..],
&["router", "doctor", "--local"],
&["router", "logs", "summary", "--local"],
&["router", "tls", "ca"],
&["router", "clients", "list"],
] {
let cli = Cli::try_parse_from(argv).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_cli(cli);
assert!(
relaxed
.token_secret
.as_deref()
.is_some_and(|secret| !secret.is_empty()),
"{argv:?} must be able to start without a signing secret"
);
}
}
#[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_cli(cli);
assert_eq!(relaxed.token_secret.as_deref(), Some("real-secret"));
}
#[test]
fn the_stand_in_secret_cannot_sign_or_encrypt() {
let cli = Cli::try_parse_from(["router", "tokens", "issue"]).expect("parses");
let secret = relax_token_secret_for_cli(cli)
.token_secret
.expect("a stand-in is installed");
assert!(crate::token_secret::is_placeholder(&secret));
assert!(
crate::token::TokenManager::new(&secret)
.issue_token(1, "")
.is_err(),
"a stand-in must not be able to sign a token"
);
}
#[test]
fn serving_still_demands_a_real_secret() {
let cli = Cli::try_parse_from(["router", "serve"]).expect("parses");
let relaxed = relax_token_secret_for_cli(cli);
assert!(
relaxed.token_secret.as_deref().is_none_or(str::is_empty),
"the router signs every token it issues"
);
}
#[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"
);
}
}