Update build/test scripts with clean progress indicators and minimal output

This commit is contained in:
Timur Gordon
2025-11-06 23:55:17 +01:00
parent bbced35996
commit 8a02fffcca
11 changed files with 82 additions and 1009 deletions

View File

@@ -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");
}

View File

@@ -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");
}
}

View File

@@ -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());
}
}
}

View File

@@ -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());
}
}
}

View File

@@ -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);
}
}