test: verify scoped authority storage lifetime and physical initialization

This commit is contained in:
2026-10-31 04:58:20 +09:00
parent 2c1f5dfafe
commit 5627f47e2b
7 changed files with 401 additions and 11 deletions
+3
View File
@@ -5,6 +5,9 @@ edition.workspace = true
license.workspace = true
publish = false
[features]
test-support = []
[dependencies]
rusqlite.workspace = true
thiserror.workspace = true
+35
View File
@@ -121,13 +121,28 @@ impl FeatureRegistration {
}
}
#[cfg(feature = "test-support")]
#[path = "open_observation.rs"]
mod open_observation;
#[cfg(feature = "test-support")]
pub use open_observation::OpenObservation;
#[derive(Clone, Debug)]
pub struct RegisteredFeature {
manager_id: Uuid,
feature_id: String,
#[cfg(feature = "test-support")]
observation: Arc<open_observation::Counters>,
}
impl RegisteredFeature {
/// Instance-local physical-operation counters for fixture assertions. The
/// observer retains no database and cannot replace or suppress durable I/O.
#[cfg(feature = "test-support")]
pub fn open_observation(&self) -> OpenObservation {
self.observation.snapshot()
}
pub fn feature_id(&self) -> &str {
&self.feature_id
}
@@ -219,6 +234,8 @@ impl FeatureStorage {
Ok(RegisteredFeature {
manager_id: self.inner.id,
feature_id,
#[cfg(feature = "test-support")]
observation: Arc::default(),
})
}
@@ -279,14 +296,32 @@ impl FeatureStorage {
}
let feature_dir = self.feature_dir(scope_id);
#[cfg(feature = "test-support")]
let mut counted_sync = |path: &Path| {
registration.observation.sync_attempt();
let result = sync(path);
if result.is_ok() {
registration.observation.sync_success();
}
result
};
#[cfg(feature = "test-support")]
durable_create_directory(&feature_dir, &mut counted_sync)?;
#[cfg(not(feature = "test-support"))]
durable_create_directory(&feature_dir, &mut sync)?;
let path = feature_dir.join(format!("{}.sqlite", registration.feature_id));
#[cfg(feature = "test-support")]
registration.observation.open_attempt();
let connection = Connection::open_with_flags(
path,
OpenFlags::SQLITE_OPEN_READ_WRITE | OpenFlags::SQLITE_OPEN_CREATE,
)?;
#[cfg(feature = "test-support")]
registration.observation.open_success();
configure_connection(&connection)?;
migrate(&connection, &registration.feature_id, migrations)?;
#[cfg(feature = "test-support")]
registration.observation.initialized();
let connection = Arc::new(FeatureDatabaseInner {
scope_id: scope_id.to_string(),
feature_id: registration.feature_id.clone(),
@@ -0,0 +1,64 @@
use std::sync::atomic::{AtomicU64, Ordering};
/// Read-only observations for one registration (and its clones) across its scopes.
/// Counts real I/O boundaries, not API opens, and retains no physical database.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct OpenObservation {
pub physical_open_attempts: u64,
pub physical_opens: u64,
pub completed_initializations: u64,
pub directory_sync_attempts: u64,
pub directory_sync_successes: u64,
}
impl OpenObservation {
pub fn since(self, before: Self) -> Self {
Self {
physical_open_attempts: self.physical_open_attempts - before.physical_open_attempts,
physical_opens: self.physical_opens - before.physical_opens,
completed_initializations: self.completed_initializations
- before.completed_initializations,
directory_sync_attempts: self.directory_sync_attempts - before.directory_sync_attempts,
directory_sync_successes: self.directory_sync_successes
- before.directory_sync_successes,
}
}
}
#[derive(Debug, Default)]
pub(super) struct Counters {
physical_open_attempts: AtomicU64,
physical_opens: AtomicU64,
completed_initializations: AtomicU64,
directory_sync_attempts: AtomicU64,
directory_sync_successes: AtomicU64,
}
impl Counters {
pub(super) fn snapshot(&self) -> OpenObservation {
OpenObservation {
physical_open_attempts: self.physical_open_attempts.load(Ordering::Relaxed),
physical_opens: self.physical_opens.load(Ordering::Relaxed),
completed_initializations: self.completed_initializations.load(Ordering::Relaxed),
directory_sync_attempts: self.directory_sync_attempts.load(Ordering::Relaxed),
directory_sync_successes: self.directory_sync_successes.load(Ordering::Relaxed),
}
}
pub(super) fn open_attempt(&self) {
self.physical_open_attempts.fetch_add(1, Ordering::Relaxed);
}
pub(super) fn open_success(&self) {
self.physical_opens.fetch_add(1, Ordering::Relaxed);
}
pub(super) fn initialized(&self) {
self.completed_initializations
.fetch_add(1, Ordering::Relaxed);
}
pub(super) fn sync_attempt(&self) {
self.directory_sync_attempts.fetch_add(1, Ordering::Relaxed);
}
pub(super) fn sync_success(&self) {
self.directory_sync_successes
.fetch_add(1, Ordering::Relaxed);
}
}