quinjet 0.0.37

A fast, live, keyboard-first Git source-control interface for the terminal
use super::*;

#[test]
fn version_names_the_binary() -> Result<()> {
    let run = run_in(None, &["--version"])?.success()?;
    ensure!(
        run.stdout.starts_with("quinjet "),
        "unexpected version line: {}",
        run.stdout
    );
    Ok(())
}

#[test]
fn help_lists_every_group_verb() -> Result<()> {
    let run = run_in(None, &["--help"])?.success()?;
    for verb in [
        "tui",
        "status",
        "diff",
        "stage",
        "unstage",
        "discard",
        "commit",
        "fetch",
        "pull",
        "push",
        "sync",
        "log",
        "show",
        "branch",
        "stash",
        "cherry-pick",
        "revert",
        "resolve",
        "repos",
        "pr",
        "completions",
        "man",
        "capabilities",
        "update",
    ] {
        ensure!(run.stdout.contains(verb), "--help does not mention {verb}");
    }
    Ok(())
}

#[test]
fn tui_help_lists_every_theme_and_appearance() -> Result<()> {
    let run = run_in(None, &["tui", "--help"])?.success()?;
    ensure!(run.stdout.contains("--theme <THEME>"));
    ensure!(run.stdout.contains("--appearance <APPEARANCE>"));
    for theme in [
        "quinjet",
        "catppuccin",
        "dracula",
        "everforest",
        "gruvbox",
        "nord",
        "one",
        "rose-pine",
        "solarized",
        "tokyo-night",
        "ayu",
        "monokai",
    ] {
        ensure!(run.stdout.contains(theme), "tui help omitted {theme}");
    }
    for appearance in ["system", "light", "dark"] {
        ensure!(
            run.stdout.contains(appearance),
            "tui help omitted {appearance}"
        );
    }
    Ok(())
}

#[test]
fn tui_accepts_theme_and_appearance_before_claiming_the_terminal() -> Result<()> {
    let run = run_in(
        None,
        &["tui", "--theme", "catppuccin", "--appearance", "dark"],
    )?;
    ensure!(run.code == 1, "non-interactive TUI exited {}", run.code);
    ensure!(
        run.stderr.contains("requires an interactive terminal"),
        "theme arguments did not reach TUI startup: {}",
        run.stderr
    );
    ensure!(!run.stderr.contains("unexpected argument"));
    Ok(())
}

#[test]
fn every_subcommand_answers_help() -> Result<()> {
    for path in COMMAND_PATHS {
        let mut args = path.to_vec();
        args.push("--help");
        let run = run_in(None, &args)?;
        ensure!(run.code == 0, "{path:?} --help exited {}", run.code);
    }
    Ok(())
}

#[test]
fn root_help_leads_with_examples_and_documentation() -> Result<()> {
    let run = run_in(None, &["--help"])?.success()?;
    ensure!(run.stdout.contains("Examples:"));
    ensure!(run.stdout.contains("quinjet status --json"));
    ensure!(run.stdout.contains("Documentation:"));
    Ok(())
}

#[test]
fn unknown_verbs_and_implicit_paths_are_usage_errors() -> Result<()> {
    for value in ["statsu", "/tmp/somewhere"] {
        let run = run_in(None, &[value])?;
        ensure!(run.code == 2, "{value} exited {}", run.code);
        ensure!(
            run.stderr.contains("unrecognized subcommand"),
            "unexpected error for {value}: {}",
            run.stderr
        );
    }
    Ok(())
}

#[test]
fn clap_rejects_incomplete_and_inert_arguments() -> Result<()> {
    for args in [
        &["stage"][..],
        &["unstage"][..],
        &["discard"][..],
        &["resolve", "README.md"][..],
        &["status", "--interval", "0"][..],
        &["pr", "checks", "1", "--watch", "--exit-code"][..],
    ] {
        let run = run_in(None, args)?;
        ensure!(run.code == 2, "{args:?} exited {}", run.code);
        ensure!(!run.stderr.is_empty(), "{args:?} produced no usage error");
    }
    Ok(())
}

#[test]
fn the_pr_launch_flag_is_refused_alongside_a_verb() -> Result<()> {
    let run = run_in(None, &["--pr", "12", "status"])?;
    ensure!(run.code == 1, "expected exit 1, got {}", run.code);
    ensure!(
        run.stderr.contains("terminal interface"),
        "the error does not explain where --pr applies: {}",
        run.stderr
    );
    Ok(())
}

#[test]
fn unknown_flags_are_usage_errors() -> Result<()> {
    let run = run_in(None, &["status", "--no-such-flag"])?;
    ensure!(run.code == 2, "expected exit 2, got {}", run.code);
    ensure!(
        run.stderr.contains("--no-such-flag"),
        "usage error does not name the flag"
    );
    Ok(())
}

#[test]
fn a_missing_repository_is_a_plain_failure() -> Result<()> {
    let scratch = Scratch::directory()?;
    let run = scratch.quinjet(&["status"])?;
    ensure!(run.code == 1, "expected exit 1, got {}", run.code);
    ensure!(
        run.stderr.contains("error:"),
        "failure did not report on stderr: {}",
        run.stderr
    );
    Ok(())
}

#[test]
fn status_reports_the_branch_in_both_faces() -> Result<()> {
    let scratch = Scratch::repository()?;
    let text = scratch.quinjet(&["status"])?.success()?;
    ensure!(
        text.stdout.contains("main"),
        "status does not name the branch: {}",
        text.stdout
    );
    let json = scratch.quinjet(&["status", "--json"])?.success()?;
    let document = json.json()?;
    ensure!(
        document["branch"]["head"] == "main",
        "unexpected JSON branch: {document}"
    );
    Ok(())
}

#[test]
fn captured_and_json_output_never_include_progress() -> Result<()> {
    let scratch = Scratch::repository()?;
    for args in [vec!["status"], vec!["status", "--json"]] {
        let run = scratch.quinjet(&args)?.success()?;
        ensure!(
            run.stderr.is_empty(),
            "{args:?} wrote progress to captured stderr: {}",
            run.stderr
        );
    }
    Ok(())
}