Something went wrong. Try again.
Identities for entities did.bot
agent llm did
Something went wrong. Try again.
8.8 kB · 280 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281//! The command line as `didbot --list` and a script see it: help and//! version on stdout with exit 0, a usage refusal on stderr with exit 2, and//! `operate` refusing to open a browser for nobody.
use std::process::{Command, Output};
fn operator(args: &[&str]) -> Output { Command::new(env!("CARGO_BIN_EXE_didbot-operator")) .args(args) // A home that holds no session, so no verb finds one by accident. .env( "XDG_CONFIG_HOME", std::env::temp_dir().join("didbot-operator-nowhere"), ) .output() .expect("run didbot-operator")}
#[test]fn help_goes_to_stdout_and_succeeds() { for args in [ &["--help"][..], &["-h"], &["operate", "--help"], &["estop", "--help"], &["account", "--help"], &["app", "--help"], ] { let output = operator(args); assert!(output.status.success(), "{args:?} exited {}", output.status); let stdout = String::from_utf8_lossy(&output.stdout); assert!( stdout.contains("Usage: didbot"), "{args:?} did not spell the command the way a person types it:\n{stdout}" ); assert!(output.stderr.is_empty(), "{args:?} wrote to stderr"); } let stdout = String::from_utf8_lossy(&operator(&["--help"]).stdout).into_owned(); for verb in [ "operate", "login", "estop", "account", "app", "announce", "--server", "--json", ] { assert!( stdout.contains(verb), "--help does not mention {verb}:\n{stdout}" ); } let stdout = String::from_utf8_lossy(&operator(&["operate", "--help"]).stdout).into_owned(); for flag in [ "--check", "--kind", "--oidc", "--fingerprint", "--creates", "--creates-beneath", ] { assert!( stdout.contains(flag), "operate --help does not mention {flag}:\n{stdout}" ); }}
/// `--list` reads the version off this line, and it has to be this/// binary's name, not the dispatcher's spelling.#[test]fn version_names_the_binary() { let output = operator(&["--version"]); assert!(output.status.success()); assert_eq!( String::from_utf8_lossy(&output.stdout).trim(), format!("didbot-operator {}", env!("CARGO_PKG_VERSION")) );}
#[test]fn a_wrong_command_line_exits_two_on_stderr() { for args in [ &[][..], &["frobnicate"], &["estop", "--server", "pds.example", "--pause", "--revoke"], &["login"], &["operate"], // A parked key is admitted by its fingerprint or by a question, never // by a flag that skips both. &["operate", "h1.pds.example", "op.example", "--yes"], // An allowance names kinds, and a kind is one of four labels. &[ "operate", "h1.pds.example", "op.example", "--creates", "bogus", ], &[ "operate", "h1.pds.example", "op.example", "--creates-beneath", "agent", "bogus", ], ] { let output = operator(args); assert_eq!( output.status.code(), Some(2), "{args:?}: {}", String::from_utf8_lossy(&output.stderr) ); assert!(!output.stderr.is_empty(), "{args:?} refused silently"); assert!(output.stdout.is_empty(), "{args:?} put a refusal on stdout"); }}
/// A claim needs the operator's own account to sign in as; asking for one/// is a usage error, before any browser or network.#[test]fn operate_without_an_operator_is_refused_before_signing_in() { let output = operator(&["operate", "pds.example"]); assert_eq!(output.status.code(), Some(2)); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.starts_with("didbot operate: "), "{stderr}"); assert!(stderr.contains("<OPERATOR>"), "{stderr}");}
/// `--oidc` is an issuer and then `claim=value` pairs; anything else is a/// command line, refused before any sign-in.#[test]fn operate_refuses_a_malformed_oidc_identity_before_signing_in() { for args in [ &[ "operate", "web.pds.example", "op.example", "--oidc", "https://issuer.example", ][..], &[ "operate", "web.pds.example", "op.example", "--oidc", "https://issuer.example", "no-equals-sign", ], ] { let output = operator(args); assert_eq!( output.status.code(), Some(2), "{args:?}: {}", String::from_utf8_lossy(&output.stderr) ); assert!(output.stdout.is_empty(), "{args:?} put a refusal on stdout"); }}
/// `--creates bogus` is refused by name, with the kinds it could have said,/// before any sign-in.#[test]fn operate_refuses_an_unknown_kind_in_an_allowance_before_signing_in() { let output = operator(&[ "operate", "laptop.pds.example", "op.example", "--creates", "bogus", ]); assert_eq!(output.status.code(), Some(2)); let stderr = String::from_utf8_lossy(&output.stderr); assert!(stderr.contains("bogus"), "{stderr}"); for kind in ["service", "host", "pipeline", "agent"] { assert!(stderr.contains(kind), "{stderr} does not offer {kind}"); }}
/// Every account verb is gated on the operator session and refuses before/// it reaches a deployment when there is none.////// The session is the whole of the authorization for these, so a verb that/// ran without one would be a verb reaching a deployment as nobody.#[test]fn an_account_verb_with_no_session_reaches_no_deployment() { for verb in [ "lock", "unlock", "lift-quarantine", "erase", "delete", "revoke-tokens", ] { let output = operator(&[ "account", verb, "kestrel.agents.example", "--server", "pds.example", ]); assert!(!output.status.success(), "{verb} ran without a session"); let stderr = String::from_utf8_lossy(&output.stderr); assert!( stderr.starts_with("didbot account:"), "{verb}'s refusal is one line naming the command:\n{stderr}" ); }}
/// Ending an app's logins is gated on the operator session too, and names/// the application by `client_id` rather than by anything this deployment/// invented for it.#[test]fn ending_an_apps_logins_with_no_session_reaches_no_deployment() { let output = operator(&[ "app", "end-logins", "--client", "https://app.pds.example/client.json", "--server", "pds.example", ]); assert!(!output.status.success(), "end-logins ran without a session"); let stderr = String::from_utf8_lossy(&output.stderr); assert!( stderr.starts_with("didbot app:"), "the refusal is one line naming the command:\n{stderr}" );}
/// The account is optional, and its absence is what "every account" is/// spelled as. A verb that required it could not end an app everywhere.#[test]fn ending_an_apps_logins_takes_an_optional_account() { let output = operator(&[ "app", "end-logins", "--client", "https://app.pds.example/client.json", "--server", "pds.example", ]); let stderr = String::from_utf8_lossy(&output.stderr); assert!( !stderr.contains("required"), "--account must be optional:\n{stderr}" ); let stdout = String::from_utf8_lossy(&operator(&["app", "end-logins", "--help"]).stdout).into_owned(); for flag in ["--client", "--account"] { assert!( stdout.contains(flag), "app end-logins --help does not mention {flag}:\n{stdout}" ); }}
/// The subtree is asked for and never assumed: `--recursive` is the flag,/// and it is on `delete` alone.#[test]fn only_delete_takes_the_subtree() { let stdout = String::from_utf8_lossy(&operator(&["account", "delete", "--help"]).stdout).into_owned(); assert!( stdout.contains("--recursive"), "account delete --help does not mention --recursive:\n{stdout}" ); for verb in [ "lock", "unlock", "lift-quarantine", "erase", "revoke-tokens", ] { let stdout = String::from_utf8_lossy(&operator(&["account", verb, "--help"]).stdout).into_owned(); assert!( !stdout.contains("--recursive"), "account {verb} --help offers --recursive:\n{stdout}" ); }}