#![allow(clippy::await_holding_lock)]
use aedb::commit::validation::Mutation;
use aedb::config::{AedbConfig, DurabilityMode, RecoveryMode};
use aedb::faults::{self, FaultPlan};
use aedb::offline;
use aedb::{AedbInstance, CommitFinality};
use std::sync::Mutex;
use tempfile::tempdir;
static SERIAL: Mutex<()> = Mutex::new(());
fn durable_config() -> AedbConfig {
AedbConfig {
durability_mode: DurabilityMode::Full,
recovery_mode: RecoveryMode::Permissive,
hash_chain_required: false,
..AedbConfig::default()
}
}
async fn seed(db: &AedbInstance) {
db.create_project("p").await.expect("project");
db.kv_set("p", "app", b"k0".to_vec(), b"v0".to_vec())
.await
.expect("seed kv");
}
#[tokio::test]
async fn injected_wal_sync_failure_aborts_commit_and_store_stays_consistent() {
let _guard = SERIAL.lock().unwrap();
faults::reset();
let dir = tempdir().expect("temp");
let config = durable_config();
let db = AedbInstance::open_anonymous(config.clone(), dir.path()).expect("open");
seed(&db).await;
faults::arm("wal_sync", FaultPlan::Always);
let result = db
.commit_with_finality(
Mutation::KvSet {
project_id: "p".into(),
scope_id: "app".into(),
key: b"k1".to_vec(),
value: b"v1".to_vec(),
},
CommitFinality::Durable,
)
.await;
assert!(
result.is_err(),
"commit must fail when the WAL sync fault fires"
);
faults::reset();
db.kv_set("p", "app", b"k2".to_vec(), b"v2".to_vec())
.await
.expect("commit after fault clears");
db.shutdown().await.expect("shutdown");
drop(db);
let report = offline::verify_database(dir.path(), &config);
assert!(report.ok, "store must stay consistent: {report:?}");
faults::reset();
}
#[tokio::test]
async fn injected_manifest_write_failure_surfaces_on_checkpoint() {
let _guard = SERIAL.lock().unwrap();
faults::reset();
let dir = tempdir().expect("temp");
let config = durable_config();
let db = AedbInstance::open_anonymous(config.clone(), dir.path()).expect("open");
seed(&db).await;
faults::arm("manifest_write", FaultPlan::Once);
let checkpoint = db.checkpoint_now().await;
assert!(
checkpoint.is_err(),
"checkpoint must fail when manifest write fault fires"
);
faults::reset();
db.checkpoint_now().await.expect("checkpoint after clear");
db.shutdown().await.expect("shutdown");
drop(db);
let report = offline::verify_database(dir.path(), &config);
assert!(report.ok, "store must stay consistent: {report:?}");
faults::reset();
}