Update osiris dependency to use osiris-core package
This commit is contained in:
@@ -1,118 +1,212 @@
|
||||
use std::process::Command;
|
||||
use std::time::Duration;
|
||||
use tokio::time::timeout;
|
||||
use tokio::time::sleep;
|
||||
use runner_rust::{JobBuilder, Client};
|
||||
use runner_rust::job::JobSignature;
|
||||
use uuid::Uuid;
|
||||
use rhai::Engine;
|
||||
|
||||
/// Test the SAL runner in script mode with a simple ping script
|
||||
/// Helper function to create a SAL engine for testing
|
||||
fn create_test_sal_engine() -> Engine {
|
||||
// Create a basic Rhai engine for testing
|
||||
Engine::new()
|
||||
}
|
||||
|
||||
/// Test job execution with empty signatures array
|
||||
/// This verifies that jobs without signatures can execute successfully
|
||||
#[tokio::test]
|
||||
async fn test_sal_runner_script_mode_ping() {
|
||||
let output = timeout(
|
||||
Duration::from_secs(10),
|
||||
run_sal_runner_script_mode("test_sal_ping")
|
||||
).await;
|
||||
|
||||
match output {
|
||||
Ok(result) => {
|
||||
assert!(result.is_ok(), "SAL runner should execute successfully");
|
||||
let stdout = result.unwrap();
|
||||
assert!(stdout.contains("pong"),
|
||||
"Output should contain 'pong' response: {}", stdout);
|
||||
}
|
||||
Err(_) => panic!("Test timed out after 10 seconds"),
|
||||
}
|
||||
async fn test_job_execution_without_signatures() {
|
||||
let redis_url = "redis://localhost:6379";
|
||||
let runner_id = format!("test-runner-{}", Uuid::new_v4());
|
||||
|
||||
// Create client
|
||||
let mut client = Client::builder()
|
||||
.redis_url(redis_url)
|
||||
.build()
|
||||
.await
|
||||
.expect("Failed to create client");
|
||||
|
||||
// Create job with empty signatures array
|
||||
let job = JobBuilder::new()
|
||||
.caller_id("test_caller")
|
||||
.context_id("test_context")
|
||||
.payload("print(\"Hello from unsigned job\");")
|
||||
.runner(&runner_id)
|
||||
.executor("rhai")
|
||||
.timeout(30)
|
||||
.build()
|
||||
.expect("Job creation should succeed");
|
||||
|
||||
let job_id = job.id.clone();
|
||||
|
||||
// Verify signatures array is empty
|
||||
assert!(job.signatures.is_empty(), "Job should have no signatures");
|
||||
|
||||
// Save job to Redis
|
||||
client.store_job_in_redis(&job).await
|
||||
.expect("Failed to save job to Redis");
|
||||
|
||||
// Queue the job
|
||||
client.dispatch_job(&job_id, &runner_id).await
|
||||
.expect("Failed to queue job");
|
||||
|
||||
// Spawn runner in background
|
||||
let runner_id_clone = runner_id.clone();
|
||||
let redis_url_clone = redis_url.to_string();
|
||||
let runner_handle = tokio::spawn(async move {
|
||||
let (shutdown_tx, shutdown_rx) = tokio::sync::mpsc::channel::<()>(1);
|
||||
|
||||
// Run for 5 seconds then shutdown
|
||||
tokio::spawn(async move {
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
let _ = shutdown_tx.send(()).await;
|
||||
});
|
||||
|
||||
runner_rust::spawn_sync_runner(
|
||||
runner_id_clone,
|
||||
redis_url_clone,
|
||||
shutdown_rx,
|
||||
create_test_sal_engine,
|
||||
).await
|
||||
});
|
||||
|
||||
// Wait for job to be processed
|
||||
sleep(Duration::from_secs(3)).await;
|
||||
|
||||
// Check job result
|
||||
let result = client.get_result(&job_id).await
|
||||
.expect("Failed to get job result");
|
||||
|
||||
assert!(result.is_some(), "Job should have produced a result");
|
||||
|
||||
// Cleanup
|
||||
let _ = runner_handle.await;
|
||||
client.delete_job(&job_id).await.ok();
|
||||
}
|
||||
|
||||
/// Test the OSIS runner in script mode with a simple ping script
|
||||
/// Test job signature verification with valid signatures
|
||||
/// This verifies that jobs with valid signatures are accepted
|
||||
#[tokio::test]
|
||||
async fn test_osis_runner_script_mode_ping() {
|
||||
let output = timeout(
|
||||
Duration::from_secs(10),
|
||||
run_osis_runner_script_mode("test_osis_ping")
|
||||
).await;
|
||||
|
||||
match output {
|
||||
Ok(result) => {
|
||||
assert!(result.is_ok(), "OSIS runner should execute successfully");
|
||||
let stdout = result.unwrap();
|
||||
assert!(stdout.contains("pong"),
|
||||
"Output should contain 'pong' response: {}", stdout);
|
||||
}
|
||||
Err(_) => panic!("Test timed out after 10 seconds"),
|
||||
}
|
||||
async fn test_job_signature_verification() {
|
||||
use secp256k1::{Secp256k1, SecretKey, Message};
|
||||
use sha2::{Sha256, Digest};
|
||||
|
||||
let redis_url = "redis://localhost:6379";
|
||||
let runner_id = format!("test-runner-{}", Uuid::new_v4());
|
||||
|
||||
// Create client
|
||||
let mut client = Client::builder()
|
||||
.redis_url(redis_url)
|
||||
.build()
|
||||
.await
|
||||
.expect("Failed to create client");
|
||||
|
||||
// Generate a keypair for signing
|
||||
let secp = Secp256k1::new();
|
||||
let secret_key = SecretKey::from_slice(&[0xcd; 32])
|
||||
.expect("32 bytes, within curve order");
|
||||
let public_key = secp256k1::PublicKey::from_secret_key(&secp, &secret_key);
|
||||
|
||||
// Create job
|
||||
let mut job = JobBuilder::new()
|
||||
.caller_id("test_caller")
|
||||
.context_id("test_context")
|
||||
.payload("print(\"Hello from signed job\");")
|
||||
.runner(&runner_id)
|
||||
.executor("rhai")
|
||||
.timeout(30)
|
||||
.build()
|
||||
.expect("Job creation should succeed");
|
||||
|
||||
let job_id = job.id.clone();
|
||||
|
||||
// Sign the job
|
||||
let canonical = job.canonical_representation();
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(canonical.as_bytes());
|
||||
let hash = hasher.finalize();
|
||||
let message = Message::from_digest_slice(&hash)
|
||||
.expect("32 bytes");
|
||||
let signature = secp.sign_ecdsa(&message, &secret_key);
|
||||
|
||||
// Add signature to job
|
||||
job.signatures.push(JobSignature {
|
||||
public_key: hex::encode(public_key.serialize()),
|
||||
signature: hex::encode(signature.serialize_compact()),
|
||||
});
|
||||
|
||||
// Verify the job has a signature
|
||||
assert_eq!(job.signatures.len(), 1, "Job should have one signature");
|
||||
|
||||
// Verify signatures are valid
|
||||
job.verify_signatures()
|
||||
.expect("Signature verification should succeed");
|
||||
|
||||
// Save and queue job
|
||||
client.store_job_in_redis(&job).await
|
||||
.expect("Failed to save job to Redis");
|
||||
client.dispatch_job(&job_id, &runner_id).await
|
||||
.expect("Failed to queue job");
|
||||
|
||||
// Spawn runner
|
||||
let runner_id_clone = runner_id.clone();
|
||||
let redis_url_clone = redis_url.to_string();
|
||||
let runner_handle = tokio::spawn(async move {
|
||||
let (shutdown_tx, shutdown_rx) = tokio::sync::mpsc::channel::<()>(1);
|
||||
|
||||
tokio::spawn(async move {
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
let _ = shutdown_tx.send(()).await;
|
||||
});
|
||||
|
||||
runner_rust::spawn_sync_runner(
|
||||
runner_id_clone,
|
||||
redis_url_clone,
|
||||
shutdown_rx,
|
||||
create_test_sal_engine,
|
||||
).await
|
||||
});
|
||||
|
||||
// Wait for processing
|
||||
sleep(Duration::from_secs(3)).await;
|
||||
|
||||
// Check result
|
||||
let result = client.get_result(&job_id).await
|
||||
.expect("Failed to get job result");
|
||||
|
||||
assert!(result.is_some(), "Signed job should have produced a result");
|
||||
|
||||
// Cleanup
|
||||
let _ = runner_handle.await;
|
||||
client.delete_job(&job_id).await.ok();
|
||||
}
|
||||
|
||||
/// Helper function to run SAL runner in script mode
|
||||
async fn run_sal_runner_script_mode(
|
||||
runner_id: &str
|
||||
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let output = Command::new("cargo")
|
||||
.args(&[
|
||||
"run", "--bin", "runner_sal", "--",
|
||||
runner_id,
|
||||
"-s", "ping"
|
||||
])
|
||||
.output()?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
||||
} else {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
Err(format!("Command failed: {}", stderr).into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to run OSIS runner in script mode
|
||||
async fn run_osis_runner_script_mode(
|
||||
runner_id: &str
|
||||
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
|
||||
let output = Command::new("cargo")
|
||||
.args(&[
|
||||
"run", "--bin", "runner_osis", "--",
|
||||
runner_id,
|
||||
"-s", "ping"
|
||||
])
|
||||
.output()?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
||||
} else {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
Err(format!("Command failed: {}", stderr).into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Test basic compilation and help output
|
||||
/// Test job with invalid signature is rejected
|
||||
#[tokio::test]
|
||||
async fn test_sal_runner_help() {
|
||||
let output = Command::new("cargo")
|
||||
.args(&["run", "--bin", "runner_sal", "--", "--help"])
|
||||
.output()
|
||||
.expect("Failed to execute command");
|
||||
|
||||
assert!(output.status.success(), "Help command should succeed");
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(stdout.contains("Usage") || stdout.contains("USAGE"),
|
||||
"Help output should contain usage information");
|
||||
async fn test_job_invalid_signature_rejected() {
|
||||
// Create job with invalid signature
|
||||
let mut job = JobBuilder::new()
|
||||
.caller_id("test_caller")
|
||||
.context_id("test_context")
|
||||
.payload("print(\"This should fail\");")
|
||||
.runner("test-runner")
|
||||
.executor("rhai")
|
||||
.build()
|
||||
.expect("Job creation should succeed");
|
||||
|
||||
// Add invalid signature
|
||||
job.signatures.push(JobSignature {
|
||||
public_key: "04invalid_public_key".to_string(),
|
||||
signature: "invalid_signature".to_string(),
|
||||
});
|
||||
|
||||
// Verify signatures should fail
|
||||
let result = job.verify_signatures();
|
||||
assert!(result.is_err(), "Invalid signature should be rejected");
|
||||
}
|
||||
|
||||
/// Test basic compilation and help output for OSIS runner
|
||||
#[tokio::test]
|
||||
async fn test_osis_runner_help() {
|
||||
let output = Command::new("cargo")
|
||||
.args(&["run", "--bin", "runner_osis", "--", "--help"])
|
||||
.output()
|
||||
.expect("Failed to execute command");
|
||||
|
||||
assert!(output.status.success(), "Help command should succeed");
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
assert!(stdout.contains("Usage") || stdout.contains("USAGE"),
|
||||
"Help output should contain usage information");
|
||||
}
|
||||
|
||||
/// Test library functionality - job creation and basic operations
|
||||
/// Test job creation and serialization
|
||||
#[tokio::test]
|
||||
async fn test_job_creation_and_serialization() {
|
||||
use runner_rust::JobBuilder;
|
||||
|
||||
let job = JobBuilder::new()
|
||||
.caller_id("test_caller")
|
||||
.context_id("test_context")
|
||||
@@ -127,4 +221,17 @@ async fn test_job_creation_and_serialization() {
|
||||
assert_eq!(job.payload, "ping");
|
||||
assert_eq!(job.runner, "default");
|
||||
assert_eq!(job.executor, "rhai");
|
||||
assert!(job.signatures.is_empty(), "Default job should have no signatures");
|
||||
|
||||
// Test serialization
|
||||
let json = serde_json::to_string(&job)
|
||||
.expect("Job should serialize to JSON");
|
||||
|
||||
// Test deserialization
|
||||
let deserialized: runner_rust::Job = serde_json::from_str(&json)
|
||||
.expect("Job should deserialize from JSON");
|
||||
|
||||
assert_eq!(job.id, deserialized.id);
|
||||
assert_eq!(job.caller_id, deserialized.caller_id);
|
||||
assert_eq!(job.signatures.len(), deserialized.signatures.len());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user