diff --git a/crates/ecstore/src/disk/endpoint.rs b/crates/ecstore/src/disk/endpoint.rs index 5339d9641..eb7ee49e4 100644 --- a/crates/ecstore/src/disk/endpoint.rs +++ b/crates/ecstore/src/disk/endpoint.rs @@ -82,17 +82,22 @@ impl TryFrom<&str> for Endpoint { #[cfg(not(windows))] let path = Path::new(&path).absolutize()?; - // On windows having a preceding SlashSeparator will cause problems, if the - // command line already has C:/ bool { + let bytes = path.as_bytes(); + bytes.len() >= 4 && bytes[0] == b'/' && bytes[1].is_ascii_alphabetic() && bytes[2] == b':' && bytes[3] == b'/' +} + /// parse a file path into a URL. fn url_parse_from_file_path(value: &str) -> Result { // Only check if the arg is an ip address and ask for scheme since its absent. @@ -242,6 +253,14 @@ fn url_parse_from_file_path(value: &str) -> Result { mod test { use super::*; + fn expected_file_path(path: &str) -> String { + Path::new(path).absolutize().unwrap().to_string_lossy().replace('\\', "/") + } + + fn expected_file_url(path: &str) -> Url { + url_parse_from_file_path(path).unwrap() + } + #[test] fn test_new_endpoint() { #[derive(Default)] @@ -255,7 +274,7 @@ mod test { let u2 = Url::parse("https://example.org/path").unwrap(); let u4 = Url::parse("http://192.168.253.200/path").unwrap(); let u6 = Url::parse("http://server:/path").unwrap(); - let root_slash_foo = Url::from_file_path("/foo").unwrap(); + let root_slash_foo = expected_file_url("/foo"); let test_cases = [ TestCase { @@ -416,7 +435,7 @@ mod test { // Test file path display let file_endpoint = Endpoint::try_from("/tmp/data").unwrap(); let display_str = format!("{file_endpoint}"); - assert_eq!(display_str, "/tmp/data"); + assert_eq!(display_str, expected_file_path("/tmp/data")); // Test URL display let url_endpoint = Endpoint::try_from("http://example.com:9000/path").unwrap(); @@ -479,12 +498,25 @@ mod test { #[test] fn test_endpoint_get_file_path() { let file_endpoint = Endpoint::try_from("/tmp/data").unwrap(); - assert_eq!(file_endpoint.get_file_path(), "/tmp/data"); + assert_eq!(file_endpoint.get_file_path(), expected_file_path("/tmp/data")); let url_endpoint = Endpoint::try_from("http://example.com:9000/path/to/data").unwrap(); assert_eq!(url_endpoint.get_file_path(), "/path/to/data"); } + #[cfg(windows)] + #[test] + fn test_windows_url_drive_path_requires_separator_after_colon() { + let drive_path_endpoint = Endpoint::try_from("http://host/C:/data").unwrap(); + assert_eq!(drive_path_endpoint.get_type(), EndpointType::Url); + assert!(has_leading_slash_windows_drive(Url::parse("http://host/C:/data").unwrap().path())); + + let url_path_endpoint = Endpoint::try_from("http://host/C:foo").unwrap(); + assert_eq!(url_path_endpoint.get_type(), EndpointType::Url); + assert!(!has_leading_slash_windows_drive(Url::parse("http://host/C:foo").unwrap().path())); + assert_eq!(url_path_endpoint.get_file_path(), "/C:foo"); + } + #[test] fn test_endpoint_clone_and_equality() { let endpoint1 = Endpoint::try_from("/tmp/data").unwrap(); @@ -503,7 +535,7 @@ mod test { // Test with complex paths let complex_path = "/var/lib/rustfs/data/bucket1"; let endpoint = Endpoint::try_from(complex_path).unwrap(); - assert_eq!(endpoint.get_file_path(), complex_path); + assert_eq!(endpoint.get_file_path(), expected_file_path(complex_path)); assert!(endpoint.is_local); assert_eq!(endpoint.get_type(), EndpointType::Path); } @@ -512,7 +544,7 @@ mod test { fn test_endpoint_with_spaces_in_path() { let path_with_spaces = "/Users/test/Library/Application Support/rustfs/data"; let endpoint = Endpoint::try_from(path_with_spaces).unwrap(); - assert_eq!(endpoint.get_file_path(), path_with_spaces); + assert_eq!(endpoint.get_file_path(), expected_file_path(path_with_spaces)); assert!(endpoint.is_local); assert_eq!(endpoint.get_type(), EndpointType::Path); } @@ -532,7 +564,7 @@ mod test { // Verify that get_file_path() decodes the percent-encoded path correctly assert_eq!( endpoint.get_file_path(), - "/Users/test/Library/Application Support/rustfs/data", + expected_file_path("/Users/test/Library/Application Support/rustfs/data"), "get_file_path() should decode percent-encoded spaces" ); } @@ -544,7 +576,7 @@ mod test { let endpoint = Endpoint::try_from(path_with_special).unwrap(); // get_file_path() should return the original path with decoded characters - assert_eq!(endpoint.get_file_path(), path_with_special); + assert_eq!(endpoint.get_file_path(), expected_file_path(path_with_special)); } #[test] diff --git a/crates/ecstore/src/disk/fs.rs b/crates/ecstore/src/disk/fs.rs index d2299d458..cdd70aef7 100644 --- a/crates/ecstore/src/disk/fs.rs +++ b/crates/ecstore/src/disk/fs.rs @@ -545,7 +545,7 @@ mod tests { // Create two different files tokio::fs::write(&file1_path, b"content1").await.unwrap(); - tokio::fs::write(&file2_path, b"content2").await.unwrap(); + tokio::fs::write(&file2_path, b"different content").await.unwrap(); // Get metadata let metadata1 = tokio::fs::metadata(&file1_path).await.unwrap(); diff --git a/crates/ecstore/src/disk/local.rs b/crates/ecstore/src/disk/local.rs index 61d0c18d8..ee45c5509 100644 --- a/crates/ecstore/src/disk/local.rs +++ b/crates/ecstore/src/disk/local.rs @@ -2153,9 +2153,6 @@ impl DiskAPI for LocalDisk { #[allow(unsafe_code)] #[tracing::instrument(level = "debug", skip(self))] async fn read_file_zero_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result { - use std::time::Instant; - - let start = Instant::now(); let volume_dir = self.get_bucket_path(volume)?; if !skip_access_checks(volume) { access(&volume_dir) @@ -2188,6 +2185,9 @@ impl DiskAPI for LocalDisk { #[cfg(unix)] { use memmap2::MmapOptions; + use std::time::Instant; + + let start = Instant::now(); let file_path_clone = file_path.clone(); let should_reclaim_after_read = should_reclaim_file_cache_after_read(length); @@ -2272,8 +2272,7 @@ impl DiskAPI for LocalDisk { f.seek(SeekFrom::Start(offset as u64)).await?; } - let mut buffer = Vec::with_capacity(length); - buffer.resize(length, 0); + let mut buffer = vec![0; length]; f.read_exact(&mut buffer).await?; Ok(Bytes::from(buffer)) @@ -3497,10 +3496,10 @@ mod test { let disk_info = disk.disk_info(&disk_info_opts).await.unwrap(); // Basic checks on disk info - // Note: On macOS and some other Unix systems, fs_type may be empty + // Note: On macOS, Windows, and some other systems, fs_type may be empty // because statvfs does not provide filesystem type information. // This is a platform limitation, not a bug. - #[cfg(not(target_os = "macos"))] + #[cfg(not(any(target_os = "macos", windows)))] assert!(!disk_info.fs_type.is_empty(), "fs_type should not be empty on this platform"); assert!(disk_info.total > 0); assert!(disk_info.free <= disk_info.total); diff --git a/crates/ecstore/src/disk/mod.rs b/crates/ecstore/src/disk/mod.rs index 9602aca63..7df4bdb85 100644 --- a/crates/ecstore/src/disk/mod.rs +++ b/crates/ecstore/src/disk/mod.rs @@ -789,7 +789,6 @@ mod tests { use super::*; use endpoint::Endpoint; use local::LocalDisk; - use std::path::PathBuf; use tokio::fs; use uuid::Uuid; @@ -1094,7 +1093,7 @@ mod tests { assert!(disk.is_ok()); let disk = disk.unwrap(); - assert_eq!(disk.path(), PathBuf::from(test_dir).canonicalize().unwrap()); + assert_eq!(disk.path(), rustfs_utils::canonicalize(test_dir).unwrap()); assert!(disk.is_local()); // Note: is_online() might return false for local disks without proper initialization // This is expected behavior for test environments diff --git a/crates/ecstore/src/endpoints.rs b/crates/ecstore/src/endpoints.rs index 263bd2ce2..ecea4e994 100644 --- a/crates/ecstore/src/endpoints.rs +++ b/crates/ecstore/src/endpoints.rs @@ -749,6 +749,7 @@ fn validate_local_cross_device_mounts(local_paths: &[String]) -> Result<()> { #[cfg(test)] mod test { + use path_absolutize::Absolutize; use rustfs_utils::must_get_local_ips; use super::*; @@ -1452,9 +1453,10 @@ mod test { } fn must_file_path(s: impl AsRef) -> url::Url { - let url = url::Url::from_file_path(s.as_ref()); + let path = s.as_ref().absolutize().expect("absolute test path"); + let url = url::Url::from_file_path(&path); - assert!(url.is_ok(), "failed to convert path to URL: {}", s.as_ref().display()); + assert!(url.is_ok(), "failed to convert path to URL: {}", path.display()); url.unwrap() } diff --git a/crates/ecstore/src/notification_sys.rs b/crates/ecstore/src/notification_sys.rs index 0da0f2e34..7edf4e7d3 100644 --- a/crates/ecstore/src/notification_sys.rs +++ b/crates/ecstore/src/notification_sys.rs @@ -872,10 +872,7 @@ mod tests { let result = call_peer_with_timeout( Duration::from_millis(5), "peer-3", - || async { - tokio::time::sleep(Duration::from_millis(25)).await; - Ok::<_, Error>(build_props("slow")) - }, + std::future::pending::>, || build_props("fallback"), ) .await; diff --git a/crates/io-core/src/direct_io.rs b/crates/io-core/src/direct_io.rs index 6704f57e8..d9de7c700 100644 --- a/crates/io-core/src/direct_io.rs +++ b/crates/io-core/src/direct_io.rs @@ -287,7 +287,7 @@ mod tests { #[cfg(not(target_os = "linux"))] { // Non-Linux should return UnsupportedPlatform - let file = std::fs::File::open("/dev/null").unwrap(); + let file = std::fs::File::open(std::env::current_exe().unwrap()).unwrap(); assert!(matches!(DirectIoReader::new(file, 0, 512), Err(DirectIoError::UnsupportedPlatform))); } } diff --git a/crates/io-core/src/io_profile.rs b/crates/io-core/src/io_profile.rs index d43cdfe62..7618eb949 100644 --- a/crates/io-core/src/io_profile.rs +++ b/crates/io-core/src/io_profile.rs @@ -219,6 +219,15 @@ pub fn detect_storage_media(storage_detection_enabled: bool, storage_media_overr } } + #[cfg(not(any(target_os = "linux", target_os = "macos")))] + { + if let Ok(media) = detect_platform_storage_media() + && media != StorageMedia::Unknown + { + return media; + } + } + StorageMedia::Unknown } diff --git a/crates/object-capacity/src/scan.rs b/crates/object-capacity/src/scan.rs index dae73cad8..7074bd467 100644 --- a/crates/object-capacity/src/scan.rs +++ b/crates/object-capacity/src/scan.rs @@ -648,6 +648,7 @@ mod tests { use super::*; use crate::capacity_manager::{DataSource, HybridStrategyConfig, create_isolated_manager}; use rustfs_common::capacity_scope::{CapacityScope, CapacityScopeDisk}; + #[cfg(unix)] use rustfs_config::ENV_CAPACITY_FOLLOW_SYMLINKS; use serial_test::serial; @@ -671,6 +672,7 @@ mod tests { let file_path = temp_dir.path().join("test.txt"); let mut file = File::create(&file_path).unwrap(); file.write_all(b"Hello, World!").unwrap(); + drop(file); let size = get_dir_size_async(temp_dir.path()).await.unwrap(); assert_eq!(size.used_bytes, 13); @@ -709,10 +711,12 @@ mod tests { let file1 = temp_dir.path().join("file1.txt"); let mut f1 = File::create(&file1).unwrap(); f1.write_all(b"content1").unwrap(); + drop(f1); let file2 = subdir.join("file2.txt"); let mut f2 = File::create(&file2).unwrap(); f2.write_all(b"content2").unwrap(); + drop(f2); let size = get_dir_size_async(temp_dir.path()).await.unwrap(); assert_eq!(size.used_bytes, 16); @@ -736,6 +740,7 @@ mod tests { let file_path = temp_dir.path().join("test.txt"); let mut file = File::create(&file_path).unwrap(); file.write_all(b"Hello, World!").unwrap(); + drop(file); let disks = vec![ CapacityDiskRef { diff --git a/crates/obs/src/cleaner/core.rs b/crates/obs/src/cleaner/core.rs index 69980c0cb..13aead31f 100644 --- a/crates/obs/src/cleaner/core.rs +++ b/crates/obs/src/cleaner/core.rs @@ -504,7 +504,7 @@ impl LogCleaner { if let Some(err) = last_err { return Err(err); } - return Ok(()); + Ok(()) } #[cfg(not(windows))] diff --git a/crates/protocols/src/common/dummy_storage.rs b/crates/protocols/src/common/dummy_storage.rs index 2926d66e7..df2ad83e4 100644 --- a/crates/protocols/src/common/dummy_storage.rs +++ b/crates/protocols/src/common/dummy_storage.rs @@ -13,6 +13,7 @@ // limitations under the License. #![cfg(test)] +#![allow(dead_code)] //! Storage-backend double for protocol driver unit tests. //! diff --git a/crates/protocols/src/sftp/config.rs b/crates/protocols/src/sftp/config.rs index d0e934acb..4fee437a3 100644 --- a/crates/protocols/src/sftp/config.rs +++ b/crates/protocols/src/sftp/config.rs @@ -28,6 +28,7 @@ use super::constants::limits::{ READ_CACHE_TOTAL_MEM_MIN, READ_CACHE_WINDOW_DEFAULT, READ_CACHE_WINDOW_MAX, READ_CACHE_WINDOW_MIN, S3_MAX_PART_SIZE, S3_MIN_PART_SIZE, }; +#[cfg(unix)] use russh::keys::PublicKeyBase64; use std::net::SocketAddr; #[cfg(unix)] @@ -38,6 +39,7 @@ use thiserror::Error; /// Upper bound on file size accepted as a candidate host key (1 MiB). /// Guards against accidentally reading huge non-key files in the host /// key directory. Real keys are well under 10 KiB. +#[cfg(unix)] const MAX_HOST_KEY_FILE_SIZE: u64 = 1024 * 1024; /// PEM pre-encapsulation boundary marker prefix per RFC 7468 section 3. @@ -45,6 +47,7 @@ const MAX_HOST_KEY_FILE_SIZE: u64 = 1024 * 1024; /// space, the label, and five more hyphens. Used to distinguish a file /// that looks like a private key but failed to decode (passphrase, corrupt) /// from a file that is genuinely something else (a .pub key, a README). +#[cfg(unix)] const PEM_BEGIN_MARKER: &str = "-----BEGIN"; /// Errors that can occur during SFTP server initialization. @@ -457,6 +460,7 @@ impl SftpConfig { #[cfg(test)] mod tests { use super::*; + #[cfg(unix)] use std::os::unix::fs::OpenOptionsExt; use tempfile::TempDir; @@ -464,17 +468,21 @@ mod tests { // label / five-hyphen) are composed at runtime by build_pem_block // so the source file emits no contiguous private-key marker that // secret scanners would flag. Throwaway test-vector keys. + #[cfg(unix)] const PEM_BOUNDARY_DASHES: &str = "-----"; + #[cfg(unix)] const PEM_OPENSSH_LABEL: &str = "OPENSSH PRIVATE KEY"; /// Wrap a base64 body in the OpenSSH-format PEM boundary markers. /// The boundary string is composed at runtime from PEM_BOUNDARY_DASHES /// and PEM_OPENSSH_LABEL so the source file does not contain the full /// marker as a contiguous literal. + #[cfg(unix)] fn build_pem_block(body: &str) -> String { format!("{d}BEGIN {l}{d}\n{body}\n{d}END {l}{d}\n", d = PEM_BOUNDARY_DASHES, l = PEM_OPENSSH_LABEL,) } + #[cfg(unix)] fn test_ed25519_pem() -> String { // Throwaway Ed25519 private key, no passphrase. build_pem_block( @@ -486,6 +494,7 @@ mod tests { ) } + #[cfg(unix)] fn test_ecdsa_pem() -> String { // ECDSA P-256 fixture key for the algorithm-preference sort // test. Not passphrase-protected. @@ -516,6 +525,7 @@ mod tests { } /// Write a file at the given path with the given content and mode. + #[cfg(unix)] fn write_file_with_mode(path: &Path, content: &str, mode: u32) { let mut opts = std::fs::OpenOptions::new(); opts.write(true).create(true).truncate(true).mode(mode); @@ -599,6 +609,7 @@ mod tests { } #[tokio::test] + #[cfg(unix)] async fn load_host_keys_fails_when_dir_missing() { let path = PathBuf::from("/this/path/does/not/exist/sftp-host-keys"); let err = SftpConfig::load_host_keys(&path).await.expect_err("missing dir must error"); @@ -606,6 +617,7 @@ mod tests { } #[tokio::test] + #[cfg(unix)] async fn load_host_keys_fails_when_dir_empty() { let dir = TempDir::new().expect("tempdir"); let err = SftpConfig::load_host_keys(dir.path()) @@ -615,6 +627,17 @@ mod tests { } #[tokio::test] + #[cfg(not(unix))] + async fn load_host_keys_rejects_non_unix_platform() { + let dir = TempDir::new().expect("tempdir"); + let err = SftpConfig::load_host_keys(dir.path()) + .await + .expect_err("non-Unix platforms must be rejected"); + assert!(matches!(err, SftpInitError::UnsupportedPlatform { .. })); + } + + #[tokio::test] + #[cfg(unix)] async fn load_host_keys_rejects_insecure_permissions() { let dir = TempDir::new().expect("tempdir"); let key_path = dir.path().join("ssh_host_ed25519_key"); @@ -632,6 +655,7 @@ mod tests { } #[tokio::test] + #[cfg(unix)] async fn load_host_keys_loads_one_valid_ed25519_key() { let dir = TempDir::new().expect("tempdir"); let key_path = dir.path().join("ssh_host_ed25519_key"); @@ -642,6 +666,7 @@ mod tests { } #[tokio::test] + #[cfg(unix)] async fn load_host_keys_skips_non_key_files() { let dir = TempDir::new().expect("tempdir"); // Real key plus an unrelated file. @@ -654,6 +679,7 @@ mod tests { } #[tokio::test] + #[cfg(unix)] async fn load_host_keys_handles_empty_file() { let dir = TempDir::new().expect("tempdir"); write_file_with_mode(&dir.path().join("empty"), "", 0o600); @@ -665,6 +691,7 @@ mod tests { } #[tokio::test] + #[cfg(unix)] async fn load_host_keys_skips_passphrase_protected_key_with_warn() { // Build content that looks like a private key but cannot be decoded // (we pass None as the passphrase). Exercises the load_host_keys @@ -682,6 +709,7 @@ mod tests { } #[tokio::test] + #[cfg(unix)] async fn load_host_keys_sorts_ed25519_before_ecdsa() { let dir = TempDir::new().expect("tempdir"); // Write ECDSA first to confirm sort ordering rather than insertion order. diff --git a/crates/targets/src/config/target_args.rs b/crates/targets/src/config/target_args.rs index 42226f93f..93b1825b2 100644 --- a/crates/targets/src/config/target_args.rs +++ b/crates/targets/src/config/target_args.rs @@ -605,6 +605,10 @@ mod tests { }; use rustfs_ecstore::config::KVS; + fn absolute_test_path(path: &str) -> String { + std::env::temp_dir().join(path).to_string_lossy().into_owned() + } + fn amqp_base_config() -> KVS { let mut config = KVS::new(); config.insert(AMQP_URL.to_string(), "amqp://127.0.0.1:5672/%2f".to_string()); @@ -787,8 +791,9 @@ mod tests { #[test] fn build_mysql_args_applies_defaults() { - let args = build_mysql_args(&mysql_base_config(), "/custom/queue", TargetType::NotifyEvent).expect("valid mysql args"); - assert_eq!(args.queue_dir, "/custom/queue"); + let queue_dir = absolute_test_path("custom-queue"); + let args = build_mysql_args(&mysql_base_config(), &queue_dir, TargetType::NotifyEvent).expect("valid mysql args"); + assert_eq!(args.queue_dir, queue_dir); assert_eq!(args.queue_limit, 100000); assert_eq!(args.max_open_connections, 2); } @@ -846,7 +851,7 @@ mod tests { let err = validate_mysql_config(&config, "").expect_err("relative tls_ca should fail"); assert!(err.to_string().contains("tls_ca must be an absolute path")); - config.insert(MYSQL_TLS_CA.to_string(), "/etc/ssl/mysql/ca.pem".to_string()); + config.insert(MYSQL_TLS_CA.to_string(), absolute_test_path("mysql-ca.pem")); config.insert(MYSQL_TLS_CLIENT_CERT.to_string(), "client.pem".to_string()); config.insert(MYSQL_TLS_CLIENT_KEY.to_string(), "client.key".to_string()); @@ -857,14 +862,17 @@ mod tests { #[test] fn build_mysql_args_accepts_absolute_tls_paths() { let mut config = mysql_base_config(); - config.insert(MYSQL_TLS_CA.to_string(), "/etc/ssl/mysql/ca.pem".to_string()); - config.insert(MYSQL_TLS_CLIENT_CERT.to_string(), "/etc/ssl/mysql/client.pem".to_string()); - config.insert(MYSQL_TLS_CLIENT_KEY.to_string(), "/etc/ssl/mysql/client.key".to_string()); + let tls_ca = absolute_test_path("mysql-ca.pem"); + let tls_client_cert = absolute_test_path("mysql-client.pem"); + let tls_client_key = absolute_test_path("mysql-client.key"); + config.insert(MYSQL_TLS_CA.to_string(), tls_ca.clone()); + config.insert(MYSQL_TLS_CLIENT_CERT.to_string(), tls_client_cert.clone()); + config.insert(MYSQL_TLS_CLIENT_KEY.to_string(), tls_client_key.clone()); let args = build_mysql_args(&config, "", TargetType::NotifyEvent).expect("absolute mysql TLS paths should pass"); - assert_eq!(args.tls_ca, "/etc/ssl/mysql/ca.pem"); - assert_eq!(args.tls_client_cert, "/etc/ssl/mysql/client.pem"); - assert_eq!(args.tls_client_key, "/etc/ssl/mysql/client.key"); + assert_eq!(args.tls_ca, tls_ca); + assert_eq!(args.tls_client_cert, tls_client_cert); + assert_eq!(args.tls_client_key, tls_client_key); } fn redis_base_config() -> KVS { diff --git a/crates/targets/src/target/mysql.rs b/crates/targets/src/target/mysql.rs index 13656a717..1450ade22 100644 --- a/crates/targets/src/target/mysql.rs +++ b/crates/targets/src/target/mysql.rs @@ -832,6 +832,10 @@ where mod tests { use super::*; + fn absolute_test_path(path: &str) -> String { + std::env::temp_dir().join(path).to_string_lossy().into_owned() + } + #[test] fn parse_dsn_format() { let dsn = MySqlDsn::parse("rustfs:secret123@tcp(mysql.example.com:3306)/rustfs_events").expect("valid DSN"); @@ -1189,10 +1193,10 @@ mod tests { dsn_string: "rustfs:password@tcp(127.0.0.1:3306)/db".to_string(), table: "events".to_string(), format: "access".to_string(), - tls_ca: "/etc/ssl/mysql/ca.pem".to_string(), - tls_client_cert: "/etc/ssl/mysql/client.pem".to_string(), - tls_client_key: "/etc/ssl/mysql/client.key".to_string(), - queue_dir: "/tmp".to_string(), + tls_ca: absolute_test_path("mysql-ca.pem"), + tls_client_cert: absolute_test_path("mysql-client.pem"), + tls_client_key: absolute_test_path("mysql-client.key"), + queue_dir: absolute_test_path("mysql-queue"), queue_limit: 100, max_open_connections: 2, target_type: TargetType::NotifyEvent, diff --git a/crates/targets/src/target/redis.rs b/crates/targets/src/target/redis.rs index c5e3508bf..133b84001 100644 --- a/crates/targets/src/target/redis.rs +++ b/crates/targets/src/target/redis.rs @@ -730,6 +730,10 @@ mod tests { use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpListener; + fn absolute_test_path(path: &str) -> String { + std::env::temp_dir().join(path).to_string_lossy().into_owned() + } + fn base_args() -> RedisArgs { RedisArgs { enable: true, @@ -782,7 +786,7 @@ mod tests { url: Url::parse("rediss://127.0.0.1:6379").unwrap(), tls: RedisTlsConfig { policy: Some(RedisTlsPolicy::CustomCa), - ca_path: "/tmp/ca.pem".to_string(), + ca_path: absolute_test_path("redis-ca.pem"), ..RedisTlsConfig::default() }, ..base_args()