diff --git a/crates/cli/tests/admin_expand.rs b/crates/cli/tests/admin_expand.rs index 10eef86..dcd2049 100644 --- a/crates/cli/tests/admin_expand.rs +++ b/crates/cli/tests/admin_expand.rs @@ -1,12 +1,12 @@ #![cfg(not(windows))] -use std::io::{Read, Write}; +use std::io::{ErrorKind, Read, Write}; use std::net::{TcpListener, TcpStream}; use std::path::PathBuf; use std::process::Command; use std::sync::mpsc::{self, Receiver}; use std::thread::{self, JoinHandle}; -use std::time::Duration; +use std::time::{Duration, Instant}; #[derive(Debug)] struct CapturedAdminRequest { @@ -65,11 +65,26 @@ fn start_admin_test_server( response_body: &'static str, ) -> (String, Receiver, JoinHandle<()>) { let listener = TcpListener::bind("127.0.0.1:0").expect("bind admin test server"); + listener + .set_nonblocking(true) + .expect("set admin test server nonblocking"); let endpoint = format!("http://{}", listener.local_addr().expect("server address")); let (sender, receiver) = mpsc::channel(); let handle = thread::spawn(move || { - let (mut stream, _) = listener.accept().expect("accept admin request"); + let deadline = Instant::now() + Duration::from_secs(5); + let (mut stream, _) = loop { + match listener.accept() { + Ok(connection) => break connection, + Err(error) if error.kind() == ErrorKind::WouldBlock => { + if Instant::now() >= deadline { + panic!("timed out waiting for admin request"); + } + thread::sleep(Duration::from_millis(10)); + } + Err(error) => panic!("accept admin request: {error}"), + } + }; let request = read_admin_request(&mut stream); sender.send(request).expect("send captured request"); @@ -128,6 +143,47 @@ fn scale_start_dispatches_to_rebalance_start_with_expansion_json() { handle.join().expect("admin test server finished"); } +#[test] +fn expand_status_dispatches_to_rebalance_status_json() { + let config_dir = tempfile::tempdir().expect("create config dir"); + let (endpoint, receiver, handle) = start_admin_test_server( + r#"{"id":"rebalance-123","pools":[],"stoppedAt":"2026-05-06T00:00:00Z"}"#, + ); + + let output = Command::new(rc_binary()) + .args(["--json", "admin", "expand", "status", "myalias"]) + .env("RC_CONFIG_DIR", config_dir.path()) + .env("RC_HOST_myalias", rc_host_alias(&endpoint)) + .output() + .expect("run rc command"); + + assert!( + output.status.success(), + "stderr: {}", + String::from_utf8_lossy(&output.stderr) + ); + + let stdout = String::from_utf8(output.stdout).expect("stdout should be UTF-8"); + let payload: serde_json::Value = serde_json::from_str(&stdout).expect("JSON output"); + assert_eq!(payload["id"], "rebalance-123"); + assert_eq!(payload["stoppedAt"], "2026-05-06T00:00:00Z"); + assert_eq!( + payload["pools"] + .as_array() + .expect("pools should be an array") + .len(), + 0 + ); + + let request = receiver + .recv_timeout(Duration::from_secs(5)) + .expect("captured admin request"); + assert_eq!(request.method, "GET"); + assert_eq!(request.target, "/rustfs/admin/v3/rebalance/status"); + + handle.join().expect("admin test server finished"); +} + #[test] fn expand_stop_dispatches_to_rebalance_stop_with_expansion_json() { let config_dir = tempfile::tempdir().expect("create config dir");