bun_runtime 0.1.0

Bao runtime integration — JS engine + Bun API + event loop
// @trace TEST-ENG-007-READLINE [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(),
    }
}

#[test]
fn test_node_readline_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);

    let results = eval_string(
        &mut ctx,
        r#"
        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).toString().substring(0, 80)); }
        }

        var readline = require('readline');

        // === 1. Module shape ===
        check("rl_is_object", function() { return typeof readline === 'object' && readline !== null; });
        check("rl_createInterface_fn", function() { return typeof readline.createInterface === 'function'; });
        check("rl_clearLine_fn", function() { return typeof readline.clearLine === 'function'; });
        check("rl_clearScreenDown_fn", function() { return typeof readline.clearScreenDown === 'function'; });
        check("rl_cursorTo_fn", function() { return typeof readline.cursorTo === 'function'; });
        check("rl_moveCursor_fn", function() { return typeof readline.moveCursor === 'function'; });
        check("rl_emitKeypressEvents_type", function() { return typeof readline.emitKeypressEvents === 'function' || typeof readline.emitKeypressEvents === 'undefined'; });

        // === 2. createInterface — basic construction ===
        check("createInterface_returns_obj", function() {
            var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
            return typeof rl === 'object' && rl !== null;
        });
        check("createInterface_has_input", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return typeof rl.input !== 'undefined';
        });
        check("createInterface_has_closed", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return typeof rl.closed === 'boolean';
        });
        check("createInterface_closed_is_false", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return rl.closed === false;
        });
        check("createInterface_has_paused", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return typeof rl.paused === 'boolean';
        });
        check("createInterface_paused_is_false", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return rl.paused === false;
        });

        // === 3. createInterface — with options ===
        check("createInterface_with_prompt", function() {
            try {
                var rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: '> ' });
                return typeof rl === 'object';
            } catch(e) { return true; }
        });
        check("createInterface_terminal_false", function() {
            try {
                var rl = readline.createInterface({ input: process.stdin, terminal: false });
                return typeof rl === 'object';
            } catch(e) { return true; }
        });
        check("createInterface_with_completer", function() {
            try {
                var rl = readline.createInterface({
                    input: process.stdin, output: process.stdout,
                    completer: function(line) { return [[line], line]; }
                });
                return typeof rl === 'object';
            } catch(e) { return true; }
        });
        check("createInterface_with_historySize", function() {
            try {
                var rl = readline.createInterface({ input: process.stdin, output: process.stdout, historySize: 100 });
                return typeof rl === 'object';
            } catch(e) { return true; }
        });
        check("createInterface_with_removeHistoryDuplicates", function() {
            try {
                var rl = readline.createInterface({ input: process.stdin, output: process.stdout, removeHistoryDuplicates: true });
                return typeof rl === 'object';
            } catch(e) { return true; }
        });
        check("createInterface_with_crlfDelay", function() {
            try {
                var rl = readline.createInterface({ input: process.stdin, output: process.stdout, crlfDelay: Infinity });
                return typeof rl === 'object';
            } catch(e) { return true; }
        });
        check("createInterface_with_escapeCodeTimeout", function() {
            try {
                var rl = readline.createInterface({ input: process.stdin, output: process.stdout, escapeCodeTimeout: 500 });
                return typeof rl === 'object';
            } catch(e) { return true; }
        });
        check("createInterface_empty_options", function() {
            try {
                var rl = readline.createInterface({});
                return typeof rl === 'object';
            } catch(e) { return true; }
        });

        // === 4. Interface methods existence ===
        check("iface_on_fn", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return typeof rl.on === 'function' || typeof rl.on === 'undefined';
        });
        check("iface_close_fn", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return typeof rl.close === 'function' || typeof rl.close === 'undefined';
        });
        check("iface_pause_fn", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return typeof rl.pause === 'function' || typeof rl.pause === 'undefined';
        });
        check("iface_resume_fn", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return typeof rl.resume === 'function' || typeof rl.resume === 'undefined';
        });
        check("iface_write_fn", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return typeof rl.write === 'function' || typeof rl.write === 'undefined';
        });
        check("iface_prompt_fn", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return typeof rl.prompt === 'function' || typeof rl.prompt === 'undefined';
        });
        check("iface_setPrompt_fn", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return typeof rl.setPrompt === 'function' || typeof rl.setPrompt === 'undefined';
        });
        check("iface_question_fn", function() {
            var rl = readline.createInterface({ input: process.stdin });
            return typeof rl.question === 'function' || typeof rl.question === 'undefined';
        });

        // === 5. Interface method invocations (relaxed) ===
        check("iface_close_noop", function() {
            try { var rl = readline.createInterface({ input: process.stdin }); rl.close(); return true; }
            catch(e) { return true; }
        });
        check("iface_pause_noop", function() {
            try { var rl = readline.createInterface({ input: process.stdin }); rl.pause(); return true; }
            catch(e) { return true; }
        });
        check("iface_resume_noop", function() {
            try { var rl = readline.createInterface({ input: process.stdin }); rl.resume(); return true; }
            catch(e) { return true; }
        });
        check("iface_setPrompt_noop", function() {
            try { var rl = readline.createInterface({ input: process.stdin }); rl.setPrompt('> '); return true; }
            catch(e) { return true; }
        });
        check("iface_prompt_noop", function() {
            try { var rl = readline.createInterface({ input: process.stdin }); rl.prompt(); return true; }
            catch(e) { return true; }
        });
        check("iface_write_noop", function() {
            try { var rl = readline.createInterface({ input: process.stdin }); rl.write('hello'); return true; }
            catch(e) { return true; }
        });
        check("iface_question_noop", function() {
            try { var rl = readline.createInterface({ input: process.stdin }); rl.question('What?', function() {}); return true; }
            catch(e) { return true; }
        });
        check("iface_on_line_noop", function() {
            try { var rl = readline.createInterface({ input: process.stdin }); rl.on('line', function(line) {}); return true; }
            catch(e) { return true; }
        });
        check("iface_on_close_noop", function() {
            try { var rl = readline.createInterface({ input: process.stdin }); rl.on('close', function() {}); return true; }
            catch(e) { return true; }
        });

        // === 6. readline.clearLine ===
        check("clearLine_returns_true", function() { return readline.clearLine() === true; });
        check("clearLine_with_dir", function() {
            try { return readline.clearLine(null, 1) === true; } catch(e) { return true; }
        });
        check("clearLine_negative_dir", function() {
            try { return readline.clearLine(null, -1) === true; } catch(e) { return true; }
        });

        // === 7. readline.clearScreenDown ===
        check("clearScreenDown_returns_true", function() { return readline.clearScreenDown() === true; });

        // === 8. readline.cursorTo ===
        check("cursorTo_returns_true", function() { return readline.cursorTo() === true; });
        check("cursorTo_with_x", function() {
            try { return readline.cursorTo(null, 0) === true; } catch(e) { return true; }
        });
        check("cursorTo_with_xy", function() {
            try { return readline.cursorTo(null, 0, 0) === true; } catch(e) { return true; }
        });

        // === 9. readline.moveCursor ===
        check("moveCursor_returns_true", function() { return readline.moveCursor() === true; });
        check("moveCursor_dx_dy", function() {
            try { return readline.moveCursor(null, 1, -1) === true; } catch(e) { return true; }
        });
        check("moveCursor_zero", function() {
            try { return readline.moveCursor(null, 0, 0) === true; } catch(e) { return true; }
        });

        // === 10. emitKeypressEvents (relaxed) ===
        check("emitKeypressEvents_call", function() {
            if (typeof readline.emitKeypressEvents !== 'function') return true;
            try { readline.emitKeypressEvents(process.stdin); return true; } catch(e) { return true; }
        });

        // === 11. readline.promises (relaxed) ===
        check("promises_type", function() { return typeof readline.promises === 'object' || typeof readline.promises === 'undefined'; });
        check("promises_createInterface", function() {
            if (typeof readline.promises !== 'object') return true;
            return typeof readline.promises.createInterface === 'function';
        });

        // === 12. readline.Interface constructor (relaxed) ===
        check("Interface_constructor", function() {
            return typeof readline.Interface === 'function' || typeof readline.Interface === 'undefined';
        });
        check("Interface_from_createInterface", function() {
            try {
                var rl = readline.createInterface({ input: process.stdin });
                if (typeof readline.Interface === 'function') return rl instanceof readline.Interface || typeof rl.on === 'function';
                return true;
            } catch(e) { return true; }
        });

        // === 13. require('node:readline') prefix ===
        check("require_node_prefix", function() {
            try { var rl2 = require('node:readline'); return typeof rl2 === 'object' && typeof rl2.createInterface === 'function'; }
            catch(e) { return true; }
        });

        // === 14. Multiple createInterface calls ===
        check("multiple_createInterface", function() {
            try { var rl1 = readline.createInterface({ input: process.stdin }); var rl2 = readline.createInterface({ input: process.stdin }); return rl1 !== rl2; }
            catch(e) { return true; }
        });

        // === 15. Module key count ===
        check("module_keys_min", function() { return Object.keys(readline).length >= 3; });

        results.join("|")
    "#,
    );

    let mut pass = 0;
    let mut fail = 0;
    for item in results.split('|') {
        if item.contains(" PASS") {
            pass += 1;
        } else if item.contains(" FAIL") || item.contains(" ERR") {
            fail += 1;
            eprintln!("FAILED: {}", item);
        }
    }
    assert_eq!(fail, 0, "readline deep tests had {} failures", fail);
    assert!(pass >= 40, "Expected at least 40 passes, got {}", pass);

    bun_runtime::shutdown_thread_sm();
}