Files
alkblobs/tests/conformance/binding.rs
T
glm-5.3-flash 532adfa707 feat(store): the Store facade — constructor modes, size-threshold dispatch, the factory binding
The wave-2 facade skeleton and routing (tasks/store-construction.md):
the public surface consumers program against, with the ops methods
still to land against it in store-read-paths/gc-protocol/store-put-paths.

- src/store.rs — the public `Store` type (engine-blind, ADR-007/008/
  010) over crate-internal tier enums (`KvEngine`{mem}, `LargeEngine`{
  local}): the engine-representation design point decided as concrete
  + enum dispatch, generic rejected (pub(crate) engines, no dyn);
  the three constructor modes as named constructors — `mem_only`,
  `kv_only_mem`, `dual_tier_mem_local` (the ADR-012 §6.3
  dispatch-coverage shape) — with `StoreMode` introspection; the
  write-side routing seam (`tier_for`), the read-side fall-through
  order (`kv()` then `large()`, ADR-012 §6.1), and the `GcHome`
  GC-state field; the ADR-012 §3 fleet declaration ruled deferred to
  the engine waves (nothing fleet-valid is constructible yet)
- src/dispatch.rs — the pure routing function (`route`, at-or-below
  → kv), `DEFAULT_DISPATCH_THRESHOLD` (128 KiB) exported at the
  crate root, boundary-value unit tests (exactly-at-threshold,
  degenerate tunings, purity)
- src/gc.rs — `GcHome`: the store-instance GC-state home the
  gc-protocol task fills (in-process; fleet state stays on the
  engine-state seam per ADR-010 §2/ADR-012 §4)
- tests/conformance — the wave-2 binding made as anticipated: the
  factory's `Store` associated type collapsed into the concrete
  `alkblobs::Store` (`open` vends it directly), recorded in
  factory.rs + README; the wave-1 mock probe retired, replaced by
  mem-gated binding demo factories (binding.rs) over both
  constructible shapes

Verification: `cargo test` (default), `cargo test --features mem`,
`cargo test --all-features` green (41/53/53 unit + conformance
targets green in all three shapes); `cargo clippy --all-targets
-- -D warnings` green in all three feature shapes; `cargo fmt
--check` green; `cargo doc --no-deps` adds no warnings beyond the
pre-existing private-intra-doc-links class.
2026-10-11 22:18:32 +00:00

141 lines
4.9 KiB
Rust

//! The wave-2 factory binding demonstrator (mem-gated — mem is the
//! skip-free reference shape): the concrete `Store` facade vended by
//! [`StoreFactory::open`] in both constructible wave-2 shapes, the
//! factory contract's isolation + idempotent-teardown requirements
//! exercised, the wave-1 mock probe retired by the binding. The real
//! columns carry these factories forward with rows (suite-column-mem,
//! suite-column-local).
#![allow(dead_code)]
use std::path::PathBuf;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Mutex;
use std::time::{SystemTime, UNIX_EPOCH};
use crate::factory::StoreFactory;
use alkblobs::{Error, Store, StoreMode, DEFAULT_DISPATCH_THRESHOLD};
/// The binding demo factory, mem-only mode: a fresh `BTreeMap`-backed
/// store per `open` (isolated by construction — the ephemeral engine
/// shares nothing across instances), teardown a no-op. Mem is the
/// skip-free reference shape, so this column never skips.
struct MemOnlyBinding;
impl StoreFactory for MemOnlyBinding {
fn name(&self) -> &'static str {
"mem-only (binding demo)"
}
async fn open(&self) -> Result<Store, Error> {
Ok(Store::mem_only())
}
async fn teardown(&self) -> Result<(), Error> {
Ok(())
}
}
/// A fresh temp root per factory `open` — the isolated backing for the
/// dual-tier binding demo. No Drop guard: teardown owns removal (the
/// factory contract's ownership; a factory dropped without teardown
/// leaks its temp roots — harmless temp debris, and teardown makes
/// sense over the abandoned instance per the contract).
fn fresh_temp_root() -> PathBuf {
static SEQ: AtomicU64 = AtomicU64::new(0);
let seq = SEQ.fetch_add(1, Ordering::Relaxed);
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
let dir = std::env::temp_dir().join(format!(
"alkblobs-conformance-{}-{:x}-{seq}",
std::process::id(),
nanos
));
std::fs::create_dir_all(&dir).expect("temp root created");
dir
}
/// The binding demo factory, dual-tier mode (the ADR-012 §6.3
/// dispatch-coverage shape, `kv = mem` + `large = local` at the default
/// threshold): a fresh temp directory per `open` so two opens never
/// share state, teardown removing every root it has vended (idempotent
/// — a repeated teardown is a no-op) and safe against abandoned
/// instances (each root is private to its open, so a half-open
/// instance's root is removable the same way).
struct DualTierBinding {
roots: Mutex<Vec<PathBuf>>,
}
impl Default for DualTierBinding {
fn default() -> Self {
DualTierBinding {
roots: Mutex::new(Vec::new()),
}
}
}
impl StoreFactory for DualTierBinding {
fn name(&self) -> &'static str {
"dual-tier mem+local (binding demo)"
}
async fn open(&self) -> Result<Store, Error> {
let path = fresh_temp_root();
let store = Store::dual_tier_mem_local(path.clone(), DEFAULT_DISPATCH_THRESHOLD).await?;
self.roots
.lock()
.unwrap_or_else(|e| e.into_inner())
.push(path);
Ok(store)
}
async fn teardown(&self) -> Result<(), Error> {
for root in self
.roots
.lock()
.unwrap_or_else(|e| e.into_inner())
.drain(..)
{
match std::fs::remove_dir_all(&root) {
Ok(()) => {}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {}
Err(e) => return Err(Error::Io(e.to_string())),
}
}
Ok(())
}
}
/// The binding holds: the demo factory vends the concrete facade in
/// the mem-only mode, reports its identity, and tears down idempotently.
#[tokio::test]
async fn binding_factory_vends_the_concrete_store_facade() {
let factory = MemOnlyBinding;
assert_eq!(factory.name(), "mem-only (binding demo)");
let store = factory.open().await.expect("open vends the store");
assert_eq!(store.mode(), StoreMode::MemOnly);
factory.teardown().await.expect("teardown is idempotent");
factory.teardown().await.expect("teardown repeats cleanly");
}
/// The binding holds in dual-tier mode too: every `open` is isolated
/// (fresh backing), the vend store reports the dispatch-coverage
/// shape, and teardown across open-then-teardown cycles is idempotent.
#[tokio::test]
async fn dual_tier_binding_factory_vends_isolated_stores() {
let factory = DualTierBinding::default();
assert_eq!(factory.name(), "dual-tier mem+local (binding demo)");
for _ in 0..2 {
let store = factory.open().await.expect("open vends the store");
assert_eq!(store.mode(), StoreMode::DualTier, "dispatch-coverage shape");
}
factory
.teardown()
.await
.expect("teardown removes the roots");
factory.teardown().await.expect("teardown repeats cleanly");
}