bun_runtime 0.1.2

Bao runtime integration — JS engine + Bun API + event loop
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
// @trace TEST-ENG-DNS-NET [req:REQ-ENG-007] [level:integration]

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);

    // =============================================
    // === DNS module ===
    // =============================================
    assert!(
        eval_bool(&mut ctx, "typeof require('dns') === 'object'"),
        "dns should be object"
    );

    // dns.lookup exists
    assert!(
        eval_bool(&mut ctx, "typeof require('dns').lookup === 'function'"),
        "dns.lookup should be function"
    );

    // dns.resolve exists
    assert!(
        eval_bool(&mut ctx, "typeof require('dns').resolve === 'function'"),
        "dns.resolve should be function"
    );

    // dns.resolve4 exists
    assert!(
        eval_bool(&mut ctx, "typeof require('dns').resolve4 === 'function'"),
        "dns.resolve4 should be function"
    );

    // dns.resolve6 exists
    assert!(
        eval_bool(&mut ctx, "typeof require('dns').resolve6 === 'function'"),
        "dns.resolve6 should be function"
    );

    // dns.reverse exists
    assert!(
        eval_bool(&mut ctx, "typeof require('dns').reverse === 'function'"),
        "dns.reverse should be function"
    );

    // dns.Resolver constructor
    assert!(
        eval_bool(&mut ctx, "typeof require('dns').Resolver === 'function'"),
        "dns.Resolver should be function"
    );

    // dns.lookupService
    assert!(
        eval_bool(
            &mut ctx,
            "typeof require('dns').lookupService === 'function'"
        ),
        "dns.lookupService should be function"
    );

    // dns.lookup returns address for localhost
    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
    );

    // dns.lookup returns family
    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
    );

    // dns.resolve4 returns array for localhost
    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
    );

    // dns.promises — Promise-based namespace
    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"
    );
    // promises.lookup returns a real Promise
    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"
    );

    // promises.resolve must run a real DNS query: the Promise executor must
    // call the outer dns.resolve, not the executor's own `resolve` callback
    // (shadowing bug: promise fulfilled with the hostname string, query never
    // executed). Compare against promises.resolve4 for shape consistency.
    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
    );

    // Resolver instance has getServers/setServers
    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"
    );

    // Resolver resolve method
    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"
    );

    // =============================================
    // === Net module ===
    // =============================================
    assert!(
        eval_bool(&mut ctx, "typeof require('net') === 'object'"),
        "net should be object"
    );

    // net.createServer
    assert!(
        eval_bool(
            &mut ctx,
            "typeof require('net').createServer === 'function'"
        ),
        "net.createServer should be function"
    );

    // net.Socket
    assert!(
        eval_bool(&mut ctx, "typeof require('net').Socket === 'function'"),
        "net.Socket should be function"
    );

    // net.Server
    assert!(
        eval_bool(&mut ctx, "typeof require('net').Server === 'function'"),
        "net.Server should be function"
    );

    // net.connect
    assert!(
        eval_bool(&mut ctx, "typeof require('net').connect === 'function'"),
        "net.connect should be function"
    );

    // net.createConnection
    assert!(
        eval_bool(
            &mut ctx,
            "typeof require('net').createConnection === 'function'"
        ),
        "net.createConnection should be function"
    );

    // net.isIP
    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
    );

    // net.isIPv4
    assert!(
        eval_bool(&mut ctx, "require('net').isIPv4('192.168.1.1')"),
        "isIPv4('192.168.1.1') should return true"
    );

    // net.isIPv4 invalid
    assert!(
        !eval_bool(&mut ctx, "require('net').isIPv4('not-an-ip')"),
        "isIPv4('not-an-ip') should return false"
    );

    // net.isIP invalid
    let invalid_ip = eval_number(&mut ctx, "require('net').isIP('hello')");
    assert_eq!(invalid_ip, 0.0, "isIP('hello') should return 0");

    // net.isIPv6
    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"
    );

    // createServer returns object
    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"
    );

    // Server has listen/close/on methods
    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"
    );

    // Server.address returns object
    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
    );

    // Socket constructor
    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"
    );

    // Socket has connect/write/end/destroy methods
    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"
    );

    // Socket.destroyed property
    assert!(
        eval_bool(
            &mut ctx,
            r#"
        var net = require('net');
        var sock = new net.Socket();
        sock.destroyed === false
    "#
        ),
        "new Socket().destroyed should be false"
    );

    // Socket has on/emit (EventEmitter)
    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();
}