diff --git a/swarm-nats-auth/src/main.rs b/swarm-nats-auth/src/main.rs index f98c896f..58dc5c8b 100644 --- a/swarm-nats-auth/src/main.rs +++ b/swarm-nats-auth/src/main.rs @@ -94,7 +94,11 @@ struct Args { /// A suffix on the hive's id rather than a prefix of its own, because /// `agent-` reads as *the agent called ``* — the one thing /// this identity does not carry. - #[arg(long, default_value = "-agent")] + /// + /// A suffix starts with `-`, so it has to be accepted as a value rather + /// than read as a short-flag cluster — without this the default below + /// cannot be passed back in on the command line that produced it. + #[arg(long, default_value = "-agent", allow_hyphen_values = true)] agent_client_suffix: String, /// Client ids allowed to read every hive's status. Repeatable. The @@ -275,3 +279,75 @@ async fn main() -> anyhow::Result<()> { } anyhow::bail!("subscription to {AUTH_SUBJECT} ended") } + +#[cfg(test)] +mod tests { + use super::*; + + /// The argument vector `swarm-nats.nix` actually builds, in the form it + /// builds it: every flag space-separated, and the suffix's value opening + /// with `-`. Read as a short-flag cluster it aborts before `main` runs, + /// and a responder that never starts denies every client — which reaches + /// the operator as `authorization violation` on an unrelated process. + #[test] + fn the_units_own_argument_vector_parses() { + let args = Args::try_parse_from([ + "swarm-nats-auth", + "--user-seed-file", + "/run/credentials/callout-user.seed", + "--issuer-seed-file", + "/run/credentials/issuer.seed", + "--client-secret-file", + "/run/credentials/oidc-client.secret", + "--introspection-url", + "https://auth.example/api/oidc/introspection", + "--hive-client-prefix", + "hive-", + "--agent-client-suffix", + "-agent", + ]) + .expect("the unit's own argument vector must parse"); + assert_eq!(args.agent_client_suffix, "-agent"); + } + + /// The control for the case above: an ordinary value parses through the + /// same flag, so a failure there is about the leading `-` and not about + /// the argument being unknown. + #[test] + fn an_agent_suffix_without_a_leading_dash_parses_too() { + let args = Args::try_parse_from([ + "swarm-nats-auth", + "--user-seed-file", + "/run/credentials/callout-user.seed", + "--issuer-seed-file", + "/run/credentials/issuer.seed", + "--client-secret-file", + "/run/credentials/oidc-client.secret", + "--introspection-url", + "https://auth.example/api/oidc/introspection", + "--agent-client-suffix", + "agent", + ]) + .expect("an ordinary suffix must parse"); + assert_eq!(args.agent_client_suffix, "agent"); + } + + /// The default is the value the deployment passes, so the two cannot + /// drift into disagreeing about what an agent client is called. + #[test] + fn the_default_suffix_is_what_the_module_passes() { + let args = Args::try_parse_from([ + "swarm-nats-auth", + "--user-seed-file", + "/run/credentials/callout-user.seed", + "--issuer-seed-file", + "/run/credentials/issuer.seed", + "--client-secret-file", + "/run/credentials/oidc-client.secret", + "--introspection-url", + "https://auth.example/api/oidc/introspection", + ]) + .expect("the required flags alone must parse"); + assert_eq!(args.agent_client_suffix, "-agent"); + } +}