pub mod frame;
#[cfg(all(feature = "wasm", target_arch = "wasm32", target_os = "unknown"))]
mod browser;
#[cfg(all(feature = "wasm", target_arch = "wasm32", target_os = "unknown"))]
pub use browser::{BrowserWal, IndexedDbWal, OpfsWal, OpfsWalError, WalBackend};
#[cfg(all(test, not(feature = "wasm")))]
mod parity_tests {
use super::frame;
use crate::wal::{DurableWal, FsyncPolicy, WalEntry, WalError};
fn temp_wal_path() -> std::path::PathBuf {
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(0);
let mut path = std::env::temp_dir();
let unique = format!(
"haematite-opfs-parity-{}-{}.wal",
std::process::id(),
COUNTER.fetch_add(1, Ordering::Relaxed)
);
path.push(unique);
path
}
#[test]
fn frame_bytes_are_byte_identical_to_native_writer() -> Result<(), WalError> {
let entries = vec![
WalEntry::put(b"alpha".to_vec(), b"beta".to_vec()),
WalEntry::delete(b"gamma".to_vec()),
];
let path = temp_wal_path();
let _ = std::fs::remove_file(&path);
{
let mut wal = DurableWal::new(&path, FsyncPolicy::PerWrite)?;
for entry in &entries {
wal.append(entry)?;
}
}
let native_bytes = std::fs::read(&path)?;
let _ = std::fs::remove_file(&path);
let mut frame_bytes = Vec::new();
for entry in &entries {
frame_bytes.extend_from_slice(&frame::frame_entry(entry));
}
assert_eq!(frame_bytes, native_bytes);
Ok(())
}
#[test]
fn opfs_frames_decode_via_native_reader() -> Result<(), WalError> {
let entries = vec![
WalEntry::put(b"k1".to_vec(), b"v1".to_vec()),
WalEntry::put(b"k2".to_vec(), b"v2".to_vec()),
WalEntry::delete(b"k3".to_vec()),
];
let mut frame_bytes = Vec::new();
for entry in &entries {
frame_bytes.extend_from_slice(&frame::frame_entry(entry));
}
let path = temp_wal_path();
std::fs::write(&path, &frame_bytes)?;
let contents = DurableWal::read_file(&path)?;
let _ = std::fs::remove_file(&path);
assert_eq!(contents.entries(), entries.as_slice());
Ok(())
}
#[test]
fn native_frames_decode_via_opfs_layer() -> Result<(), WalError> {
let entries = vec![
WalEntry::put(b"one".to_vec(), b"1".to_vec()),
WalEntry::delete(b"two".to_vec()),
];
let path = temp_wal_path();
let _ = std::fs::remove_file(&path);
{
let mut wal = DurableWal::new(&path, FsyncPolicy::PerWrite)?;
for entry in &entries {
wal.append(entry)?;
}
}
let native_bytes = std::fs::read(&path)?;
let _ = std::fs::remove_file(&path);
let decoded = frame::decode_entries(&native_bytes)?;
assert_eq!(decoded, entries);
Ok(())
}
#[test]
fn crc32_identical_across_backends() {
let entry = WalEntry::put(b"checksum".to_vec(), b"payload".to_vec());
let framed = frame::frame_entry(&entry);
let entry_bytes = &framed[4..];
let crc_offset = entry_bytes.len() - 4;
let stored = u32::from_le_bytes([
entry_bytes[crc_offset],
entry_bytes[crc_offset + 1],
entry_bytes[crc_offset + 2],
entry_bytes[crc_offset + 3],
]);
assert_eq!(stored, entry.crc32());
assert_eq!(stored, entry.computed_crc32());
assert_eq!(entry.operation_type(), crate::wal::OperationType::Put);
}
}