lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit cb00b55db5cb194332b8cc4e505ee2c547beb102
parent 800aa5981357bd2b71d629c83f77a26af27a342d
Author: triesap <tyson@radroots.org>
Date:   Tue, 25 Aug 2026 22:02:39 +0000

service-host: keep admin socket tests path-safe

- allocate native admin fixtures beneath a short deterministic temp root
- preserve isolated random directories and automatic cleanup semantics
- exercise every client server and Unix binding vector under extbuild
- keep the helper private and test-only with no public API drift

Diffstat:
Mcrates/service_host/src/admin/client.rs | 12+++++++-----
Mcrates/service_host/src/admin/mod.rs | 2++
Mcrates/service_host/src/admin/server.rs | 22++++++++++++----------
Acrates/service_host/src/admin/test_support.rs | 8++++++++
Mcrates/service_host/src/admin/unix.rs | 30++++++++++++++++--------------
5 files changed, 45 insertions(+), 29 deletions(-)

diff --git a/crates/service_host/src/admin/client.rs b/crates/service_host/src/admin/client.rs @@ -18,6 +18,8 @@ use hyper_util::rt::TokioIo; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio::task::JoinHandle; +#[cfg(test)] +use super::test_support; use super::{ ADMIN_CONTRACT_VERSION, ADMIN_ROUTE_PATH_MAX_UTF8_BYTES, AdminCorrelationId, AdminFailureResponse, AdminHttpMethod, AdminOperationId, AdminSuccessResponse, @@ -897,7 +899,7 @@ mod tests { #[tokio::test] async fn server_client_round_trip_preserves_version_and_correlation() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let (socket, binding) = binding(&directory).await; let server = AdminServer::new(echo_router(), AdminTransportLimits::DEFAULT, FixedEntropy) .expect("admin server"); @@ -956,7 +958,7 @@ mod tests { #[tokio::test] async fn unavailable_socket_and_deadline_are_typed_and_safe() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let missing = directory.path().join("missing.sock"); let client = AdminClient::new(&missing, AdminTransportLimits::DEFAULT).expect("client"); let target = AdminClientTarget::new("/v1/status").expect("target"); @@ -992,7 +994,7 @@ mod tests { #[tokio::test] async fn version_malformed_duplicate_and_oversized_responses_fail_closed() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let target = AdminClientTarget::new("/v1/status").expect("target"); let cases = [ ( @@ -1092,7 +1094,7 @@ mod tests { #[tokio::test] async fn request_and_query_limits_fail_before_socket_access() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let missing = directory.path().join("missing.sock"); let client = AdminClient::new( &missing, @@ -1152,7 +1154,7 @@ mod tests { assert!(!debug.contains("secret-id")); assert!(!debug.contains("protected")); - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let socket = directory.path().join("protected-admin.sock"); let client = AdminClient::new(&socket, AdminTransportLimits::DEFAULT).expect("client"); assert!(!format!("{client:?}").contains("protected-admin.sock")); diff --git a/crates/service_host/src/admin/mod.rs b/crates/service_host/src/admin/mod.rs @@ -7,6 +7,8 @@ mod model; mod peer; #[cfg(any(target_os = "linux", target_os = "macos"))] mod server; +#[cfg(all(test, any(target_os = "linux", target_os = "macos")))] +mod test_support; #[cfg(any(target_os = "linux", target_os = "macos"))] mod unix; diff --git a/crates/service_host/src/admin/server.rs b/crates/service_host/src/admin/server.rs @@ -22,6 +22,8 @@ use hyper_util::rt::{TokioIo, TokioTimer}; use tokio::sync::Semaphore; use tokio::task::JoinSet; +#[cfg(test)] +use super::test_support; use super::{ ADMIN_CONTRACT_VERSION, AdminCorrelationId, AdminError, AdminErrorCode, AdminErrorMessage, AdminFailureResponse, AdminMutationRequest, AdminTransportLimits, UnixAdminSocketBinding, @@ -1361,7 +1363,7 @@ mod tests { #[tokio::test] async fn serves_valid_json_with_exact_caller_correlation_and_no_web_headers() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let (socket, binding) = binding(&directory).await; let server = AdminServer::new( echo_router(), @@ -1401,7 +1403,7 @@ mod tests { #[tokio::test] async fn rejects_oversized_and_malformed_json_before_the_handler() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let (socket, binding) = binding(&directory).await; let calls = Arc::new(AtomicUsize::new(0)); let handler_calls = Arc::clone(&calls); @@ -1454,7 +1456,7 @@ mod tests { #[tokio::test] async fn rejects_invalid_mutation_envelopes_duplicates_and_nested_null_before_dispatch() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let (socket, binding) = binding(&directory).await; let calls = Arc::new(AtomicUsize::new(0)); let handler_calls = Arc::clone(&calls); @@ -1506,7 +1508,7 @@ mod tests { #[tokio::test] async fn parameterized_routes_percent_decode_bounded_values_without_service_authority() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let (socket, binding) = binding(&directory).await; let calls = Arc::new(AtomicUsize::new(0)); let handler_calls = Arc::clone(&calls); @@ -1570,7 +1572,7 @@ mod tests { #[tokio::test] async fn caller_correlation_precedes_entropy_and_survives_timeout_handoff() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let (socket, binding) = binding(&directory).await; let entropy_calls = Arc::new(AtomicUsize::new(0)); let mut router = AdminRouter::new(); @@ -1616,7 +1618,7 @@ mod tests { #[tokio::test] async fn rejects_http_1_0_before_dispatch() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let (socket, binding) = binding(&directory).await; let calls = Arc::new(AtomicUsize::new(0)); let handler_calls = Arc::clone(&calls); @@ -1649,7 +1651,7 @@ mod tests { #[tokio::test] async fn request_deadline_returns_a_safe_timeout_and_cancels_the_handler_future() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let (socket, binding) = binding(&directory).await; let mut router = AdminRouter::new(); router @@ -1680,7 +1682,7 @@ mod tests { #[tokio::test] async fn enforces_header_query_response_and_body_correlation_boundaries() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let (socket, binding) = binding(&directory).await; let calls = Arc::new(AtomicUsize::new(0)); let handler_calls = Arc::clone(&calls); @@ -1766,7 +1768,7 @@ mod tests { #[tokio::test] async fn connection_admission_never_exceeds_the_configured_limit() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let (socket, binding) = binding(&directory).await; let entered = Arc::new(Notify::new()); let release = Arc::new(Notify::new()); @@ -1827,7 +1829,7 @@ mod tests { #[tokio::test] async fn graceful_cancellation_stops_admission_and_drains_an_active_request() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let (socket, binding) = binding(&directory).await; let entered = Arc::new(Notify::new()); let release = Arc::new(Notify::new()); diff --git a/crates/service_host/src/admin/test_support.rs b/crates/service_host/src/admin/test_support.rs @@ -0,0 +1,8 @@ +use std::path::Path; + +pub(super) fn short_tempdir() -> tempfile::TempDir { + tempfile::Builder::new() + .prefix("rsh") + .tempdir_in(Path::new("/tmp")) + .expect("short temporary directory") +} diff --git a/crates/service_host/src/admin/unix.rs b/crates/service_host/src/admin/unix.rs @@ -17,6 +17,8 @@ use rustix::fs::{ use rustix::process::geteuid; use super::peer::{AdminPeerAuthorizationPolicy, PeerAuthorizer}; +#[cfg(test)] +use super::test_support; const WRITER_LOCK_FILE_NAME: &str = ".radroots-admin-writer.lock"; /// Final owner-only mode for the runtime directory. @@ -536,7 +538,7 @@ mod tests { #[tokio::test] async fn binds_owner_only_socket_sets_modes_and_cleans_up() { - let directory = tempfile::tempdir().expect("temporary directory"); + let directory = super::test_support::short_tempdir(); let socket = directory.path().join("admin.sock"); let authority = UnixAdminSocketWriterAuthority::acquire(directory.path()).expect("writer authority"); @@ -577,7 +579,7 @@ mod tests { #[cfg(target_os = "linux")] #[tokio::test] async fn configured_admin_group_sets_group_access_modes_and_identity() { - let directory = tempfile::tempdir().expect("temporary directory"); + let directory = super::test_support::short_tempdir(); let socket = directory.path().join("admin.sock"); let admin_gid = rustix::process::getegid().as_raw(); let policy = AdminPeerAuthorizationPolicy::with_admin_gid(admin_gid) @@ -616,7 +618,7 @@ mod tests { #[tokio::test] async fn refuses_a_live_socket_owned_outside_the_writer_guard() { - let directory = tempfile::tempdir().expect("temporary directory"); + let directory = super::test_support::short_tempdir(); let socket = directory.path().join("admin.sock"); let live = UnixListener::bind(&socket).expect("live listener"); let authority = @@ -632,7 +634,7 @@ mod tests { #[tokio::test] async fn recovers_only_a_proven_stale_socket() { - let directory = tempfile::tempdir().expect("temporary directory"); + let directory = super::test_support::short_tempdir(); let socket = directory.path().join("admin.sock"); drop(UnixListener::bind(&socket).expect("stale listener")); assert!(socket.exists()); @@ -649,8 +651,8 @@ mod tests { #[tokio::test] async fn refuses_paths_outside_the_authorized_runtime_directory() { - let directory = tempfile::tempdir().expect("runtime directory"); - let outside = tempfile::tempdir().expect("outside directory"); + let directory = super::test_support::short_tempdir(); + let outside = super::test_support::short_tempdir(); let authority = UnixAdminSocketWriterAuthority::acquire(directory.path()).expect("writer authority"); @@ -662,7 +664,7 @@ mod tests { #[tokio::test] async fn refuses_non_socket_entries_without_unlinking_them() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let socket = directory.path().join("admin.sock"); fs::write(&socket, b"not a socket").expect("sentinel file"); let authority = @@ -680,7 +682,7 @@ mod tests { #[test] fn one_writer_authority_excludes_a_second_writer() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let first = UnixAdminSocketWriterAuthority::acquire(directory.path()) .expect("first writer authority"); let error = UnixAdminSocketWriterAuthority::acquire(directory.path()) @@ -691,8 +693,8 @@ mod tests { #[test] fn refuses_a_symlink_runtime_directory_and_wrong_owner_identity() { - let target = tempfile::tempdir().expect("runtime directory"); - let link_parent = tempfile::tempdir().expect("link parent"); + let target = super::test_support::short_tempdir(); + let link_parent = super::test_support::short_tempdir(); let link = link_parent.path().join("runtime"); symlink(target.path(), &link).expect("runtime symlink"); assert!(matches!( @@ -716,7 +718,7 @@ mod tests { #[tokio::test] async fn cleanup_never_unlinks_a_replacement_socket() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let socket = directory.path().join("admin.sock"); let authority = UnixAdminSocketWriterAuthority::acquire(directory.path()).expect("writer authority"); @@ -734,7 +736,7 @@ mod tests { #[test] fn public_errors_and_authority_debug_never_reveal_runtime_paths() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let authority = UnixAdminSocketWriterAuthority::acquire(directory.path()).expect("writer authority"); let debug = format!("{authority:?}"); @@ -749,7 +751,7 @@ mod tests { #[test] fn helper_admission_checks_bind_every_identity_and_mode_dimension() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let uid = geteuid().as_raw(); assert!(validate_owner(uid, uid).is_ok()); assert_eq!( @@ -827,7 +829,7 @@ mod tests { #[test] fn live_directory_revalidation_rejects_mode_identity_and_path_drift() { - let directory = tempfile::tempdir().expect("runtime directory"); + let directory = super::test_support::short_tempdir(); let authority = UnixAdminSocketWriterAuthority::acquire(directory.path()).unwrap(); assert!(authority.ensure_directory_identity().is_ok()); assert_eq!(