mod common;
use assert_cmd::Command;
#[test]
fn no_controlling_terminal_exits_with_clear_error() {
let mut cmd = Command::cargo_bin("rusty-vipe").expect("binary built");
cmd.env_remove("RUSTY_VIPE_TEST_BYPASS_TTY");
cmd.env("RUSTY_VIPE_TEST_FAIL_TTY", "1");
cmd.env_remove("VISUAL");
cmd.env("EDITOR", "vi");
cmd.env_remove("RUSTY_VIPE_STRICT");
let output = cmd.write_stdin("").assert().failure().get_output().clone();
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("no controlling terminal"),
"FR-015: stderr must mention 'no controlling terminal'; got: {stderr:?}"
);
assert_ne!(output.status.code(), Some(0), "must exit non-zero");
}
#[test]
fn no_controlling_terminal_emits_exact_fr015_text() {
let mut cmd = Command::cargo_bin("rusty-vipe").expect("binary built");
cmd.env_remove("RUSTY_VIPE_TEST_BYPASS_TTY");
cmd.env("RUSTY_VIPE_TEST_FAIL_TTY", "1");
cmd.env_remove("VISUAL");
cmd.env("EDITOR", "vi");
cmd.env_remove("RUSTY_VIPE_STRICT");
let output = cmd.write_stdin("").assert().failure().get_output().clone();
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("rusty-vipe: no controlling terminal; cannot launch editor"),
"FR-015 stderr text mismatch; got: {stderr:?}"
);
}
#[test]
fn no_controlling_terminal_strict_mode_also_reports_clearly() {
let mut cmd = Command::cargo_bin("rusty-vipe").expect("binary built");
cmd.env_remove("RUSTY_VIPE_TEST_BYPASS_TTY");
cmd.env("RUSTY_VIPE_TEST_FAIL_TTY", "1");
cmd.env_remove("VISUAL");
cmd.env("EDITOR", "vi");
cmd.env_remove("RUSTY_VIPE_STRICT");
cmd.arg("--strict");
let output = cmd.write_stdin("").assert().failure().get_output().clone();
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("no controlling terminal"),
"Strict-mode no-TTY error must surface; got: {stderr:?}"
);
}
#[test]
fn no_controlling_terminal_does_not_leak_pipe_stdin_to_editor() {
let mut cmd = Command::cargo_bin("rusty-vipe").expect("binary built");
cmd.env_remove("RUSTY_VIPE_TEST_BYPASS_TTY");
cmd.env("RUSTY_VIPE_TEST_FAIL_TTY", "1");
cmd.env_remove("VISUAL");
cmd.env("EDITOR", "vi");
cmd.env_remove("RUSTY_VIPE_STRICT");
let output = cmd
.write_stdin("DISTINCTIVE_MARKER_STDIN_PAYLOAD\n")
.assert()
.failure()
.get_output()
.clone();
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
!stdout.contains("DISTINCTIVE_MARKER_STDIN_PAYLOAD"),
"FR-015: producer's pipe stdin must NEVER reach downstream output; got stdout: {stdout:?}"
);
}
#[test]
fn piped_stdio_with_tty_still_works() {
let fake = common::fake_editor_path();
let fake_str = fake.to_string_lossy().replace('\\', "/");
let editor_value = format!("{fake_str} '--transform=passthrough'");
let mut cmd = Command::cargo_bin("rusty-vipe").expect("binary built");
cmd.env("RUSTY_VIPE_TEST_BYPASS_TTY", "1");
cmd.env_remove("RUSTY_VIPE_TEST_FAIL_TTY");
cmd.env("EDITOR", &editor_value);
cmd.env_remove("VISUAL");
let output = cmd
.write_stdin("piped-stdin-payload\n")
.assert()
.success()
.get_output()
.clone();
assert_eq!(output.stdout, b"piped-stdin-payload\n");
}