mod agents;
mod config;
mod config_doctor;
mod config_init;
mod daemon;
mod helpers;
mod hook;
mod init;
pub mod jobs;
mod queue;
pub mod rabs_gc;
pub(crate) mod shim;
mod speedscore;
mod status;
mod sync;
pub mod types;
pub mod why;
mod workers;
mod workers_deploy;
mod workers_init;
mod workers_setup;
pub use daemon::{
StopOptions, daemon_logs, daemon_reload, daemon_restart, daemon_start, daemon_status,
daemon_stop,
};
pub use hook::{hook_install, hook_status, hook_test, hook_uninstall};
pub use shim::{shim_install, shim_status, shim_uninstall};
pub use status::{admit, check, diagnose, self_test, status_overview};
pub use queue::{cancel_build, queue_status};
pub use workers::{
workers_benchmark_filtered, workers_capabilities, workers_compare, workers_disable,
workers_drain, workers_enable, workers_list, workers_probe,
};
pub use agents::{agents_install_hook, agents_list, agents_status, agents_uninstall_hook};
pub(crate) use config::{apply_config_set, default_config_path};
pub use config::{
config_diff, config_edit, config_export, config_get, config_lint, config_reset, config_set,
config_show, config_validate,
};
pub use config_doctor::{ConfigDoctorResponse, config_doctor};
pub use config_init::config_init;
pub use speedscore::speedscore;
pub use sync::sync_force;
pub use workers_init::{workers_discover, workers_init};
pub use workers_deploy::workers_deploy_binary;
pub use workers_setup::{workers_setup, workers_sync_toolchain};
pub use init::init_wizard;
pub use types::*;
#[cfg(test)]
pub(crate) use helpers::set_test_config_dir_override;
pub use helpers::{config_dir, load_workers_from_config};
#[cfg(not(unix))]
use crate::error::PlatformError;
use anyhow::Result;
use serde::Deserialize;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
#[cfg(unix)]
use tokio::net::UnixStream;
#[derive(Debug, Clone, Deserialize)]
struct DaemonHealthResponse {
status: String,
version: String,
uptime_seconds: u64,
}
#[cfg(not(unix))]
async fn query_daemon_health(_socket_path: &str) -> Result<DaemonHealthResponse> {
Err(PlatformError::UnixOnly {
feature: "daemon health check".to_string(),
})?
}
#[cfg(unix)]
async fn query_daemon_health(socket_path: &str) -> Result<DaemonHealthResponse> {
let stream = UnixStream::connect(socket_path).await?;
let (reader, mut writer) = stream.into_split();
let request = "GET /health\n";
writer.write_all(request.as_bytes()).await?;
writer.flush().await?;
let mut reader = BufReader::new(reader);
let mut line = String::new();
let mut body = String::new();
let mut in_body = false;
loop {
line.clear();
let n = reader.read_line(&mut line).await?;
if n == 0 {
break;
}
if in_body {
body.push_str(&line);
} else if line.trim().is_empty() {
in_body = true;
}
}
let health: DaemonHealthResponse = serde_json::from_str(body.trim())?;
Ok(health)
}
#[cfg(unix)]
pub(crate) use helpers::send_daemon_command_to_socket;
pub(crate) use helpers::{configured_socket_path, send_daemon_command};
#[cfg(test)]
mod tests {
use super::helpers::{
default_socket_path, extract_version_numbers, indent_lines, major_minor_version,
major_version, major_version_mismatch, runtime_label, rust_version_mismatch,
urlencoding_encode,
};
use super::status::{
build_diagnose_decision, build_diagnose_decision_with_config, build_diagnose_slot_estimate,
build_dry_run_summary,
};
use super::workers::{
collect_local_capability_warnings, collect_refresh_warnings, has_any_capabilities,
summarize_capabilities,
};
use super::*;
use crate::status_types::{
WorkerCapabilitiesFromApi, WorkerCapabilitiesRefreshFromApi, format_bytes,
};
use crate::ui::context::{OutputConfig, OutputContext, OutputMode};
use crate::ui::writer::SharedOutputBuffer;
use rch_common::test_guard;
use rch_common::{ApiError, ApiResponse, ErrorCode, WorkerCapabilities, WorkerConfig};
use rch_common::{Classification, CompilationKind, RequiredRuntime, WorkerId};
use rch_common::{
SelectedWorker, SelectionDiagnostics, SelectionReason, WorkerSelectionDiagnostic,
WorkerSelectionDiagnosticDecision,
};
use serde::Serialize;
use std::path::PathBuf;
fn assert_golden_json(
actual: serde_json::Value,
expected: &serde_json::Value,
fixture_path: &str,
) {
if &actual == expected {
return;
}
let actual_json = serde_json::to_string_pretty(&actual).expect("actual JSON renders");
let expected_json = serde_json::to_string_pretty(expected).expect("expected JSON renders");
panic!(
"golden mismatch in {fixture_path}\n\
expected_blake3={}\n\
actual_blake3={}\n\
bless path: review the semantic diff, update the fixture expected_* field, \
and record both hashes in the Beads closeout.\n\
expected:\n{expected_json}\n\
actual:\n{actual_json}",
blake3::hash(expected_json.as_bytes()),
blake3::hash(actual_json.as_bytes())
);
}
struct TestConfigDirGuard;
impl TestConfigDirGuard {
fn new(path: PathBuf) -> Self {
set_test_config_dir_override(Some(path));
Self
}
}
impl Drop for TestConfigDirGuard {
fn drop(&mut self) {
set_test_config_dir_override(None);
}
}
fn json_test_context() -> (OutputContext, SharedOutputBuffer) {
let stdout_buf = SharedOutputBuffer::new();
let stderr_buf = SharedOutputBuffer::new();
let ctx = OutputContext::with_writers(
OutputConfig {
force_mode: Some(OutputMode::Json),
..Default::default()
},
stdout_buf.as_writer(false),
stderr_buf.as_writer(false),
);
(ctx, stdout_buf)
}
#[test]
fn api_response_ok_creates_success_response() {
let _guard = test_guard!();
let response = ApiResponse::ok("test cmd", "test data".to_string());
assert!(response.success);
assert_eq!(response.command, Some("test cmd".to_string()));
assert_eq!(response.api_version, "1.0");
assert!(response.data.is_some());
assert_eq!(response.data.unwrap(), "test data");
assert!(response.error.is_none());
}
#[test]
fn api_response_err_creates_error_response() {
let _guard = test_guard!();
let response: ApiResponse<()> =
ApiResponse::err("failed cmd", ApiError::internal("error message"));
assert!(!response.success);
assert_eq!(response.command, Some("failed cmd".to_string()));
assert!(response.data.is_none());
assert!(response.error.is_some());
let error = response.error.unwrap();
assert_eq!(error.code, "RCH-E504"); }
#[test]
fn api_response_err_with_specific_error_code() {
let _guard = test_guard!();
let response: ApiResponse<()> = ApiResponse::err(
"cmd",
ApiError::new(ErrorCode::SshConnectionFailed, "Worker not available"),
);
assert!(!response.success);
assert!(response.error.is_some());
let error = response.error.unwrap();
assert_eq!(error.code, "RCH-E100"); }
#[test]
fn api_response_ok_serializes_without_error_field() {
let _guard = test_guard!();
let response = ApiResponse::ok("test", "data".to_string());
let json = serde_json::to_value(&response).unwrap();
assert!(json.get("error").is_none());
assert_eq!(json["data"], "data");
assert!(json["success"].as_bool().unwrap());
}
#[test]
fn api_response_err_serializes_without_data_field() {
let _guard = test_guard!();
let response: ApiResponse<String> =
ApiResponse::err("test", ApiError::internal("error msg"));
let json = serde_json::to_value(&response).unwrap();
assert!(json.get("data").is_none());
assert!(json.get("error").is_some());
assert!(!json["success"].as_bool().unwrap());
}
#[test]
fn api_response_with_complex_data_serializes() {
let _guard = test_guard!();
#[derive(Serialize)]
struct ComplexData {
name: String,
count: u32,
items: Vec<String>,
}
let data = ComplexData {
name: "test".to_string(),
count: 3,
items: vec!["a".to_string(), "b".to_string()],
};
let response = ApiResponse::ok("complex", data);
let json = serde_json::to_value(&response).unwrap();
assert_eq!(json["data"]["name"], "test");
assert_eq!(json["data"]["count"], 3);
assert_eq!(json["data"]["items"].as_array().unwrap().len(), 2);
}
#[test]
fn config_init_simple_json_creates_files() {
let _guard = test_guard!();
let temp_dir = tempfile::tempdir().expect("temp dir should be creatable");
let _cfg_guard = TestConfigDirGuard::new(temp_dir.path().to_path_buf());
let (ctx, stdout_buf) = json_test_context();
config_init(&ctx, false, true).expect("config init should succeed");
assert!(temp_dir.path().join("config.toml").exists());
assert!(temp_dir.path().join("workers.toml").exists());
let value: serde_json::Value =
serde_json::from_str(&stdout_buf.to_string_lossy()).expect("json output");
assert_eq!(value["success"], true);
assert_eq!(value["command"], "config init");
assert_eq!(value["data"]["created"].as_array().unwrap().len(), 2);
assert_eq!(
value["data"]["already_existed"].as_array().unwrap().len(),
0
);
}
#[test]
fn config_init_simple_json_reports_existing_files() {
let _guard = test_guard!();
let temp_dir = tempfile::tempdir().expect("temp dir should be creatable");
let _cfg_guard = TestConfigDirGuard::new(temp_dir.path().to_path_buf());
let (ctx1, _stdout_buf1) = json_test_context();
config_init(&ctx1, false, true).expect("first config init should succeed");
let (ctx2, stdout_buf2) = json_test_context();
config_init(&ctx2, false, true).expect("second config init should succeed");
let value: serde_json::Value =
serde_json::from_str(&stdout_buf2.to_string_lossy()).expect("json output");
assert_eq!(value["success"], true);
assert_eq!(value["command"], "config init");
assert_eq!(value["data"]["created"].as_array().unwrap().len(), 0);
assert_eq!(
value["data"]["already_existed"].as_array().unwrap().len(),
2
);
}
#[tokio::test(flavor = "current_thread")]
async fn workers_list_json_uses_config_override() {
let _guard = test_guard!();
let temp_dir = tempfile::tempdir().expect("temp dir should be creatable");
let _cfg_guard = TestConfigDirGuard::new(temp_dir.path().to_path_buf());
let (ctx, _stdout_buf) = json_test_context();
config_init(&ctx, false, true).expect("config init should succeed");
let (ctx2, stdout_buf2) = json_test_context();
workers_list(false, &ctx2)
.await
.expect("workers list should succeed");
let value: serde_json::Value =
serde_json::from_str(&stdout_buf2.to_string_lossy()).expect("json output");
assert_eq!(value["success"], true);
assert_eq!(value["command"], "workers list");
assert_eq!(value["data"]["count"], 1);
assert_eq!(value["data"]["workers"][0]["id"], "worker1");
}
#[test]
fn config_validate_json_success_with_example_files() {
let _guard = test_guard!();
let temp_dir = tempfile::tempdir().expect("temp dir should be creatable");
let _cfg_guard = TestConfigDirGuard::new(temp_dir.path().to_path_buf());
let (ctx1, _stdout_buf1) = json_test_context();
config_init(&ctx1, false, true).expect("config init should succeed");
let (ctx2, stdout_buf2) = json_test_context();
if std::path::PathBuf::from(shellexpand::tilde("~/.ssh/id_rsa").to_string()).exists() {
config_validate(&ctx2).expect("config validate should succeed");
let value: serde_json::Value =
serde_json::from_str(&stdout_buf2.to_string_lossy()).expect("json output");
assert_eq!(value["success"], true);
assert_eq!(value["command"], "config validate");
assert_eq!(value["data"]["valid"], true);
assert_eq!(value["data"]["errors"].as_array().unwrap().len(), 0);
}
}
#[test]
fn worker_info_from_worker_config_converts_all_fields() {
let _guard = test_guard!();
let config = WorkerConfig {
id: WorkerId::new("test-worker"),
host: "192.168.1.100".to_string(),
user: "admin".to_string(),
identity_file: "~/.ssh/key.pem".to_string(),
total_slots: 16,
priority: 50,
tags: vec!["fast".to_string(), "ssd".to_string()],
tools: Vec::new(),
};
let info = WorkerInfo::from(&config);
assert_eq!(info.id, "test-worker");
assert_eq!(info.host, "192.168.1.100");
assert_eq!(info.user, "admin");
assert_eq!(info.total_slots, 16);
assert_eq!(info.priority, 50);
assert_eq!(info.tags, vec!["fast", "ssd"]);
}
#[test]
fn worker_info_from_worker_config_with_empty_tags() {
let _guard = test_guard!();
let config = WorkerConfig {
id: WorkerId::new("minimal"),
host: "host.example.com".to_string(),
user: "ubuntu".to_string(),
identity_file: "~/.ssh/id_rsa".to_string(),
total_slots: 8,
priority: 100,
tags: vec![],
tools: Vec::new(),
};
let info = WorkerInfo::from(&config);
assert!(info.tags.is_empty());
}
#[test]
fn worker_info_serializes_correctly() {
let _guard = test_guard!();
let config = WorkerConfig {
id: WorkerId::new("w1"),
host: "host".to_string(),
user: "user".to_string(),
identity_file: "key".to_string(),
total_slots: 4,
priority: 75,
tags: vec!["gpu".to_string()],
tools: Vec::new(),
};
let info = WorkerInfo::from(&config);
let json = serde_json::to_value(&info).unwrap();
assert_eq!(json["id"], "w1");
assert_eq!(json["host"], "host");
assert_eq!(json["total_slots"], 4);
assert_eq!(json["tags"].as_array().unwrap().len(), 1);
}
#[test]
fn workers_list_response_serializes() {
let _guard = test_guard!();
let response = WorkersListResponse {
workers: vec![],
count: 0,
};
let json = serde_json::to_value(&response).unwrap();
assert_eq!(json["count"], 0);
assert!(json["workers"].as_array().unwrap().is_empty());
}
#[test]
fn workers_list_response_with_workers_serializes() {
let _guard = test_guard!();
let workers = vec![
WorkerInfo {
id: "w1".to_string(),
host: "host1".to_string(),
user: "u1".to_string(),
total_slots: 8,
priority: 100,
tags: vec![],
speedscore: None,
disk: Default::default(),
},
WorkerInfo {
id: "w2".to_string(),
host: "host2".to_string(),
user: "u2".to_string(),
total_slots: 16,
priority: 50,
tags: vec!["fast".to_string()],
speedscore: Some(85.5),
disk: Default::default(),
},
];
let response = WorkersListResponse { workers, count: 2 };
let json = serde_json::to_value(&response).unwrap();
assert_eq!(json["count"], 2);
let workers_arr = json["workers"].as_array().unwrap();
assert_eq!(workers_arr.len(), 2);
assert_eq!(workers_arr[0]["id"], "w1");
assert_eq!(workers_arr[1]["id"], "w2");
}
#[test]
fn worker_probe_result_success_serializes() {
let _guard = test_guard!();
let result = WorkerProbeResult {
id: "worker1".to_string(),
host: "192.168.1.1".to_string(),
status: "healthy".to_string(),
latency_ms: Some(42),
error: None,
error_code: None,
capabilities: None,
missing_components: Vec::new(),
disk: Default::default(),
};
let json = serde_json::to_value(&result).unwrap();
assert_eq!(json["id"], "worker1");
assert_eq!(json["status"], "healthy");
assert_eq!(json["latency_ms"], 42);
assert!(json.get("error").is_none()); assert!(json.get("error_code").is_none()); }
#[test]
fn worker_probe_result_failure_serializes() {
let _guard = test_guard!();
let result = WorkerProbeResult {
id: "worker2".to_string(),
host: "192.168.1.2".to_string(),
status: "unreachable".to_string(),
latency_ms: None,
error: Some("Connection refused".to_string()),
error_code: Some("RCH-E108".to_string()),
capabilities: None,
missing_components: Vec::new(),
disk: Default::default(),
};
let json = serde_json::to_value(&result).unwrap();
assert_eq!(json["status"], "unreachable");
assert!(json.get("latency_ms").is_none()); assert_eq!(json["error"], "Connection refused");
assert_eq!(json["error_code"], "RCH-E108");
}
#[test]
fn daemon_status_response_running_serializes() {
let _guard = test_guard!();
let response = DaemonStatusResponse {
running: true,
socket_path: "/tmp/rch.sock".to_string(),
uptime_seconds: Some(3600),
};
let json = serde_json::to_value(&response).unwrap();
assert!(json["running"].as_bool().unwrap());
assert_eq!(json["socket_path"], "/tmp/rch.sock");
assert_eq!(json["uptime_seconds"], 3600);
}
#[test]
fn daemon_status_response_not_running_serializes() {
let _guard = test_guard!();
let response = DaemonStatusResponse {
running: false,
socket_path: "/tmp/rch.sock".to_string(),
uptime_seconds: None,
};
let json = serde_json::to_value(&response).unwrap();
assert!(!json["running"].as_bool().unwrap());
assert!(json.get("uptime_seconds").is_none());
}
#[test]
fn system_overview_serializes() {
let _guard = test_guard!();
let response = SystemOverview {
daemon_running: true,
hook_installed: true,
workers_count: 3,
workers: None,
};
let json = serde_json::to_value(&response).unwrap();
assert!(json["daemon_running"].as_bool().unwrap());
assert!(json["hook_installed"].as_bool().unwrap());
assert_eq!(json["workers_count"], 3);
assert!(json.get("workers").is_none());
}
#[test]
fn config_show_response_serializes() {
let _guard = test_guard!();
let response = ConfigShowResponse {
general: ConfigGeneralSection {
enabled: true,
force_local: false,
force_remote: false,
log_level: "info".to_string(),
socket_path: "/tmp/rch.sock".to_string(),
},
compilation: ConfigCompilationSection {
confidence_threshold: 0.85,
min_local_time_ms: 2000,
remote_speedup_threshold: 1.2,
local_slots: 0,
build_slots: 4,
test_slots: 8,
check_slots: 2,
build_timeout_sec: 300,
test_timeout_sec: 1800,
bun_timeout_sec: 600,
external_timeout_enabled: true,
allow_local_fallback: true,
remote_build_jobs: "auto".to_string(),
},
transfer: ConfigTransferSection {
compression_level: 3,
exclude_patterns: vec!["target/".to_string()],
remote_base: "/tmp/rch".to_string(),
max_transfer_mb: None,
max_transfer_time_ms: None,
sync_timeout_ms: None,
bwlimit_kbps: None,
estimated_bandwidth_bps: None,
rsync_bin: None,
adaptive_compression: false,
min_compression_level: 1,
max_compression_level: 19,
verify_artifacts: false,
verify_max_size_bytes: 100 * 1024 * 1024,
},
environment: ConfigEnvironmentSection {
allowlist: vec!["RUSTFLAGS".to_string()],
},
circuit: ConfigCircuitSection {
failure_threshold: 3,
success_threshold: 2,
error_rate_threshold: 0.5,
window_secs: 60,
open_cooldown_secs: 30,
half_open_max_probes: 2,
},
output: ConfigOutputSection {
visibility: rch_common::OutputVisibility::None,
first_run_complete: false,
},
self_healing: ConfigSelfHealingSection {
hook_starts_daemon: true,
daemon_installs_hooks: true,
auto_start_cooldown_secs: 30,
auto_start_timeout_secs: 3,
},
sources: vec!["~/.config/rch/config.toml".to_string()],
value_sources: None,
};
let json = serde_json::to_value(&response).unwrap();
assert!(json["general"]["enabled"].as_bool().unwrap());
assert_eq!(json["compilation"]["confidence_threshold"], 0.85);
assert_eq!(json["compilation"]["build_timeout_sec"], 300);
assert_eq!(json["compilation"]["external_timeout_enabled"], true);
assert_eq!(json["transfer"]["compression_level"], 3);
assert_eq!(json["circuit"]["failure_threshold"], 3);
}
#[test]
fn config_init_response_serializes() {
let _guard = test_guard!();
let response = ConfigInitResponse {
created: vec!["config.toml".to_string()],
already_existed: vec!["workers.toml".to_string()],
};
let json = serde_json::to_value(&response).unwrap();
assert_eq!(json["created"].as_array().unwrap().len(), 1);
assert_eq!(json["already_existed"].as_array().unwrap().len(), 1);
}
#[test]
fn config_validation_response_valid_serializes() {
let _guard = test_guard!();
let response = ConfigValidationResponse {
errors: vec![],
warnings: vec![],
valid: true,
};
let json = serde_json::to_value(&response).unwrap();
assert!(json["valid"].as_bool().unwrap());
assert!(json["errors"].as_array().unwrap().is_empty());
}
#[test]
fn config_validation_response_with_issues_serializes() {
let _guard = test_guard!();
let response = ConfigValidationResponse {
errors: vec![ConfigValidationIssue {
file: "config.toml".to_string(),
message: "Invalid syntax".to_string(),
}],
warnings: vec![ConfigValidationIssue {
file: "workers.toml".to_string(),
message: "Deprecated field".to_string(),
}],
valid: false,
};
let json = serde_json::to_value(&response).unwrap();
assert!(!json["valid"].as_bool().unwrap());
assert_eq!(json["errors"][0]["message"], "Invalid syntax");
assert_eq!(json["warnings"][0]["file"], "workers.toml");
}
#[test]
fn config_set_response_serializes() {
let _guard = test_guard!();
let response = ConfigSetResponse {
key: "general.log_level".to_string(),
value: "debug".to_string(),
config_path: "/home/user/.config/rch/config.toml".to_string(),
};
let json = serde_json::to_value(&response).unwrap();
assert_eq!(json["key"], "general.log_level");
assert_eq!(json["value"], "debug");
}
#[test]
fn config_reset_response_serializes() {
let _guard = test_guard!();
let response = ConfigResetResponse {
key: "general.enabled".to_string(),
value: "true".to_string(),
config_path: "/home/user/.config/rch/config.toml".to_string(),
};
let json = serde_json::to_value(&response).unwrap();
assert_eq!(json["key"], "general.enabled");
assert_eq!(json["value"], "true");
}
#[test]
fn config_export_response_serializes() {
let _guard = test_guard!();
let response = ConfigExportResponse {
format: "toml".to_string(),
content: "[general]\nenabled = true".to_string(),
};
let json = serde_json::to_value(&response).unwrap();
assert_eq!(json["format"], "toml");
assert!(json["content"].as_str().unwrap().contains("enabled"));
}
#[test]
fn diagnose_decision_intercepts_when_confident() {
let _guard = test_guard!();
let classification =
Classification::compilation(CompilationKind::CargoBuild, 0.95, "cargo build");
let decision = build_diagnose_decision(&classification, 0.85);
assert!(decision.would_intercept);
assert!(decision.reason.contains(">=") || decision.reason.contains("threshold"));
}
#[test]
fn diagnose_decision_applies_config_gates_like_the_hook() {
let _guard = test_guard!();
let go_test = Classification::compilation(CompilationKind::GoTest, 0.95, "go test");
let cargo_build =
Classification::compilation(CompilationKind::CargoBuild, 0.95, "cargo build");
let mut config = rch_common::RchConfig::default();
assert!(build_diagnose_decision_with_config(&go_test, &config).would_intercept);
config.execution.allowlist = vec!["cargo".to_string()];
let decision = build_diagnose_decision_with_config(&go_test, &config);
assert!(!decision.would_intercept);
assert!(
decision.reason.contains("execution.allowlist"),
"reason must name the gate: {}",
decision.reason
);
assert!(build_diagnose_decision_with_config(&cargo_build, &config).would_intercept);
config.general.force_local = true;
let decision = build_diagnose_decision_with_config(&cargo_build, &config);
assert!(!decision.would_intercept);
assert!(decision.reason.contains("force_local"));
config.general.force_local = false;
config.general.enabled = false;
assert!(!build_diagnose_decision_with_config(&cargo_build, &config).would_intercept);
config.general.enabled = true;
config.compilation.confidence_threshold = 0.99;
assert!(!build_diagnose_decision_with_config(&cargo_build, &config).would_intercept);
config.general.force_remote = true;
assert!(build_diagnose_decision_with_config(&cargo_build, &config).would_intercept);
let not_build = Classification::not_compilation("ls");
let decision = build_diagnose_decision_with_config(¬_build, &config);
assert!(!decision.would_intercept);
assert!(decision.reason.contains("not classified"));
}
#[test]
fn diagnose_decision_rejects_when_below_threshold() {
let _guard = test_guard!();
let classification =
Classification::compilation(CompilationKind::CargoCheck, 0.80, "cargo check");
let decision = build_diagnose_decision(&classification, 0.85);
assert!(!decision.would_intercept);
assert!(decision.reason.contains("below threshold"));
}
#[test]
fn diagnose_response_serializes() {
let _guard = test_guard!();
let classification =
Classification::compilation(CompilationKind::CargoBuild, 0.95, "cargo build");
let response = DiagnoseResponse {
classification,
tiers: Vec::new(),
command: "cargo build".to_string(),
normalized_command: "cargo build".to_string(),
decision: DiagnoseDecision {
would_intercept: true,
reason: "meets confidence threshold".to_string(),
},
threshold: DiagnoseThreshold {
value: 0.85,
source: "default".to_string(),
},
daemon: DiagnoseDaemonStatus {
socket_path: "/tmp/rch.sock".to_string(),
socket_exists: false,
reachable: false,
status: None,
version: None,
uptime_seconds: None,
error: Some("daemon socket not found".to_string()),
},
required_runtime: RequiredRuntime::Rust,
local_capabilities: None,
capabilities_warnings: Vec::new(),
worker_selection: None,
placement: rch_common::resolve_placement(|_| None),
speed_gate: None,
dry_run: None,
};
let json = serde_json::to_value(&response).unwrap();
assert_eq!(json["command"], "cargo build");
assert_eq!(json["classification"]["confidence"], 0.95);
assert_eq!(json["threshold"]["value"], 0.85);
}
#[test]
fn diagnose_slot_estimate_honors_cargo_test_jobs() {
let _guard = test_guard!();
let config = rch_common::CompilationConfig {
build_slots: 6,
test_slots: 10,
check_slots: 3,
..Default::default()
};
let (estimated_cores, cargo_jobs) = build_diagnose_slot_estimate(
Some(CompilationKind::CargoTest),
"cargo test -j 1 -p frankenterm-core-replay-types -- --nocapture",
&config,
);
assert_eq!(estimated_cores, 1);
assert_eq!(cargo_jobs, Some(1));
}
#[test]
fn diagnose_worker_selection_serializes_cargo_jobs() {
let _guard = test_guard!();
let worker_selection = DiagnoseWorkerSelection {
estimated_cores: 1,
cargo_jobs: Some(1),
worker: None,
reason: SelectionReason::NoAdmissibleWorkers("insufficient_slots=1".to_string()),
diagnostics: None,
};
let json = serde_json::to_value(&worker_selection).unwrap();
assert_eq!(json["estimated_cores"], 1);
assert_eq!(json["cargo_jobs"], 1);
}
#[test]
fn workers_capabilities_report_serializes_with_local() {
let _guard = test_guard!();
let report = WorkersCapabilitiesReport {
workers: vec![],
local: Some(WorkerCapabilities {
rustc_version: Some("rustc 1.87.0-nightly".to_string()),
bun_version: None,
node_version: None,
npm_version: None,
..Default::default()
}),
required_runtime: Some(RequiredRuntime::Rust),
warnings: vec!["warn".to_string()],
};
let json = serde_json::to_value(&report).unwrap();
assert_eq!(json["local"]["rustc_version"], "rustc 1.87.0-nightly");
assert_eq!(json["required_runtime"], "rust");
assert!(json["warnings"].is_array());
}
#[test]
fn local_capability_warnings_include_missing_and_mismatch() {
let _guard = test_guard!();
let local = WorkerCapabilities {
rustc_version: Some("rustc 1.87.0-nightly".to_string()),
bun_version: None,
node_version: None,
npm_version: None,
..Default::default()
};
let workers = vec![
WorkerCapabilitiesFromApi {
id: "w-missing".to_string(),
host: "host".to_string(),
user: "user".to_string(),
capabilities: WorkerCapabilities::new(),
refresh: None,
},
WorkerCapabilitiesFromApi {
id: "w-old".to_string(),
host: "host".to_string(),
user: "user".to_string(),
capabilities: WorkerCapabilities {
rustc_version: Some("rustc 1.86.0-nightly".to_string()),
bun_version: None,
node_version: None,
npm_version: None,
..Default::default()
},
refresh: None,
},
];
let warnings = collect_local_capability_warnings(&workers, &local);
assert!(warnings.iter().any(|w| w.contains("missing Rust runtime")));
assert!(warnings.iter().any(|w| w.contains("Rust version mismatch")));
}
#[test]
fn refresh_warnings_include_cached_capability_snapshots() {
let _guard = test_guard!();
let workers = vec![WorkerCapabilitiesFromApi {
id: "slow-worker".to_string(),
host: "host".to_string(),
user: "user".to_string(),
capabilities: WorkerCapabilities::new(),
refresh: Some(WorkerCapabilitiesRefreshFromApi {
attempted: true,
live: false,
source: "cached_after_probe_failure".to_string(),
message: Some("probe timed out".to_string()),
}),
}];
let warnings = collect_refresh_warnings(&workers);
assert_eq!(warnings.len(), 1);
assert!(warnings[0].contains("slow-worker"));
assert!(warnings[0].contains("probe timed out"));
}
#[test]
fn hook_action_response_success_serializes() {
let _guard = test_guard!();
let response = HookActionResponse {
action: "install".to_string(),
success: true,
settings_path: "~/.config/claude-code/settings.json".to_string(),
message: Some("Hook installed successfully".to_string()),
};
let json = serde_json::to_value(&response).unwrap();
assert_eq!(json["action"], "install");
assert!(json["success"].as_bool().unwrap());
assert_eq!(json["message"], "Hook installed successfully");
}
#[test]
fn hook_action_response_without_message_serializes() {
let _guard = test_guard!();
let response = HookActionResponse {
action: "uninstall".to_string(),
success: true,
settings_path: "path".to_string(),
message: None,
};
let json = serde_json::to_value(&response).unwrap();
assert!(json.get("message").is_none());
}
#[test]
fn hook_test_response_serializes() {
let _guard = test_guard!();
let response = HookTestResponse {
classification_tests: vec![ClassificationTestResult {
command: "cargo build".to_string(),
is_compilation: true,
confidence: 0.95,
expected_intercept: true,
passed: true,
}],
daemon_connected: true,
daemon_response: Some("OK".to_string()),
workers_configured: 2,
workers: vec![],
};
let json = serde_json::to_value(&response).unwrap();
assert!(json["daemon_connected"].as_bool().unwrap());
assert_eq!(json["workers_configured"], 2);
let tests = json["classification_tests"].as_array().unwrap();
assert_eq!(tests[0]["command"], "cargo build");
assert!(tests[0]["passed"].as_bool().unwrap());
}
#[test]
fn classification_test_result_serializes() {
let _guard = test_guard!();
let result = ClassificationTestResult {
command: "bun test".to_string(),
is_compilation: true,
confidence: 0.92,
expected_intercept: true,
passed: true,
};
let json = serde_json::to_value(&result).unwrap();
assert_eq!(json["command"], "bun test");
assert!(json["is_compilation"].as_bool().unwrap());
assert_eq!(json["confidence"], 0.92);
}
#[test]
fn error_code_codes_have_rch_prefix() {
let _guard = test_guard!();
assert_eq!(ErrorCode::ConfigNotFound.code_string(), "RCH-E001");
assert_eq!(ErrorCode::ConfigValidationError.code_string(), "RCH-E004");
assert_eq!(ErrorCode::ConfigInvalidWorker.code_string(), "RCH-E008");
assert_eq!(ErrorCode::SshConnectionFailed.code_string(), "RCH-E100");
assert_eq!(
ErrorCode::InternalDaemonNotRunning.code_string(),
"RCH-E502"
);
assert_eq!(ErrorCode::InternalStateError.code_string(), "RCH-E504");
}
#[test]
fn config_dir_returns_some() {
let _guard = test_guard!();
let dir = config_dir();
if let Some(path) = dir {
assert!(path.to_string_lossy().contains("rch"));
}
}
#[test]
fn api_version_is_expected_value() {
let _guard = test_guard!();
assert_eq!(rch_common::API_VERSION, "1.0");
}
#[test]
fn default_socket_path_returns_valid_path() {
let _guard = test_guard!();
let path = default_socket_path();
assert!(
path.ends_with("rch.sock"),
"Socket path should end with rch.sock, got: {}",
path
);
assert!(
path.starts_with('/'),
"Socket path should be absolute, got: {}",
path
);
}
#[test]
fn dry_run_summary_not_intercepted() {
let _guard = test_guard!();
let summary = build_dry_run_summary(false, "not a compilation command", &None, false);
assert!(!summary.would_offload);
assert_eq!(summary.reason, "not a compilation command");
assert_eq!(summary.pipeline_steps.len(), 1);
assert_eq!(summary.pipeline_steps[0].name, "Local execution");
assert!(!summary.pipeline_steps[0].skipped);
}
#[test]
fn dry_run_summary_intercepted_no_daemon() {
let _guard = test_guard!();
let summary = build_dry_run_summary(
true,
"meets confidence threshold",
&None,
false, );
assert!(!summary.would_offload);
assert!(summary.reason.contains("daemon is not reachable"));
assert_eq!(summary.pipeline_steps.len(), 6);
assert!(!summary.pipeline_steps[0].skipped);
assert_eq!(summary.pipeline_steps[0].name, "Classification");
assert!(summary.pipeline_steps[1].skipped);
assert_eq!(summary.pipeline_steps[1].name, "Daemon query");
assert!(summary.pipeline_steps[2].skipped);
assert!(summary.pipeline_steps[3].skipped);
assert!(summary.pipeline_steps[4].skipped);
assert!(summary.pipeline_steps[5].skipped);
for step in &summary.pipeline_steps[2..] {
assert_eq!(
step.skip_reason.as_deref(),
Some("Daemon not reachable"),
"skipped step {} should explain the daemon gate",
step.name
);
}
}
#[test]
fn dry_run_summary_intercepted_with_worker() {
let _guard = test_guard!();
let worker = SelectedWorker {
id: WorkerId::new("test-worker"),
host: "worker.example.com".to_string(),
user: "rch".to_string(),
identity_file: "~/.ssh/id_rsa".to_string(),
slots_available: 4,
speed_score: 1.5,
declared_os: None,
};
let worker_selection = DiagnoseWorkerSelection {
estimated_cores: 4,
cargo_jobs: None,
worker: Some(worker),
reason: SelectionReason::Success,
diagnostics: None,
};
let summary = build_dry_run_summary(
true,
"meets confidence threshold",
&Some(worker_selection),
true, );
assert!(summary.would_offload);
assert!(summary.reason.contains("worker available"));
assert_eq!(summary.pipeline_steps.len(), 6);
for step in &summary.pipeline_steps {
assert!(!step.skipped, "Step {} should not be skipped", step.name);
}
}
#[test]
fn dry_run_summary_intercepted_without_worker_reports_no_offload() {
let _guard = test_guard!();
let worker_selection = DiagnoseWorkerSelection {
estimated_cores: 4,
cargo_jobs: None,
worker: None,
reason: SelectionReason::NoAdmissibleWorkers("critical_pressure=1".to_string()),
diagnostics: None,
};
let summary = build_dry_run_summary(
true,
"meets confidence threshold",
&Some(worker_selection),
true,
);
assert!(!summary.would_offload);
assert!(
summary
.reason
.contains("no admissible workers: critical_pressure=1")
);
assert_eq!(summary.pipeline_steps.len(), 6);
assert!(!summary.pipeline_steps[0].skipped);
assert!(!summary.pipeline_steps[1].skipped);
assert!(summary.pipeline_steps[2].skipped);
assert_eq!(
summary.pipeline_steps[2].skip_reason.as_deref(),
Some("no admissible workers: critical_pressure=1")
);
assert!(summary.pipeline_steps[3].skipped);
assert_eq!(
summary.pipeline_steps[3].skip_reason.as_deref(),
Some("no admissible workers: critical_pressure=1")
);
assert!(summary.pipeline_steps[4].skipped);
assert_eq!(
summary.pipeline_steps[4].skip_reason.as_deref(),
Some("no admissible workers: critical_pressure=1")
);
assert!(summary.pipeline_steps[5].skipped);
assert_eq!(
summary.pipeline_steps[5].skip_reason.as_deref(),
Some("no admissible workers: critical_pressure=1")
);
let fixture_path = "../../../tests/goldens/ft_4tp7g/dry_run_summary_no_worker.json";
let fixture: serde_json::Value = serde_json::from_str(include_str!(
"../../../tests/goldens/ft_4tp7g/dry_run_summary_no_worker.json"
))
.expect("dry-run summary no-worker golden fixture parses");
assert_eq!(fixture["schema_version"], "rch.golden.dry_run_summary.v1");
assert_golden_json(
serde_json::to_value(&summary).expect("dry-run summary serializes"),
fixture
.get("expected_summary")
.expect("golden fixture has expected_summary"),
fixture_path,
);
}
#[test]
fn diagnose_dry_run_response_golden_no_worker() {
let _guard = test_guard!();
let worker_selection = DiagnoseWorkerSelection {
estimated_cores: 4,
cargo_jobs: None,
worker: None,
reason: SelectionReason::NoAdmissibleWorkers("critical_pressure=1".to_string()),
diagnostics: Some(SelectionDiagnostics {
required_runtime: RequiredRuntime::Rust,
estimated_cores: 4,
min_success_rate: 0.8,
fallback_min_success_rate: 0.5,
active_project_exclusion_count: 0,
workers: vec![WorkerSelectionDiagnostic {
worker_id: WorkerId::new("fixture-worker"),
status: "healthy".to_string(),
circuit_state: "closed".to_string(),
pressure_state: "critical".to_string(),
pressure_reason_code: "disk_ratio_below_critical".to_string(),
success_rate: Some(0.95),
min_success_rate: 0.8,
fallback_min_success_rate: 0.5,
required_runtime: RequiredRuntime::Rust,
runtime_available: true,
available_slots: 8,
total_slots: 8,
estimated_cores: 4,
active_project_excluded: false,
final_decision: WorkerSelectionDiagnosticDecision::Deny,
final_reason: "critical pressure: disk_ratio_below_critical".to_string(),
reason_codes: vec!["pressure.critical".to_string()],
}],
}),
};
let dry_run = build_dry_run_summary(
true,
"Compilation command with confidence 0.95 >= threshold 0.85",
&Some(worker_selection.clone()),
true,
);
let response = DiagnoseResponse {
classification: Classification::compilation(
CompilationKind::CargoTest,
0.95,
"cargo test",
),
tiers: Vec::new(),
command: "cargo test -p frankenterm-core --lib agent_mail -- --nocapture".to_string(),
normalized_command: "cargo test -p frankenterm-core --lib agent_mail -- --nocapture"
.to_string(),
decision: DiagnoseDecision {
would_intercept: true,
reason: "Compilation command with confidence 0.95 >= threshold 0.85".to_string(),
},
threshold: DiagnoseThreshold {
value: 0.85,
source: "fixture".to_string(),
},
daemon: DiagnoseDaemonStatus {
socket_path: "/tmp/rch.sock".to_string(),
socket_exists: true,
reachable: true,
status: Some("healthy".to_string()),
version: Some("1.0.26".to_string()),
uptime_seconds: Some(123),
error: None,
},
required_runtime: RequiredRuntime::Rust,
local_capabilities: None,
capabilities_warnings: Vec::new(),
worker_selection: Some(worker_selection),
placement: rch_common::resolve_placement(|_| None),
speed_gate: None,
dry_run: Some(dry_run),
};
let fixture_path = "../../../tests/goldens/ft_4tp7g/diagnose_dry_run_no_worker.json";
let fixture: serde_json::Value = serde_json::from_str(include_str!(
"../../../tests/goldens/ft_4tp7g/diagnose_dry_run_no_worker.json"
))
.expect("diagnose dry-run no-worker golden fixture parses");
assert_eq!(fixture["schema_version"], "rch.golden.diagnose_dry_run.v1");
assert_golden_json(
serde_json::to_value(&response).expect("diagnose response serializes"),
fixture
.get("expected_response")
.expect("golden fixture has expected_response"),
fixture_path,
);
}
#[test]
fn dry_run_pipeline_step_serializes() {
let _guard = test_guard!();
let step = DryRunPipelineStep {
step: 1,
name: "classify".to_string(),
description: "Analyze command".to_string(),
skipped: false,
skip_reason: None,
estimated_duration_ms: Some(2),
};
let json = serde_json::to_value(&step).unwrap();
assert_eq!(json["step"], 1);
assert_eq!(json["name"], "classify");
assert_eq!(json["skipped"], false);
assert_eq!(json["estimated_duration_ms"], 2);
assert!(json.get("skip_reason").is_none());
}
#[test]
fn dry_run_pipeline_step_skipped_serializes() {
let _guard = test_guard!();
let step = DryRunPipelineStep {
step: 2,
name: "select".to_string(),
description: "Select worker".to_string(),
skipped: true,
skip_reason: Some("daemon not reachable".to_string()),
estimated_duration_ms: None,
};
let json = serde_json::to_value(&step).unwrap();
assert_eq!(json["skipped"], true);
assert_eq!(json["skip_reason"], "daemon not reachable");
assert!(json.get("estimated_duration_ms").is_none());
}
#[test]
fn dry_run_transfer_estimate_serializes() {
let _guard = test_guard!();
let estimate = DryRunTransferEstimate {
bytes: 1024 * 1024 * 10, human_size: "10.00 MB".to_string(),
files: 150,
estimated_time_ms: 2500,
would_skip: false,
skip_reason: None,
};
let json = serde_json::to_value(&estimate).unwrap();
assert_eq!(json["bytes"], 10485760);
assert_eq!(json["human_size"], "10.00 MB");
assert_eq!(json["files"], 150);
assert_eq!(json["estimated_time_ms"], 2500);
assert_eq!(json["would_skip"], false);
}
#[test]
fn dry_run_transfer_estimate_would_skip_serializes() {
let _guard = test_guard!();
let estimate = DryRunTransferEstimate {
bytes: 1024 * 1024 * 500, human_size: "500.00 MB".to_string(),
files: 5000,
estimated_time_ms: 60000,
would_skip: true,
skip_reason: Some("exceeds max_transfer_mb threshold".to_string()),
};
let json = serde_json::to_value(&estimate).unwrap();
assert_eq!(json["would_skip"], true);
assert_eq!(json["skip_reason"], "exceeds max_transfer_mb threshold");
}
#[test]
fn dry_run_summary_serializes() {
let _guard = test_guard!();
let summary = DryRunSummary {
would_offload: true,
reason: "compilation command meets threshold".to_string(),
pipeline_steps: vec![DryRunPipelineStep {
step: 1,
name: "classify".to_string(),
description: "Analyze command".to_string(),
skipped: false,
skip_reason: None,
estimated_duration_ms: Some(2),
}],
transfer_estimate: None,
total_estimated_ms: Some(100),
};
let json = serde_json::to_value(&summary).unwrap();
assert_eq!(json["would_offload"], true);
assert_eq!(json["reason"], "compilation command meets threshold");
assert_eq!(json["pipeline_steps"].as_array().unwrap().len(), 1);
assert_eq!(json["total_estimated_ms"], 100);
}
#[test]
fn format_bytes_basic() {
let _guard = test_guard!();
assert_eq!(format_bytes(500), "500 B");
assert_eq!(format_bytes(1024), "1.0 KB");
assert_eq!(format_bytes(1024 * 1024), "1.0 MB");
assert_eq!(format_bytes(1024 * 1024 * 1024), "1.0 GB");
}
#[test]
fn format_bytes_fractional() {
let _guard = test_guard!();
assert_eq!(format_bytes(1536), "1.5 KB");
assert_eq!(format_bytes(1024 * 1024 + 512 * 1024), "1.5 MB");
}
#[test]
fn runtime_label_rust() {
let _guard = test_guard!();
assert_eq!(runtime_label(&RequiredRuntime::Rust), "rust");
}
#[test]
fn runtime_label_bun() {
let _guard = test_guard!();
assert_eq!(runtime_label(&RequiredRuntime::Bun), "bun");
}
#[test]
fn runtime_label_node() {
let _guard = test_guard!();
assert_eq!(runtime_label(&RequiredRuntime::Node), "node");
}
#[test]
fn runtime_label_none() {
let _guard = test_guard!();
assert_eq!(runtime_label(&RequiredRuntime::None), "none");
}
#[test]
fn has_any_capabilities_empty() {
let _guard = test_guard!();
let caps = WorkerCapabilities::new();
assert!(!has_any_capabilities(&caps));
}
#[test]
fn has_any_capabilities_with_rust() {
let _guard = test_guard!();
let caps = WorkerCapabilities {
rustc_version: Some("rustc 1.87.0".to_string()),
..Default::default()
};
assert!(has_any_capabilities(&caps));
}
#[test]
fn has_any_capabilities_with_bun() {
let _guard = test_guard!();
let caps = WorkerCapabilities {
bun_version: Some("1.2.3".to_string()),
..Default::default()
};
assert!(has_any_capabilities(&caps));
}
#[test]
fn has_any_capabilities_with_node() {
let _guard = test_guard!();
let caps = WorkerCapabilities {
node_version: Some("v20.0.0".to_string()),
..Default::default()
};
assert!(has_any_capabilities(&caps));
}
#[test]
fn has_any_capabilities_with_npm() {
let _guard = test_guard!();
let caps = WorkerCapabilities {
npm_version: Some("10.0.0".to_string()),
..Default::default()
};
assert!(has_any_capabilities(&caps));
}
#[test]
fn has_any_capabilities_with_all() {
let _guard = test_guard!();
let caps = WorkerCapabilities {
rustc_version: Some("rustc 1.87.0".to_string()),
bun_version: Some("1.2.3".to_string()),
node_version: Some("v20.0.0".to_string()),
npm_version: Some("10.0.0".to_string()),
..Default::default()
};
assert!(has_any_capabilities(&caps));
}
#[test]
fn extract_version_numbers_simple() {
let _guard = test_guard!();
assert_eq!(extract_version_numbers("1.2.3"), vec![1, 2, 3]);
}
#[test]
fn extract_version_numbers_with_prefix() {
let _guard = test_guard!();
assert_eq!(
extract_version_numbers("rustc 1.87.0-nightly"),
vec![1, 87, 0]
);
}
#[test]
fn extract_version_numbers_node_format() {
let _guard = test_guard!();
assert_eq!(extract_version_numbers("v20.11.1"), vec![20, 11, 1]);
}
#[test]
fn extract_version_numbers_empty() {
let _guard = test_guard!();
assert_eq!(extract_version_numbers(""), Vec::<u64>::new());
}
#[test]
fn extract_version_numbers_no_numbers() {
let _guard = test_guard!();
assert_eq!(
extract_version_numbers("no numbers here"),
Vec::<u64>::new()
);
}
#[test]
fn extract_version_numbers_single() {
let _guard = test_guard!();
assert_eq!(extract_version_numbers("version 42"), vec![42]);
}
#[test]
fn extract_version_numbers_large() {
let _guard = test_guard!();
assert_eq!(extract_version_numbers("2024.01.15"), vec![2024, 1, 15]);
}
#[test]
fn major_version_extracts_first() {
let _guard = test_guard!();
assert_eq!(major_version("rustc 1.87.0-nightly"), Some(1));
}
#[test]
fn major_version_node() {
let _guard = test_guard!();
assert_eq!(major_version("v20.11.1"), Some(20));
}
#[test]
fn major_version_empty() {
let _guard = test_guard!();
assert_eq!(major_version(""), None);
}
#[test]
fn major_version_no_numbers() {
let _guard = test_guard!();
assert_eq!(major_version("no version"), None);
}
#[test]
fn major_minor_version_extracts_both() {
let _guard = test_guard!();
assert_eq!(major_minor_version("rustc 1.87.0-nightly"), Some((1, 87)));
}
#[test]
fn major_minor_version_node() {
let _guard = test_guard!();
assert_eq!(major_minor_version("v20.11.1"), Some((20, 11)));
}
#[test]
fn major_minor_version_single_number() {
let _guard = test_guard!();
assert_eq!(major_minor_version("version 42"), None);
}
#[test]
fn major_minor_version_empty() {
let _guard = test_guard!();
assert_eq!(major_minor_version(""), None);
}
#[test]
fn rust_version_mismatch_same_version() {
let _guard = test_guard!();
assert!(!rust_version_mismatch(
"rustc 1.87.0-nightly",
"rustc 1.87.0-nightly"
));
}
#[test]
fn rust_version_mismatch_different_patch() {
let _guard = test_guard!();
assert!(!rust_version_mismatch("rustc 1.87.0", "rustc 1.87.1"));
}
#[test]
fn rust_version_mismatch_different_minor() {
let _guard = test_guard!();
assert!(rust_version_mismatch("rustc 1.87.0", "rustc 1.86.0"));
}
#[test]
fn rust_version_mismatch_different_major() {
let _guard = test_guard!();
assert!(rust_version_mismatch("rustc 1.87.0", "rustc 2.0.0"));
}
#[test]
fn rust_version_mismatch_invalid_local() {
let _guard = test_guard!();
assert!(!rust_version_mismatch("invalid", "rustc 1.87.0"));
}
#[test]
fn rust_version_mismatch_invalid_remote() {
let _guard = test_guard!();
assert!(!rust_version_mismatch("rustc 1.87.0", "invalid"));
}
#[test]
fn major_version_mismatch_same() {
let _guard = test_guard!();
assert!(!major_version_mismatch("bun 1.2.3", "bun 1.5.0"));
}
#[test]
fn major_version_mismatch_different() {
let _guard = test_guard!();
assert!(major_version_mismatch("bun 1.2.3", "bun 2.0.0"));
}
#[test]
fn major_version_mismatch_invalid_local() {
let _guard = test_guard!();
assert!(!major_version_mismatch("no version", "bun 1.2.3"));
}
#[test]
fn major_version_mismatch_invalid_remote() {
let _guard = test_guard!();
assert!(!major_version_mismatch("bun 1.2.3", "no version"));
}
#[test]
fn summarize_capabilities_empty() {
let _guard = test_guard!();
let caps = WorkerCapabilities::new();
assert_eq!(summarize_capabilities(&caps), "unknown");
}
#[test]
fn summarize_capabilities_rust_only() {
let _guard = test_guard!();
let caps = WorkerCapabilities {
rustc_version: Some("rustc 1.87.0".to_string()),
..Default::default()
};
assert_eq!(summarize_capabilities(&caps), "rustc rustc 1.87.0");
}
#[test]
fn summarize_capabilities_all() {
let _guard = test_guard!();
let caps = WorkerCapabilities {
rustc_version: Some("1.87".to_string()),
bun_version: Some("1.2".to_string()),
node_version: Some("20.0".to_string()),
npm_version: Some("10.0".to_string()),
..Default::default()
};
let result = summarize_capabilities(&caps);
assert!(result.contains("rustc 1.87"));
assert!(result.contains("bun 1.2"));
assert!(result.contains("node 20.0"));
assert!(result.contains("npm 10.0"));
}
#[test]
fn indent_lines_single() {
let _guard = test_guard!();
assert_eq!(indent_lines("hello", " "), " hello");
}
#[test]
fn indent_lines_multiple() {
let _guard = test_guard!();
assert_eq!(indent_lines("a\nb\nc", ">> "), ">> a\n>> b\n>> c");
}
#[test]
fn indent_lines_empty() {
let _guard = test_guard!();
assert_eq!(indent_lines("", " "), "");
}
#[test]
fn indent_lines_empty_prefix() {
let _guard = test_guard!();
assert_eq!(indent_lines("a\nb", ""), "a\nb");
}
#[test]
fn urlencoding_encode_alphanumeric() {
let _guard = test_guard!();
assert_eq!(urlencoding_encode("abc123"), "abc123");
}
#[test]
fn urlencoding_encode_safe_chars() {
let _guard = test_guard!();
assert_eq!(urlencoding_encode("a-b_c.d~e"), "a-b_c.d~e");
}
#[test]
fn urlencoding_encode_spaces() {
let _guard = test_guard!();
assert_eq!(urlencoding_encode("hello world"), "hello%20world");
}
#[test]
fn urlencoding_encode_special() {
let _guard = test_guard!();
assert_eq!(urlencoding_encode("a=b&c"), "a%3Db%26c");
}
#[test]
fn urlencoding_encode_unicode() {
let _guard = test_guard!();
let result = urlencoding_encode("hello\u{00E9}"); assert!(result.starts_with("hello%"));
assert!(result.len() > 6); }
#[test]
fn urlencoding_encode_empty() {
let _guard = test_guard!();
assert_eq!(urlencoding_encode(""), "");
}
#[test]
fn is_default_verify_size_true() {
let _guard = test_guard!();
assert!(is_default_verify_size(&(100 * 1024 * 1024)));
}
#[test]
fn is_default_verify_size_false() {
let _guard = test_guard!();
assert!(!is_default_verify_size(&0));
assert!(!is_default_verify_size(&(50 * 1024 * 1024)));
}
}