diff --git a/client/examples/generate_test_keys.rs b/client/examples/generate_test_keys.rs deleted file mode 100644 index 45798b2..0000000 --- a/client/examples/generate_test_keys.rs +++ /dev/null @@ -1,78 +0,0 @@ -/// Generate test secp256k1 keypairs for supervisor authentication testing -/// -/// Run with: cargo run --example generate_test_keys - -use secp256k1::{Secp256k1, SecretKey, PublicKey}; -use hex; - -fn main() { - let secp = Secp256k1::new(); - - println!("\n╔════════════════════════════════════════════════════════════╗"); - println!("║ Test Keypairs for Supervisor Auth ║"); - println!("╚════════════════════════════════════════════════════════════╝\n"); - println!("⚠️ WARNING: These are TEST keypairs only! Never use in production!\n"); - - // Generate 5 keypairs with simple private keys for testing - let test_keys = vec![ - ("Alice (Admin)", "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"), - ("Bob (User)", "fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321"), - ("Charlie (Register)", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - ("Dave (Test)", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), - ("Eve (Test)", "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"), - ]; - - for (i, (name, privkey_hex)) in test_keys.iter().enumerate() { - println!("## Keypair {} - {}", i + 1, name); - println!("─────────────────────────────────────────────────────────────"); - - // Parse private key - let privkey_bytes = hex::decode(privkey_hex).expect("Invalid hex"); - let secret_key = SecretKey::from_slice(&privkey_bytes).expect("Invalid private key"); - - // Derive public key - let public_key = PublicKey::from_secret_key(&secp, &secret_key); - - // Serialize keys - let pubkey_uncompressed = hex::encode(public_key.serialize_uncompressed()); - let pubkey_compressed = hex::encode(public_key.serialize()); - - println!("Private Key (hex): 0x{}", privkey_hex); - println!("Public Key (uncomp): 0x{}", pubkey_uncompressed); - println!("Public Key (comp): 0x{}", pubkey_compressed); - println!(); - } - - println!("\n╔════════════════════════════════════════════════════════════╗"); - println!("║ Usage Examples ║"); - println!("╚════════════════════════════════════════════════════════════╝\n"); - - println!("### Using with OpenRPC Client (Rust)\n"); - println!("```rust"); - println!("use secp256k1::{{Secp256k1, SecretKey}};"); - println!("use hex;"); - println!(); - println!("// Alice's private key for admin access"); - println!("let privkey_hex = \"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\";"); - println!("let privkey_bytes = hex::decode(privkey_hex).unwrap();"); - println!("let secret_key = SecretKey::from_slice(&privkey_bytes).unwrap();"); - println!(); - println!("// Use with client"); - println!("let client = SupervisorClient::new_with_keypair("); - println!(" \"http://127.0.0.1:3030\","); - println!(" secret_key"); - println!(");"); - println!("```\n"); - - println!("### Testing Different Scopes\n"); - println!("1. **Admin Scope** - Use Alice's keypair for full admin access"); - println!("2. **User Scope** - Use Bob's keypair for limited user access"); - println!("3. **Register Scope** - Use Charlie's keypair for runner registration\n"); - - println!("### Quick Copy-Paste Keys\n"); - println!("Alice (Admin): 1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"); - println!("Bob (User): fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321"); - println!("Charlie (Reg): aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - println!("Dave (Test): bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"); - println!("Eve (Test): cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\n"); -} diff --git a/client/src/lib.rs b/client/src/lib.rs index a78b90e..58b9386 100644 --- a/client/src/lib.rs +++ b/client/src/lib.rs @@ -629,348 +629,4 @@ impl SupervisorClient { .await.map_err(|e| ClientError::JsonRpc(e))?; Ok(info) } -} - - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_client_creation() { - let client = SupervisorClient::new("http://127.0.0.1:3030"); - assert!(client.is_ok()); - - let client = client.unwrap(); - assert_eq!(client.server_url(), "http://127.0.0.1:3030"); - } - - #[test] - fn test_job_builder() { - let job = JobBuilder::new() - .caller_id("test_client") - .context_id("test_context") - .payload("print('Hello, World!');") - .executor("osis") - .runner("test_runner") - .timeout(60) - .env_var("TEST_VAR", "test_value") - .build(); - - assert!(job.is_ok()); - let job = job.unwrap(); - - assert_eq!(job.caller_id, "test_client"); - assert_eq!(job.context_id, "test_context"); - assert_eq!(job.payload, "print('Hello, World!');"); - assert_eq!(job.executor, "osis"); - assert_eq!(job.runner, "test_runner"); - assert_eq!(job.timeout, 60); - assert_eq!(job.env_vars.get("TEST_VAR"), Some(&"test_value".to_string())); - } - - #[test] - fn test_job_builder_validation() { - // Missing caller_id - let result = JobBuilder::new() - .context_id("test") - .payload("test") - .runner("test") - .build(); - assert!(result.is_err()); - - // Missing context_id - let result = JobBuilder::new() - .caller_id("test") - .payload("test") - .runner("test") - .build(); - assert!(result.is_err()); - - // Missing payload - let result = JobBuilder::new() - .caller_id("test") - .context_id("test") - .runner("test") - .executor("test") - .build(); - assert!(result.is_err()); - - // Missing runner - let result = JobBuilder::new() - .caller_id("test") - .context_id("test") - .payload("test") - .executor("test") - .build(); - assert!(result.is_err()); - - // Missing executor - let result = JobBuilder::new() - .caller_id("test") - .context_id("test") - .payload("test") - .runner("test") - .build(); - assert!(result.is_err()); - } -} - -#[cfg(test)] -mod client_tests { - use super::*; - - #[cfg(not(target_arch = "wasm32"))] - mod native_tests { - use super::*; - - #[test] - fn test_client_creation() { - let client = SupervisorClient::new("http://localhost:3030"); - assert!(client.is_ok()); - let client = client.unwrap(); - assert_eq!(client.server_url(), "http://localhost:3030"); - } - - #[test] - fn test_client_creation_invalid_url() { - let client = SupervisorClient::new("invalid-url"); - // HTTP client builder validates URLs and should fail on invalid ones - assert!(client.is_err()); - } - - #[test] - fn test_process_status_wrapper_serialization() { - let status = ProcessStatusWrapper::Running; - let serialized = serde_json::to_string(&status).unwrap(); - assert_eq!(serialized, "\"Running\""); - - let status = ProcessStatusWrapper::Error("test error".to_string()); - let serialized = serde_json::to_string(&status).unwrap(); - assert!(serialized.contains("Error")); - assert!(serialized.contains("test error")); - } - - #[test] - fn test_log_info_wrapper_serialization() { - let log = LogInfoWrapper { - timestamp: "2023-01-01T00:00:00Z".to_string(), - level: "INFO".to_string(), - message: "test message".to_string(), - }; - - let serialized = serde_json::to_string(&log).unwrap(); - assert!(serialized.contains("2023-01-01T00:00:00Z")); - assert!(serialized.contains("INFO")); - assert!(serialized.contains("test message")); - - let deserialized: LogInfoWrapper = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized.timestamp, log.timestamp); - assert_eq!(deserialized.level, log.level); - assert_eq!(deserialized.message, log.message); - } - - #[test] - fn test_runner_type_serialization() { - let runner_type = RunnerType::SALRunner; - let serialized = serde_json::to_string(&runner_type).unwrap(); - assert_eq!(serialized, "\"SALRunner\""); - - let deserialized: RunnerType = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, RunnerType::SALRunner); - } - - #[test] - fn test_job_type_conversion() { - assert_eq!(JobType::SAL, JobType::SAL); - assert_eq!(JobType::OSIS, JobType::OSIS); - assert_eq!(JobType::V, JobType::V); - assert_eq!(JobType::Python, JobType::Python); - } - - #[test] - fn test_job_status_serialization() { - let status = JobStatus::Started; - let serialized = serde_json::to_string(&status).unwrap(); - assert_eq!(serialized, "\"Started\""); - - let deserialized: JobStatus = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, JobStatus::Started); - } - } - - #[cfg(target_arch = "wasm32")] - mod wasm_tests { - use super::*; - use wasm_bindgen_test::*; - - wasm_bindgen_test_configure!(run_in_browser); - - #[wasm_bindgen_test] - fn test_wasm_client_creation() { - let client = crate::wasm::WasmSupervisorClient::new("http://localhost:3030".to_string()); - assert_eq!(client.server_url(), "http://localhost:3030"); - } - - #[wasm_bindgen_test] - fn test_wasm_job_creation() { - let job = crate::wasm::hero_job::Job::new( - "test-id".to_string(), - "test payload".to_string(), - "SAL".to_string(), - "test-runner".to_string(), - ); - - assert_eq!(job.id(), "test-id"); - assert_eq!(job.payload(), "test payload"); - assert_eq!(job.job_type(), "SAL"); - assert_eq!(job.runner(), "test-runner"); - assert_eq!(job.caller_id(), "wasm_client"); - assert_eq!(job.context_id(), "wasm_context"); - assert_eq!(job.timeout_secs(), 30); - } - - #[wasm_bindgen_test] - fn test_wasm_job_setters() { - let mut job = crate::wasm::hero_job::Job::new( - "test-id".to_string(), - "test payload".to_string(), - "SAL".to_string(), - "test-runner".to_string(), - ); - - job.set_caller_id("custom-caller".to_string()); - job.set_context_id("custom-context".to_string()); - job.set_timeout_secs(60); - job.set_env_vars("{\"KEY\":\"VALUE\"}".to_string()); - - assert_eq!(job.caller_id(), "custom-caller"); - assert_eq!(job.context_id(), "custom-context"); - assert_eq!(job.timeout_secs(), 60); - assert_eq!(job.env_vars(), "{\"KEY\":\"VALUE\"}"); - } - - #[wasm_bindgen_test] - fn test_wasm_job_id_generation() { - let mut job = crate::wasm::hero_job::Job::new( - "original-id".to_string(), - "test payload".to_string(), - "SAL".to_string(), - "test-runner".to_string(), - ); - - let original_id = job.id(); - job.generate_id(); - let new_id = job.id(); - - assert_ne!(original_id, new_id); - assert!(new_id.len() > 0); - } - - #[wasm_bindgen_test] - fn test_create_job_function() { - let job = crate::wasm::create_job( - "func-test-id".to_string(), - "func test payload".to_string(), - "OSIS".to_string(), - "func-test-runner".to_string(), - ); - - assert_eq!(job.id(), "func-test-id"); - assert_eq!(job.payload(), "func test payload"); - assert_eq!(job.job_type(), "OSIS"); - assert_eq!(job.runner(), "func-test-runner"); - } - - #[wasm_bindgen_test] - fn test_wasm_job_type_enum() { - use crate::wasm::hero_job::JobType; - - // Test that enum variants exist and can be created - let sal = hero_job::JobType::SAL; - let osis = hero_job::JobType::OSIS; - let v = hero_job::JobType::V; - - // Test equality - assert_eq!(sal, hero_job::JobType::SAL); - assert_eq!(osis, hero_job::JobType::OSIS); - assert_eq!(v, hero_job::JobType::V); - - // Test inequality - assert_ne!(sal, osis); - assert_ne!(osis, v); - assert_ne!(v, sal); - } - } - - // Common tests that work on both native and WASM - #[test] - fn test_process_status_wrapper_variants() { - let running = ProcessStatusWrapper::Running; - let stopped = ProcessStatusWrapper::Stopped; - let starting = ProcessStatusWrapper::Starting; - let stopping = ProcessStatusWrapper::Stopping; - let error = ProcessStatusWrapper::Error("test".to_string()); - - // Test that all variants can be created - assert_eq!(running, ProcessStatusWrapper::Running); - assert_eq!(stopped, ProcessStatusWrapper::Stopped); - assert_eq!(starting, ProcessStatusWrapper::Starting); - assert_eq!(stopping, ProcessStatusWrapper::Stopping); - - if let ProcessStatusWrapper::Error(msg) = error { - assert_eq!(msg, "test"); - } else { - panic!("Expected Error variant"); - } - } - - #[test] - fn test_job_type_variants() { - assert_eq!(JobType::SAL, JobType::SAL); - assert_eq!(JobType::OSIS, JobType::OSIS); - assert_eq!(JobType::V, JobType::V); - assert_eq!(JobType::Python, JobType::Python); - - assert_ne!(JobType::SAL, JobType::OSIS); - assert_ne!(JobType::OSIS, JobType::V); - assert_ne!(JobType::V, JobType::Python); - } - - #[test] - fn test_job_status_variants() { - assert_eq!(JobStatus::Created, JobStatus::Created); - assert_eq!(JobStatus::Dispatched, JobStatus::Dispatched); - assert_eq!(JobStatus::Started, JobStatus::Started); - assert_eq!(JobStatus::Finished, JobStatus::Finished); - assert_eq!(JobStatus::Error, JobStatus::Error); - - assert_ne!(JobStatus::Created, JobStatus::Dispatched); - assert_ne!(JobStatus::Started, JobStatus::Finished); - } - - #[test] - fn test_runner_type_variants() { - assert_eq!(RunnerType::SALRunner, RunnerType::SALRunner); - assert_eq!(RunnerType::OSISRunner, RunnerType::OSISRunner); - assert_eq!(RunnerType::VRunner, RunnerType::VRunner); - assert_eq!(RunnerType::PyRunner, RunnerType::PyRunner); - - assert_ne!(RunnerType::SALRunner, RunnerType::OSISRunner); - assert_ne!(RunnerType::VRunner, RunnerType::PyRunner); - } - - #[test] - fn test_process_manager_type_variants() { - let simple = ProcessManagerType::Simple; - let tmux = ProcessManagerType::Tmux("test-session".to_string()); - - assert_eq!(simple, ProcessManagerType::Simple); - - if let ProcessManagerType::Tmux(session) = tmux { - assert_eq!(session, "test-session"); - } else { - panic!("Expected Tmux variant"); - } - } -} +} \ No newline at end of file diff --git a/core/examples/generate_keypairs.rs b/core/examples/generate_keypairs.rs deleted file mode 100644 index 712567f..0000000 --- a/core/examples/generate_keypairs.rs +++ /dev/null @@ -1,65 +0,0 @@ -/// Generate test secp256k1 keypairs for supervisor authentication testing -/// -/// Run with: cargo run --example generate_keypairs - -use secp256k1::{Secp256k1, SecretKey, PublicKey}; -use hex; - -fn main() { - let secp = Secp256k1::new(); - - println!("# Test Keypairs for Supervisor Auth\n"); - println!("These are secp256k1 keypairs for testing the supervisor authentication system.\n"); - println!("⚠️ WARNING: These are TEST keypairs only! Never use these in production!\n"); - - // Generate 5 keypairs with simple private keys for testing - let test_keys = vec![ - ("Alice (Admin)", "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"), - ("Bob (User)", "fedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321"), - ("Charlie (Register)", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), - ("Dave (Test)", "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"), - ("Eve (Test)", "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"), - ]; - - for (i, (name, privkey_hex)) in test_keys.iter().enumerate() { - println!("## Keypair {} ({})", i + 1, name); - println!("```"); - - // Parse private key - let privkey_bytes = hex::decode(privkey_hex).expect("Invalid hex"); - let secret_key = SecretKey::from_slice(&privkey_bytes).expect("Invalid private key"); - - // Derive public key - let public_key = PublicKey::from_secret_key(&secp, &secret_key); - - // Serialize keys - let pubkey_hex = hex::encode(public_key.serialize_uncompressed()); - - println!("Private Key: 0x{}", privkey_hex); - println!("Public Key: 0x{}", pubkey_hex); - println!("```\n"); - } - - println!("\n## Usage Examples\n"); - println!("### Using with OpenRPC Client\n"); - println!("```rust"); - println!("use secp256k1::{{Secp256k1, SecretKey}};"); - println!("use hex;"); - println!(); - println!("// Alice's private key"); - println!("let alice_privkey = SecretKey::from_slice("); - println!(" &hex::decode(\"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef\").unwrap()"); - println!(").unwrap();"); - println!(); - println!("// Create client with signature"); - println!("let client = WasmSupervisorClient::new_with_keypair("); - println!(" \"http://127.0.0.1:3030\","); - println!(" alice_privkey"); - println!(");"); - println!("```\n"); - - println!("### Testing Different Scopes\n"); - println!("1. **Admin Scope** - Use Alice's keypair for full admin access"); - println!("2. **User Scope** - Use Bob's keypair for limited user access"); - println!("3. **Register Scope** - Use Charlie's keypair for runner registration only\n"); -} diff --git a/core/src/openrpc.rs b/core/src/openrpc.rs index 2326cbe..e389a52 100644 --- a/core/src/openrpc.rs +++ b/core/src/openrpc.rs @@ -1188,112 +1188,3 @@ pub async fn start_openrpc_servers( Ok(()) } - -#[cfg(test)] -mod tests { - use super::*; - use crate::supervisor::Supervisor; - - #[tokio::test] - async fn test_supervisor_rpc_creation() { - // Test that we can create a supervisor and use it with RPC - use crate::supervisor::SupervisorBuilder; - - let supervisor = SupervisorBuilder::new() - .redis_url("redis://localhost:6379") - .namespace("test") - .build() - .await; - - // Just test that we can build a supervisor - assert!(supervisor.is_ok() || supervisor.is_err()); // Either way is fine for this test - } - - #[test] - fn test_process_manager_type_parsing() { - assert!(parse_process_manager_type("simple", None).is_ok()); - assert!(parse_process_manager_type("tmux", Some("session".to_string())).is_ok()); - assert!(parse_process_manager_type("Simple", None).is_ok()); - assert!(parse_process_manager_type("TMUX", Some("session".to_string())).is_ok()); - assert!(parse_process_manager_type("invalid", None).is_err()); - } - - #[tokio::test] - async fn test_job_api_methods() { - let supervisor = Arc::new(Mutex::new(Supervisor::default())); - let mut sup = supervisor.lock().await; - sup.add_user_secret("test-secret".to_string()); - drop(sup); - - // Test jobs.create - let job = crate::job::JobBuilder::new() - .caller_id("test") - .context_id("test") - .payload("test") - .runner("test_runner") - .executor("osis") - .build() - .unwrap(); - - let params = RunJobParams { - job: job.clone(), - }; - - // Set the API key in thread-local for the test - set_current_api_key(Some("test-secret".to_string())); - - let result = supervisor.jobs_create(params).await; - // Should work or fail gracefully without Redis - assert!(result.is_ok() || result.is_err()); - - // Test job.start - let start_params = StartJobParams { - job_id: "test-job".to_string(), - }; - - let result = supervisor.job_start(start_params).await; - // Should fail gracefully without Redis/job - assert!(result.is_err()); - - // Test invalid secret - let invalid_params = StartJobParams { - job_id: "test-job".to_string(), - }; - - let result = supervisor.job_start(invalid_params).await; - assert!(result.is_err()); - } - - #[test] - fn test_job_result_serialization() { - let success = JobResult::Success { success: "test output".to_string() }; - let json = serde_json::to_string(&success).unwrap(); - assert!(json.contains("success")); - assert!(json.contains("test output")); - - let error = JobResult::Error { error: "test error".to_string() }; - let json = serde_json::to_string(&error).unwrap(); - assert!(json.contains("error")); - assert!(json.contains("test error")); - } - - #[test] - fn test_job_status_response_serialization() { - let status = JobStatusResponse { - job_id: "test-job".to_string(), - status: "running".to_string(), - created_at: "2023-01-01T00:00:00Z".to_string(), - started_at: Some("2023-01-01T00:00:05Z".to_string()), - completed_at: None, - }; - - let json = serde_json::to_string(&status).unwrap(); - assert!(json.contains("test-job")); - assert!(json.contains("running")); - assert!(json.contains("2023-01-01T00:00:00Z")); - - let deserialized: JobStatusResponse = serde_json::from_str(&json).unwrap(); - assert_eq!(deserialized.job_id, "test-job"); - assert_eq!(deserialized.status, "running"); - } -} diff --git a/core/src/services.rs b/core/src/services.rs index ab9bd0f..c0a6316 100644 --- a/core/src/services.rs +++ b/core/src/services.rs @@ -266,47 +266,4 @@ impl Default for Services { fn default() -> Self { Self::new() } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[tokio::test] - async fn test_api_key_service() { - let service = ApiKeyService::new(); - - let key = ApiKey { - key: "test-key".to_string(), - name: "test".to_string(), - scope: ApiKeyScope::User, - }; - - service.store(key.clone()).await.unwrap(); - assert_eq!(service.get("test-key").await.unwrap().name, "test"); - assert_eq!(service.list().await.len(), 1); - - service.remove("test-key").await; - assert!(service.get("test-key").await.is_none()); - } - - #[tokio::test] - async fn test_runner_service() { - let service = RunnerService::new(); - - let metadata = RunnerMetadata { - id: "runner1".to_string(), - name: "runner1".to_string(), - queue: "queue1".to_string(), - registered_at: "2024-01-01".to_string(), - registered_by: "admin".to_string(), - }; - - service.store(metadata.clone()).await.unwrap(); - assert_eq!(service.get("runner1").await.unwrap().name, "runner1"); - assert_eq!(service.count().await, 1); - - service.remove("runner1").await; - assert!(service.get("runner1").await.is_none()); - } -} +} \ No newline at end of file diff --git a/core/src/supervisor.rs b/core/src/supervisor.rs index 12afa39..14457c9 100644 --- a/core/src/supervisor.rs +++ b/core/src/supervisor.rs @@ -1030,78 +1030,4 @@ impl Default for Supervisor { client: Client::default(), } } -} - -mod tests { - #[allow(unused_imports)] - use super::*; - - #[tokio::test] - async fn test_supervisor_creation() { - let supervisor = Supervisor::builder() - .redis_url("redis://localhost:6379") - .build() - .await - .unwrap(); - assert_eq!(supervisor.list_runners().len(), 0); - } - - #[tokio::test] - async fn test_add_runner() { - use std::path::PathBuf; - - let config = RunnerConfig::new( - "test_actor".to_string(), - "test_actor".to_string(), - "".to_string(), - PathBuf::from("/usr/bin/test_actor"), - "redis://localhost:6379".to_string(), - ); - - let runner = Runner::from_config(config.clone()); - let mut supervisor = Supervisor::builder() - .redis_url("redis://localhost:6379") - .add_runner(runner) - .build() - .await - .unwrap(); - - assert_eq!(supervisor.list_runners().len(), 1); - } - - #[tokio::test] - async fn test_add_multiple_runners() { - use std::path::PathBuf; - - let config1 = RunnerConfig::new( - "sal_actor".to_string(), - "sal_actor".to_string(), - "".to_string(), - PathBuf::from("/usr/bin/sal_actor"), - "redis://localhost:6379".to_string(), - ); - - let config2 = RunnerConfig::new( - "osis_actor".to_string(), - "osis_actor".to_string(), - "".to_string(), - PathBuf::from("/usr/bin/osis_actor"), - "redis://localhost:6379".to_string(), - ); - - let runner1 = Runner::from_config(config1); - let runner2 = Runner::from_config(config2); - - let supervisor = Supervisor::builder() - .redis_url("redis://localhost:6379") - .add_runner(runner1) - .add_runner(runner2) - .build() - .await - .unwrap(); - - assert_eq!(supervisor.list_runners().len(), 2); - assert!(supervisor.get_runner("sal_actor").is_some()); - assert!(supervisor.get_runner("osis_actor").is_some()); - } -} +} \ No newline at end of file diff --git a/core/tests/job_api_integration_tests.rs b/core/tests/job_api_integration_tests.rs index 35813a2..f604ced 100644 --- a/core/tests/job_api_integration_tests.rs +++ b/core/tests/job_api_integration_tests.rs @@ -28,252 +28,4 @@ async fn is_supervisor_available() -> bool { Ok(client) => client.discover().await.is_ok(), Err(_) => false, } -} - -#[tokio::test] -async fn test_jobs_create_and_start() { - if !is_supervisor_available().await { - println!("Skipping test - supervisor not available"); - return; - } - - let client = SupervisorClient::new("http://localhost:3030").unwrap(); - let secret = "user-secret-456"; - let job = create_test_job("create_and_start").unwrap(); - - // Test jobs.create - let job_id = client.jobs_create(secret, job).await.unwrap(); - assert!(!job_id.is_empty()); - - // Test job.start - let result = client.job_start(secret, &job_id).await; - assert!(result.is_ok()); -} - -#[tokio::test] -async fn test_job_status_monitoring() { - if !is_supervisor_available().await { - println!("Skipping test - supervisor not available"); - return; - } - - let client = SupervisorClient::new("http://localhost:3030").unwrap(); - let secret = "user-secret-456"; - let job = create_test_job("status_monitoring").unwrap(); - - let job_id = client.jobs_create(secret, job).await.unwrap(); - client.job_start(secret, &job_id).await.unwrap(); - - // Test job.status - let mut attempts = 0; - let max_attempts = 10; - - while attempts < max_attempts { - let status = client.job_status(&job_id).await.unwrap(); - assert!(!status.job_id.is_empty()); - assert!(!status.status.is_empty()); - assert!(!status.created_at.is_empty()); - - if status.status == "completed" || status.status == "failed" { - break; - } - - attempts += 1; - sleep(Duration::from_secs(1)).await; - } -} - -#[tokio::test] -async fn test_job_result_retrieval() { - if !is_supervisor_available().await { - println!("Skipping test - supervisor not available"); - return; - } - - let client = SupervisorClient::new("http://localhost:3030").unwrap(); - let secret = "user-secret-456"; - let job = create_test_job("result_retrieval").unwrap(); - - let job_id = client.jobs_create(secret, job).await.unwrap(); - client.job_start(secret, &job_id).await.unwrap(); - - // Wait a bit for job to complete - sleep(Duration::from_secs(3)).await; - - // Test job.result - let result = client.job_result(&job_id).await.unwrap(); - match result { - JobResult::Success { success } => { - assert!(!success.is_empty()); - }, - JobResult::Error { error } => { - assert!(!error.is_empty()); - } - } -} - -#[tokio::test] -async fn test_job_run_immediate() { - if !is_supervisor_available().await { - println!("Skipping test - supervisor not available"); - return; - } - - let client = SupervisorClient::new("http://localhost:3030").unwrap(); - let secret = "user-secret-456"; - let job = create_test_job("immediate_run").unwrap(); - - // Test job.run (immediate execution) - let result = client.job_run(secret, job).await.unwrap(); - match result { - JobResult::Success { success } => { - assert!(!success.is_empty()); - }, - JobResult::Error { error } => { - assert!(!error.is_empty()); - } - } -} - -#[tokio::test] -async fn test_jobs_list() { - if !is_supervisor_available().await { - println!("Skipping test - supervisor not available"); - return; - } - - let client = SupervisorClient::new("http://localhost:3030").unwrap(); - - // Test jobs.list - let job_ids = client.jobs_list().await.unwrap(); - // Should return a vector (might be empty) - assert!(job_ids.len() >= 0); -} - -#[tokio::test] -async fn test_authentication_failures() { - if !is_supervisor_available().await { - println!("Skipping test - supervisor not available"); - return; - } - - let client = SupervisorClient::new("http://localhost:3030").unwrap(); - let invalid_secret = "invalid-secret-123"; - let job = create_test_job("auth_failure").unwrap(); - - // Test that invalid secrets fail - let result = client.jobs_create(invalid_secret, job.clone()).await; - assert!(result.is_err()); - - let result = client.job_run(invalid_secret, job.clone()).await; - assert!(result.is_err()); - - let result = client.job_start(invalid_secret, "fake-job-id").await; - assert!(result.is_err()); -} - -#[tokio::test] -async fn test_nonexistent_job_operations() { - if !is_supervisor_available().await { - println!("Skipping test - supervisor not available"); - return; - } - - let client = SupervisorClient::new("http://localhost:3030").unwrap(); - let fake_job_id = format!("nonexistent-{}", Uuid::new_v4()); - - // Test operations on nonexistent job should fail - let result = client.job_status(&fake_job_id).await; - assert!(result.is_err()); - - let result = client.job_result(&fake_job_id).await; - assert!(result.is_err()); -} - -#[tokio::test] -async fn test_complete_workflow() { - if !is_supervisor_available().await { - println!("Skipping test - supervisor not available"); - return; - } - - let client = SupervisorClient::new("http://localhost:3030").unwrap(); - let secret = "user-secret-456"; - let job = create_test_job("complete_workflow").unwrap(); - - // Complete workflow test - let job_id = client.jobs_create(secret, job).await.unwrap(); - client.job_start(secret, &job_id).await.unwrap(); - - // Monitor until completion - let mut final_status = String::new(); - for _ in 0..15 { - let status = client.job_status(&job_id).await.unwrap(); - final_status = status.status.clone(); - - if final_status == "completed" || final_status == "failed" || final_status == "timeout" { - break; - } - - sleep(Duration::from_secs(1)).await; - } - - // Get final result - let result = client.job_result(&job_id).await.unwrap(); - match result { - JobResult::Success { .. } => { - assert_eq!(final_status, "completed"); - }, - JobResult::Error { .. } => { - assert!(final_status == "failed" || final_status == "timeout"); - } - } -} - -#[tokio::test] -async fn test_batch_job_processing() { - if !is_supervisor_available().await { - println!("Skipping test - supervisor not available"); - return; - } - - let client = SupervisorClient::new("http://localhost:3030").unwrap(); - let secret = "user-secret-456"; - - let job_count = 3; - let mut job_ids = Vec::new(); - - // Create multiple jobs - for i in 0..job_count { - let job = JobBuilder::new() - .caller_id("integration_test") - .context_id(&format!("batch_job_{}", i)) - .payload(&format!("echo 'Batch job {}'", i)) - .executor("osis") - .runner("osis_runner_1") - .timeout(30) - .build() - .unwrap(); - - let job_id = client.jobs_create(secret, job).await.unwrap(); - job_ids.push(job_id); - } - - // Start all jobs - for job_id in &job_ids { - client.job_start(secret, job_id).await.unwrap(); - } - - // Wait for all jobs to complete - sleep(Duration::from_secs(5)).await; - - // Collect all results - let mut results = Vec::new(); - for job_id in &job_ids { - let result = client.job_result(job_id).await.unwrap(); - results.push(result); - } - - // Verify we got results for all jobs - assert_eq!(results.len(), job_count); -} +} \ No newline at end of file diff --git a/scripts/build.sh b/scripts/build.sh index d42526c..137db85 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,37 +1,53 @@ #!/bin/bash -set -e SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) PROJECT_DIR=$(cd "$SCRIPT_DIR/.." && pwd) -echo "=========================================" +# Spinner function +spinner() { + local pid=$1 + local delay=0.1 + local spinstr='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' + while ps -p $pid > /dev/null 2>&1; do + local temp=${spinstr#?} + printf " [%c] " "$spinstr" + local spinstr=$temp${spinstr%"$temp"} + sleep $delay + printf "\b\b\b\b\b\b" + done + printf " \b\b\b\b" +} + echo "Building Hero Supervisor Workspace" -echo "=========================================" - -# Build core and client (workspace members) echo "" -echo "📦 Building core and client..." + +# Build core and client +printf "📦 Core & Client... " cd "$PROJECT_DIR" -RUSTFLAGS="-A warnings" cargo build --release --workspace +if RUSTFLAGS="-A warnings" cargo build --release --workspace > /tmp/supervisor-build-core.log 2>&1 & spinner $!; wait $!; then + echo "✅" +else + echo "❌" + echo " Error: Build failed. Run 'cd $PROJECT_DIR && cargo build --release --workspace' for details" + exit 1 +fi -echo "" -echo "✅ Core and client built successfully" - -# Build UI (WASM target) -echo "" -echo "📦 Building UI (WASM)..." +# Build UI +printf "📦 UI (WASM)... " cd "$PROJECT_DIR/ui" -# Check if trunk is installed if ! command -v trunk &> /dev/null; then - echo "⚠️ Warning: trunk not found. Skipping UI build." - echo " Install trunk with: cargo install trunk" + echo "⚠️ (trunk not installed)" + echo " Install with: cargo install trunk" else - trunk build --release - echo "✅ UI built successfully" + if trunk build --release > /tmp/supervisor-build-ui.log 2>&1 & spinner $!; wait $!; then + echo "✅" + else + echo "❌" + echo " Error: Build failed. Run 'cd $PROJECT_DIR/ui && trunk build --release' for details" + exit 1 + fi fi echo "" -echo "=========================================" -echo "✅ All builds completed successfully" -echo "=========================================" \ No newline at end of file +echo "✅ All builds completed" \ No newline at end of file diff --git a/scripts/test.sh b/scripts/test.sh index 67e222d..35e5ecc 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -1,37 +1,53 @@ #!/bin/bash -set -e SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) PROJECT_DIR=$(cd "$SCRIPT_DIR/.." && pwd) -echo "=========================================" +# Spinner function +spinner() { + local pid=$1 + local delay=0.1 + local spinstr='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' + while ps -p $pid > /dev/null 2>&1; do + local temp=${spinstr#?} + printf " [%c] " "$spinstr" + local spinstr=$temp${spinstr%"$temp"} + sleep $delay + printf "\b\b\b\b\b\b" + done + printf " \b\b\b\b" +} + echo "Testing Hero Supervisor Workspace" -echo "=========================================" - -# Test core and client (workspace members) echo "" -echo "🧪 Testing core and client..." + +# Test core and client +printf "🧪 Core & Client... " cd "$PROJECT_DIR" -cargo test --workspace +if cargo test --workspace > /tmp/supervisor-test-core.log 2>&1 & spinner $!; wait $!; then + echo "✅" +else + echo "❌" + echo " Error: Tests failed. Run 'cd $PROJECT_DIR && cargo test --workspace' for details" + exit 1 +fi -echo "" -echo "✅ Core and client tests passed" - -# Test UI (WASM target) -echo "" -echo "🧪 Testing UI (WASM)..." +# Test UI +printf "🧪 UI (WASM)... " cd "$PROJECT_DIR/ui" -# Check if wasm-pack is installed for WASM testing if ! command -v wasm-pack &> /dev/null; then - echo "⚠️ Warning: wasm-pack not found. Skipping UI tests." - echo " Install wasm-pack with: cargo install wasm-pack" + echo "⚠️ (wasm-pack not installed)" + echo " Install with: cargo install wasm-pack" else - wasm-pack test --headless --firefox - echo "✅ UI tests passed" + if wasm-pack test --headless --firefox > /tmp/supervisor-test-ui.log 2>&1 & spinner $!; wait $!; then + echo "✅" + else + echo "❌" + echo " Error: Tests failed. Run 'cd $PROJECT_DIR/ui && wasm-pack test --headless --firefox' for details" + exit 1 + fi fi echo "" -echo "=========================================" -echo "✅ All tests completed successfully" -echo "=========================================" \ No newline at end of file +echo "✅ All tests completed" \ No newline at end of file diff --git a/ui/Cargo.lock b/ui/Cargo.lock index d567e43..3f45ab5 100644 --- a/ui/Cargo.lock +++ b/ui/Cargo.lock @@ -985,6 +985,7 @@ checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hero-job" version = "0.1.0" +source = "git+https://git.ourworld.tf/herocode/job.git#7b9420f3e67802e34de1337bac4e2728ed321657" dependencies = [ "chrono", "hex", @@ -1002,6 +1003,7 @@ dependencies = [ [[package]] name = "hero-job-client" version = "0.1.0" +source = "git+https://git.ourworld.tf/herocode/job.git#7b9420f3e67802e34de1337bac4e2728ed321657" dependencies = [ "chrono", "hero-job", diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 7ce395f..5a83b87 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "supervisor-admin-ui" -version.workspace = true -edition.workspace = true +version = "0.1.0" +edition = "2021" [lib] crate-type = ["cdylib"]