- docs honesty audit passed: README/CHANGELOG/crate docs vs shipped reality; the quick-start fences run against the real API and the tables match ADR-013 §4 and the manifest - two inline fixes from the matrix re-run: tests/readme_examples.rs (landed after feature-graph-verification recorded its matrix) was breaking every no-sqlite/no-mem build with unconditional mem_only/dual_tier_sqlite_local calls — fences now feature-gated to the constructors they exercise with per-fn imports; the README pg row now clean-skips on unset ALKBLOBS_PG_HOST and defaults to the harness values (was a hard connect to localhost:5432) - one docs-consistency drift miss fixed: AGENTS.md Architecture Context bullet still read 'ADRs 001-006 accepted' - the lifecycle flip: the seven release-facing specs (overview, requirements, hashing-and-keys, store-api, backends-and-dispatch, gc-and-namespaces, ops-surface) draft -> stable with the dated gate note; the architecture README table + Current State + Lifecycle line moved with them; open-questions.md stays draft - wave 12 stamped completed in the implementation plan; AGENTS.md status: Phase 1 complete - release-ready, pre-publish Verification (live pglo-poc harness per AGENTS.md): - cargo test 209 green; cargo test --all-features 460 green (pg columns ran, not skipped) - clippy default + --all-features (-D warnings) clean; fmt --check clean; cargo doc --no-deps zero warnings both configs - fuzz corpus-replay gate green (7/7) - cargo publish --dry-run --allow-dirty green (92 files, 2.4MiB) - feature matrix rows 5-10 re-run green after the readme fix
126 lines
4.1 KiB
Rust
126 lines
4.1 KiB
Rust
//! The README quick start's verification: each fence's call shape
|
|
//! compiles and runs against the real API as an async test (the
|
|
//! filesystem arguments are minted per-run instead of the README's
|
|
//! literal `./` paths, so the suite never writes the repo root). Each
|
|
//! fence is feature-gated to the constructor it exercises (mem, the
|
|
//! sqlite default, the postgres kv-only form) — the same gate the
|
|
//! constructor carries — so the file compiles on every matrix build;
|
|
//! the pg row additionally needs its live harness (`ALKBLOBS_PG_*`
|
|
//! env — unset means a clean skip, the conformance columns'
|
|
//! harness convention), like the conformance pg columns.
|
|
|
|
#[cfg(feature = "mem")]
|
|
#[tokio::test]
|
|
async fn readme_quick_start_mem() {
|
|
use alkblobs::Store;
|
|
let store = Store::mem_only();
|
|
|
|
let content: &[u8] = b"hello, blobs";
|
|
let (key, pin) = store
|
|
.put(Some(content.len() as u64), std::io::Cursor::new(content))
|
|
.await
|
|
.unwrap();
|
|
|
|
let (len, blob) = store.get(&key.as_bytes()).await.unwrap().unwrap();
|
|
assert_eq!(blob.read_at(0, len).await.unwrap(), content);
|
|
|
|
drop(pin);
|
|
let report = store.sweep().await.unwrap();
|
|
assert!(report.aborted.is_some());
|
|
assert_eq!(report.deleted, 0);
|
|
}
|
|
|
|
#[cfg(feature = "sqlite")]
|
|
#[tokio::test]
|
|
async fn readme_quick_start_sqlite_local() {
|
|
use alkblobs::{Store, DEFAULT_DISPATCH_THRESHOLD};
|
|
use std::time::Duration;
|
|
|
|
let dir = std::env::temp_dir().join(format!("alkblobs-readme-sqlite-{}", std::process::id()));
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
let store = Store::dual_tier_sqlite_local(
|
|
dir.join("./blobs.db"),
|
|
dir.join("./blobs-root"),
|
|
DEFAULT_DISPATCH_THRESHOLD,
|
|
Duration::from_secs(300),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let content: &[u8] = b"durable path";
|
|
let (key, _pin) = store
|
|
.put(Some(content.len() as u64), std::io::Cursor::new(content))
|
|
.await
|
|
.unwrap();
|
|
assert!(store.has(&key.as_bytes()).await.unwrap());
|
|
|
|
std::fs::remove_dir_all(&dir).unwrap();
|
|
}
|
|
|
|
#[cfg(feature = "postgres")]
|
|
#[tokio::test]
|
|
async fn readme_quick_start_postgres_kv_only() {
|
|
use alkblobs::{PgOpts, Store, DEFAULT_DISPATCH_THRESHOLD};
|
|
use std::sync::atomic::{AtomicU64, Ordering};
|
|
use std::time::Duration;
|
|
|
|
let host = match std::env::var("ALKBLOBS_PG_HOST") {
|
|
Ok(v) if !v.is_empty() => v,
|
|
_ => {
|
|
eprintln!(
|
|
"skipping (no postgres harness server): set ALKBLOBS_PG_HOST/PORT/USER/PASSWORD/DB"
|
|
);
|
|
return;
|
|
}
|
|
};
|
|
let port = std::env::var("ALKBLOBS_PG_PORT")
|
|
.ok()
|
|
.filter(|v| !v.is_empty())
|
|
.unwrap_or_else(|| "15432".to_string());
|
|
let user = std::env::var("ALKBLOBS_PG_USER")
|
|
.ok()
|
|
.filter(|v| !v.is_empty())
|
|
.unwrap_or_else(|| "postgres".to_string());
|
|
let password = std::env::var("ALKBLOBS_PG_PASSWORD")
|
|
.ok()
|
|
.filter(|v| !v.is_empty())
|
|
.unwrap_or_else(|| "poc".to_string());
|
|
let db = std::env::var("ALKBLOBS_PG_DB")
|
|
.ok()
|
|
.filter(|v| !v.is_empty())
|
|
.unwrap_or_else(|| "blobs".to_string());
|
|
let dsn = format!("host={host} port={port} user={user} password={password} dbname={db}");
|
|
static SEQ: AtomicU64 = AtomicU64::new(0);
|
|
let schema = format!(
|
|
"alkblobs_readme_pg_kv_{}_{}",
|
|
std::process::id(),
|
|
SEQ.fetch_add(1, Ordering::Relaxed)
|
|
);
|
|
|
|
let store = Store::kv_only_postgres(
|
|
&dsn,
|
|
PgOpts {
|
|
schema: schema.clone(),
|
|
..PgOpts::default()
|
|
},
|
|
DEFAULT_DISPATCH_THRESHOLD,
|
|
Duration::from_secs(300),
|
|
)
|
|
.await
|
|
.unwrap();
|
|
|
|
let content: &[u8] = b"pool-backed path";
|
|
let (key, _pin) = store
|
|
.put(Some(content.len() as u64), std::io::Cursor::new(content))
|
|
.await
|
|
.unwrap();
|
|
assert!(store.has(&key.as_bytes()).await.unwrap());
|
|
|
|
let (client, conn) = tokio_postgres::connect(&dsn, tokio_postgres::NoTls)
|
|
.await
|
|
.unwrap();
|
|
tokio::spawn(conn);
|
|
let drop_schema = format!("DROP SCHEMA {schema} CASCADE");
|
|
client.batch_execute(&drop_schema).await.unwrap();
|
|
}
|