rave_engine 0.8.0

A secure and efficient JSON Schema validation and Rhai script execution engine
Documentation
use crate::rhai_engine::{RhaiEngine, RhaiEngineConfig};
use serde_json::json;

#[test]
fn test_max_operations() {
    // Create config with a specific operation limit
    let config = RhaiEngineConfig::default()
        .with_max_operations(1000)
        .with_use_holochain_time(false); // Set a lower limit for testing
    let engine = RhaiEngine::with_config(config).unwrap();
    let input = json!({});

    // Script that will exceed the operation limit
    let script = r#"
        let x = 0;
        for i in 0..2000 { x += i; } // This will exceed 1000 operations
    "#;
    let script_bytes = rmp_serde::to_vec(&script).unwrap();
    let result = engine.execute(&input, script_bytes, None);
    println!("result: {result:?}");
    assert!(matches!(result, Err(e) if e.to_string().contains("ErrorTooManyOperations")));
}

#[test]
fn test_max_script_size() {
    let config = RhaiEngineConfig::default()
        .with_max_script_size(1000) // 1KB script size limit
        .with_use_holochain_time(false);
    let engine = RhaiEngine::with_config(config).unwrap();

    // Test input size limit
    let large_input = json!({
        "data": "x".repeat(2000) // 2KB input
    });
    let small_script = r#"let x = 0;"#;
    let small_script_bytes = rmp_serde::to_vec(&small_script).unwrap();
    let result = engine.execute(&large_input, small_script_bytes, None);
    assert!(matches!(result, Err(e) if e.to_string().contains("Input too large")));

    // Test code size limit
    let small_input = json!({});
    let large_script = "let x = 0;".repeat(200); // ~2KB script
    let large_script_bytes = rmp_serde::to_vec(&large_script).unwrap();
    let result = engine.execute(&small_input, large_script_bytes, None);
    assert!(matches!(result, Err(e) if e.to_string().contains("Script size limit exceeded")));
}

#[test]
fn test_max_call_depth() {
    let config = RhaiEngineConfig::default()
        .with_max_call_depth(5)
        .with_use_holochain_time(false); // Set a low call depth limit
    let max_depth = config.max_recursion_depth;
    let engine = RhaiEngine::with_config(config).unwrap();
    let input = json!({});

    // Script that will exceed the call depth limit
    let script = r#"
        fn recursive(n) {
            if n <= 0 { return 0; }
            return recursive(n - 1);
        }
        recursive(10);
    "#;

    let script_bytes = rmp_serde::to_vec(&script).unwrap();
    let result = engine.execute(&input, script_bytes, None);
    assert!(matches!(
        result,
        Err(e) if e.to_string().contains(&format!("Recursion depth exceeded: {max_depth} levels"))
    ));
}

#[test]
fn test_input_size_limit() {
    let config = RhaiEngineConfig::default()
        .with_max_script_size(1000)
        .with_use_holochain_time(false);
    let engine = RhaiEngine::with_config(config).unwrap();

    // Create a large input object that exceeds the size limit
    let large_input = json!({
        "data": "x".repeat(2000) // 2KB input
    });

    let script = r#"let x = 0;"#;
    let script_bytes = rmp_serde::to_vec(&script).unwrap();
    let result = engine.execute(&large_input, script_bytes, None);
    assert!(matches!(result, Err(e) if e.to_string().contains("Input too large")));
}

#[test]
fn test_sandbox_mode() {
    // Test with sandbox disabled
    let config = RhaiEngineConfig::default()
        .with_enable_sandbox(false)
        .with_use_holochain_time(false);
    let engine = RhaiEngine::with_config(config).unwrap();
    let input = json!({});

    // Script that would normally be blocked by operation limits
    let script = r#"
        let x = 0;
        for i in 0..2000 { x += i; }
    "#;

    // Should execute without operation limit errors when sandbox is disabled
    let script_bytes = rmp_serde::to_vec(&script).unwrap();
    let result = engine.execute(&input, script_bytes, None);
    println!("result: {result:?}");
    assert!(result.is_ok());
}

#[test]
fn test_module_imports() {
    let config = RhaiEngineConfig::default().with_use_holochain_time(false);
    let engine = RhaiEngine::with_config(config).unwrap();
    let input = json!({});

    // Script that tries to import a module
    let script = r#"
        import "some_module";
    "#;

    let script_bytes = rmp_serde::to_vec(&script).unwrap();
    let result = engine.execute(&input, script_bytes, None);
    assert!(matches!(result, Err(e) if e.to_string().contains("Module import not allowed")));
}