vibe-tests 0.0.1

Integration test framework for MCP servers with LLM-powered tool calling.
Documentation
//! Engine accessor and lifecycle management.
//! Provides global singleton engine with automatic init and cleanup.

use crate::EngineTests;
use ctor::ctor;
use dtor::dtor;
use std::sync::OnceLock;
use tokio::sync::{Mutex, MutexGuard};

/// Global engine singleton.
/// Initialized by `engine_config!` via `#[ctor]`, cleaned up by `#[dtor]`.
pub static ENGINE: OnceLock<Mutex<EngineTests>> = OnceLock::new();

/// Returns the initialized engine instance.
/// First call runs `init()` (compose up + on_start). Subsequent calls return immediately.
pub async fn engine() -> MutexGuard<'static, EngineTests> {
    let mutex = ENGINE.get().expect("engine_config! not called");
    let mut engine = mutex.lock().await;
    engine.init().await.expect("Failed to initialize engine");
    engine
}

/// Configures and stores the engine before tests run.
/// Usage: `vibe_tests::engine_config! { EngineTests::builder()...build().expect("...") }`
#[macro_export]
macro_rules! engine_config {
    ($($tt:tt)*) => {
        #[$crate::ctor::ctor(unsafe)]
        #[allow(non_snake_case)]
        fn init_engine_globally() {
            $crate::base::engines::ENGINE.get_or_init(|| {
                $crate::tokio::sync::Mutex::new({ $($tt)* })
            });
        }
    };
}

/// Handle Ctrl+C gracefully — warn user about unreleased resources.
#[ctor(unsafe)]
fn setup_signal_handler() {
    ctrlc::set_handler(move || {
        println!("\n\n⚠ Ctrl+C detected — emergency exit.");
        println!("Callback on_stop will not be called. Resources may not be released.");
        std::process::exit(0);
    })
    .ok();
}

/// Cleanup on process exit.
/// Calls engine.shutdown() → compose down, on_stop, kill children.
#[dtor(unsafe)]
fn cleanup() {
    if let Some(mutex) = ENGINE.get() {
        if let Ok(mut engine) = mutex.try_lock() {
            engine.shutdown();
        }
    }
}