#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::panic,
clippy::needless_raw_string_hashes,
clippy::duration_suboptimal_units,
clippy::branches_sharing_code,
clippy::used_underscore_binding,
clippy::single_char_pattern,
clippy::ignore_without_reason,
clippy::cloned_ref_to_slice_refs,
clippy::doc_overindented_list_items,
clippy::match_wildcard_for_single_variants,
clippy::ignored_unit_patterns,
clippy::needless_collect,
clippy::unnecessary_map_or,
clippy::manual_flatten,
clippy::manual_strip,
clippy::future_not_send,
clippy::unnested_or_patterns,
clippy::no_effect_underscore_binding,
clippy::literal_string_with_formatting_args
)]
use assert_cmd::Command;
use predicates::prelude::*;
fn ggen() -> Command {
Command::cargo_bin("ggen").expect("Failed to find ggen binary")
}
#[test]
#[ignore]
fn test_cli_help_main() {
ggen()
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains("ggen"))
.stdout(predicate::str::contains("template"))
.stdout(predicate::str::contains("market"))
.stdout(predicate::str::contains("project"))
.stdout(predicate::str::contains("graph"));
}
#[test]
fn test_cli_help_short_flag() {
ggen()
.arg("-h")
.assert()
.success()
.stdout(predicate::str::contains("Usage").or(predicate::str::contains("ggen")));
}
#[test]
fn test_cli_version() {
ggen()
.arg("--version")
.assert()
.success()
.stdout(predicate::str::contains("ggen").or(predicate::str::contains(".")));
}
#[test]
fn test_cli_version_short_flag() {
ggen()
.arg("-V")
.assert()
.success()
.stdout(predicate::str::contains("ggen").or(predicate::str::contains(".")));
}
#[test]
fn test_cli_invalid_command() {
ggen()
.arg("nonexistent-command")
.assert()
.failure()
.stderr(predicate::str::contains("error").or(predicate::str::contains("invalid")));
}
#[test]
fn test_cli_invalid_flag() {
ggen()
.arg("--nonexistent-flag")
.assert()
.failure()
.stderr(predicate::str::contains("error").or(predicate::str::contains("unexpected")));
}
#[test]
#[ignore]
fn test_cli_missing_required_arg() {
ggen()
.arg("template")
.arg("show")
.assert()
.failure()
.stderr(predicate::str::contains("required").or(predicate::str::contains("error")));
}
#[test]
#[ignore]
fn test_doctor_basic_check() {
ggen().arg("doctor").assert().success().stdout(
predicate::str::contains("Checking")
.or(predicate::str::contains("System"))
.or(predicate::str::contains("Prerequisites")),
);
}
#[test]
#[ignore]
fn test_doctor_verbose_output() {
ggen().arg("doctor").arg("--verbose").assert().success();
}
#[test]
#[ignore]
fn test_help_progressive_basic() {
ggen()
.arg("help-me")
.assert()
.success()
.stdout(predicate::str::contains("help").or(predicate::str::contains("Getting started")));
}
#[test]
#[ignore]
fn test_help_progressive_with_level() {
ggen()
.arg("help-me")
.arg("--level")
.arg("beginner")
.assert()
.success();
}
#[test]
#[ignore]
fn test_help_progressive_topic() {
ggen()
.arg("help-me")
.arg("--topic")
.arg("templates")
.assert()
.success();
}
fn discovered_nouns() -> Vec<String> {
let output = ggen().arg("--help").output().expect("run ggen --help");
let stdout = String::from_utf8_lossy(&output.stdout);
let mut nouns = Vec::new();
let mut in_commands = false;
for line in stdout.lines() {
if line.trim() == "Commands:" {
in_commands = true;
continue;
}
if !in_commands {
continue;
}
if line.trim().is_empty() {
break; }
let bytes = line.as_bytes();
let is_name_line = bytes.len() >= 3 && &bytes[0..2] == b" " && bytes[2] != b' ';
if !is_name_line {
continue; }
if let Some(name) = line.split_whitespace().next() {
if name != "help" {
nouns.push(name.to_string());
}
}
}
nouns
}
#[test]
fn test_all_nouns_have_help() {
let nouns = discovered_nouns();
assert!(
!nouns.is_empty(),
"ggen --help should advertise at least one noun"
);
for noun in &nouns {
ggen()
.arg(noun)
.arg("--help")
.assert()
.success()
.stdout(predicate::str::contains(noun.as_str()).or(predicate::str::contains("Usage")));
}
}
#[test]
fn test_error_suggests_help() {
let output = ggen().arg("invalid-command").output().unwrap();
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("help")
|| stderr.contains("--help")
|| stderr.contains("usage")
|| stderr.contains("error"),
"Error should suggest help: {}",
stderr
);
}
#[test]
fn test_cli_no_args_shows_help() {
let output = ggen().output().unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stdout.contains("Usage")
|| stdout.contains("help")
|| stderr.contains("Usage")
|| stderr.contains("help"),
"Should show usage information"
);
}
#[test]
#[ignore]
fn test_cli_subcommand_no_verb() {
ggen().arg("template").assert().failure().stderr(
predicate::str::contains("required")
.or(predicate::str::contains("subcommand"))
.or(predicate::str::contains("error")),
);
}
#[test]
#[ignore]
fn test_cli_help_shows_examples() {
let output = ggen().arg("template").arg("--help").output().unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("new") || stdout.contains("list") || stdout.contains("show"),
"Help should show available verbs"
);
}
#[test]
fn test_cli_consistent_error_format() {
let outputs = vec![
ggen().arg("invalid-command").output().unwrap(),
ggen().arg("template").arg("invalid-verb").output().unwrap(),
ggen().arg("--invalid-flag").output().unwrap(),
];
for output in outputs {
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("error")
|| stderr.contains("Error")
|| stderr.contains("invalid")
|| stderr.contains("unexpected"),
"Error messages should be clear: {}",
stderr
);
}
}
#[test]
fn test_cli_exit_codes_correct() {
ggen().arg("--help").assert().success();
ggen().arg("invalid-command").assert().failure();
}
#[test]
fn test_cli_handles_ctrl_c_gracefully() {
let output = ggen().arg("--help").output().unwrap();
assert!(output.status.success());
}
#[test]
fn test_cli_colored_output_flag() {
let _ = ggen().arg("--help").arg("--color=never").output();
ggen()
.arg("--help")
.arg("--color=always")
.assert()
.success();
}
#[test]
#[ignore]
fn test_cli_json_output_flag() {
let commands_with_json = vec![
vec!["template", "list", "--json"],
vec!["market", "list", "--json"],
vec!["market", "search", "test", "--json"],
];
for args in commands_with_json {
let output = ggen().args(&args).output().unwrap();
if output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout);
if !stdout.trim().is_empty() {
serde_json::from_str::<serde_json::Value>(&stdout)
.expect("JSON output should be valid");
}
}
}
}
#[test]
fn test_cli_performance_help_fast() {
let start = std::time::Instant::now();
ggen().arg("--help").assert().success();
let duration = start.elapsed();
assert!(
duration.as_millis() < 1000,
"Help should be instant: {:?}",
duration
);
}
#[test]
#[ignore]
fn test_cli_doctor_checks_essentials() {
let output = ggen().arg("doctor").output().unwrap();
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(!stdout.is_empty(), "Doctor should produce output");
}