#![cfg(feature = "firecracker")]
#![cfg(feature = "firecracker")]
use terraphim_rlm::{
config::{BackendType, RlmConfig},
executor::{Capability, ExecutionEnvironment},
rlm::TerraphimRlm,
};
fn test_config() -> RlmConfig {
let mut config = RlmConfig::minimal();
config.pool_min_size = 1;
config.pool_max_size = 2;
config.pool_target_size = 1;
config.vm_boot_timeout_ms = 30_000;
config.allocation_timeout_ms = 5_000;
config.time_budget_ms = 10_000;
config.token_budget = 1000;
config.max_recursion_depth = 2;
config.max_iterations = 5;
config
}
#[tokio::test]
async fn test_rlm_creation_with_firecracker() {
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let config = test_config();
let rlm = TerraphimRlm::new(config).await;
match rlm {
Ok(_) => {
println!("TerraphimRlm created successfully with Firecracker backend");
}
Err(e) => {
println!(
"TerraphimRlm creation failed (expected if Firecracker not fully configured): {}",
e
);
}
}
}
#[tokio::test]
async fn test_rlm_creation_with_executor() {
use terraphim_rlm::executor::FirecrackerExecutor;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let config = test_config();
let executor = FirecrackerExecutor::new(config.clone())
.expect("Failed to create FirecrackerExecutor (KVM should be available)");
let rlm = TerraphimRlm::with_executor(config, executor)
.expect("Failed to create TerraphimRlm with custom executor");
assert_eq!(TerraphimRlm::version(), terraphim_rlm::VERSION);
assert!(
rlm.config()
.backend_preference
.contains(&BackendType::Firecracker)
|| rlm.config().backend_preference.is_empty()
);
}
#[tokio::test]
async fn test_session_lifecycle_real() {
use terraphim_rlm::executor::FirecrackerExecutor;
use terraphim_rlm::types::SessionState;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let config = test_config();
let executor =
FirecrackerExecutor::new(config.clone()).expect("Failed to create FirecrackerExecutor");
executor
.initialize()
.await
.expect("Failed to initialize FirecrackerExecutor");
let rlm = TerraphimRlm::with_executor(config, executor).expect("Failed to create TerraphimRlm");
let session = rlm
.create_session()
.await
.expect("Failed to create session");
assert_eq!(session.state, SessionState::Initializing);
let retrieved = rlm.get_session(&session.id).expect("Failed to get session");
assert_eq!(retrieved.id, session.id);
rlm.set_context_variable(&session.id, "test_key", "test_value")
.expect("Failed to set context variable");
let value = rlm
.get_context_variable(&session.id, "test_key")
.expect("Failed to get context variable");
assert_eq!(value, Some("test_value".to_string()));
rlm.destroy_session(&session.id)
.await
.expect("Failed to destroy session");
assert!(rlm.get_session(&session.id).is_err());
}
#[tokio::test]
async fn test_execute_code_real() {
use terraphim_rlm::executor::FirecrackerExecutor;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let config = test_config();
let executor =
FirecrackerExecutor::new(config.clone()).expect("Failed to create FirecrackerExecutor");
executor
.initialize()
.await
.expect("Failed to initialize FirecrackerExecutor");
let rlm = TerraphimRlm::with_executor(config, executor).expect("Failed to create TerraphimRlm");
let session = rlm
.create_session()
.await
.expect("Failed to create session");
let result = rlm
.execute_code(&session.id, "print('Hello from RLM!')")
.await
.expect("Failed to execute code");
println!("Code execution result: {:?}", result);
if result.stdout.contains("Hello from RLM!") {
println!("SUCCESS: Code executed in real VM!");
} else if result
.stdout
.contains("[FirecrackerExecutor] No VM available")
{
println!("INFO: VM not allocated (expected if pool not initialized)");
}
assert_eq!(result.exit_code, 0);
rlm.destroy_session(&session.id).await.ok();
}
#[tokio::test]
async fn test_execute_command_real() {
use terraphim_rlm::executor::FirecrackerExecutor;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let config = test_config();
let executor =
FirecrackerExecutor::new(config.clone()).expect("Failed to create FirecrackerExecutor");
let rlm = TerraphimRlm::with_executor(config, executor).expect("Failed to create TerraphimRlm");
let session = rlm
.create_session()
.await
.expect("Failed to create session");
let result = rlm
.execute_command(&session.id, "echo 'Hello from RLM!'")
.await
.expect("Failed to execute command");
println!("Command execution result: {:?}", result);
assert_eq!(result.exit_code, 0);
rlm.destroy_session(&session.id).await.ok();
}
#[tokio::test]
async fn test_snapshots_real() {
use terraphim_rlm::executor::FirecrackerExecutor;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let config = test_config();
let executor =
FirecrackerExecutor::new(config.clone()).expect("Failed to create FirecrackerExecutor");
let rlm = TerraphimRlm::with_executor(config, executor).expect("Failed to create TerraphimRlm");
let session = rlm
.create_session()
.await
.expect("Failed to create session");
let snapshot = rlm.create_snapshot(&session.id, "test_snapshot").await;
match snapshot {
Ok(s) => {
println!("Created snapshot: {}", s.name);
assert_eq!(s.name, "test_snapshot");
}
Err(e) => {
println!(
"Snapshot creation failed (expected if VM not assigned): {}",
e
);
}
}
let snapshots = rlm
.list_snapshots(&session.id)
.await
.expect("Failed to list snapshots");
println!("Snapshots: {:?}", snapshots);
rlm.destroy_session(&session.id).await.ok();
}
#[tokio::test]
async fn test_health_check_real() {
use terraphim_rlm::executor::FirecrackerExecutor;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let config = test_config();
let executor =
FirecrackerExecutor::new(config.clone()).expect("Failed to create FirecrackerExecutor");
let rlm = TerraphimRlm::with_executor(config, executor).expect("Failed to create TerraphimRlm");
let health = rlm.health_check().await.expect("Health check failed");
println!("Health check result: {}", health);
}
#[tokio::test]
async fn test_session_extension_real() {
use terraphim_rlm::executor::FirecrackerExecutor;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let config = test_config();
let executor =
FirecrackerExecutor::new(config.clone()).expect("Failed to create FirecrackerExecutor");
let rlm = TerraphimRlm::with_executor(config, executor).expect("Failed to create TerraphimRlm");
let session = rlm
.create_session()
.await
.expect("Failed to create session");
let original_expiry = session.expires_at;
let extended = rlm
.extend_session(&session.id)
.expect("Failed to extend session");
assert!(extended.expires_at > original_expiry);
assert_eq!(extended.extension_count, 1);
rlm.destroy_session(&session.id).await.ok();
}
#[tokio::test]
async fn test_executor_capabilities_real() {
use terraphim_rlm::executor::FirecrackerExecutor;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let config = test_config();
let executor =
FirecrackerExecutor::new(config.clone()).expect("Failed to create FirecrackerExecutor");
assert!(executor.has_capability(Capability::VmIsolation));
assert!(executor.has_capability(Capability::Snapshots));
assert!(executor.has_capability(Capability::PythonExecution));
assert!(executor.has_capability(Capability::BashExecution));
assert!(!executor.has_capability(Capability::ContainerIsolation));
}
#[tokio::test]
async fn test_query_llm_real() {
use terraphim_rlm::executor::FirecrackerExecutor;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let ollama_available = reqwest::get("http://127.0.0.1:11434/api/tags")
.await
.map(|r| r.status().is_success())
.unwrap_or(false);
if !ollama_available {
eprintln!("Skipping test: Ollama not available");
return;
}
let config = test_config();
let executor =
FirecrackerExecutor::new(config.clone()).expect("Failed to create FirecrackerExecutor");
let rlm = TerraphimRlm::with_executor(config, executor).expect("Failed to create TerraphimRlm");
let session = rlm
.create_session()
.await
.expect("Failed to create session");
let result = rlm
.query_llm(&session.id, "What is 2+2? Answer with just the number.")
.await;
match result {
Ok(response) => {
println!("LLM response: {}", response.response);
println!("Tokens used: {}", response.tokens_used);
assert!(!response.response.is_empty());
}
Err(e) => {
println!("LLM query failed: {}", e);
}
}
rlm.destroy_session(&session.id).await.ok();
}
#[tokio::test]
async fn test_full_query_loop_real() {
use terraphim_rlm::executor::FirecrackerExecutor;
use terraphim_rlm::query_loop::TerminationReason;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let ollama_available = reqwest::get("http://127.0.0.1:11434/api/tags")
.await
.map(|r| r.status().is_success())
.unwrap_or(false);
if !ollama_available {
eprintln!("Skipping test: Ollama not available");
return;
}
let config = test_config();
let executor =
FirecrackerExecutor::new(config.clone()).expect("Failed to create FirecrackerExecutor");
let rlm = TerraphimRlm::with_executor(config, executor).expect("Failed to create TerraphimRlm");
let session = rlm
.create_session()
.await
.expect("Failed to create session");
let result = rlm.query(&session.id, "Calculate 2+2").await;
match result {
Ok(query_result) => {
println!("Query result: {:?}", query_result.result);
println!("Termination reason: {:?}", query_result.termination_reason);
println!("Iterations: {}", query_result.iterations);
match query_result.termination_reason {
TerminationReason::FinalReached => {
println!("Query completed successfully");
}
TerminationReason::TokenBudgetExhausted => {
println!("Query ran out of tokens");
}
TerminationReason::TimeBudgetExhausted => {
println!("Query ran out of time");
}
TerminationReason::MaxIterationsReached => {
println!("Query reached max iterations");
}
TerminationReason::RecursionDepthExhausted => {
println!("Query reached max recursion depth");
}
TerminationReason::Error { message } => {
println!("Query encountered an error: {}", message);
}
TerminationReason::Cancelled => {
println!("Query was cancelled");
}
TerminationReason::FinalVarReached { variable } => {
println!("Query returned variable: {}", variable);
}
}
}
Err(e) => {
println!("Query failed: {}", e);
}
}
rlm.destroy_session(&session.id).await.ok();
}
#[tokio::test]
async fn test_doc_examples_real() {
use terraphim_rlm::executor::FirecrackerExecutor;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let config = RlmConfig::default();
let executor =
FirecrackerExecutor::new(config.clone()).expect("Failed to create FirecrackerExecutor");
let rlm = TerraphimRlm::with_executor(config, executor).expect("Failed to create TerraphimRlm");
let session = rlm
.create_session()
.await
.expect("Failed to create session");
let result = rlm
.execute_code(&session.id, "print('Hello, RLM!')")
.await
.expect("Failed to execute code");
println!("Output: {}", result.stdout);
let result = rlm
.execute_command(&session.id, "ls -la")
.await
.expect("Failed to execute command");
println!("Output: {}", result.stdout);
rlm.destroy_session(&session.id).await.ok();
}
#[tokio::test]
async fn test_cleanup_real() {
use terraphim_rlm::executor::FirecrackerExecutor;
if !terraphim_rlm::executor::is_kvm_available() {
eprintln!("Skipping test: KVM not available");
return;
}
let config = test_config();
let executor =
FirecrackerExecutor::new(config.clone()).expect("Failed to create FirecrackerExecutor");
let rlm = TerraphimRlm::with_executor(config, executor).expect("Failed to create TerraphimRlm");
for i in 0..3 {
let session = rlm
.create_session()
.await
.expect("Failed to create session");
rlm.set_context_variable(&session.id, "index", &i.to_string())
.expect("Failed to set context variable");
rlm.destroy_session(&session.id)
.await
.expect("Failed to destroy session");
}
let stats = rlm.get_stats();
assert_eq!(stats.active_sessions, 0);
}