use bao_engine::context::JsContext;
use bao_engine::value::JsValue;
fn eval_string(ctx: &mut JsContext, source: &str) -> String {
match ctx.eval(source, "<test>") {
Ok(JsValue::String(s)) => s,
Ok(JsValue::Number(n)) => format!("{}", n),
Ok(JsValue::Bool(b)) => if b { "true" } else { "false" }.to_string(),
_ => String::new(),
}
}
#[test]
fn test_url_format_query_object() {
bun_runtime::install_exit_handler();
bun_runtime::bun_api::init_process_start();
let mut ctx = JsContext::for_test().expect("Failed to create JSContext");
ctx.set_global_setup(bun_runtime::globals::install_all);
let results = eval_string(
&mut ctx,
r##"
var url = require('url');
// Pinned scheme/host so the assertions isolate the QUERY behavior.
var B = {protocol:'http:', host:'h.co'};
var results = [];
function check(label, fn) {
try { var ok = fn(); results.push(label + (ok ? " PASS" : " FAIL")); }
catch(e) { results.push(label + " ERR:" + (e.message || e)); }
}
function fmt(extra) {
var init = Object.assign({}, B, extra);
return url.format(init);
}
// Object/array/nested-shallow value shapes (Node: qs.stringify via
// url.format).
check("qs_shapes", function() {
return fmt({pathname:'/p', query:{a:'x y', b:[1,2], c:{d:1}, e:null, f:true, g:'a&b=c'}})
=== 'http://h.co/p?a=x%20y&b=1&b=2&c=&e=&f=true&g=a%26b%3Dc';
});
// search wins over query (Node precedence).
check("search_wins", function() {
return fmt({pathname:'/p', search:'?a=1', query:{b:2}}) === 'http://h.co/p?a=1';
});
// A string query is ignored (Node only serializes object queries).
check("string_query_ignored", function() {
return fmt({pathname:'/p', query:'a=1&b=2'}) === 'http://h.co/p';
});
// A bare search gains its leading '?'.
check("bare_search_prefix", function() {
return fmt({pathname:'/p', search:'a=1'}) === 'http://h.co/p?a=1';
});
// Empty-string search is falsy in Node — the query takes over.
check("empty_search_falls_to_query", function() {
return fmt({pathname:'/p', search:'', query:{b:2}}) === 'http://h.co/p?b=2';
});
// Empty query object → no '?' at all.
check("empty_query_object", function() {
return fmt({pathname:'/p', query:{}}) === 'http://h.co/p';
});
// undefined value emits the bare key.
check("undefined_value", function() {
return fmt({pathname:'/p', query:{a:undefined}}) === 'http://h.co/p?a=';
});
// Function values emit the bare key (Node type dispatch).
check("fn_value_bare", function() {
return fmt({pathname:'/p', query:{a:function(){}}}) === 'http://h.co/p?a=';
});
// bigint serializes via String(value).
check("bigint_value", function() {
return fmt({pathname:'/p', query:{a:1n}}) === 'http://h.co/p?a=1';
});
// Array as the query itself: index keys.
check("array_query", function() {
return fmt({pathname:'/p', query:[1,2]}) === 'http://h.co/p?0=1&1=2';
});
// %20 (not '+') with the encodeURIComponent unescaped set
// (-_.!~*'()).
check("encoding_set", function() {
return fmt({pathname:'/p', query:{'k k':'v v'}}) === 'http://h.co/p?k%20k=v%20v'
&& fmt({pathname:'/p', query:{"k~!()*'": 'v$'}}) === "http://h.co/p?k~!()*'=v%24";
});
// Full shape: protocol (colon-suffixed — colon normalization for a
// bare "https" is a reported pre-existing deviation, not asserted)
// + hostname + string port (numeric-port coercion likewise
// reported) + pathname + query + hash.
check("full_shape", function() {
return url.format({protocol:'https:', hostname:'h.co', port:'8080',
pathname:'/x', query:{k:1}, hash:'#f'})
=== 'https://h.co:8080/x?k=1#f';
});
// Regressions: href short-circuit, string passthrough, parse
// round-trip.
check("href_shortcircuit", function() {
return url.format({href:'http://a.b/c'}) === 'http://a.b/c';
});
check("string_passthrough", function() {
return url.format('http://a.b/c') === 'http://a.b/c';
});
check("parse_roundtrip", function() {
return url.format(url.parse('https://e.com/p?k=1')) === 'https://e.com/p?k=1';
});
results.join("\n")
"##,
);
let fails: Vec<&str> = results.lines().filter(|l| !l.ends_with(" PASS")).collect();
assert!(
fails.is_empty(),
"url.format query expectations failed:\n{}",
results
);
eprintln!(
"[PASS] TEST-ENG-007-URL-FORMAT-QUERY: {} checks vs Node 24 ground truth",
results.lines().count()
);
bun_runtime::shutdown_thread_sm();
}