//! Integration test for `ScriptProvider` using a temp Nu script. //! //! Requires `nu` on PATH. The test is marked `#[ignore]` by default so CI //! without Nushell installed doesn't fail; run explicitly with: //! `cargo test -p ontoref-compute --test integration_script_provider -- --ignored` #[cfg(feature = "script")] mod script_tests { use std::io::Write; use ontoref_compute::providers::script::ScriptProvider; use ontoref_compute::{ComputeProvider, InstanceSpec}; /// Write a minimal Nu script that responds to `spawn` with a valid Instance JSON. fn write_reference_script(path: &std::path::Path) { let script = r#"#!/usr/bin/env nu # Reference script provider — responds to spawn/destroy/status/list/health. let req = $in | from json match $req.op { "spawn" => { { result: "spawn", handle: "script-test-0000000000000001", ip: null, ssh_host: null, ssh_port: null, expires_at: null, provider: "script-nu", metadata: {} } | to json }, "destroy" => { { result: "ack" } | to json }, "status" => { { result: "status", value: "running" } | to json }, "list" => { { result: "list", value: [] } | to json }, "health" => { { result: "health", value: true } | to json }, _ => { exit 1 } } "#; let mut f = std::fs::File::create(path).expect("create script file"); f.write_all(script.as_bytes()).expect("write script"); #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; let mut perms = f.metadata().expect("metadata").permissions(); perms.set_mode(0o755); std::fs::set_permissions(path, perms).expect("chmod"); } } #[tokio::test] #[ignore = "requires nu on PATH"] async fn script_provider_spawn_parses_valid_instance() { let tmp = tempfile::tempdir().expect("tempdir"); let script_path = tmp.path().join("provider.nu"); write_reference_script(&script_path); // Verify nu is available before running. let nu_check = std::process::Command::new("nu").arg("--version").output(); if nu_check.is_err() || !nu_check.unwrap().status.success() { eprintln!("nu not found on PATH — skipping"); return; } let provider = ScriptProvider::new(std::path::PathBuf::from("nu"), script_path); let spec = InstanceSpec::new("test-image:latest", 2, 1024); let inst = provider.spawn(spec).await.expect("spawn should succeed"); assert_eq!(inst.handle.as_str(), "script-test-0000000000000001"); assert_eq!(inst.provider, "script-nu"); } #[tokio::test] #[ignore = "requires nu on PATH"] async fn script_provider_destroy_is_idempotent() { let tmp = tempfile::tempdir().expect("tempdir"); let script_path = tmp.path().join("provider.nu"); write_reference_script(&script_path); if std::process::Command::new("nu").arg("--version").output().is_err() { return; } let provider = ScriptProvider::new(std::path::PathBuf::from("nu"), script_path); let handle = ontoref_compute::InstanceHandle::new("script-test-0000000000000001"); provider.destroy(&handle).await.expect("first destroy"); provider.destroy(&handle).await.expect("second destroy (idempotent)"); } #[tokio::test] #[ignore = "requires nu on PATH"] async fn script_provider_health_returns_ok() { let tmp = tempfile::tempdir().expect("tempdir"); let script_path = tmp.path().join("provider.nu"); write_reference_script(&script_path); if std::process::Command::new("nu").arg("--version").output().is_err() { return; } let provider = ScriptProvider::new(std::path::PathBuf::from("nu"), script_path); let health = provider.health().await.expect("health"); assert!(health.ok); } }