use super::*;
use std::sync::atomic::{AtomicI64, Ordering};
use std::time::Duration;
#[test]
fn validated_runtime_clock_wraps_direct_field_assignment() {
let raw = Arc::new(AtomicI64::new(2_000));
let raw_clock = {
let raw = Arc::clone(&raw);
Arc::new(move || raw.load(Ordering::SeqCst)) as Arc<dyn Fn() -> i64 + Send + Sync>
};
let mut config = StoreConfig::new("target/test-clock-wrap");
config.clock = Some(clock_from_fn(raw_clock));
let runtime = config.validated().expect("config validates");
assert_eq!(runtime.now_us(), 2_000);
raw.store(1_500, Ordering::SeqCst);
assert_eq!(
runtime.now_us(),
2_000,
"validated runtime clock must clamp direct-field regressions"
);
}
#[test]
fn cache_now_us_clamps_negative_custom_clock_values() {
let raw_clock = Arc::new(|| -42_i64) as Arc<dyn Fn() -> i64 + Send + Sync>;
let mut config = StoreConfig::new("target/test-cache-clock-clamp");
config.clock = Some(clock_from_fn(raw_clock));
let runtime = config.validated().expect("config validates");
assert_eq!(
runtime.cache_now_us(),
0,
"projection/cache metadata clock must not persist negative timestamps"
);
}
#[test]
fn cache_now_us_preserves_zero_custom_clock_value() {
let raw_clock = Arc::new(|| 0_i64) as Arc<dyn Fn() -> i64 + Send + Sync>;
let mut config = StoreConfig::new("target/test-cache-clock-zero");
config.clock = Some(clock_from_fn(raw_clock));
let runtime = config.validated().expect("config validates");
assert_eq!(
runtime.cache_now_us(),
0,
"PROPERTY: zero is a valid cache timestamp boundary, not a negative-clock violation"
);
}
#[test]
fn index_topology_tiles64_simd_builder_sets_only_simd_overlay() {
let topology = IndexTopology::default().with_tiles64_simd(true);
assert!(
topology.tiles64_simd_enabled(),
"PROPERTY: with_tiles64_simd(true) must enable the SIMD overlay"
);
assert!(
!IndexTopology::default().tiles64_simd_enabled(),
"PROPERTY: default topology keeps the experimental SIMD overlay disabled"
);
assert!(
topology.soa_enabled() == IndexTopology::default().soa_enabled()
&& topology.entity_groups_enabled() == IndexTopology::default().entity_groups_enabled()
&& topology.tiles64_enabled() == IndexTopology::default().tiles64_enabled(),
"PROPERTY: with_tiles64_simd must not silently reset the rest of the topology"
);
}
#[test]
fn validated_accepts_documented_inclusive_upper_bounds() {
let mut config = StoreConfig::new("target/test-config-upper-bounds");
config.writer.pressure_retry_threshold_pct = 100;
config.batch.max_size = 4096;
config
.validated()
.expect("documented inclusive upper bounds should validate");
}
#[test]
fn validated_rejects_values_above_documented_upper_bounds() {
let mut pressure = StoreConfig::new("target/test-config-pressure-too-high");
pressure.writer.pressure_retry_threshold_pct = 101;
assert!(
matches!(
pressure.validated(),
Err(crate::store::StoreError::Configuration(_))
),
"PROPERTY: pressure retry threshold above 100 must be rejected"
);
let mut batch = StoreConfig::new("target/test-config-batch-too-large");
batch.batch.max_size = 4097;
assert!(
matches!(
batch.validated(),
Err(crate::store::StoreError::Configuration(_))
),
"PROPERTY: batch.max_size above 4096 must be rejected"
);
let mut single_append = StoreConfig::new("target/test-config-single-append-too-large");
single_append.single_append_max_bytes = 64 * 1024 * 1024 + 1;
assert!(
matches!(
single_append.validated(),
Err(crate::store::StoreError::Configuration(_))
),
"PROPERTY: single_append_max_bytes above 64MB must be rejected"
);
let mut batch_bytes = StoreConfig::new("target/test-config-batch-bytes-too-large");
batch_bytes.batch.max_bytes = 16 * 1024 * 1024 + 1;
assert!(
matches!(
batch_bytes.validated(),
Err(crate::store::StoreError::Configuration(_))
),
"PROPERTY: batch.max_bytes above 16MB must be rejected"
);
}
#[test]
fn validated_rejects_zero_payload_size_boundaries() {
let mut single_append = StoreConfig::new("target/test-config-single-append-zero");
single_append.single_append_max_bytes = 0;
assert!(
matches!(
single_append.validated(),
Err(crate::store::StoreError::Configuration(_))
),
"PROPERTY: single_append_max_bytes of zero must be rejected"
);
let mut batch_bytes = StoreConfig::new("target/test-config-batch-bytes-zero");
batch_bytes.batch.max_bytes = 0;
assert!(
matches!(
batch_bytes.validated(),
Err(crate::store::StoreError::Configuration(_))
),
"PROPERTY: batch.max_bytes of zero must be rejected"
);
}
#[test]
fn validated_config_debug_names_runtime_policy_fields() {
let runtime = StoreConfig::new("target/test-validated-debug")
.validated()
.expect("config validates");
let rendered = format!("{runtime:?}");
assert!(
rendered.contains("ValidatedStoreConfig")
&& rendered.contains("pressure_retry_threshold")
&& rendered.contains("group_commit_drain_budget")
&& rendered.contains("signing_registry"),
"PROPERTY: ValidatedStoreConfig Debug must name the runtime policy fields, got: {rendered}"
);
}
#[test]
fn process_boot_ns_is_nonzero_and_stable_in_process() {
let clock = SystemClock::new();
let first = clock.process_boot_ns();
let second = clock.process_boot_ns();
assert_ne!(
first, 0,
"PROPERTY: process_boot_ns must expose the captured wall-clock anchor, not zero/default"
);
assert_eq!(
first, second,
"PROPERTY: process_boot_ns must stay stable for the process lifetime"
);
}
#[test]
fn now_mono_ns_advances_beyond_nonzero_sentinel() {
let clock = SystemClock::new();
std::thread::sleep(Duration::from_millis(1));
let elapsed = clock.now_mono_ns();
assert!(
elapsed > 1,
"PROPERTY: now_mono_ns must report elapsed nanoseconds from the process anchor, not a fixed sentinel; got {elapsed}"
);
}
#[test]
fn duration_micros_preserves_zero_and_one_microsecond_boundaries() {
assert_eq!(
duration_micros(Duration::ZERO),
0,
"PROPERTY: zero duration must remain zero, not a default/nonzero sentinel"
);
assert_eq!(
duration_micros(Duration::from_micros(1)),
1,
"PROPERTY: one microsecond must round-trip exactly"
);
}