use hero_openrpc_server::{OpenRpcServer, OpenRpcServerConfig, OpenRpcApiServer, Transport, types::*}; use hero_supervisor::{Supervisor, SupervisorBuilder}; use hero_job::{JobBuilder, JobStatus, ScriptType}; use jsonrpsee_types::error::ErrorCode; use std::sync::Arc; use tokio::sync::RwLock; use std::time::Duration; /// Helper function to create a test supervisor async fn create_test_supervisor() -> Arc> { let supervisor = SupervisorBuilder::new() .redis_url("redis://localhost:6379") .build() .await .expect("Failed to create test supervisor"); Arc::new(RwLock::new(supervisor)) } /// Helper function to create a test OpenRPC server async fn create_test_server() -> OpenRpcServer { use std::net::SocketAddr; use std::path::PathBuf; let config = OpenRpcServerConfig { transport: Transport::WebSocket("127.0.0.1:0".parse::().unwrap()), supervisor_config_path: None, db_path: PathBuf::from("/tmp/test_openrpc.db"), }; OpenRpcServer::new(config).await.expect("Failed to create OpenRPC server") } #[tokio::test] async fn test_fetch_nonce() { let server = create_test_server().await; let public_key = "test_public_key".to_string(); let result = server.fetch_nonce(public_key).await; assert!(result.is_ok()); let nonce = result.unwrap(); assert!(!nonce.is_empty()); assert_eq!(nonce.len(), 64); // Should be a 32-byte hex string } #[tokio::test] async fn test_create_job_success() { let server = create_test_server().await; let job_params = JobParams { script: "print('Hello, World!');".to_string(), script_type: ScriptType::OSIS, caller_id: "test_caller".to_string(), context_id: "test_context".to_string(), timeout: Some(60), prerequisites: None, }; let result = server.create_job(job_params).await; assert!(result.is_ok()); let job_id = result.unwrap(); assert!(!job_id.is_empty()); // Job ID should be a valid UUID format assert!(uuid::Uuid::parse_str(&job_id).is_ok()); } #[tokio::test] async fn test_create_job_with_prerequisites() { let server = create_test_server().await; let job_params = JobParams { script: "print('Job with prerequisites');".to_string(), script_type: ScriptType::SAL, caller_id: "test_caller".to_string(), context_id: "test_context".to_string(), timeout: Some(120), prerequisites: Some(vec!["prereq_job_1".to_string(), "prereq_job_2".to_string()]), }; let result = server.create_job(job_params).await; assert!(result.is_ok()); let job_id = result.unwrap(); assert!(!job_id.is_empty()); } #[tokio::test] async fn test_create_job_invalid_params() { let server = create_test_server().await; // Test with empty caller_id (should fail JobBuilder validation) let job_params = JobParams { script: "print('Test');".to_string(), script_type: ScriptType::OSIS, caller_id: "".to_string(), // Empty caller_id should fail context_id: "test_context".to_string(), timeout: Some(60), prerequisites: None, }; let result = server.create_job(job_params).await; assert!(result.is_err()); assert_eq!(result.unwrap_err(), ErrorCode::InvalidParams); } #[tokio::test] async fn test_start_job() { let server = create_test_server().await; // First create a job let job_params = JobParams { script: "print('Test job');".to_string(), script_type: ScriptType::OSIS, caller_id: "test_caller".to_string(), context_id: "test_context".to_string(), timeout: Some(60), prerequisites: None, }; let job_id = server.create_job(job_params).await.unwrap(); // Then start the job let result = server.start_job(job_id).await; assert!(result.is_ok()); let start_result = result.unwrap(); assert!(start_result.success); } #[tokio::test] async fn test_get_job_status() { let server = create_test_server().await; // First create a job let job_params = JobParams { script: "print('Status test');".to_string(), script_type: ScriptType::OSIS, caller_id: "test_caller".to_string(), context_id: "test_context".to_string(), timeout: Some(60), prerequisites: None, }; let job_id = server.create_job(job_params).await.unwrap(); // Get job status let result = server.get_job_status(job_id).await; assert!(result.is_ok()); let status = result.unwrap(); // Status should be one of the valid JobStatus variants match status { JobStatus::Dispatched | JobStatus::WaitingForPrerequisites | JobStatus::Started | JobStatus::Error | JobStatus::Finished => { // Valid status } } } #[tokio::test] async fn test_get_job_output() { let server = create_test_server().await; // First create a job let job_params = JobParams { script: "print('Output test');".to_string(), script_type: ScriptType::OSIS, caller_id: "test_caller".to_string(), context_id: "test_context".to_string(), timeout: Some(60), prerequisites: None, }; let job_id = server.create_job(job_params).await.unwrap(); // Get job output let result = server.get_job_output(job_id).await; assert!(result.is_ok()); let output = result.unwrap(); assert!(!output.is_empty()); } #[tokio::test] async fn test_list_jobs() { let server = create_test_server().await; // Create a few jobs first for i in 0..3 { let job_params = JobParams { script: format!("print('Job {}');", i), script_type: ScriptType::OSIS, caller_id: "test_caller".to_string(), context_id: "test_context".to_string(), timeout: Some(60), prerequisites: None, }; let _ = server.create_job(job_params).await.unwrap(); } // List all jobs let result = server.list_jobs().await; assert!(result.is_ok()); let jobs = result.unwrap(); assert!(jobs.len() >= 3); // Should have at least the 3 jobs we created // Verify job structure for job in jobs { assert!(!job.id.is_empty()); assert!(uuid::Uuid::parse_str(&job.id).is_ok()); } } #[tokio::test] async fn test_stop_job() { let server = create_test_server().await; // First create and start a job let job_params = JobParams { script: "print('Stop test');".to_string(), script_type: ScriptType::OSIS, caller_id: "test_caller".to_string(), context_id: "test_context".to_string(), timeout: Some(60), prerequisites: None, }; let job_id = server.create_job(job_params).await.unwrap(); let _ = server.start_job(job_id.clone()).await.unwrap(); // Stop the job let result = server.stop_job(job_id).await; assert!(result.is_ok()); } #[tokio::test] async fn test_delete_job() { let server = create_test_server().await; // First create a job let job_params = JobParams { script: "print('Delete test');".to_string(), script_type: ScriptType::OSIS, caller_id: "test_caller".to_string(), context_id: "test_context".to_string(), timeout: Some(60), prerequisites: None, }; let job_id = server.create_job(job_params).await.unwrap(); // Delete the job let result = server.delete_job(job_id).await; assert!(result.is_ok()); } #[tokio::test] async fn test_clear_all_jobs() { let server = create_test_server().await; // Create a few jobs first for i in 0..3 { let job_params = JobParams { script: format!("print('Clear test {}');", i), script_type: ScriptType::OSIS, caller_id: "test_caller".to_string(), context_id: "test_context".to_string(), timeout: Some(60), prerequisites: None, }; let _ = server.create_job(job_params).await.unwrap(); } // Clear all jobs let result = server.clear_all_jobs().await; assert!(result.is_ok()); // Verify jobs are cleared let jobs = server.list_jobs().await.unwrap(); assert_eq!(jobs.len(), 0); } #[tokio::test] async fn test_run_job() { let server = create_test_server().await; let script = "print('Run job test');".to_string(); let script_type = ScriptType::OSIS; let prerequisites = None; let result = server.run_job(script, script_type, prerequisites).await; assert!(result.is_ok()); let output = result.unwrap(); assert!(!output.is_empty()); assert!(output.contains("Run job test")); } #[tokio::test] async fn test_play_script() { let server = create_test_server().await; let script = "print('Play script test');".to_string(); let result = server.play(script.clone()).await; assert!(result.is_ok()); let play_result = result.unwrap(); assert!(!play_result.output.is_empty()); assert!(play_result.output.contains(&script)); } #[tokio::test] async fn test_get_job_logs() { let server = create_test_server().await; // First create a job let job_params = JobParams { script: "print('Logs test');".to_string(), script_type: ScriptType::OSIS, caller_id: "test_caller".to_string(), context_id: "test_context".to_string(), timeout: Some(60), prerequisites: None, }; let job_id = server.create_job(job_params).await.unwrap(); // Get job logs let result = server.get_job_logs(job_id).await; assert!(result.is_ok()); let logs_result = result.unwrap(); assert!(!logs_result.logs.is_empty()); } #[tokio::test] async fn test_job_builder_integration() { // Test that JobBuilder is working correctly with all the fields let job_params = JobParams { script: "print('JobBuilder test');".to_string(), script_type: ScriptType::V, caller_id: "test_caller".to_string(), context_id: "test_context".to_string(), timeout: Some(300), prerequisites: Some(vec!["prereq1".to_string(), "prereq2".to_string()]), }; // Build job using JobBuilder (similar to what the server does) let mut builder = JobBuilder::new() .caller_id(&job_params.caller_id) .context_id(&job_params.context_id) .script(&job_params.script) .script_type(job_params.script_type); if let Some(timeout_secs) = job_params.timeout { builder = builder.timeout(Duration::from_secs(timeout_secs)); } if let Some(prerequisites) = job_params.prerequisites { builder = builder.prerequisites(prerequisites); } let job = builder.build(); assert!(job.is_ok()); let job = job.unwrap(); assert_eq!(job.caller_id, "test_caller"); assert_eq!(job.context_id, "test_context"); assert_eq!(job.script, "print('JobBuilder test');"); assert_eq!(job.script_type, ScriptType::V); assert_eq!(job.timeout, Duration::from_secs(300)); assert_eq!(job.prerequisites, vec!["prereq1".to_string(), "prereq2".to_string()]); } #[tokio::test] async fn test_error_handling() { let server = create_test_server().await; // Test getting status for non-existent job let result = server.get_job_status("non_existent_job".to_string()).await; // Should return an error or handle gracefully match result { Ok(_) => { // Some implementations might return a default status }, Err(error_code) => { assert_eq!(error_code, ErrorCode::InvalidParams); } } // Test getting output for non-existent job let result = server.get_job_output("non_existent_job".to_string()).await; match result { Ok(output) => { // Should return "No output available" or similar assert!(output.contains("No output available") || output.is_empty()); }, Err(error_code) => { assert_eq!(error_code, ErrorCode::InvalidParams); } } }