- the LFS twins carry the VersionedLfsRows wrap themselves (the refs-twins' model): the twin assemblies build the wrap over their own counter at open(), lfs_rows() returns the wrapped surface and a new lfs_version() accessor mirrors refs()/refs_version(); the composition entry composes the twins' wrapped surface unchanged — no double wrap, LfsMedia consults the one counter the wrap bumps - the through-store default-cache constructor (PackGen::through_store) doc-stamped deliberate per-piece probe surface (the durable() posture): pins reachable by no maintenance chain; production through-store serving is the entry's shared-cache shape or through_store_with - mem_registry removed (review 006 R-4): production-superseded by the entry's mem shape, zero non-test consumers; the lib.rs re-export gone, the module/backend docs updated, the consumers re-homed to testutil::mem_registry_pair, the OQ-03 inventory seed (review-wave-6.md) updated — the publish inventory omits the name Verification: cargo test (376), -p alkgit (376), -p alkgit-substrate (184), --all-features (379); both clippies -D warnings; fmt; cargo doc --no-deps (0 warnings); the fuzz corpus gate (13); the live-pg harness run (208 passed, 0 skips); taskgraph validate (90 tasks)
138 lines
5.2 KiB
Rust
138 lines
5.2 KiB
Rust
//! The test-harness helper home (review 006 R-3, the wave-10
|
|
//! consolidation): the one `#[cfg(test)]` module the suites' duplicated
|
|
//! helper copies collapsed into — the awaited-condition poll, the
|
|
//! record fixtures, the wake-runner spawn/join set, the unique-sqlite
|
|
//! db path and the scratch dir. Zero runtime coupling: the module is
|
|
//! compiled only under test, is `pub(crate)` (not public API
|
|
//! surface), and its consumers are the test modules across
|
|
//! `substrate/src/`. The engine-specific live-pg harness helpers
|
|
//! (schema + DSN plumbing) stay in [`crate::postgres::live_harness`];
|
|
//! this home is engine-neutral.
|
|
//!
|
|
//! The fixture-path helpers take the consuming module's name as the
|
|
//! `fixture` argument (the per-module temp prefixes the deleted copies
|
|
//! spelled inline), and keep the process id in the name so parallel
|
|
//! test binaries never collide; `unique_db_path` carries a process
|
|
//! counter (the sqlite twins open the same path twice in one test),
|
|
//! `scratch_dir` the nanosecond stamp.
|
|
|
|
#[cfg(feature = "sqlite")]
|
|
use std::sync::atomic::{AtomicU64, Ordering};
|
|
use std::time::Duration;
|
|
|
|
use alkstore::StopToken;
|
|
|
|
use alkgit::{RepoRecord, Visibility};
|
|
|
|
use crate::reactor::Reactor;
|
|
|
|
/// Poll an awaited condition until it lands or the wall-clock deadline
|
|
/// expires (the suites' shared shape — the 10s deadline the
|
|
/// maintenance suite's copy pinned, the pass path identical at either
|
|
/// deadline). The predicate is sync; a test that must poll an async
|
|
/// read awaits it inside its own loop instead.
|
|
pub(crate) async fn wait_until(pred: impl Fn() -> bool) {
|
|
let deadline = tokio::time::Instant::now() + Duration::from_secs(10);
|
|
while !pred() && tokio::time::Instant::now() < deadline {
|
|
tokio::time::sleep(Duration::from_millis(10)).await;
|
|
}
|
|
assert!(
|
|
pred(),
|
|
"the awaited condition did not land inside the deadline"
|
|
);
|
|
}
|
|
|
|
/// The public record fixture.
|
|
pub(crate) fn record(id: &str) -> RepoRecord {
|
|
RepoRecord {
|
|
repo_id: id.to_owned(),
|
|
visibility: Visibility::Public,
|
|
grants: Default::default(),
|
|
}
|
|
}
|
|
|
|
/// The private record fixture.
|
|
pub(crate) fn private_record(id: &str) -> RepoRecord {
|
|
RepoRecord {
|
|
repo_id: id.to_owned(),
|
|
visibility: Visibility::Private,
|
|
grants: Default::default(),
|
|
}
|
|
}
|
|
|
|
/// Spawn the reactor's wake runner (the tests' shared shape) and
|
|
/// return its stop token and join handle — the join asserts the
|
|
/// runner ran clean.
|
|
pub(crate) fn spawn_wake_runner(
|
|
reactor: std::sync::Arc<Reactor>,
|
|
) -> (StopToken, tokio::task::JoinHandle<()>) {
|
|
let stop = StopToken::new();
|
|
let join = tokio::spawn({
|
|
let stop = stop.clone();
|
|
async move {
|
|
reactor
|
|
.run_wake_runner(stop)
|
|
.await
|
|
.expect("the wake runner ran clean");
|
|
}
|
|
});
|
|
(stop, join)
|
|
}
|
|
|
|
/// Cancel the stop token and join the spawned run inside the 5s
|
|
/// deadline, asserting a clean end (the wake-runner/stream-consumer
|
|
/// sets' shared shape).
|
|
pub(crate) async fn stop_and_join(stop: StopToken, join: tokio::task::JoinHandle<()>) {
|
|
stop.cancel();
|
|
tokio::time::timeout(Duration::from_secs(5), join)
|
|
.await
|
|
.expect("the run ended inside the deadline")
|
|
.expect("the run ended clean");
|
|
}
|
|
|
|
/// A unique sqlite temp db path under the process id and a process
|
|
/// counter (the sqlite twins open the same path twice in one test) —
|
|
/// the engine-gated fixture the sqlite-combo suites consume.
|
|
#[cfg(all(test, feature = "sqlite"))]
|
|
pub(crate) fn unique_db_path(fixture: &str, tag: &str) -> std::path::PathBuf {
|
|
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
|
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
|
|
std::env::temp_dir().join(format!(
|
|
"alkgit-substrate-{fixture}-{}-{n}-{tag}.db",
|
|
std::process::id()
|
|
))
|
|
}
|
|
|
|
/// A unique scratch directory under the process id and the
|
|
/// nanosecond stamp, created on demand (its contents the test's own
|
|
/// cleanup business).
|
|
pub(crate) fn scratch_dir(fixture: &str, tag: &str) -> std::path::PathBuf {
|
|
let nanos = std::time::SystemTime::now()
|
|
.duration_since(std::time::UNIX_EPOCH)
|
|
.unwrap_or_default()
|
|
.as_nanos();
|
|
let dir = std::env::temp_dir().join(format!(
|
|
"alkgit-substrate-{fixture}-{tag}-{}-{nanos}",
|
|
std::process::id()
|
|
));
|
|
std::fs::create_dir_all(&dir).unwrap_or_default();
|
|
dir
|
|
}
|
|
|
|
/// The mem binding's record pair — the in-memory adapter bound onto
|
|
/// the core's registry traits with the write-through-provider shape
|
|
/// the twin assemblies construct (the removed public `mem_registry`
|
|
/// constructor's test-only survivor; review 006 R-4 — the production
|
|
/// composition of the same adapter is the entry's mem shape,
|
|
/// [`crate::compose::RegistryLegs::mem`]).
|
|
pub(crate) fn mem_registry_pair() -> (
|
|
std::sync::Arc<dyn crate::registry::RegistryWrite>,
|
|
std::sync::Arc<dyn alkgit::GitRegistryStore>,
|
|
) {
|
|
use crate::registry::{CachedRegistryProvider, InMemoryRegistry, RegistryBackend};
|
|
let registry = std::sync::Arc::new(InMemoryRegistry::new());
|
|
let provider = std::sync::Arc::new(CachedRegistryProvider::new());
|
|
let store = RegistryBackend::with_cached_provider(registry.clone(), provider);
|
|
(registry, std::sync::Arc::new(store))
|
|
}
|