circles/research/launcher/tests/spawn_test.rs
2025-07-08 22:49:47 +02:00

174 lines
6.2 KiB
Rust

use futures_util::{SinkExt, StreamExt};
use circles_launcher::{new_launcher, setup_multi_circle_server, shutdown_circles, Args, CircleConfig};
use secp256k1::Secp256k1;
use tokio_tungstenite::connect_async;
use url::Url;
use std::str::FromStr;
#[tokio::test]
async fn test_launcher_builder_pattern() {
// Test the new builder pattern API
let secp = Secp256k1::new();
let (secret_key, public_key) = secp.generate_keypair(&mut secp256k1::rand::thread_rng());
let public_key_str = public_key.to_string();
// Use the builder pattern to create a launcher
let launcher = new_launcher()
.add_circle(&public_key_str)
.port(8088)
.redis_url("redis://127.0.0.1:6379")
.worker_binary("../target/debug/worker") // Use debug for tests
.enable_auth(false);
// Note: We can't easily test the full launch in unit tests since it requires
// actual binaries and Redis. This test verifies the builder pattern works.
// Verify the builder created the launcher correctly
// (This is more of a compilation test than a runtime test)
assert!(true, "Builder pattern works correctly");
}
#[tokio::test]
async fn test_circle_config_parsing() {
// Test parsing circle configurations from strings
let public_key_only = "02a1b2c3d4e5f6789012345678901234567890123456789012345678901234567890";
let config = CircleConfig::from_str(public_key_only).expect("Failed to parse public key only");
assert_eq!(config.public_key, public_key_only);
assert!(config.init_script.is_none());
// Test with init script
let with_script = "02a1b2c3d4e5f6789012345678901234567890123456789012345678901234567890:init.rhai";
let config = CircleConfig::from_str(with_script).expect("Failed to parse with script");
assert_eq!(config.public_key, public_key_only);
assert_eq!(config.init_script, Some("init.rhai".to_string()));
// Test invalid format
let invalid = "invalid:too:many:colons";
let result = CircleConfig::from_str(invalid);
assert!(result.is_err(), "Should fail with invalid format");
}
#[tokio::test]
async fn test_args_structure() {
// Test that Args structure works correctly with the new API
let secp = Secp256k1::new();
let (_, public_key) = secp.generate_keypair(&mut secp256k1::rand::thread_rng());
let public_key_str = public_key.to_string();
let args = Args {
port: 8089,
circles: vec![public_key_str.clone()],
redis_url: "redis://127.0.0.1:6379".to_string(),
enable_auth: false,
worker_binary: Some("../target/debug/worker".to_string()),
debug: true,
verbose: 1,
};
// Verify args structure
assert_eq!(args.port, 8089);
assert_eq!(args.circles.len(), 1);
assert_eq!(args.circles[0], public_key_str);
assert!(!args.enable_auth);
assert!(args.worker_binary.is_some());
}
#[tokio::test]
async fn test_setup_multi_circle_server_validation() {
// Test validation in setup_multi_circle_server
let args = Args {
port: 8090,
circles: vec![], // Empty circles should cause error
redis_url: "redis://127.0.0.1:6379".to_string(),
enable_auth: false,
worker_binary: None, // Missing worker binary should cause error
debug: true,
verbose: 0,
};
// This should fail due to missing worker binary
let result = setup_multi_circle_server(&args).await;
assert!(result.is_err(), "Should fail with missing worker binary");
if let Err(e) = result {
let error_msg = e.to_string();
assert!(
error_msg.contains("Worker binary path is required"),
"Error should mention missing worker binary, got: {}",
error_msg
);
}
}
#[tokio::test]
async fn test_circle_config_validation() {
// Test that invalid public keys are rejected
let invalid_key = "not_a_valid_public_key";
let result = CircleConfig::from_str(invalid_key);
assert!(result.is_err(), "Should reject invalid public key");
// Test valid public key format
let valid_key = "02a1b2c3d4e5f6789012345678901234567890123456789012345678901234567890";
let result = CircleConfig::from_str(valid_key);
assert!(result.is_ok(), "Should accept valid public key");
}
#[tokio::test]
async fn test_launcher_cleanup_functionality() {
// Test that cleanup functionality exists and can be called
// Note: This doesn't test actual cleanup since we don't have running services
use circles_launcher::cleanup_launcher;
// This should not panic and should handle the case where no services exist
let result = cleanup_launcher().await;
// It's OK if this fails due to no services - we're just testing the API exists
let _ = result;
assert!(true, "Cleanup function exists and can be called");
}
// Integration test that would require actual binaries and Redis
// Commented out since it requires external dependencies
/*
#[tokio::test]
#[ignore] // Use `cargo test -- --ignored` to run this test
async fn test_full_launcher_integration() {
// This test requires:
// 1. Redis server running on localhost:6379
// 2. Worker binary built at ../target/debug/worker
// 3. WebSocket server binary built at ../target/debug/circles_server
let secp = Secp256k1::new();
let (_, public_key) = secp.generate_keypair(&mut secp256k1::rand::thread_rng());
let public_key_str = public_key.to_string();
let args = Args {
port: 8091,
circles: vec![public_key_str.clone()],
redis_url: "redis://127.0.0.1:6379".to_string(),
enable_auth: false,
worker_binary: Some("../target/debug/worker".to_string()),
debug: true,
verbose: 1,
};
// Setup the multi-circle server
let result = setup_multi_circle_server(&args).await;
assert!(result.is_ok(), "Failed to setup multi-circle server: {:?}", result.err());
let (running_circles, outputs) = result.unwrap();
// Verify outputs
assert_eq!(outputs.len(), 1);
assert_eq!(outputs[0].public_key, public_key_str);
// Test WebSocket connection
let ws_url = &outputs[0].ws_url;
let connection_result = connect_async(ws_url).await;
assert!(connection_result.is_ok(), "Failed to connect to WebSocket");
// Cleanup
shutdown_circles(running_circles).await;
}
*/