use super::fs::{ReadFaultKind, SimFs};
use crate::coordinate::Coordinate;
use crate::event::EventKind;
use crate::id::EventId;
use crate::store::platform::fs::StoreFs;
use crate::store::{Store, StoreConfig, StoreError};
use std::sync::Arc;
const KIND: EventKind = EventKind::custom(0xC, 0x5);
fn open_with_one_event(dir: &std::path::Path, sim_fs: &Arc<SimFs>) -> (Store, EventId) {
let config = StoreConfig::new(dir).with_fs(Arc::clone(sim_fs) as Arc<dyn StoreFs>);
let store = Store::open(config).expect("open store over SimFs");
let coord = Coordinate::new("entity:read-fault", "scope:read-fault").expect("coordinate");
let receipt = store
.append(&coord, KIND, &serde_json::json!({ "n": 1 }))
.expect("append one event to the active segment");
(store, receipt.event_id)
}
#[test]
fn read_fault_short_read_zero_surfaces_corrupt_eof() {
let dir = tempfile::tempdir().expect("tmpdir");
let sim_fs = Arc::new(SimFs::new(0x0EAD_0001, 0));
let (store, event_id) = open_with_one_event(dir.path(), &sim_fs);
store
.get(event_id)
.expect("PROPERTY: an honest active-segment read must succeed (control)");
sim_fs.arm_read_fault_on(1, ReadFaultKind::ShortRead { bytes_read: 0 });
let err = store
.get(event_id)
.expect_err("a torn active-segment read must surface an error");
assert!(
matches!(&err, StoreError::CorruptSegment { detail, .. } if detail.contains("unexpected EOF")),
"PROPERTY: a routed read faulted with ShortRead{{0}} must map to corrupt_eof, got {err:?}"
);
}
#[test]
fn read_fault_partial_short_read_surfaces_corrupt_segment() {
let dir = tempfile::tempdir().expect("tmpdir");
let sim_fs = Arc::new(SimFs::new(0x0EAD_0002, 0));
let (store, event_id) = open_with_one_event(dir.path(), &sim_fs);
store
.get(event_id)
.expect("PROPERTY: an honest active-segment read must succeed (control)");
sim_fs.arm_read_fault_on(1, ReadFaultKind::ShortRead { bytes_read: 4 });
let err = store
.get(event_id)
.expect_err("a partial active-segment read must surface an error");
assert!(
matches!(
&err,
StoreError::CorruptSegment { detail, .. } if detail.contains("ended before requested length")
),
"PROPERTY: a routed read faulted with a non-zero ShortRead must map to a corrupt-segment error, got {err:?}"
);
}
#[test]
fn read_fault_io_surfaces_store_io() {
let dir = tempfile::tempdir().expect("tmpdir");
let sim_fs = Arc::new(SimFs::new(0x0EAD_0003, 0));
let (store, event_id) = open_with_one_event(dir.path(), &sim_fs);
store
.get(event_id)
.expect("PROPERTY: an honest active-segment read must succeed (control)");
sim_fs.arm_read_fault_on(1, ReadFaultKind::Io);
let err = store
.get(event_id)
.expect_err("a faulted active-segment read must surface an error");
assert!(
matches!(&err, StoreError::Io(_)),
"PROPERTY: a routed read faulted with Io must surface as StoreError::Io, got {err:?}"
);
}
#[test]
fn read_fault_is_one_shot_on_the_targeted_occurrence() {
let dir = tempfile::tempdir().expect("tmpdir");
let sim_fs = Arc::new(SimFs::new(0x0EAD_0004, 0).with_read_fault_on(1, ReadFaultKind::Io));
let (store, event_id) = open_with_one_event(dir.path(), &sim_fs);
let err = store
.get(event_id)
.expect_err("the first read must fault (the targeted occurrence)");
assert!(
matches!(&err, StoreError::Io(_)),
"PROPERTY: the pre-armed Io read fault must surface as StoreError::Io, got {err:?}"
);
store.get(event_id).expect(
"PROPERTY: a read fault targets exactly one occurrence; the next read must succeed",
);
}