do-memory-mcp 0.1.26

Model Context Protocol (MCP) server with secure code execution sandbox for AI agents
Documentation
//! Unified sandbox tests.

#![allow(unused_imports)]

use crate::types::{ExecutionContext, ExecutionResult, SandboxConfig};
use crate::unified_sandbox::{BackendChoice, SandboxBackend, UnifiedSandbox};
use base64::Engine;

/// Check if running in CI environment where Node.js sandbox tests are flaky
#[allow(dead_code)]
fn should_skip_sandbox_tests() -> bool {
    std::env::var("CI").is_ok() || std::env::var("GITHUB_ACTIONS").is_ok()
}

#[tokio::test]
async fn test_unified_sandbox_nodejs_backend() -> Result<(), anyhow::Error> {
    // Skip in CI due to Node.js sandbox timeout flakiness
    if should_skip_sandbox_tests() {
        return Ok(());
    }

    let sandbox = UnifiedSandbox::new(SandboxConfig::restrictive(), SandboxBackend::NodeJs).await?;

    let context = ExecutionContext::new("test".to_string(), serde_json::json!({}));
    let result = sandbox.execute("console.log('test')", context).await?;

    match &result {
        ExecutionResult::Success { stdout, .. } => {
            assert!(
                stdout.contains("test") || !stdout.is_empty(),
                "Expected non-empty stdout, got: {:?}",
                stdout
            );
        }
        _ => panic!("Expected success but got: {:?}", result),
    }

    let metrics = sandbox.get_metrics().await;
    assert_eq!(metrics.total_executions, 1);
    assert_eq!(metrics.node_executions, 1);
    assert_eq!(metrics.wasm_executions, 0);

    Ok(())
}

#[tokio::test]
async fn test_unified_sandbox_wasm_backend() -> Result<(), anyhow::Error> {
    let sandbox = UnifiedSandbox::new(SandboxConfig::restrictive(), SandboxBackend::Wasm).await?;

    // Use pre-compiled WASM module instead of JavaScript (Javy plugin not bundled)
    let wasm_bytes = wat::parse_str(
        r#"
        (module
            (func (export "main") (result i32)
                i32.const 42
            )
        )
    "#,
    )?;

    let context = ExecutionContext::new("test".to_string(), serde_json::json!({}));
    let wasm_payload = format!(
        "wasm_base64:{}",
        base64::prelude::BASE64_STANDARD.encode(wasm_bytes)
    );
    let result = sandbox.execute(&wasm_payload, context).await?;

    match &result {
        ExecutionResult::Success { .. } => {} // Success
        _ => panic!("Expected success but got: {:?}", result),
    }

    let metrics = sandbox.get_metrics().await;
    assert_eq!(metrics.total_executions, 1);
    assert_eq!(metrics.node_executions, 0);
    assert_eq!(metrics.wasm_executions, 1);

    Ok(())
}

#[tokio::test]
async fn test_unified_sandbox_hybrid_backend() -> Result<(), anyhow::Error> {
    // Skip in CI due to Node.js sandbox timeout flakiness
    if should_skip_sandbox_tests() {
        return Ok(());
    }

    let sandbox = UnifiedSandbox::new(
        SandboxConfig::default(),
        SandboxBackend::Hybrid {
            wasm_ratio: 0.5,
            intelligent_routing: true,
        },
    )
    .await?;

    let context = ExecutionContext::new("test".to_string(), serde_json::json!({}));

    // Execute multiple times to test routing - all JavaScript, routing will decide backend
    for i in 0..10 {
        let code = format!("console.log('test{}')", i);

        let result = sandbox.execute(&code, context.clone()).await?;
        match &result {
            ExecutionResult::Success { .. } => {} // Success
            _ => panic!("Expected success for iteration {} but got: {:?}", i, result),
        }
    }

    let metrics = sandbox.get_metrics().await;
    assert_eq!(metrics.total_executions, 10);
    // With random routing and JavaScript code, should use Node.js (Javy not fully working)
    assert!(metrics.node_executions > 0);

    Ok(())
}

