#![allow(
clippy::unwrap_used,
reason = "tests use unwrap for clarity and brevity"
)]
#![allow(
clippy::shadow_unrelated,
reason = "tests shadow variables for readability"
)]
#![allow(
clippy::match_same_arms,
reason = "exhaustive match documents known variants"
)]
#![allow(
clippy::explicit_counter_loop,
reason = "counter loop is clearer for version validation"
)]
#![allow(clippy::panic, reason = "tests use panic for assertions")]
#![allow(
clippy::drop_non_drop,
reason = "explicit drops for lending iterator documentation"
)]
#![allow(clippy::str_to_string, reason = "tests use to_string/to_owned freely")]
use futures::StreamExt;
use mnesis::{ErrorId, Version};
use mnesis_inmemory::InMemoryStore;
use mnesis_inmemory::InMemoryStoreError;
use mnesis_store::PendingBatch;
use mnesis_store::error::StoreError;
use mnesis_store::pending_envelope;
use mnesis_store::store::RawEventStore;
type TestStoreError = StoreError<InMemoryStoreError, std::io::Error, std::io::Error>;
fn label(s: &str) -> ErrorId {
ErrorId::from_display(&s)
}
#[test]
fn c2_builder_accepts_empty_event_type() {
let envelope = pending_envelope(Version::INITIAL)
.event_type("")
.payload(vec![1])
.build()
.expect("valid envelope");
assert_eq!(envelope.event_type(), "");
}
#[tokio::test]
async fn h5_append_with_non_sequential_versions() {
let store = InMemoryStore::new();
let envelopes = vec![
pending_envelope(Version::new(3).unwrap())
.event_type("E")
.payload(vec![])
.build()
.expect("valid envelope"),
pending_envelope(Version::new(1).unwrap())
.event_type("E")
.payload(vec![])
.build()
.expect("valid envelope"),
pending_envelope(Version::new(5).unwrap())
.event_type("E")
.payload(vec![])
.build()
.expect("valid envelope"),
];
let result = store
.append(
&mnesis_store::StreamKey::from_slice(b"s1"),
None,
PendingBatch::new(&envelopes).expect("non-empty batch"),
)
.await;
assert!(
result.is_err(),
"Append should reject non-sequential version order"
);
}
#[tokio::test]
async fn h5_append_with_duplicate_versions() {
let store = InMemoryStore::new();
let envelopes = vec![
pending_envelope(Version::INITIAL)
.event_type("E")
.payload(vec![])
.build()
.expect("valid envelope"),
pending_envelope(Version::INITIAL)
.event_type("E")
.payload(vec![])
.build()
.expect("valid envelope"), ];
let result = store
.append(
&mnesis_store::StreamKey::from_slice(b"s1"),
None,
PendingBatch::new(&envelopes).expect("non-empty batch"),
)
.await;
assert!(result.is_err(), "Append should reject duplicate versions");
}
#[test]
fn h4_store_error_size() {
let size = std::mem::size_of::<TestStoreError>();
assert!(
size <= 256,
"StoreError is {size} bytes — too large for stack on constrained devices"
);
}
#[test]
fn m2_pending_envelope_no_partial_eq() {
let e1 = pending_envelope(Version::INITIAL)
.event_type("E")
.payload(vec![1])
.build()
.expect("valid envelope");
let e2 = pending_envelope(Version::INITIAL)
.event_type("E")
.payload(vec![1])
.build()
.expect("valid envelope");
assert_eq!(e1.version(), e2.version());
assert_eq!(e1.event_type(), e2.event_type());
assert_eq!(e1.payload(), e2.payload());
}
#[tokio::test]
async fn read_nonexistent_stream_returns_empty() {
let store = InMemoryStore::new();
let mut stream = store
.read_stream(
&mnesis_store::StreamKey::from_slice(b"does-not-exist"),
Version::INITIAL,
)
.await
.unwrap();
assert!(
stream.next().await.is_none(),
"Reading a nonexistent stream should return empty, not error"
);
}
#[tokio::test]
async fn streams_are_isolated() {
let store = InMemoryStore::new();
let e1 = vec![
pending_envelope(Version::INITIAL)
.event_type("EventA")
.payload(vec![1])
.build()
.expect("valid envelope"),
];
let e2 = vec![
pending_envelope(Version::INITIAL)
.event_type("EventB")
.payload(vec![2])
.build()
.expect("valid envelope"),
];
store
.append(
&mnesis_store::StreamKey::from_slice(b"stream-a"),
None,
PendingBatch::new(&e1).expect("non-empty batch"),
)
.await
.unwrap();
store
.append(
&mnesis_store::StreamKey::from_slice(b"stream-b"),
None,
PendingBatch::new(&e2).expect("non-empty batch"),
)
.await
.unwrap();
let mut stream = store
.read_stream(
&mnesis_store::StreamKey::from_slice(b"stream-a"),
Version::INITIAL,
)
.await
.unwrap();
let envelope = stream.next().await.unwrap().unwrap();
assert_eq!(envelope.event_type(), "EventA");
assert_eq!(envelope.payload(), &[1]);
drop(envelope);
assert!(stream.next().await.is_none());
let mut stream = store
.read_stream(
&mnesis_store::StreamKey::from_slice(b"stream-b"),
Version::INITIAL,
)
.await
.unwrap();
let envelope = stream.next().await.unwrap().unwrap();
assert_eq!(envelope.event_type(), "EventB");
assert_eq!(envelope.payload(), &[2]);
drop(envelope);
assert!(stream.next().await.is_none());
}
#[test]
fn store_error_variants_are_known() {
let err: TestStoreError = StoreError::StreamNotFound {
stream_id: label("test"),
};
match err {
StoreError::Conflict { .. } => {}
StoreError::StreamNotFound { .. } => {}
StoreError::Encode(_) => {}
StoreError::Decode(_) => {}
StoreError::Adapter(_) => {}
StoreError::Kernel(_) => {}
StoreError::VersionOverflow => {}
StoreError::EnvelopeSynthesis(_) => {}
StoreError::Envelope(_) => {}
_ => {}
}
}