quinjet 0.0.49

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

#[cfg(not(windows))]
#[test]
fn shell_integration_makes_q_immediate_without_restoring_removals() -> Result<()> {
    let scratch = Scratch::directory()?;
    let bin = scratch.path.join("bin");
    let data = scratch.path.join("data");
    let state = scratch.path.join(".local/state/quinjet");
    let executable = bin.join("quinjet");
    let shortcut = bin.join("q");
    let completion = data.join("bash-completion/completions/quinjet");
    let bashrc = scratch.path.join(".bashrc");
    fs::create_dir_all(&bin)?;
    fs::create_dir_all(&state)?;
    fs::create_dir_all(completion.parent().context("completion had no parent")?)?;
    let staged = bin.join("quinjet-stage");
    fs::copy(env!("CARGO_BIN_EXE_quinjet"), &staged)?;
    fs::rename(staged, &executable)?;
    fs::write(state.join("bash-installed"), "installed\n")?;
    fs::write(&completion, "stale completion\n")?;
    fs::write(
        &bashrc,
        "# >>> quinjet shortcut >>>\nalias q='quinjet'\n# <<< quinjet shortcut <<<\n",
    )?;

    let maintain = || -> Result<Run> {
        let mut update = ProcessCommand::new(&executable);
        update
            .args(["completions", "bash", "--install", "--automatic"])
            .env("HOME", &scratch.path)
            .env("XDG_DATA_HOME", &data)
            .env("PATH", &bin)
            .env("SHELL", "/bin/bash")
            .env("PSModulePath", "inherited-but-not-active");
        isolate_git(&mut update);
        Run::from(copied_binary_output(
            &mut update,
            "failed to refresh completions",
        )?)?
        .success()
    };

    fs::write(&shortcut, "unrelated q command\n")?;
    drop(maintain()?);
    ensure!(fs::read_to_string(&shortcut)? == "unrelated q command\n");
    ensure!(fs::read_to_string(&bashrc)?.contains("quinjet shortcut"));
    fs::remove_file(&shortcut)?;
    fs::remove_file(state.join("shortcut-installed"))?;

    drop(maintain()?);
    ensure!(fs::read_to_string(&completion)?.contains("complete -F _quinjet"));
    ensure!(shortcut.exists());
    ensure!(!fs::read_to_string(&bashrc)?.contains("quinjet shortcut"));
    ensure!(state.join("shortcut-installed").is_file());

    let mut path = vec![bin.clone()];
    if let Some(existing) = std::env::var_os("PATH") {
        path.extend(std::env::split_paths(&existing));
    }
    let mut invoke_q = ProcessCommand::new("q");
    invoke_q
        .arg("--version")
        .env("PATH", std::env::join_paths(path)?);
    let q = Run::from(copied_binary_output(
        &mut invoke_q,
        "q was unavailable in the current shell",
    )?)?
    .success()?;
    ensure!(q.stdout.contains("quinjet"));

    fs::remove_file(&shortcut)?;
    drop(maintain()?);
    ensure!(!shortcut.exists());

    fs::remove_file(&completion)?;
    drop(maintain()?);
    ensure!(!completion.exists());

    let mut restore = ProcessCommand::new(&executable);
    restore
        .args(["completions", "bash", "--install"])
        .env("HOME", &scratch.path)
        .env("XDG_DATA_HOME", &data)
        .env("PATH", &bin)
        .env("SHELL", "/bin/bash");
    isolate_git(&mut restore);
    drop(
        Run::from(copied_binary_output(
            &mut restore,
            "failed to restore completions",
        )?)?
        .success()?,
    );
    ensure!(completion.exists());
    ensure!(shortcut.exists());
    Ok(())
}

#[cfg(all(not(windows), not(debug_assertions)))]
#[test]
fn shell_integration_without_a_detected_shell_still_installs_q() -> Result<()> {
    let scratch = Scratch::directory()?;
    let bin = scratch.path.join("bin");
    let executable = bin.join("quinjet");
    let shortcut = bin.join("q");
    fs::create_dir_all(&bin)?;
    let staged = bin.join("quinjet-stage");
    fs::copy(env!("CARGO_BIN_EXE_quinjet"), &staged)?;
    fs::rename(staged, &executable)?;

    let mut first_run = ProcessCommand::new(&executable);
    first_run
        .arg("--version")
        .env("HOME", &scratch.path)
        .env("PATH", &bin)
        .env_remove("PSModulePath")
        .env_remove("SHELL");
    isolate_git(&mut first_run);
    drop(Run::from(copied_binary_output(&mut first_run, "failed first run")?)?.success()?);
    ensure!(shortcut.exists());

    let mut invoke_q = ProcessCommand::new("q");
    invoke_q
        .arg("--version")
        .env("HOME", &scratch.path)
        .env("PATH", &bin)
        .env_remove("PSModulePath")
        .env_remove("SHELL");
    let q = Run::from(copied_binary_output(
        &mut invoke_q,
        "q was unavailable on PATH",
    )?)?
    .success()?;
    ensure!(q.stdout.contains("quinjet"));
    Ok(())
}

#[cfg(windows)]
#[test]
fn shell_integration_makes_q_immediate_on_windows() -> Result<()> {
    let scratch = Scratch::directory()?;
    let bin = scratch.path.join("bin");
    let data = scratch.path.join("data");
    let executable = bin.join("quinjet.exe");
    let shortcut = bin.join("q.cmd");
    fs::create_dir_all(&bin)?;
    let staged = bin.join("quinjet-stage.exe");
    fs::copy(env!("CARGO_BIN_EXE_quinjet"), &staged)?;
    fs::rename(staged, &executable)?;

    let mut install = ProcessCommand::new(&executable);
    install
        .args(["completions", "bash", "--install"])
        .env("HOME", &scratch.path)
        .env("USERPROFILE", &scratch.path)
        .env("LOCALAPPDATA", scratch.path.join("local"))
        .env("XDG_DATA_HOME", &data)
        .env("PATH", &bin);
    isolate_git(&mut install);
    drop(
        Run::from(
            install
                .output()
                .context("failed to install shell integration")?,
        )?
        .success()?,
    );
    ensure!(shortcut.exists());

    let mut path = vec![bin];
    if let Some(existing) = std::env::var_os("PATH") {
        path.extend(std::env::split_paths(&existing));
    }
    let mut invoke_q = ProcessCommand::new("cmd.exe");
    invoke_q
        .args(["/D", "/C", "q --version"])
        .env("PATH", std::env::join_paths(path)?);
    let q = Run::from(
        invoke_q
            .output()
            .context("q was unavailable in the current shell")?,
    )?
    .success()?;
    ensure!(q.stdout.contains("quinjet"));
    Ok(())
}