Files
alkgit/substrate/src/testutil.rs
T
glm-5.3-flash 525bd3776f refactor(substrate): w10 — the per-piece parity class + the mem_registry disposition (review 006 R-6/R-4, F-w10-B2)
- 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)
2026-10-24 17:14:13 +00:00

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))
}