rsql_repl 0.20.0

rsql library for creating a REPL command line SQL interface
Documentation
#![cfg(all(unix, feature = "driver-rusqlite"))]
#![expect(
    clippy::panic_in_result_fn,
    reason = "test assertions intentionally panic when verification fails"
)]

use expectrl::{ControlCode, Eof, Expect, Session};
use rsql_core::{Configuration, EditMode};
use rsql_repl::shell::{ShellArgs, ShellBuilder};
use std::env;
use std::process::Command;
use std::time::Duration;

const EXIT_CODE_OUTPUT: &str = "REPL_EXIT_CODE=0";

fn assert_ctrl_c_exits_repl(child_test: &str) -> anyhow::Result<()> {
    let mut command = Command::new(env::current_exe()?);
    command
        .arg("--ignored")
        .arg("--exact")
        .arg(child_test)
        .arg("--nocapture");

    let mut session = Session::spawn(command)?;
    session.set_expect_timeout(Some(Duration::from_secs(30)));

    let _ = session.expect("rsql> ")?;
    session.send(ControlCode::EndOfText)?;
    let _ = session.expect(EXIT_CODE_OUTPUT)?;
    let _ = session.expect(Eof)?;

    Ok(())
}

#[test]
fn ctrl_c_exits_repl_in_emacs_mode() -> anyhow::Result<()> {
    assert_ctrl_c_exits_repl("run_repl_child_in_emacs_mode")
}

#[test]
fn ctrl_c_exits_repl_in_vi_mode() -> anyhow::Result<()> {
    assert_ctrl_c_exits_repl("run_repl_child_in_vi_mode")
}

#[test]
#[ignore = "spawned in a pseudo-terminal by ctrl_c_exits_repl_in_emacs_mode"]
fn run_repl_child_in_emacs_mode() -> anyhow::Result<()> {
    run_repl_child(EditMode::Emacs)
}

#[test]
#[ignore = "spawned in a pseudo-terminal by ctrl_c_exits_repl_in_vi_mode"]
fn run_repl_child_in_vi_mode() -> anyhow::Result<()> {
    run_repl_child(EditMode::Vi)
}

fn run_repl_child(edit_mode: EditMode) -> anyhow::Result<()> {
    let configuration = Configuration {
        color: false,
        edit_mode,
        history: false,
        program_name: "rsql".to_string(),
        ..Default::default()
    };
    let args = ShellArgs {
        url: "rusqlite://".to_string(),
        ..Default::default()
    };
    let mut shell = ShellBuilder::default()
        .with_configuration(configuration)
        .build();

    let exit_code = tokio::runtime::Runtime::new()?.block_on(shell.execute(&args))?;
    println!("REPL_EXIT_CODE={exit_code}");
    assert_eq!(exit_code, 0);

    Ok(())
}