vibe-ready 0.2.0

Composable runtime, logging, scheduling, and storage foundations for vibe-coding Rust projects.
Documentation
use std::sync::mpsc;
use std::time::Duration;

use crate::api::engine_config::{VibeLogBackend, VibeStoreBackend};
use crate::api::platform_type::VibePlatformType;

fn strict_engine_config(name: &str) -> VibeEngineConfig {
    VibeEngineConfig::builder()
        .platform(VibePlatformType::MacOS)
        .app_name(format!("strict-{name}-{}", crate::platform::now()))
        .namespace("tests")
        .store_root_path(std::env::temp_dir().join(format!("strict-engine-{name}-{}", crate::platform::now())))
        .log_backend(VibeLogBackend::Noop)
        .store_backend(VibeStoreBackend::Noop)
        .runtime_worker_threads(1)
        .callback_threads(1)
        .queue_capacity(2, 2)
        .priority_queue_capacity(8)
        .build()
}

#[test]
fn closed_engine_rejects_work_and_destroy_zero_timeout_errors() -> Result<(), VibeEngineError> {
    let engine = VibeEngine::create(strict_engine_config("closed"))?;
    let timeout_error = engine
        .destroy_with_timeout(Duration::ZERO)
        .expect_err("zero timeout should be rejected");
    assert_eq!(timeout_error.code(), VibeEngineErrorCode::TimeoutError.code());
    engine.destroy_with_timeout(Duration::from_secs(2))?;

    assert_eq!(engine.state(), VibeEngineState::Closed);
    assert_eq!(
        engine.invoke(async { 1 }).expect_err("closed invoke").code(),
        VibeEngineErrorCode::PostError.code()
    );
    let priority_error = match engine.post_with_priority("closed", VibeTaskPriority::Normal, async {}) {
        Ok(_) => panic!("closed priority post should fail"),
        Err(error) => error,
    };
    assert_eq!(priority_error.code(), VibeEngineErrorCode::PostError.code());
    let delayed_error = match engine.schedule_after("closed", Duration::from_millis(1), |_| async {}) {
        Ok(_) => panic!("closed delayed schedule should fail"),
        Err(error) => error,
    };
    assert_eq!(delayed_error.code(), VibeEngineErrorCode::PostError.code());
    let periodic_error = match engine.schedule_every("closed", Duration::from_millis(1), |_| async {}) {
        Ok(_) => panic!("closed periodic schedule should fail"),
        Err(error) => error,
    };
    assert_eq!(periodic_error.code(), VibeEngineErrorCode::PostError.code());
    Ok(())
}

#[test]
fn destroy_callback_reports_result_on_callback_pool() -> Result<(), VibeEngineError> {
    let engine = VibeEngine::create(strict_engine_config("destroy-callback"))?;
    let (tx, rx) = mpsc::channel();
    engine.destroy(move |result| {
        tx.send(result.map(|_| ())).expect("send destroy result");
    });
    rx.recv_timeout(Duration::from_secs(2))
        .expect("destroy callback")?;
    assert_eq!(engine.state(), VibeEngineState::Closed);
    Ok(())
}

#[test]
fn log_listener_receives_inserted_log_and_can_be_cleared() -> Result<(), VibeEngineError> {
    let engine = VibeEngine::create(strict_engine_config("log-listener"))?;
    let (tx, rx) = mpsc::channel();
    engine.set_log_listener(Some(Box::new(move |info| {
        let _ = tx.send((info.tag(), info.content()));
    })));
    std::thread::sleep(Duration::from_millis(50));
    engine.insert_log(true, LogLevel::Info, "tag".into(), "content".into());
    let received = rx.recv_timeout(Duration::from_secs(2)).expect("log listener");
    assert_eq!(received, ("tag".to_string(), "content".to_string()));
    engine.set_log_listener(None);
    engine.destroy_with_timeout(Duration::from_secs(2))?;
    Ok(())
}