use bao_engine::context::{JsContext, thread_realm_global};
use bao_engine::module_loader::ModuleLoader;
use mozjs::rooted;
fn make_ctx() -> JsContext {
let mut ctx = JsContext::for_test().expect("JsContext::for_test");
ctx.set_global_setup(bun_runtime::globals::install_all);
ctx
}
#[test]
fn module_top_level_throw_surfaces_error() {
let mut ctx = make_ctx();
ctx.eval("void 0;", "<realm-init>").expect("realm init");
let global_ptr = thread_realm_global().expect("realm global");
let mut cx = ctx.cx();
rooted!(&in(cx) let global = global_ptr);
let result = ModuleLoader::eval_module_in_realm(
&mut cx,
"throw new Error('boom-top-level');",
"<throw>.mjs",
None,
global.handle(),
);
let err = result.expect_err("top-level throw must surface as Err, not Ok");
assert!(
err.message.contains("boom-top-level"),
"error message must contain the thrown content, got: {}",
err.message
);
}
#[test]
fn module_top_level_await_rejection_surfaces_error() {
let mut ctx = make_ctx();
ctx.eval("void 0;", "<realm-init>").expect("realm init");
let global_ptr = thread_realm_global().expect("realm global");
let mut cx = ctx.cx();
rooted!(&in(cx) let global = global_ptr);
let result = ModuleLoader::eval_module_in_realm(
&mut cx,
"await Promise.reject(new Error('tla-reject'));",
"<tla-reject>.mjs",
None,
global.handle(),
);
let err = result.expect_err("rejected top-level await must surface as Err");
assert!(
err.message.contains("tla-reject"),
"error message must contain the rejection reason, got: {}",
err.message
);
}
#[test]
fn module_success_path_unchanged() {
let mut ctx = make_ctx();
ctx.eval("void 0;", "<realm-init>").expect("realm init");
let global_ptr = thread_realm_global().expect("realm global");
let mut cx = ctx.cx();
rooted!(&in(cx) let global = global_ptr);
ModuleLoader::eval_module_in_realm(
&mut cx,
"globalThis.fromOkModule = 'present';",
"<ok>.mjs",
None,
global.handle(),
)
.expect("successful module must still evaluate to Ok");
let r = ctx
.eval("globalThis.fromOkModule;", "<verify>")
.expect("verify eval");
assert_eq!(
r.as_string(),
Some("present"),
"successful module side effect must be observable"
);
}
#[test]
fn module_pending_top_level_await_is_not_an_error() {
let mut ctx = make_ctx();
ctx.eval("void 0;", "<realm-init>").expect("realm init");
let global_ptr = thread_realm_global().expect("realm global");
let mut cx = ctx.cx();
rooted!(&in(cx) let global = global_ptr);
ModuleLoader::eval_module_in_realm(
&mut cx,
"await new Promise(() => {});",
"<tla-pending>.mjs",
None,
global.handle(),
)
.expect("pending (never-settling) top-level await must not be an error");
}
#[test]
fn module_then_skips_after_eval_on_throw() {
let mut ctx = make_ctx();
ctx.eval("void 0;", "<realm-init>").expect("realm init");
let global_ptr = thread_realm_global().expect("realm global");
let mut cx = ctx.cx();
rooted!(&in(cx) let global = global_ptr);
let mut after_eval_ran = false;
let result = ModuleLoader::eval_module_in_realm_then(
&mut cx,
"throw new Error('boom-then');",
"<throw-then>.mjs",
None,
global.handle(),
|_realm_cx| {
after_eval_ran = true;
},
);
let err = result.expect_err("throwing module in _then variant must Err");
assert!(
err.message.contains("boom-then"),
"error message must contain the thrown content, got: {}",
err.message
);
assert!(
!after_eval_ran,
"after_eval must not run after a failed module evaluation"
);
}
#[test]
fn realm_still_usable_after_module_error() {
let mut ctx = make_ctx();
ctx.eval("void 0;", "<realm-init>").expect("realm init");
let global_ptr = thread_realm_global().expect("realm global");
let mut cx = ctx.cx();
rooted!(&in(cx) let global = global_ptr);
let _ = ModuleLoader::eval_module_in_realm(
&mut cx,
"throw new Error('boom-poison-check');",
"<poison>.mjs",
None,
global.handle(),
)
.expect_err("first eval must fail");
ModuleLoader::eval_module_in_realm(
&mut cx,
"globalThis.recovered = true;",
"<recovered>.mjs",
None,
global.handle(),
)
.expect("realm must remain usable after a surfaced module error");
let r = ctx
.eval("globalThis.recovered;", "<verify>")
.expect("verify eval");
assert_eq!(r.as_bool(), Some(true));
}