use super::*;
#[test]
fn test_ipc_eval_stdin_fallback() {
let sessions_dir = tempfile::tempdir().expect("Failed to create temp sessions dir");
let output = sanitized_arf_command()
.args(["ipc", "eval"])
.env("ARF_IPC_SESSIONS_DIR", sessions_dir.path())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.expect("Failed to spawn arf");
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
!stderr.contains("NO_CODE_PROVIDED"),
"Should not complain about missing code when stdin is provided: {}",
stderr
);
assert!(
stdout.contains("SESSION_NOT_FOUND") || stderr.contains("SESSION_NOT_FOUND"),
"Should fail at IPC level with SESSION_NOT_FOUND: stdout={} stderr={}",
stdout,
stderr
);
}
#[test]
fn test_ipc_send_stdin_fallback() {
let sessions_dir = tempfile::tempdir().expect("Failed to create temp sessions dir");
let output = sanitized_arf_command()
.args(["ipc", "send"])
.env("ARF_IPC_SESSIONS_DIR", sessions_dir.path())
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.output()
.expect("Failed to spawn arf");
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
!stderr.contains("NO_CODE_PROVIDED"),
"Should not complain about missing code when stdin is provided: {}",
stderr
);
assert!(
stdout.contains("SESSION_NOT_FOUND") || stderr.contains("SESSION_NOT_FOUND"),
"Should fail at IPC level with SESSION_NOT_FOUND: stdout={} stderr={}",
stdout,
stderr
);
}
#[test]
fn test_ipc_list_ignores_empty_arf_config() {
let sessions_dir = tempfile::tempdir().expect("Failed to create temp sessions dir");
let output = sanitized_arf_command()
.args(["ipc", "list"])
.env("ARF_CONFIG", "")
.env("ARF_IPC_SESSIONS_DIR", sessions_dir.path())
.output()
.expect("Failed to run arf ipc list");
assert!(
output.status.success(),
"ipc list should ignore empty ARF_CONFIG: {}",
String::from_utf8_lossy(&output.stderr)
);
let json: serde_json::Value = serde_json::from_slice(&output.stdout)
.unwrap_or_else(|error| panic!("stdout should be JSON: {error}\n{:?}", output.stdout));
assert!(json["sessions"].is_array());
}
#[test]
fn test_eval_sources_user_rprofile() {
let mut rprofile = NamedTempFile::new().expect("Failed to create temp .Rprofile");
writeln!(rprofile, "cat('ARF_RPROFILE_MARKER\\n')").expect("Failed to write .Rprofile");
let rprofile_path = rprofile.path().to_path_buf();
let output = sanitized_arf_command()
.env("R_PROFILE_USER", &rprofile_path)
.args(["-e", "1"])
.output()
.expect("Failed to run arf -e");
assert!(output.status.success(), "arf -e should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stdout.contains("ARF_RPROFILE_MARKER") || stderr.contains("ARF_RPROFILE_MARKER"),
".Rprofile should be sourced by arf -e. stdout={stdout}, stderr={stderr}"
);
}
#[cfg(unix)]
#[test]
fn test_eval_empty_r_profile_user_sources_default_rprofile() {
let working_dir = tempfile::tempdir().expect("Failed to create working directory");
let rprofile_path = working_dir.path().join(".Rprofile");
std::fs::write(&rprofile_path, "cat('ARF_RPROFILE_MARKER\\n')\n")
.expect("Failed to write .Rprofile");
let output = sanitized_arf_command()
.current_dir(working_dir.path())
.env("R_PROFILE_USER", "")
.args(["-e", "1"])
.output()
.expect("Failed to run arf -e");
assert!(output.status.success(), "arf -e should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stdout.contains("ARF_RPROFILE_MARKER") || stderr.contains("ARF_RPROFILE_MARKER"),
"default .Rprofile should be sourced when R_PROFILE_USER is empty. \
stdout={stdout}, stderr={stderr}"
);
}
#[test]
fn test_eval_vanilla_skips_user_rprofile() {
let mut rprofile = NamedTempFile::new().expect("Failed to create temp .Rprofile");
writeln!(rprofile, "cat('ARF_RPROFILE_MARKER\\n')").expect("Failed to write .Rprofile");
let rprofile_path = rprofile.path().to_path_buf();
let output = sanitized_arf_command()
.env("R_PROFILE_USER", &rprofile_path)
.args(["--vanilla", "-e", "1"])
.output()
.expect("Failed to run arf --vanilla -e");
assert!(output.status.success(), "arf --vanilla -e should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
!stdout.contains("ARF_RPROFILE_MARKER") && !stderr.contains("ARF_RPROFILE_MARKER"),
".Rprofile must not be sourced under --vanilla. stdout={stdout}, stderr={stderr}"
);
}
#[test]
fn test_positional_script_rejected() {
let output = sanitized_arf_command()
.arg("some_script.R")
.output()
.expect("Failed to run arf");
assert!(
!output.status.success(),
"positional script arg should be rejected"
);
assert_eq!(
output.status.code(),
Some(2),
"positional script rejection should use clap's parse error exit code"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("some_script.R"),
"Error should mention the offending token: {}",
stderr
);
}
#[test]
fn test_eval_with_subcommand_rejected() {
let output = sanitized_arf_command()
.args(["-e", "1+1", "completions", "bash"])
.output()
.expect("Failed to run arf");
assert!(
!output.status.success(),
"--eval with subcommand should be rejected"
);
assert_eq!(
output.status.code(),
Some(2),
"--eval with subcommand should exit with code 2"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("--eval")
&& stderr.contains("cannot be used")
&& stderr.contains("completions"),
"Should show conflict error mentioning --eval and the subcommand: {}",
stderr
);
}
#[test]
fn test_file_with_subcommand_rejected() {
let output = sanitized_arf_command()
.args(["-f", "some.R", "completions", "bash"])
.output()
.expect("Failed to run arf");
assert!(
!output.status.success(),
"--file with subcommand should be rejected"
);
assert_eq!(
output.status.code(),
Some(2),
"--file with subcommand should exit with code 2"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("--file")
&& stderr.contains("cannot be used")
&& stderr.contains("completions"),
"Should show conflict error mentioning --file and the subcommand: {}",
stderr
);
}
#[test]
fn test_r_completion_functions() {
let output = sanitized_arf_command()
.args([
"-e",
r#"
utils:::.assignLinebuffer("pri")
utils:::.assignEnd(3)
token <- utils:::.guessTokenFromLine()
print(token)
"#,
])
.output()
.expect("Failed to run arf -e");
assert!(output.status.success(), "arf -e should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("pri"), "Token should be 'pri': {}", stdout);
}
#[test]
fn test_r_complete_token() {
let output = sanitized_arf_command()
.args([
"-e",
r#"
utils:::.assignLinebuffer("prin")
utils:::.assignEnd(4L)
utils:::.guessTokenFromLine()
utils:::.completeToken()
comps <- utils:::.retrieveCompletions()
print(comps)
"#,
])
.output()
.expect("Failed to run arf -e");
assert!(output.status.success(), "arf -e should succeed");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("print"),
"Completions should include 'print': {}",
stdout
);
}