136 lines
5.2 KiB
Rust
136 lines
5.2 KiB
Rust
use tokio::time::Duration; // Removed unused sleep
|
|
use futures_util::{sink::SinkExt, stream::StreamExt};
|
|
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
|
|
use serde_json::Value; // Removed unused json macro import
|
|
use std::process::Command;
|
|
use std::thread;
|
|
use std::sync::Once;
|
|
|
|
// Define a simple JSON-RPC request structure for sending scripts
|
|
#[derive(serde::Serialize, Debug)]
|
|
struct JsonRpcRequest {
|
|
jsonrpc: String,
|
|
method: String,
|
|
params: ScriptParams,
|
|
id: u64,
|
|
}
|
|
|
|
#[derive(serde::Serialize, Debug)]
|
|
struct ScriptParams {
|
|
script: String,
|
|
}
|
|
|
|
// Define a simple JSON-RPC error response structure for assertion
|
|
#[derive(serde::Deserialize, Debug)]
|
|
struct JsonRpcErrorResponse {
|
|
_jsonrpc: String, // Field is present in response, but not used in assert
|
|
error: JsonRpcErrorDetails,
|
|
_id: Option<Value>, // Field is present in response, but not used in assert
|
|
}
|
|
|
|
#[derive(serde::Deserialize, Debug)]
|
|
struct JsonRpcErrorDetails {
|
|
code: i32,
|
|
message: String,
|
|
}
|
|
|
|
const SERVER_ADDRESS: &str = "ws://127.0.0.1:8088/ws"; // Match port in main.rs or make configurable
|
|
const TEST_CIRCLE_NAME: &str = "test_timeout_circle";
|
|
const SERVER_STARTUP_TIME: Duration = Duration::from_secs(5); // Time to wait for server to start
|
|
const RHAI_TIMEOUT_SECONDS: u64 = 30; // Should match TASK_TIMEOUT_DURATION in circle_server_ws
|
|
|
|
static START_SERVER: Once = Once::new();
|
|
|
|
fn ensure_server_is_running() {
|
|
START_SERVER.call_once(|| {
|
|
println!("Attempting to start circle_server_ws for integration tests...");
|
|
// The server executable will be in target/debug relative to the crate root
|
|
let server_executable = "target/debug/circle_server_ws";
|
|
|
|
thread::spawn(move || {
|
|
let mut child = Command::new(server_executable)
|
|
.arg("--port=8088") // Use a specific port for testing
|
|
.arg(format!("--circle-name={}", TEST_CIRCLE_NAME))
|
|
.spawn()
|
|
.expect("Failed to start circle_server_ws. Make sure it's compiled (cargo build).");
|
|
|
|
let status = child.wait().expect("Failed to wait on server process.");
|
|
println!("Server process exited with status: {}", status);
|
|
});
|
|
println!("Server start command issued. Waiting for {}s...", SERVER_STARTUP_TIME.as_secs());
|
|
thread::sleep(SERVER_STARTUP_TIME);
|
|
println!("Presumed server started.");
|
|
});
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_rhai_script_timeout() {
|
|
ensure_server_is_running();
|
|
|
|
println!("Connecting to WebSocket server: {}", SERVER_ADDRESS);
|
|
let (mut ws_stream, _response) = connect_async(SERVER_ADDRESS)
|
|
.await
|
|
.expect("Failed to connect to WebSocket server");
|
|
println!("Connected to WebSocket server.");
|
|
|
|
// Rhai script designed to run longer than RHAI_TIMEOUT_SECONDS
|
|
// A large loop should cause a timeout.
|
|
let long_running_script = format!("
|
|
let mut x = 0;
|
|
for i in 0..999999999 {{
|
|
x = x + i;
|
|
if i % 10000000 == 0 {{
|
|
// debug(\"Looping: \" + i); // Optional: for server-side logging if enabled
|
|
}}
|
|
}}
|
|
print(x); // This line will likely not be reached due to timeout
|
|
");
|
|
|
|
let request = JsonRpcRequest {
|
|
jsonrpc: "2.0".to_string(),
|
|
method: "execute_script".to_string(),
|
|
params: ScriptParams { script: long_running_script },
|
|
id: 1,
|
|
};
|
|
|
|
let request_json = serde_json::to_string(&request).expect("Failed to serialize request");
|
|
println!("Sending long-running script request: {}", request_json);
|
|
ws_stream.send(Message::Text(request_json)).await.expect("Failed to send message");
|
|
|
|
println!("Waiting for response (expecting timeout after ~{}s)..", RHAI_TIMEOUT_SECONDS);
|
|
|
|
// Wait for a response, expecting a timeout error
|
|
// The server's timeout is RHAI_TIMEOUT_SECONDS, client should wait a bit longer.
|
|
match tokio::time::timeout(Duration::from_secs(RHAI_TIMEOUT_SECONDS + 15), ws_stream.next()).await {
|
|
Ok(Some(Ok(Message::Text(text)))) => {
|
|
println!("Received response: {}", text);
|
|
let response: Result<JsonRpcErrorResponse, _> = serde_json::from_str(&text);
|
|
match response {
|
|
Ok(err_resp) => {
|
|
assert_eq!(err_resp.error.code, -32002, "Error code should indicate timeout.");
|
|
assert!(err_resp.error.message.contains("timed out"), "Error message should indicate timeout.");
|
|
println!("Timeout test passed! Received correct timeout error.");
|
|
}
|
|
Err(e) => {
|
|
panic!("Failed to deserialize error response: {}. Raw: {}", e, text);
|
|
}
|
|
}
|
|
}
|
|
Ok(Some(Ok(other_msg))) => {
|
|
panic!("Received unexpected message type: {:?}", other_msg);
|
|
}
|
|
Ok(Some(Err(e))) => {
|
|
panic!("WebSocket error: {}", e);
|
|
}
|
|
Ok(None) => {
|
|
panic!("WebSocket stream closed unexpectedly.");
|
|
}
|
|
Err(_) => {
|
|
panic!("Test timed out waiting for server response. Server might not have sent timeout error or took too long.");
|
|
}
|
|
}
|
|
|
|
ws_stream.close(None).await.ok();
|
|
println!("Test finished, WebSocket closed.");
|
|
}
|