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(),
}
}
fn eval_bool(ctx: &mut JsContext, source: &str) -> bool {
match ctx.eval(source, "<test>") {
Ok(JsValue::Bool(b)) => b,
_ => false,
}
}
fn eval_number(ctx: &mut JsContext, source: &str) -> f64 {
match ctx.eval(source, "<test>") {
Ok(JsValue::Number(n)) => n,
_ => f64::NAN,
}
}
#[test]
fn test_dns_net_deep() {
bun_runtime::install_exit_handler();
bun_runtime::bun_api::init_process_start();
let mut ctx = JsContext::for_test().expect("JsContext");
ctx.set_global_setup(bun_runtime::globals::install_all);
assert!(
eval_bool(&mut ctx, "typeof require('dns') === 'object'"),
"dns should be object"
);
assert!(
eval_bool(&mut ctx, "typeof require('dns').lookup === 'function'"),
"dns.lookup should be function"
);
assert!(
eval_bool(&mut ctx, "typeof require('dns').resolve === 'function'"),
"dns.resolve should be function"
);
assert!(
eval_bool(&mut ctx, "typeof require('dns').resolve4 === 'function'"),
"dns.resolve4 should be function"
);
assert!(
eval_bool(&mut ctx, "typeof require('dns').resolve6 === 'function'"),
"dns.resolve6 should be function"
);
assert!(
eval_bool(&mut ctx, "typeof require('dns').reverse === 'function'"),
"dns.reverse should be function"
);
assert!(
eval_bool(&mut ctx, "typeof require('dns').Resolver === 'function'"),
"dns.Resolver should be function"
);
assert!(
eval_bool(
&mut ctx,
"typeof require('dns').lookupService === 'function'"
),
"dns.lookupService should be function"
);
let lookup_result = eval_string(
&mut ctx,
r#"
try {
var dns = require('dns');
var result = dns.lookup('localhost');
result && result.address ? result.address : 'no_result'
} catch(e) {
'lookup_err:' + (e.message || e).substring(0, 50)
}
"#,
);
assert!(
lookup_result.contains("127.0.0.1")
|| lookup_result.contains("::1")
|| lookup_result.contains("lookup_err")
|| lookup_result.contains("no_result"),
"dns.lookup should resolve localhost, got: {}",
lookup_result
);
let family_result = eval_string(
&mut ctx,
r#"
try {
var dns = require('dns');
var result = dns.lookup('localhost');
result && result.family ? String(result.family) : 'no_family'
} catch(e) {
'family_err:' + (e.message || e).substring(0, 50)
}
"#,
);
assert!(
family_result.contains("4")
|| family_result.contains("6")
|| family_result.contains("no_family"),
"dns.lookup should return family, got: {}",
family_result
);
let resolve4_result = eval_string(
&mut ctx,
r#"
try {
var dns = require('dns');
var result = dns.resolve4('localhost');
Array.isArray(result) ? 'array_ok' : 'not_array'
} catch(e) {
'resolve4_err:' + (e.message || e).substring(0, 30)
}
"#,
);
assert!(
resolve4_result.contains("array_ok") || resolve4_result.contains("resolve4_err"),
"dns.resolve4 should return array, got: {}",
resolve4_result
);
assert!(
eval_bool(&mut ctx, "typeof require('dns').promises === 'object'"),
"dns.promises should be object"
);
assert!(
eval_bool(
&mut ctx,
r#"
var p = require('dns').promises;
typeof p.lookup === 'function' && typeof p.lookupService === 'function' &&
typeof p.resolve === 'function' && typeof p.resolve4 === 'function' &&
typeof p.resolve6 === 'function' && typeof p.reverse === 'function'
"#
),
"dns.promises should expose lookup/lookupService/resolve/resolve4/resolve6/reverse"
);
assert!(
eval_bool(
&mut ctx,
r#"
var result = require('dns').promises.lookup('localhost');
result instanceof Promise && typeof result.then === 'function'
"#
),
"dns.promises.lookup should return a Promise"
);
assert!(
eval_bool(
&mut ctx,
r#"
globalThis.__promisesResolveResult = 'pending';
globalThis.__promisesResolve4Result = 'pending';
require('dns').promises.resolve('localhost').then(function(addr) {
globalThis.__promisesResolveResult =
Array.isArray(addr) ? 'array:' + addr.join(',') : 'bad_shape:' + String(addr);
}, function(err) {
globalThis.__promisesResolveResult = 'rejected:' + String((err && err.message) || err);
});
require('dns').promises.resolve4('localhost').then(function(addr) {
globalThis.__promisesResolve4Result =
Array.isArray(addr) ? 'array:' + addr.join(',') : 'bad_shape:' + String(addr);
}, function(err) {
globalThis.__promisesResolve4Result = 'rejected:' + String((err && err.message) || err);
});
true
"#
),
"dns.promises.resolve/resolve4 should return Promises"
);
let mut drain_cx = ctx.cx();
bao_engine::job_queue::JobQueue::drain(&mut drain_cx);
let promises_resolve_result = eval_string(&mut ctx, "globalThis.__promisesResolveResult");
let promises_resolve4_result = eval_string(&mut ctx, "globalThis.__promisesResolve4Result");
assert_ne!(
promises_resolve_result, "pending",
"dns.promises.resolve then-callback should have run after microtask drain"
);
assert_ne!(
promises_resolve_result, "bad_shape:localhost",
"dns.promises.resolve fulfilled with the hostname string — executor still shadowing dns.resolve"
);
assert_eq!(
promises_resolve_result, promises_resolve4_result,
"dns.promises.resolve('localhost') should match promises.resolve4 behavior, got resolve={} resolve4={}",
promises_resolve_result, promises_resolve4_result
);
assert!(
eval_bool(
&mut ctx,
r#"
var R = require('dns').Resolver;
var r = new R();
typeof r.getServers === 'function' && typeof r.setServers === 'function'
"#
),
"Resolver should have getServers/setServers"
);
assert!(
eval_bool(
&mut ctx,
r#"
var R = require('dns').Resolver;
var r = new R();
typeof r.resolve === 'function' && typeof r.resolve4 === 'function' && typeof r.resolve6 === 'function'
"#
),
"Resolver should have resolve/resolve4/resolve6"
);
assert!(
eval_bool(&mut ctx, "typeof require('net') === 'object'"),
"net should be object"
);
assert!(
eval_bool(
&mut ctx,
"typeof require('net').createServer === 'function'"
),
"net.createServer should be function"
);
assert!(
eval_bool(&mut ctx, "typeof require('net').Socket === 'function'"),
"net.Socket should be function"
);
assert!(
eval_bool(&mut ctx, "typeof require('net').Server === 'function'"),
"net.Server should be function"
);
assert!(
eval_bool(&mut ctx, "typeof require('net').connect === 'function'"),
"net.connect should be function"
);
assert!(
eval_bool(
&mut ctx,
"typeof require('net').createConnection === 'function'"
),
"net.createConnection should be function"
);
let is_ip = eval_number(&mut ctx, "require('net').isIP('127.0.0.1')");
assert_eq!(
is_ip, 4.0,
"isIP('127.0.0.1') should return 4, got: {}",
is_ip
);
assert!(
eval_bool(&mut ctx, "require('net').isIPv4('192.168.1.1')"),
"isIPv4('192.168.1.1') should return true"
);
assert!(
!eval_bool(&mut ctx, "require('net').isIPv4('not-an-ip')"),
"isIPv4('not-an-ip') should return false"
);
let invalid_ip = eval_number(&mut ctx, "require('net').isIP('hello')");
assert_eq!(invalid_ip, 0.0, "isIP('hello') should return 0");
assert!(
eval_bool(&mut ctx, "require('net').isIPv6('::1')"),
"isIPv6('::1') should return true"
);
assert!(
!eval_bool(&mut ctx, "require('net').isIPv6('127.0.0.1')"),
"isIPv6('127.0.0.1') should return false"
);
assert!(
eval_bool(
&mut ctx,
r#"
var net = require('net');
var server = net.createServer(function(socket) {});
typeof server === 'object' && server !== null
"#
),
"net.createServer should return server object"
);
assert!(
eval_bool(
&mut ctx,
r#"
var net = require('net');
var server = net.createServer(function() {});
typeof server.listen === 'function' && typeof server.close === 'function' && typeof server.on === 'function'
"#
),
"Server should have listen/close/on"
);
let addr = eval_string(
&mut ctx,
r#"
var net = require('net');
var server = net.createServer(function() {});
var a = server.address();
typeof a === 'object' ? 'addr_ok' : 'addr_fail'
"#,
);
assert!(
addr.contains("addr_ok"),
"server.address() should return object, got: {}",
addr
);
assert!(
eval_bool(
&mut ctx,
r#"
var net = require('net');
var sock = new net.Socket();
typeof sock === 'object' && sock !== null
"#
),
"new net.Socket() should return object"
);
assert!(
eval_bool(
&mut ctx,
r#"
var net = require('net');
var sock = new net.Socket();
typeof sock.connect === 'function' && typeof sock.write === 'function' && typeof sock.end === 'function' && typeof sock.destroy === 'function'
"#
),
"Socket should have connect/write/end/destroy"
);
assert!(
eval_bool(
&mut ctx,
r#"
var net = require('net');
var sock = new net.Socket();
sock.destroyed === false
"#
),
"new Socket().destroyed should be false"
);
assert!(
eval_bool(
&mut ctx,
r#"
var net = require('net');
var sock = new net.Socket();
typeof sock.on === 'function' && typeof sock.emit === 'function'
"#
),
"Socket should have on/emit (EventEmitter)"
);
bun_runtime::shutdown_thread_sm();
}