gitpane 0.16.1

Multi-repo Git workspace dashboard TUI
Documentation
use super::*;

fn run(command: &str, directory: &std::path::Path) -> String {
    let output = std::process::Command::new("sh")
        .args(["-c", command])
        .current_dir(directory)
        .output()
        .unwrap();
    assert!(
        output.status.success(),
        "{}",
        String::from_utf8_lossy(&output.stderr)
    );
    String::from_utf8(output.stdout).unwrap()
}

#[test]
fn shell_launches_preserve_values_in_each_quote_context_without_executing_them() {
    let directory = tempfile::tempdir().unwrap();
    for (target, base) in [
        ("source$(touch INJECTED).rs", "main"),
        ("source.rs", "base$(touch INJECTED)"),
        (
            "source café ' \" $(touch INJECTED) `touch INJECTED` {base}\nfile.rs",
            "branch'\";touch INJECTED;${HOME} {path}",
        ),
    ] {
        for template in [
            "printf '%s\\n' {path} {base}",
            "printf '%s\\n' \"{path}\" \"{base}\"",
            "printf '%s\\n' '{path}' '{base}'",
            "# ignored '\" {path} ${HOME}\nprintf '%s\\n' {path} {base}",
            "printf '%s\\n' pre{path}post 'pre{base}post'",
        ] {
            for (placement, mux) in [
                ("inline", Multiplexer::None),
                ("new-window", Multiplexer::None),
                ("split-window", Multiplexer::Tmux),
                ("new-window", Multiplexer::Herdr),
            ] {
                let dir = directory.path().to_str().unwrap();
                let launch =
                    plan_with_target(Some(template), placement, dir, target, Some(base), mux);
                let command = match launch {
                    LaunchPlan::Inline(command) => command,
                    LaunchPlan::Spawn(argv) => {
                        assert_eq!(&argv[..4], ["tmux", "split-window", "-c", dir]);
                        assert_eq!(&argv[4..6], ["sh", "-c"]);
                        argv[6].clone()
                    }
                    LaunchPlan::Herdr { create, command } => {
                        assert_eq!(
                            create,
                            ["herdr", "tab", "create", "--cwd", dir, "--no-focus"]
                        );
                        command.expect("configured command")
                    }
                    other => panic!("unexpected launch: {other:?}"),
                };
                let expected = if template.contains("pre{path}") {
                    format!("pre{target}post\npre{base}post\n")
                } else {
                    format!("{target}\n{base}\n")
                };
                let output = run(&command, directory.path());
                assert!(
                    !directory.path().join("INJECTED").exists(),
                    "placeholder executed shell code: {template}"
                );
                assert_eq!(output, expected, "{template} / {placement}");
            }
        }
    }
}

#[test]
fn shell_launches_keep_working_directory_and_pipes() {
    let directory = tempfile::tempdir().unwrap();
    let target = directory.path().join("space ' directory");
    std::fs::create_dir(&target).unwrap();
    let LaunchPlan::Inline(command) = plan(
        Some(
            "cd \"{path}\" && printf '%s\\n' \"$PWD\" | while IFS= read -r line; do printf '%s\\n' \"$line\"; done",
        ),
        "inline",
        target.to_str().unwrap(),
        None,
        Multiplexer::None,
    ) else {
        panic!("expected inline command");
    };
    assert_eq!(
        run(&command, directory.path()).trim(),
        target.to_str().unwrap()
    );
}

#[test]
fn shell_launches_preserve_existing_environment_and_positional_parameters() {
    let LaunchPlan::Inline(command) = plan(
        Some("printf '%s\\n' \"$GITPANE_TEST_VALUE\" \"$1\" \"{path}\""),
        "inline",
        "/tmp/repo",
        None,
        Multiplexer::None,
    ) else {
        panic!("expected inline command");
    };
    let output = std::process::Command::new("sh")
        .args(["-c", &command])
        .env("GITPANE_TEST_VALUE", "preserved")
        .output()
        .unwrap();
    assert!(output.status.success());
    assert_eq!(
        String::from_utf8(output.stdout).unwrap(),
        "preserved\n\n/tmp/repo\n"
    );
}

#[test]
fn shell_launches_reject_ambiguous_or_incomplete_placeholder_templates() {
    for template in [
        "printf '%s' \\{path}",
        "printf '%s' \"{path}",
        "printf '%s' '{path}",
        "printf '%s' {path} \\",
        "printf '%s' $(printf '%s' {path})",
        "printf '%s' `printf '%s' {path}`",
        "printf '%s' ${HOME:-{path}}",
        "printf '%s' $'{path}'",
        "printf '%s' $\"{path}\"",
        "cat <<EOF\n{path}\nEOF",
    ] {
        for placement in ["inline", "split-window", "ask"] {
            assert!(
                matches!(
                    plan(
                        Some(template),
                        placement,
                        "/tmp/repo",
                        None,
                        Multiplexer::Tmux
                    ),
                    LaunchPlan::Error(_)
                ),
                "accepted {template:?} with {placement}"
            );
        }
    }
    assert!(matches!(
        plan(
            Some("printf '%s' {base}"),
            "inline",
            "/tmp/repo",
            None,
            Multiplexer::None
        ),
        LaunchPlan::Error(_)
    ));
}