use crate::mock::{
MockConfig, MockRsyncConfig, clear_mock_overrides, set_mock_enabled_override,
set_mock_rsync_config_override, set_mock_ssh_config_override,
};
use std::sync::atomic::{AtomicUsize, Ordering};
static MOCK_WORKER_COUNTER: AtomicUsize = AtomicUsize::new(0);
#[derive(Debug, Clone)]
pub struct MockWorkerServer {
uri: String,
ssh_config: MockConfig,
rsync_config: MockRsyncConfig,
started: bool,
}
impl MockWorkerServer {
pub fn builder() -> MockWorkerServerBuilder {
MockWorkerServerBuilder::default()
}
pub fn uri(&self) -> &str {
&self.uri
}
pub fn start(&mut self) {
if self.started {
return;
}
set_mock_enabled_override(Some(true));
set_mock_ssh_config_override(Some(self.ssh_config.clone()));
set_mock_rsync_config_override(Some(self.rsync_config.clone()));
self.started = true;
}
pub fn stop(&mut self) {
if !self.started {
return;
}
clear_mock_overrides();
self.started = false;
}
}
impl Drop for MockWorkerServer {
fn drop(&mut self) {
self.stop();
}
}
#[derive(Debug, Clone)]
pub struct MockWorkerServerBuilder {
uri: Option<String>,
ssh_config: MockConfig,
rsync_config: MockRsyncConfig,
}
impl Default for MockWorkerServerBuilder {
fn default() -> Self {
Self {
uri: None,
ssh_config: MockConfig::success(),
rsync_config: MockRsyncConfig::success(),
}
}
}
impl MockWorkerServerBuilder {
pub fn bind(mut self, uri: impl Into<String>) -> Self {
self.uri = Some(normalize_uri(uri.into()));
self
}
pub fn ssh_config(mut self, config: MockConfig) -> Self {
self.ssh_config = config;
self
}
pub fn rsync_config(mut self, config: MockRsyncConfig) -> Self {
self.rsync_config = config;
self
}
pub fn build(self) -> MockWorkerServer {
MockWorkerServer {
uri: self.uri.unwrap_or_else(default_uri),
ssh_config: self.ssh_config,
rsync_config: self.rsync_config,
started: false,
}
}
}
fn default_uri() -> String {
let id = MOCK_WORKER_COUNTER.fetch_add(1, Ordering::SeqCst) + 1;
format!("mock://worker-{}", id)
}
fn normalize_uri(uri: String) -> String {
if uri.starts_with("mock://") {
uri
} else {
format!("mock://{}", uri)
}
}
#[cfg(test)]
#[allow(unsafe_code)]
mod tests {
use super::*;
use crate::mock::{clear_mock_overrides, clear_thread_mock_override, is_mock_enabled};
use std::env;
fn clear_env() {
unsafe { env::remove_var("RCH_MOCK_SSH") };
clear_thread_mock_override();
}
#[test]
fn test_default_uri_prefix_and_uniqueness() {
let server_a = MockWorkerServer::builder().build();
let server_b = MockWorkerServer::builder().build();
assert!(server_a.uri().starts_with("mock://worker-"));
assert!(server_b.uri().starts_with("mock://worker-"));
assert_ne!(server_a.uri(), server_b.uri());
}
#[test]
fn test_bind_normalizes_uri() {
let server = MockWorkerServer::builder().bind("localhost:9900").build();
assert_eq!(server.uri(), "mock://localhost:9900");
}
#[test]
fn test_bind_preserves_mock_prefix() {
let server = MockWorkerServer::builder()
.bind("mock://example:1234")
.build();
assert_eq!(server.uri(), "mock://example:1234");
}
#[test]
fn test_start_stop_toggles_mock_enabled() {
clear_env();
clear_mock_overrides();
let mut server = MockWorkerServer::builder().bind("worker-a").build();
assert!(!is_mock_enabled());
server.start();
assert!(is_mock_enabled());
server.stop();
assert!(!is_mock_enabled());
clear_mock_overrides();
}
#[test]
fn test_mock_worker_server_debug() {
let server = MockWorkerServer::builder().bind("debug-worker").build();
let debug_str = format!("{:?}", server);
assert!(debug_str.contains("MockWorkerServer"));
assert!(debug_str.contains("uri"));
assert!(debug_str.contains("mock://debug-worker"));
}
#[test]
fn test_mock_worker_server_clone() {
let server = MockWorkerServer::builder().bind("clone-worker").build();
let cloned = server.clone();
assert_eq!(cloned.uri(), "mock://clone-worker");
}
#[test]
fn test_builder_debug() {
let builder = MockWorkerServer::builder().bind("builder-debug");
let debug_str = format!("{:?}", builder);
assert!(debug_str.contains("MockWorkerServerBuilder"));
}
#[test]
fn test_builder_clone() {
let builder = MockWorkerServer::builder().bind("builder-clone");
let cloned = builder.clone();
let server = cloned.build();
assert_eq!(server.uri(), "mock://builder-clone");
}
#[test]
fn test_builder_default() {
let builder = MockWorkerServerBuilder::default();
let server = builder.build();
assert!(server.uri().starts_with("mock://worker-"));
}
#[test]
fn test_builder_ssh_config() {
let custom_config = MockConfig::connection_failure();
let server = MockWorkerServer::builder()
.ssh_config(custom_config.clone())
.build();
let debug_str = format!("{:?}", server);
assert!(debug_str.contains("ssh_config"));
}
#[test]
fn test_builder_rsync_config() {
let custom_config = MockRsyncConfig::sync_failure();
let server = MockWorkerServer::builder()
.rsync_config(custom_config.clone())
.build();
let debug_str = format!("{:?}", server);
assert!(debug_str.contains("rsync_config"));
}
#[test]
fn test_builder_method_chaining() {
let server = MockWorkerServer::builder()
.bind("chained-worker")
.ssh_config(MockConfig::success())
.rsync_config(MockRsyncConfig::success())
.build();
assert_eq!(server.uri(), "mock://chained-worker");
}
#[test]
fn test_start_is_idempotent() {
clear_env();
clear_mock_overrides();
let mut server = MockWorkerServer::builder().bind("idempotent-start").build();
server.start();
assert!(is_mock_enabled());
server.start(); assert!(is_mock_enabled());
server.start(); assert!(is_mock_enabled());
server.stop();
clear_mock_overrides();
}
#[test]
fn test_stop_is_idempotent() {
clear_env();
clear_mock_overrides();
let mut server = MockWorkerServer::builder().bind("idempotent-stop").build();
server.stop();
assert!(!is_mock_enabled());
server.stop(); assert!(!is_mock_enabled());
clear_mock_overrides();
}
#[test]
fn test_stop_without_start_is_safe() {
clear_env();
clear_mock_overrides();
let mut server = MockWorkerServer::builder().bind("no-start-stop").build();
server.stop();
assert!(!is_mock_enabled());
clear_mock_overrides();
}
#[test]
fn test_drop_clears_mock_state() {
clear_env();
clear_mock_overrides();
{
let mut server = MockWorkerServer::builder().bind("drop-test").build();
server.start();
assert!(is_mock_enabled());
}
assert!(!is_mock_enabled());
clear_mock_overrides();
}
#[test]
fn test_normalize_uri_without_prefix() {
assert_eq!(
normalize_uri("localhost:9900".to_string()),
"mock://localhost:9900"
);
}
#[test]
fn test_normalize_uri_with_prefix() {
assert_eq!(
normalize_uri("mock://already-prefixed".to_string()),
"mock://already-prefixed"
);
}
#[test]
fn test_normalize_uri_empty() {
assert_eq!(normalize_uri("".to_string()), "mock://");
}
}