#[tokio::test]
async fn test_intelligent_routing() -> Result<(), anyhow::Error> {
    // Skip in CI due to Node.js sandbox timeout flakiness
    if should_skip_sandbox_tests() {
        return Ok(());
    }

    let sandbox = UnifiedSandbox::new(
        SandboxConfig::default(),
        SandboxBackend::Hybrid {
            wasm_ratio: 0.1, // Low ratio, but intelligent routing should override
            intelligent_routing: true,
        },
    )
    .await?;

    let context = ExecutionContext::new("test".to_string(), serde_json::json!({}));

    // Simple code should route to Node.js (not WASM, as Javy is not fully working)
    let simple_result = sandbox
        .execute("console.log('simple')", context.clone())
        .await?;
    match &simple_result {
        ExecutionResult::Success { .. } => {} // Success
        _ => panic!(
            "Expected success for simple code but got: {:?}",
            simple_result
        ),
    }

    // Complex code should route to Node.js
    let complex_result = sandbox
        .execute(
            "function test() { return 'complex'; } console.log(test());",
            context,
        )
        .await?;
    match &complex_result {
        ExecutionResult::Success { .. } => {} // Success
        _ => panic!(
            "Expected success for complex code but got: {:?}",
            complex_result
        ),
    }

    let metrics = sandbox.get_metrics().await;
    assert_eq!(metrics.total_executions, 2);

    // Check routing decisions
    let routing_decisions = &metrics.routing_decisions;
    assert_eq!(routing_decisions.len(), 2);

    // Both should be Node.js (as Javy is not fully working)
    assert_eq!(routing_decisions[0].backend, "nodejs");
    assert_eq!(routing_decisions[1].backend, "nodejs");

    Ok(())
}

#[tokio::test]
async fn test_backend_health() -> Result<(), anyhow::Error> {
    let sandbox = UnifiedSandbox::new(
        SandboxConfig::restrictive(),
        SandboxBackend::Hybrid {
            wasm_ratio: 0.5,
            intelligent_routing: true,
        },
    )
    .await?;

    let health = sandbox.get_health_status().await;
    assert!(health.node_available);
    assert!(health.wasm_available);
    assert!(health.wasmtime_pool_stats.is_some());

    Ok(())
}

#[tokio::test]
async fn test_backend_update() -> Result<(), anyhow::Error> {
    let mut sandbox =
        UnifiedSandbox::new(SandboxConfig::restrictive(), SandboxBackend::NodeJs).await?;

    let context = ExecutionContext::new("test".to_string(), serde_json::json!({}));

    // Execute with Node.js backend
    let result1 = sandbox
        .execute("console.log('nodejs')", context.clone())
        .await?;
    match &result1 {
        ExecutionResult::Success { .. } => {} // Success
        _ => panic!(
            "Expected success for Node.js backend but got: {:?}",
            result1
        ),
    }

    // Update to WASM backend
    sandbox.update_backend(SandboxBackend::Wasm).await?;

    // Execute with WASM backend (use pre-compiled WASM)
    let wasm_bytes = wat::parse_str(
        r#"
        (module
            (func (export "main") (result i32)
                i32.const 42
            )
        )
    "#,
    )?;
    let wasm_payload = format!(
        "wasm_base64:{}",
        base64::prelude::BASE64_STANDARD.encode(wasm_bytes)
    );
    let result2 = sandbox.execute(&wasm_payload, context).await?;
    match &result2 {
        ExecutionResult::Success { .. } => {} // Success
        _ => panic!("Expected success for WASM backend but got: {:?}", result2),
    }

    let metrics = sandbox.get_metrics().await;
    // Verify at least one execution happened
    assert!(
        metrics.total_executions >= 1,
        "Expected at least 1 execution, got {}",
        metrics.total_executions
    );

    Ok(())
}