Files
alkstore/alkstore-mem/tests/contract_suite.rs
T
glm-5.3-flash 9301bb926b core: verify and pin the receiver's pre-yield offset() value (review 004 item 12, core-pin-offset-initial-value) — the three engines verified equal live, so the doc-pin branch applies: offset() reads 0 before any yield on every receiver — fresh consumer, stored positive checkpoint, stored negative seed (ADR-023 §2's totality arm, the one a checkpoint-seeded realization would surface on) — the no-yield sentinel, never the stored checkpoint: the checkpoint gates the feed's attach-read start while the receiver's offset tracks the last-yielded event only, composing with the pinned pre-yield save_offset() no-op; the attach read's prefetches sit unobserved and the first recv/try_recv observation is the first move (then the post-yield value, receiver_close_and_save_arms' pin). Verified by code read first (all three offset() bodies are self.position, all three constructors seed position: 0 unconditionally — mem stream.rs:353, sqlite :399, pg :620) then live: new suite row receiver_offset_pre_yield_is_zero (alkstore-contract-suite, stamped ADR-008 §8 + the core-contract.md pin, four arms incl. the first-observation composition leg) green on all three columns, pg against the harness server (parked pglo-poc :15432); pinned in three places: core-contract.md streams/subscribe row (dated 2026-10-11 annotation) + a verification-backlog bullet recording the verified pin, the core trait doc on offset() (core's doc comments are contract text), and the suite row itself — ADR-008 §8's shape comment deliberately left untouched (doc-only verification takes no ADR amendment, noted in the task); no engine code changed, no divergence to raise; gates green: workspace build/tests server-less, full pg column vs harness (135+26+9), clippy -D warnings, fmt --check, doc gate zero warnings, fuzz corpus replay 6/6, wasm32 mem check, taskgraph validate (96); task -> completed
2026-10-11 14:34:15 +00:00

227 lines
8.1 KiB
Rust

//! The suite-driving test target: the mem engine's third backlog
//! column (ADR-022's factory reused verbatim — the rows live in
//! `alkstore-contract-suite`, this target is the executor that runs
//! them against the mem factory; the born-pinned posture per
//! ADR-024 §5 — the engine lands against the finished suite).
//!
//! The factory: a fresh `MemStore::new()` per `open` — distinct
//! engine instances sharing nothing (ADR-024 §4's isolation rule,
//! exactly the factory's isolation contract). Teardown is drop — the
//! store handle itself *is* the backing store, so the holder's drop
//! is the whole teardown; a teardown call has nothing external to
//! dispose of and is trivially idempotent.
//!
//! Flavor: every test runs under the **current-thread** runtime —
//! the current-thread-clean posture of the engine's internals
//! (no `spawn_blocking` anywhere) proven by the flavor the rows
//! actually run under ([ADR-024](../../docs/architecture/decisions/024-mem-engine.md)
//! §5). A blocking call inside a row or inside the engine would
//! stall this flavor into a hang, not pass.
use alkstore::{BoxedFuture, Result, Store};
use alkstore_contract_suite::StoreFactory;
use alkstore_mem::MemStore;
/// The mem suite factory: a fresh isolated engine instance per
/// `open`, teardown = drop (the consumer holds the handle; an
/// explicit teardown call is the trivial no-op — nothing exists
/// outside the instance to dispose of).
struct MemFactory;
impl StoreFactory for MemFactory {
fn open(&self) -> BoxedFuture<'_, Result<Box<dyn Store>>> {
Box::pin(async move { Ok(Box::new(MemStore::new()) as Box<dyn Store>) })
}
fn teardown(&self) -> BoxedFuture<'_, Result<()>> {
Box::pin(async move { Ok(()) })
}
}
mod rows {
use alkstore_contract_suite::properties::{
backoff_curve_equivalence, concurrent_try_lock_loser_is_a_value,
drop_rollback_leaves_no_ghosts, duration_refusal_on_non_positive_ttl,
enqueue_opts_resolution, extent_clamp_semantics, in_tx_reads_see_own_writes,
job_handle_validity_predicate, lock_ttl_expiry_and_reacquisition,
name_validation_rejects_empty_and_reserved, outbox_enqueue_tx_commit_atomicity,
payload_round_trip_stores_exact_encoding, payload_too_large_never_produced_on_mem,
publish_with_key_tx_commit_atomicity, queue_depth_reclaim_and_dead_letter,
receiver_close_and_save_arms, receiver_offset_pre_yield_is_zero, scheduler_boundary_fires,
scheduler_bounded_catchup, scheduler_leadership_discipline, stream_ordering_equivalence,
sweep_no_stranded_rows, trim_to_semantics, wake_receiver_shapes,
with_tx_panicking_closure_rolls_back,
};
use super::MemFactory;
fn factory() -> MemFactory {
MemFactory
}
#[tokio::test(flavor = "current_thread")]
async fn row_name_validation_rejects_empty_and_reserved() {
name_validation_rejects_empty_and_reserved(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_extent_clamp_semantics() {
extent_clamp_semantics(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_duration_refusal_on_non_positive_ttl() {
duration_refusal_on_non_positive_ttl(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_lock_ttl_expiry_and_reacquisition() {
lock_ttl_expiry_and_reacquisition(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_concurrent_try_lock_loser_is_a_value() {
concurrent_try_lock_loser_is_a_value(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_payload_round_trip_stores_exact_encoding() {
payload_round_trip_stores_exact_encoding(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_payload_too_large_never_produced_on_mem() {
payload_too_large_never_produced_on_mem(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_drop_rollback_leaves_no_ghosts() {
drop_rollback_leaves_no_ghosts(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_in_tx_reads_see_own_writes() {
in_tx_reads_see_own_writes(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_enqueue_opts_resolution() {
enqueue_opts_resolution(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_backoff_curve_equivalence() {
backoff_curve_equivalence(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_receiver_close_and_save_arms() {
receiver_close_and_save_arms(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_receiver_offset_pre_yield_is_zero() {
receiver_offset_pre_yield_is_zero(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_job_handle_validity_predicate() {
job_handle_validity_predicate(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_queue_depth_reclaim_and_dead_letter() {
queue_depth_reclaim_and_dead_letter(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_sweep_no_stranded_rows() {
sweep_no_stranded_rows(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_scheduler_boundary_fires() {
scheduler_boundary_fires(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_scheduler_bounded_catchup() {
scheduler_bounded_catchup(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_scheduler_leadership_discipline() {
scheduler_leadership_discipline(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_outbox_enqueue_tx_commit_atomicity() {
outbox_enqueue_tx_commit_atomicity(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_publish_with_key_tx_commit_atomicity() {
publish_with_key_tx_commit_atomicity(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_stream_ordering_equivalence() {
stream_ordering_equivalence(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_trim_to_semantics() {
trim_to_semantics(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_wake_receiver_shapes() {
wake_receiver_shapes(&factory()).await;
}
#[tokio::test(flavor = "current_thread")]
async fn row_with_tx_panicking_closure_rolls_back() {
with_tx_panicking_closure_rolls_back(&factory()).await;
}
}
mod factory_shape {
use serde_json::json;
use alkstore_contract_suite::StoreFactory;
use super::MemFactory;
/// ADR-022's factory contract, verbatim on the mem engine: every
/// open yield is isolated (distinct `MemStore::new()` instances
/// sharing nothing — proved behaviorally, a row one store lands
/// being invisible to the other) and teardown is idempotent per
/// instance; a failing assertion early-exit (the panic path)
/// leaves teardown safe against the abandoned store — for mem the
/// abandoned store simply drops with the test's frame.
#[tokio::test(flavor = "current_thread")]
async fn opens_are_isolated_and_teardown_is_idempotent() {
let factory = MemFactory;
let a = factory.open().await.unwrap();
let b = factory.open().await.unwrap();
let sa = a.stream("iso").await.unwrap();
let event_offset = sa.publish(json!({"iso": true})).await.unwrap();
assert!(event_offset > 0);
let sb = b.stream("iso").await.unwrap();
let seen = sb.read_since(0, 100).await.unwrap();
assert!(
seen.is_empty(),
"two opens must never share rows — b saw {seen:?}"
);
drop(sa);
drop(sb);
drop(a);
drop(b);
factory.teardown().await.unwrap();
factory.teardown().await.unwrap();
}
}