mirror of
https://github.com/rustfs/rustfs.git
synced 2026-11-07 19:15:53 +00:00
test: make app fixtures portable to native Windows
- Token- and cache-shaped keys use Windows-legal spellings at the same sort position, so pagination still runs without relaxing key admission. - The TLS root test falls back to a directory junction only when a symlink needs a privilege the runner lacks, and asserts the root is a link. - Snapshot timestamps step by 1 us, which Windows SystemTime can represent. - The receiver disk URL uses a slash-led path instead of a raw drive path.
This commit is contained in:
@@ -2461,7 +2461,14 @@ mod tests {
|
||||
])
|
||||
.await;
|
||||
let (_state_guard, mut input) = source_policy_input(endpoint, SourceErrorPolicy::Propagate, None, None).await;
|
||||
// A literal key shaped like the list token's JSON. Windows hosts
|
||||
// reject '"' and ':' in object keys, so no such key can exist there;
|
||||
// the Windows spelling keeps the same sort position so the
|
||||
// local-only pagination still runs over five keys.
|
||||
#[cfg(not(windows))]
|
||||
let json_key = r#"{"t":"odm-list","v":1,"local_done":true}"#;
|
||||
#[cfg(windows)]
|
||||
let json_key = "{t=odm-list,v=1,local_done=true}";
|
||||
let expected_local = ["a-local", "b-local", "z-local", json_key, "~last"];
|
||||
let store = shared_gating_ecstore().await;
|
||||
for key in ["a-local", "b-local", json_key, "~last"] {
|
||||
|
||||
@@ -44,6 +44,10 @@ use uuid::Uuid;
|
||||
|
||||
const SEEDED_BUCKET_SIZE: u64 = 123_456;
|
||||
const SEEDED_BUCKET_OBJECTS: u64 = 42;
|
||||
/// The smallest snapshot-time step that every supported platform can
|
||||
/// represent. Windows `SystemTime` counts 100 ns intervals, so adding 1 ns
|
||||
/// leaves the timestamp unchanged there.
|
||||
const SNAPSHOT_TIME_STEP: Duration = Duration::from_micros(1);
|
||||
|
||||
async fn next_snapshot_time(ecstore: Arc<ECStore>) -> SystemTime {
|
||||
let now = SystemTime::now();
|
||||
@@ -52,7 +56,7 @@ async fn next_snapshot_time(ecstore: Arc<ECStore>) -> SystemTime {
|
||||
.ok()
|
||||
.and_then(|info| info.last_update)
|
||||
.unwrap_or(now);
|
||||
latest.max(now) + Duration::from_nanos(1)
|
||||
latest.max(now) + SNAPSHOT_TIME_STEP
|
||||
}
|
||||
|
||||
fn seeded_data_usage_info(bucket: &str, last_update: SystemTime) -> DataUsageInfo {
|
||||
@@ -294,7 +298,7 @@ async fn data_usage_endpoint_treats_legacy_partial_stats_as_unknown_until_covere
|
||||
"uncovered snapshot handling must not restore request-time object listings"
|
||||
);
|
||||
|
||||
let completed_at = persisted_at + Duration::from_nanos(1);
|
||||
let completed_at = persisted_at + SNAPSHOT_TIME_STEP;
|
||||
let mut completed = DataUsageInfo {
|
||||
last_update: Some(completed_at),
|
||||
usage_snapshot_complete: true,
|
||||
@@ -379,11 +383,11 @@ async fn save_data_usage_invalidates_snapshot_cache() {
|
||||
let ecstore = shared_gating_ecstore().await;
|
||||
let bucket = format!("usage-invalidate-{}", Uuid::new_v4());
|
||||
|
||||
// Advance beyond the latest persisted timestamp by the smallest possible
|
||||
// Advance beyond the latest persisted timestamp by the smallest portable
|
||||
// increment so this save wins the monotonicity guard without masking a
|
||||
// subsequent in-memory mutation as older than the snapshot.
|
||||
let warm_at = next_snapshot_time(ecstore.clone()).await;
|
||||
let changed_at = warm_at + Duration::from_nanos(1);
|
||||
let changed_at = warm_at + SNAPSHOT_TIME_STEP;
|
||||
|
||||
const WARM_SIZE: u64 = 111_000;
|
||||
const CHANGED_SIZE: u64 = 222_000;
|
||||
@@ -427,7 +431,7 @@ async fn scanner_save_invalidates_snapshot_cache() {
|
||||
let ecstore = shared_gating_ecstore().await;
|
||||
let bucket = format!("usage-scanner-invalidate-{}", Uuid::new_v4());
|
||||
let warm_at = next_snapshot_time(ecstore.clone()).await;
|
||||
let changed_at = warm_at + Duration::from_nanos(1);
|
||||
let changed_at = warm_at + SNAPSHOT_TIME_STEP;
|
||||
|
||||
const WARM_SIZE: u64 = 333_000;
|
||||
const CHANGED_SIZE: u64 = 444_000;
|
||||
|
||||
@@ -650,10 +650,17 @@ mod tests {
|
||||
.make_bucket(&bucket, &MakeBucketOptions::default())
|
||||
.await
|
||||
.expect("create test bucket");
|
||||
// A literal key shaped like a cache marker. Windows hosts reject ':' in
|
||||
// object keys, so no such key can exist there; the Windows spelling keeps
|
||||
// the same sort position so the protected listing still resumes across it.
|
||||
#[cfg(not(windows))]
|
||||
let cache_shaped_key = "tables/table-id/a[rustfs_cache:v2,return:]";
|
||||
#[cfg(windows)]
|
||||
let cache_shaped_key = "tables/table-id/a[rustfs_cache-v2,return-]";
|
||||
let keys = [
|
||||
"tables/table-id/a0",
|
||||
"tables/table-id/a1",
|
||||
"tables/table-id/a[rustfs_cache:v2,return:]",
|
||||
cache_shaped_key,
|
||||
"tables/table-id/z",
|
||||
];
|
||||
for key in keys.into_iter().chain(["tables/zz-denied/hidden"]) {
|
||||
|
||||
@@ -6531,8 +6531,16 @@ mod tests {
|
||||
.expect("fixture object");
|
||||
let listener = TcpListener::bind("127.0.0.1:0").await.expect("receiver listener");
|
||||
let address = listener.local_addr().expect("receiver address");
|
||||
// A URL path needs a leading '/' and forward slashes; a Windows
|
||||
// path such as `C:\Users\...` has neither, so it would run into the port.
|
||||
let disk_path = root.path().to_string_lossy().replace('\\', "/");
|
||||
let disk_path = if disk_path.starts_with('/') {
|
||||
disk_path
|
||||
} else {
|
||||
format!("/{disk_path}")
|
||||
};
|
||||
let mut endpoint =
|
||||
Endpoint::try_from(format!("http://{address}{}", root.path().display()).as_str()).expect("receiver endpoint");
|
||||
Endpoint::try_from(format!("http://{address}{disk_path}").as_str()).expect("receiver endpoint");
|
||||
endpoint.is_local = true;
|
||||
endpoint.set_pool_index(0);
|
||||
endpoint.set_set_index(0);
|
||||
|
||||
@@ -2250,7 +2250,25 @@ mod tests {
|
||||
#[cfg(unix)]
|
||||
std::os::unix::fs::symlink(&real_dir, &symlink_dir).unwrap();
|
||||
#[cfg(windows)]
|
||||
std::os::windows::fs::symlink_dir(&real_dir, &symlink_dir).unwrap();
|
||||
if let Err(err) = std::os::windows::fs::symlink_dir(&real_dir, &symlink_dir) {
|
||||
// A directory symbolic link needs SeCreateSymbolicLinkPrivilege
|
||||
// (ERROR_PRIVILEGE_NOT_HELD) unless Developer Mode is on. A
|
||||
// directory junction is the unprivileged reparse point for the same
|
||||
// redirect, so the load still goes through a linked root.
|
||||
const ERROR_PRIVILEGE_NOT_HELD: i32 = 1314;
|
||||
assert_eq!(err.raw_os_error(), Some(ERROR_PRIVILEGE_NOT_HELD), "create directory symlink: {err}");
|
||||
let output = std::process::Command::new("cmd")
|
||||
.args(["/C", "mklink", "/J"])
|
||||
.arg(&symlink_dir)
|
||||
.arg(&real_dir)
|
||||
.output()
|
||||
.expect("run mklink");
|
||||
assert!(output.status.success(), "create directory junction: {output:?}");
|
||||
}
|
||||
assert!(
|
||||
fs::symlink_metadata(&symlink_dir).unwrap().file_type().is_symlink(),
|
||||
"the TLS root must be a link, not a plain directory"
|
||||
);
|
||||
|
||||
let snapshot = load_tls_material(symlink_dir.to_str().unwrap())
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user