use std::path::Path;
use std::process::Command;
use tempfile::TempDir;
const BIN: &str = env!("CARGO_BIN_EXE_atlassian-cli");
fn readme_commands() -> Vec<String> {
let readme = Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../README.md")
.canonicalize()
.expect("README.md not found");
let text = std::fs::read_to_string(readme).expect("failed to read README.md");
let mut out = Vec::new();
for line in text.lines() {
let line = line.trim();
if !line.starts_with("atlassian-cli ") || line.ends_with('\\') {
continue;
}
let line = line.split('#').next().unwrap_or("").trim();
if line.is_empty() || line.contains('|') || out.contains(&line.to_string()) {
continue;
}
out.push(line.to_string());
}
out
}
fn split_args(line: &str) -> Vec<String> {
let mut args = Vec::new();
let mut current = String::new();
let mut quote: Option<char> = None;
let mut started = false;
for c in line.chars() {
match quote {
Some(q) if c == q => quote = None,
Some(_) => current.push(c),
None if c == '\'' || c == '"' => {
quote = Some(c);
started = true;
}
None if c.is_whitespace() => {
if started || !current.is_empty() {
args.push(std::mem::take(&mut current));
started = false;
}
}
None => current.push(c),
}
}
if started || !current.is_empty() {
args.push(current);
}
args
}
#[test]
fn every_readme_command_parses() {
let dir = TempDir::new().unwrap();
let config = dir.path().join("config.yaml");
std::fs::write(
&config,
"default_profile: t\nprofiles:\n t:\n email: a@b.c\n base_url: http://127.0.0.1:1\n workspace: w\n",
)
.unwrap();
let commands = readme_commands();
assert!(
commands.len() > 100,
"expected to find the README's command examples, found {}",
commands.len()
);
let children: Vec<_> = commands
.iter()
.map(|line| {
let args = split_args(line);
let child = Command::new(BIN)
.arg("--config")
.arg(&config)
.args(&args[1..])
.env("ATLASSIAN_CLI_TOKEN_T", "x")
.env("ATLASSIAN_CLI_BITBUCKET_TOKEN_T", "x")
.stdout(std::process::Stdio::null())
.stderr(std::process::Stdio::piped())
.spawn()
.expect("failed to run the CLI");
(line, child)
})
.collect();
let mut failures = Vec::new();
for (line, child) in children {
let output = child
.wait_with_output()
.expect("failed to wait for the CLI");
if output.status.code() == Some(2) {
let reason = String::from_utf8_lossy(&output.stderr)
.lines()
.next()
.unwrap_or("")
.to_string();
failures.push(format!(" {line}\n -> {reason}"));
}
}
assert!(
failures.is_empty(),
"{} of {} README commands do not parse:\n{}",
failures.len(),
commands.len(),
failures.join("\n")
);
}