#![cfg(feature = "bin")]
use afterburner::{Afterburner, AfterburnerError, Manifold, ScriptInvocation};
use std::io::Write;
use std::process::{Command, Stdio};
const BURN: &str = env!("CARGO_BIN_EXE_burn");
fn run_burn_script(source: &str, extra_flags: &[&str]) -> std::process::Output {
let mut child = Command::new(BURN)
.env("BURN_QUIET", "1")
.args(extra_flags)
.arg("-e")
.arg(source)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("spawn burn");
child.stdin.take();
child.wait_with_output().expect("wait burn")
}
#[test]
fn top_level_console_log_reaches_stdout() {
let out = run_burn_script(r#"console.log("hello from script mode")"#, &[]);
assert!(
out.status.success(),
"stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("hello from script mode"),
"stdout = {stdout:?}"
);
}
#[test]
fn top_level_await_resolves() {
let src = r#"
const v = await Promise.resolve(42);
console.log("resolved:", v);
"#;
let out = run_burn_script(src, &[]);
assert!(
out.status.success(),
"stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(stdout.contains("resolved: 42"), "stdout = {stdout:?}");
}
#[test]
fn uncaught_exception_yields_exit_1_with_partial_stdout() {
let src = r#"
console.log("before throw");
throw new Error("boom");
"#;
let out = run_burn_script(src, &[]);
assert_eq!(
out.status.code(),
Some(1),
"expected exit 1, got {:?}",
out.status.code()
);
let stdout = String::from_utf8_lossy(&out.stdout);
let stderr = String::from_utf8_lossy(&out.stderr);
assert!(stdout.contains("before throw"), "stdout = {stdout:?}");
assert!(stderr.contains("boom"), "stderr = {stderr:?}");
}
#[test]
fn library_run_script_with_sealed_default() {
let ab = Afterburner::new().expect("build ab");
let outcome = ab
.run_script(r#"console.log("lib"); console.log("ok");"#)
.expect("script ran");
assert_eq!(outcome.exit_code, 0);
let stdout = String::from_utf8_lossy(&outcome.stdout);
assert!(
stdout.contains("lib") && stdout.contains("ok"),
"stdout = {stdout:?}"
);
}
#[test]
fn library_script_syntax_error_yields_exit_1() {
let ab = Afterburner::new().expect("build ab");
let outcome = ab
.run_script("this is not valid js (*^(")
.expect("ran (with error)");
assert_eq!(outcome.exit_code, 1);
let stderr = String::from_utf8_lossy(&outcome.stderr);
assert!(
stderr.contains("Error") || stderr.contains("SyntaxError") || stderr.contains("expecting"),
"expected SyntaxError-ish stderr, got {stderr:?}"
);
let _ = AfterburnerError::CompileFailed("unused; satisfies import lint".into());
}
#[test]
fn library_script_uncaught_exception_yields_exit_1() {
let ab = Afterburner::new().expect("build ab");
let outcome = ab
.run_script("throw new Error('library boom');")
.expect("script ran (exit != 0 is Ok)");
assert_eq!(outcome.exit_code, 1);
let stderr = String::from_utf8_lossy(&outcome.stderr);
assert!(stderr.contains("library boom"), "stderr = {stderr:?}");
}
#[test]
fn library_run_script_with_invocation_threads_argv_env() {
let ab = Afterburner::builder()
.manifold(Manifold::sealed())
.build()
.expect("build");
let mut inv = ScriptInvocation {
argv: vec!["burn".into(), "[test]".into(), "x".into(), "y".into()],
..ScriptInvocation::default()
};
inv.env.insert("MY_FLAG".into(), "one".into());
inv.env.insert("OTHER".into(), "two".into());
let src = r#"
console.log("argv0:", process.argv[0]);
console.log("argv[2]:", process.argv[2]);
console.log("argv[3]:", process.argv[3]);
console.log("MY_FLAG:", process.env.MY_FLAG);
console.log("OTHER:", process.env.OTHER);
"#;
let outcome = ab
.run_script_with(src, &inv, ab.default_limits())
.expect("script ran");
assert_eq!(
outcome.exit_code,
0,
"stderr: {:?}",
String::from_utf8_lossy(&outcome.stderr)
);
let stdout = String::from_utf8_lossy(&outcome.stdout);
assert!(stdout.contains("argv0: burn"), "stdout = {stdout:?}");
assert!(stdout.contains("argv[2]: x"), "stdout = {stdout:?}");
assert!(stdout.contains("argv[3]: y"), "stdout = {stdout:?}");
assert!(stdout.contains("MY_FLAG: one"), "stdout = {stdout:?}");
assert!(stdout.contains("OTHER: two"), "stdout = {stdout:?}");
}
#[test]
fn cli_piped_stdin_not_expected_in_script_mode() {
let mut child = Command::new(BURN)
.env("BURN_QUIET", "1")
.arg("-e")
.arg(r#"console.log("script ignores stdin")"#)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("spawn burn");
if let Some(mut stdin) = child.stdin.take() {
let _ = stdin.write_all(b"{\"payload\": \"ignored\"}\n");
}
let out = child.wait_with_output().expect("wait burn");
assert!(
out.status.success(),
"stderr: {}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
assert!(
stdout.contains("script ignores stdin"),
"stdout = {stdout:?}"
);
}