use std::ffi::OsString;
use std::fs;
use std::path::PathBuf;
use std::process::Command;
use std::time::{SystemTime, UNIX_EPOCH};
fn unique_temp_dir(label: &str) -> PathBuf {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map_or(0_u128, |value| value.as_nanos());
std::env::temp_dir().join(format!("index-cli-main-entry-{label}-{nanos}"))
}
fn index_command(label: &str) -> Command {
let base = unique_temp_dir(label);
let mut command = Command::new(env!("CARGO_BIN_EXE_index"));
command
.env("XDG_CONFIG_HOME", base.join("config").as_os_str())
.env("XDG_CACHE_HOME", base.join("cache").as_os_str())
.env("XDG_STATE_HOME", base.join("state").as_os_str());
command
}
fn output_text(bytes: &[u8]) -> String {
String::from_utf8_lossy(bytes).to_string()
}
#[test]
fn entrypoint_emits_help_text() -> Result<(), Box<dyn std::error::Error>> {
let output = index_command("help").arg("--help").output()?;
assert!(output.status.success());
let stdout = output_text(&output.stdout);
assert!(stdout.contains("Index terminal browser prototype"));
assert!(stdout.contains("index --plain <url-or-local-html-file>"));
assert!(output.stderr.is_empty());
Ok(())
}
#[test]
fn entrypoint_emits_version_text() -> Result<(), Box<dyn std::error::Error>> {
let output = index_command("version").arg("--version").output()?;
assert!(output.status.success());
let stdout = output_text(&output.stdout);
assert!(stdout.starts_with("index "));
assert!(output.stderr.is_empty());
Ok(())
}
#[test]
fn entrypoint_renders_plain_output_from_local_file() -> Result<(), Box<dyn std::error::Error>> {
let html_path = unique_temp_dir("plain").with_extension("html");
fs::write(
&html_path,
"<html><body><h1>Main Entry Test</h1><p>plain output</p></body></html>",
)?;
let output = index_command("plain")
.args([
OsString::from("--plain"),
html_path.clone().into_os_string(),
])
.output()?;
assert!(output.status.success());
let stdout = output_text(&output.stdout);
assert!(stdout.contains("Main Entry Test"));
assert!(stdout.contains("plain output"));
assert!(output.stderr.is_empty());
let _ = fs::remove_file(html_path);
Ok(())
}
#[test]
fn entrypoint_reports_argument_errors() -> Result<(), Box<dyn std::error::Error>> {
let output = index_command("error")
.args(["--extract", "xml", "-"])
.output()?;
assert!(!output.status.success());
assert!(output.stdout.is_empty());
let stderr = output_text(&output.stderr);
assert!(stderr.contains("unsupported extraction format: xml"));
Ok(())
}
#[test]
fn entrypoint_reports_tui_runtime_errors_without_tty() -> Result<(), Box<dyn std::error::Error>> {
let output = index_command("tui-no-tty")
.args(["https://example.org"])
.output()?;
assert!(!output.status.success());
let stderr = output_text(&output.stderr);
assert!(stderr.contains("failed to run terminal UI"));
Ok(())
}