use clap::Parser;
use super::*;
use crate::cli::{Cli, Command};
fn parse(args: &[&str]) -> (SessionAction, Option<String>) {
let cli = Cli::try_parse_from(args).expect("parses");
match cli.command {
Some(Command::Sessions { action }) => (action, cli.account),
other => panic!("expected `tm sessions`, got {other:?}"),
}
}
#[test]
fn spawn_dir_names_the_new_and_start_checkouts() {
let (action, account) = parse(&[
"tm",
"sessions",
"new",
"--account",
"bobmatnyc",
"/work/itinerary",
"--task",
"t",
]);
assert_eq!(account.as_deref(), Some("bobmatnyc"));
assert_eq!(
spawn_dir(&action).expect("resolves"),
Some(PathBuf::from("/work/itinerary"))
);
let (action, account) = parse(&[
"tm",
"--account",
"bobmatnyc",
"sessions",
"start",
"--dir",
"/w",
]);
assert_eq!(account.as_deref(), Some("bobmatnyc"));
assert_eq!(
spawn_dir(&action).expect("resolves"),
Some(PathBuf::from("/w"))
);
}
#[test]
fn spawn_dir_is_none_for_a_verb_that_spawns_nothing() {
let (action, _) = parse(&[
"tm",
"sessions",
"resume",
"tm-x-01",
"--account",
"bobmatnyc",
]);
assert_eq!(spawn_dir(&action).expect("resolves"), None);
}
fn project(name: &str, repo_url: &str) -> Project {
Project {
name: name.to_string(),
repo_url: repo_url.to_string(),
default_branch: "main".to_string(),
stack_hint: None,
tags: vec![],
description: None,
gh_user: None,
gh_account: Some("bob-duetto".to_string()),
github: None,
commit_name: None,
commit_email: None,
worktree: None,
}
}
#[test]
fn pin_targets_updates_every_matching_record() {
let projects = vec![
project("itinerary", "git@github.com:acme/itinerary.git"),
project("itin-alias", "https://github.com/acme/itinerary"),
project("other", "https://github.com/acme/other"),
];
let targets = pin_targets(&projects, "https://github.com/acme/itinerary.git").expect("targets");
assert_eq!(
targets,
vec![
(
"itinerary".to_string(),
"git@github.com:acme/itinerary.git".to_string()
),
(
"itin-alias".to_string(),
"https://github.com/acme/itinerary".to_string()
),
]
);
}
#[test]
fn pin_targets_names_a_new_record_from_the_origin() {
let origin = "https://github.com/acme/itinerary.git";
let targets = pin_targets(&[], origin).expect("targets");
let expected = trusty_mpm::project::derive_name_from_url(origin).expect("a name");
assert_eq!(targets, vec![(expected, origin.to_string())]);
}
#[test]
fn pin_notice_names_the_account_it_replaces() {
assert_eq!(
pin_notice("itinerary", Some("bob-duetto"), "bobmatnyc"),
"tm: every session of project 'itinerary' now runs as gh account 'bobmatnyc' \
(was 'bob-duetto')"
);
assert!(pin_notice("itinerary", None, "bobmatnyc").ends_with("(was unpinned)"));
assert!(pin_notice("itinerary", Some(" "), "bobmatnyc").ends_with("(was unpinned)"));
}
#[test]
fn transport_notice_says_when_git_uses_ssh() {
for origin in [
"git@github.com:acme/itinerary.git",
"ssh://git@github.com/acme/itinerary.git",
] {
let notice = transport_notice(origin, "bobmatnyc");
assert!(
notice.contains("uses SSH") && notice.contains("does not pin"),
"{notice}"
);
}
let https = transport_notice("https://github.com/acme/itinerary", "bobmatnyc");
assert!(https.contains("HTTPS git as 'bobmatnyc'"), "{https}");
}
#[test]
fn read_stdin_token_trims_one_token() {
let token = read_stdin_token(" ghp_abc123\n".as_bytes()).expect("one token");
assert_eq!(token, "ghp_abc123");
}
#[test]
fn read_stdin_token_refuses_empty_or_spaced_input() {
for input in ["", " \n", "ghp_a ghp_b\n"] {
let err = read_stdin_token(input.as_bytes()).expect_err("must be refused");
assert!(err.to_string().contains("exactly one token"), "{err}");
}
}
#[test]
fn cli_account_token_stdin_requires_an_account() {
let bare = Cli::try_parse_from([
"tm",
"sessions",
"new",
"/w",
"--task",
"t",
"--account-token-stdin",
]);
assert!(bare.is_err(), "a token for no account must not parse");
let cli = Cli::try_parse_from([
"tm",
"--account",
"bobmatnyc",
"--account-token-stdin",
"sessions",
"new",
"/w",
"--task",
"t",
])
.expect("parses");
assert!(cli.account_token_stdin);
}
#[test]
fn account_notices_never_quote_the_origin_token() {
const TOKEN: &str = "ghp_9124AccountSyntheticToken0000";
let ssh = format!("ssh://octo:{TOKEN}@github.com/acme/widget.git");
let notice = transport_notice(&ssh, "octo");
assert!(!notice.contains(TOKEN), "the token reached the notice");
assert!(
notice.contains("ssh://***@github.com/acme/widget.git"),
"{notice}"
);
let unnamed = format!("https://u:{TOKEN}@host/");
let err = pin_targets(&[], &unnamed)
.expect_err("an origin naming no repository names no project")
.to_string();
assert!(!err.contains(TOKEN), "the token reached the refusal");
assert!(err.contains("https://***@host/"), "{err}");
let notice = transport_notice(
&format!("ssh://octo:pa'ss{TOKEN}@github.com/a/w.git"),
"octo",
);
assert!(!notice.contains(TOKEN), "the token reached the notice");
assert!(notice.contains("ssh://***@github.com/a/w.git"), "{notice}");
let err = pin_targets(&[], &format!("https://u:pa ss{TOKEN}@host/"))
.expect_err("an origin naming no repository names no project")
.to_string();
assert!(!err.contains(TOKEN), "the token reached the refusal");
assert!(err.contains("https://***@host/"), "{err}");
}