Skip to main content

bark/persist/adaptor/
mod.rs

1//! Storage adaptor module providing the [StorageAdaptor] trait and blanket
2//! implementation of [BarkPersister] for any type implementing [StorageAdaptor].
3//!
4//! This module provides an optimized single-table storage abstraction that can be
5//! efficiently implemented on various backends (SQLite, Postgres, MongoDB, Firebase,
6//! in-memory, etc.).
7//!
8//! The design uses structured keys:
9//! - **Primary key (`pk`)**: Unique identifier for each record
10//! - **Partition key**: Groups related records for efficient querying
11//! - **Sort key**: Enables ordered iteration and range queries
12//!
13//! # Example
14//!
15//! ```rust
16//! # use bark::persist::adaptor::memory::MemoryStorageAdaptor;
17//! # use bark::persist::adaptor::{Query, Record, StorageAdaptor, SortKey};
18//!
19//! # async fn example() -> anyhow::Result<()> {
20//! // Create an in-memory storage adaptor
21//! let mut storage = MemoryStorageAdaptor::new();
22//!
23//! // Store a record sorted by a numeric field (ascending)
24//! let record = Record {
25//!     partition: 0,
26//!     pk: "item:1".into(),
27//!     sort_key: Some(SortKey::u32_asc(42)),
28//!     data: b"hello world".to_vec(),
29//! };
30//! storage.put(record).await?;
31//!
32//! // Query with efficient index scan
33//! let query = Query::new_full_range(0).limit(10);
34//! let records = storage.query_sorted(query).await?;
35//! # Ok(())
36//! # }
37//! ```
38
39mod sort;
40
41#[cfg(feature = "filestore")]
42pub mod filestore;
43pub mod memory;
44pub use self::sort::{SortKey, SortKeyBuilder};
45
46#[cfg(feature = "indexed-db")]
47pub mod indexed_db;
48
49use std::ops::RangeBounds;
50
51use anyhow::Context;
52use bitcoin::{Amount, Transaction, Txid};
53use bitcoin::secp256k1::PublicKey;
54use bitcoin::hashes::Hash;
55#[cfg(feature = "onchain-bdk")]
56use bdk_core::Merge;
57#[cfg(feature = "onchain-bdk")]
58use bdk_wallet::ChangeSet;
59use chrono::{DateTime, Local};
60use lightning_invoice::Bolt11Invoice;
61use serde::{de::DeserializeOwned, Serialize};
62
63use ark::lightning::{PaymentHash, Preimage};
64use ark::{Vtxo, VtxoId};
65use ark::vtxo::Full;
66
67use crate::actions::{WalletActionCheckpoint, WalletActionId};
68use crate::exit::{ExitStateKind, ExitTxOrigin};
69use crate::movement::{
70	Movement, MovementId, MovementStatus, MovementSubsystem, PaymentMethod,
71};
72use crate::movement::update::MovementUpdate;
73use crate::persist::BarkPersister;
74use crate::persist::models::{
75	PaidInvoice, RoundStateId, SerdeExitChildTx, SerdeRoundState, SerdeVtxo, SerdeVtxoKey,
76	SettledLightningReceive, StoredExit, StoredRoundState, Unlocked, wallet_vtxo_from_full,
77};
78use crate::round::RoundState;
79use crate::vtxo::{VtxoState, VtxoStateKind};
80use crate::{WalletProperties, WalletVtxo};
81
82
83pub mod partition {
84	pub const PROPERTIES: u8 = 0;
85	#[allow(unused)]
86	pub const BDK_CHANGESET: u8 = 1;
87	pub const VTXO: u8 = 2;
88	pub const PUBLIC_KEY: u8 = 3;
89	pub const PENDING_BOARD: u8 = 4;
90	pub const ROUND_STATE: u8 = 5;
91	pub const MOVEMENT: u8 = 6;
92	/// was used by the now-removed `bark_lightning_send`. Do not reuse.
93	#[allow(unused)]
94	pub const LEGACY_LIGHTNING_SEND: u8 = 7;
95	/// was used by the now-removed legacy `bark_pending_lightning_receive`.
96	/// Do not reuse. In-flight receives now live in WALLET_ACTION_CHECKPOINT
97	/// and settled ones in SETTLED_LIGHTNING_RECEIVE.
98	#[allow(unused)]
99	pub const LEGACY_LIGHTNING_RECEIVE: u8 = 8;
100	pub const EXIT_VTXO: u8 = 9;
101	pub const EXIT_CHILD_TX: u8 = 10;
102	pub const MAILBOX_CHECKPOINT: u8 = 11;
103	/// was used by the now-removed `PENDING_OFFBOARD`; superseded by
104	/// [`WALLET_ACTION_CHECKPOINT`]. Do not reuse.
105	#[allow(unused)]
106	pub const LEGACY_PENDING_OFFBOARD: u8 = 12;
107	/// An index table for payment method to movement
108	pub const MOVEMENT_PAYMENT_METHOD: u8 = 13;
109	/// Per-action checkpoint payloads keyed by [crate::actions::WalletActionId].
110	pub const WALLET_ACTION_CHECKPOINT: u8 = 14;
111	/// Permanent record of every settled outgoing lightning payment,
112	/// keyed by payment hash.
113	pub const PAID_INVOICE: u8 = 15;
114	/// Permanent record of every settled incoming lightning receive,
115	/// keyed by payment hash.
116	pub const SETTLED_LIGHTNING_RECEIVE: u8 = 16;
117	/// An index from [WalletActionId] to the movement that action owns, so a
118	/// re-driven action step reuses its movement instead of duplicating it.
119	pub const MOVEMENT_ACTION: u8 = 17;
120
121	pub const LAST_IDS: u8 = u8::MAX;
122}
123
124/// A storage record with structured keys.
125#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
126pub struct Record {
127	/// Partition key for grouping related records (e.g., "vtxo", "movement").
128	///
129	/// Queries always filter by partition.
130	pub partition: u8,
131
132	/// Unique primary key
133	pub pk: Vec<u8>,
134
135	/// Optional sort key for ordered iteration within a partition.
136	///
137	/// Use [`SortKey::builder()`] to construct composite keys with
138	/// mixed sort directions.
139	///
140	/// This field may be set or changed after record insertion.
141	/// Implementation should support updating the sort key of a
142	/// record post-insert if needed.
143	pub sort_key: Option<SortKey>,
144
145	/// The record data encoded as JSON.
146	pub data: Vec<u8>,
147}
148
149impl Record {
150	/// Converts the record data to a typed value.
151	fn to_data<T: DeserializeOwned>(&self) -> anyhow::Result<T> {
152		serde_json::from_slice(&self.data).map_err(Into::into)
153	}
154
155	/// Creates a new record from a typed value.
156	fn from_data<T: Serialize>(
157		partition: u8,
158		pk: &[u8],
159		sort_key: Option<SortKey>,
160		data: &T,
161	) -> anyhow::Result<Record> {
162		Ok(Record {
163			partition,
164			pk: pk.to_vec(),
165			sort_key,
166			data: serde_json::to_vec(data)?,
167		})
168	}
169}
170
171/// A range of sort keys.
172pub trait QueryRange: RangeBounds<SortKey> + Send {}
173
174impl<R: RangeBounds<SortKey> + Send> QueryRange for R {}
175
176/// Query specification for retrieving sorted records from a partition.
177#[derive(Debug, Clone)]
178pub struct Query<R: QueryRange> {
179	/// Partition to query (required).
180	pub partition: u8,
181
182	/// Range of sort keys to query.
183	pub range: R,
184
185	/// Maximum number of records to return.
186	pub limit: Option<usize>,
187}
188
189impl<R: QueryRange> Query<R> {
190	/// Creates a new query for the given partition.
191	pub fn new(partition: u8, range: R) -> Self {
192		Self {
193			partition,
194			range,
195			limit: None,
196		}
197	}
198
199	/// Sets the maximum number of records to return.
200	///
201	/// If the range is greater than the limit, the query will
202	/// return the first `limit` records.
203	pub fn limit(mut self, limit: usize) -> Self {
204		self.limit = Some(limit);
205		self
206	}
207}
208
209impl Query<std::ops::RangeFull> {
210	pub fn new_full_range(partition: u8) -> Self {
211		Self::new(partition, ..)
212	}
213}
214
215fn serialize_payment_method(pm: &PaymentMethod) -> Vec<u8> {
216	let body = pm.value_string();
217
218	let mut buf = Vec::with_capacity(pm.type_str().len() + 1 + body.len());
219	buf.extend(pm.type_str().as_bytes().iter().copied());
220	// nb we need to separate with a non-utf8 byte
221	// we use 0xfe so that we can easily query on prefix from <type>0xfe .. <type>0xff
222	buf.push(0xfe);
223	buf.extend(body.into_bytes());
224	buf
225}
226
227/// Write a movement record and its payment-method index records through the
228/// given adaptor guard. Shared by the plain update path and the atomic
229/// action-owned create path.
230async fn write_movement_records<S: StorageAdaptor>(
231	guard: &mut S,
232	movement: &Movement,
233) -> anyhow::Result<()> {
234	let record = Record::from_data(
235		partition::MOVEMENT,
236		&movement.id.to_bytes(),
237		Some(sort::movement_sort_key(&movement.time.created_at)),
238		movement,
239	)?;
240	guard.put(record).await?;
241
242	// then add records for each payment method
243	let sent = movement.sent_to.iter().map(|d| &d.destination);
244	let rcvd = movement.received_on.iter().map(|d| &d.destination);
245	for pm in sent.chain(rcvd) {
246		let pm_bytes = serialize_payment_method(pm);
247		let primary_key = {
248			// We just need a unique key, but we will never query using this
249			let mut buf = Vec::with_capacity(pm_bytes.len() + 4);
250			buf.extend(pm_bytes.iter().copied());
251			buf.extend(movement.id.to_bytes());
252			buf
253		};
254		let record = Record::from_data(
255			partition::MOVEMENT_PAYMENT_METHOD,
256			&primary_key,
257			Some(SortKey::from_bytes(pm_bytes)),
258			&movement.id.0,
259		)?;
260		guard.put(record).await?;
261	}
262	Ok(())
263}
264
265/// Storage adaptor trait for persistence backends.
266///
267/// This trait provides a minimal interface (5 methods) that can be efficiently
268/// implemented on various storage backends while enabling query optimization.
269///
270/// # Implementor's Guide
271///
272/// ## Simple backends (memory, file-based)
273///
274/// Store records in a map/list keyed by `(partition, pk)`.
275///
276/// - `query_sorted`: query sorted records by partition, excluding those without
277///	  a sort key defined.
278/// - `get_all`: return every record in the partition regardless of whether it
279///   has a sort key. No ordering is guaranteed.
280///
281/// ## Database backends (Postgres, MongoDB, Firebase, IndexedDB, etc.)
282///
283/// Create a single table with indexes:
284///
285/// ```sql
286/// CREATE TABLE storage (
287///     pk TEXT PRIMARY KEY,
288///     partition TEXT NOT NULL,
289///     sort_key BLOB,
290///     data BLOB NOT NULL
291/// );
292/// CREATE INDEX idx_partition_sort ON storage(partition, sort_key);
293/// ```
294///
295/// Translate [`Query`] to SQL:
296///
297/// ```sql
298/// -- query_sorted: only returns records with a sort key
299/// SELECT * FROM storage
300/// WHERE partition = :partition AND sort_key IS NOT NULL
301/// ORDER BY sort_key DESC
302/// ```
303///
304/// Translate [`get_all`](StorageAdaptor::get_all) to SQL:
305///
306/// ```sql
307/// -- get_all: returns all records in the partition, no ordering
308/// SELECT * FROM storage
309/// WHERE partition = :partition
310/// ```
311#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
312#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
313pub trait StorageAdaptor: Send + Sync + 'static {
314	/// Stores a record, inserting or updating by primary key.
315	async fn put(&mut self, record: Record) -> anyhow::Result<()>;
316
317	/// Retrieves a record by primary key.
318	///
319	/// Returns `None` if the record doesn't exist.
320	async fn get(&self, partition: u8, pk: &[u8]) -> anyhow::Result<Option<Record>>;
321
322	/// Deletes a record by primary key.
323	///
324	/// Returns the deleted record if it existed, `None` otherwise.
325	async fn delete(&mut self, partition: u8, pk: &[u8]) -> anyhow::Result<Option<Record>>;
326
327	/// Queries sorted records from a partition.
328	///
329	/// Results are ordered by sort key.
330	/// Records without a sort key must not be returned, to query all
331	/// records use [`StorageAdaptor::get_all`].
332	async fn query_sorted<R: QueryRange>(&self, query: Query<R>)
333		-> anyhow::Result<Vec<Record>>;
334
335	/// Get all records in a partition.
336	///
337	/// No ordering guarantees are made.
338	async fn get_all(&self, partition: u8) -> anyhow::Result<Vec<Record>>;
339
340	/// Increments the last partition id, then stores and returns the new id.
341	async fn incremental_id(&mut self, partition: u8) -> anyhow::Result<u32> {
342		let last_partition_id = self.get(partition::LAST_IDS, &[partition]).await?
343			.map(|r| r.to_data::<u32>()).unwrap_or(Ok(0))?;
344		let next_partition_id = last_partition_id + 1;
345
346		let record = Record::from_data(
347			partition::LAST_IDS,
348			&[partition],
349			None,
350			&next_partition_id,
351		)?;
352
353		self.put(record).await?;
354		Ok(next_partition_id)
355	}
356}
357
358async fn get_vtxo<S: StorageAdaptor>(adaptor: &S, id: VtxoId) -> anyhow::Result<Option<SerdeVtxo>> {
359	match adaptor.get(partition::VTXO, &id.to_bytes()).await? {
360		Some(record) => Ok(Some(record.to_data::<SerdeVtxo>()?)),
361		None => Ok(None),
362	}
363}
364
365async fn get_check_vtxo_state<S: StorageAdaptor>(
366	adaptor: &S,
367	vtxo_id: VtxoId,
368	allowed_states: &[VtxoStateKind],
369) -> anyhow::Result<SerdeVtxo> {
370	let vtxo = get_vtxo(adaptor, vtxo_id).await?
371		.context("vtxo not found")?;
372
373	let current_state = vtxo.current_state().context("vtxo has no state")?;
374	if !allowed_states.contains(&current_state.kind()) {
375		bail!("current state {:?} not in allowed states {:?}",
376			current_state.kind(), allowed_states
377		);
378	}
379
380	Ok(vtxo)
381}
382
383async fn update_vtxo_state_checked<S: StorageAdaptor>(
384	adaptor: &mut S,
385	vtxo_id: VtxoId,
386	new_state: VtxoState,
387	allowed_old_states: &[VtxoStateKind],
388) -> anyhow::Result<WalletVtxo> {
389	let mut serde_vtxo = get_check_vtxo_state(adaptor, vtxo_id, allowed_old_states).await?;
390
391	let sk = sort::vtxo_sort_key(
392		new_state.kind(), serde_vtxo.vtxo.expiry_height(), serde_vtxo.vtxo.amount()
393	);
394
395	serde_vtxo.states.push(new_state.clone());
396	let updated_record = Record::from_data(
397		partition::VTXO,
398		&vtxo_id.to_bytes(),
399		Some(sk),
400		&serde_vtxo,
401	)?;
402
403	adaptor.put(updated_record).await?;
404
405	Ok(wallet_vtxo_from_full(&serde_vtxo.vtxo, new_state, serde_vtxo.registered))
406}
407
408pub struct StorageAdaptorWrapper<S: StorageAdaptor> {
409	inner: tokio::sync::RwLock<S>,
410}
411
412impl<S: StorageAdaptor> StorageAdaptorWrapper<S> {
413	pub fn new(inner: S) -> Self {
414		Self {
415			inner: tokio::sync::RwLock::new(inner),
416		}
417	}
418}
419
420/// Blanket implementation of `BarkPersister` for any type implementing `StorageAdaptor`.
421#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
422#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
423impl <S: StorageAdaptor> BarkPersister for StorageAdaptorWrapper<S> {
424	async fn init_wallet(&self, properties: &WalletProperties) -> anyhow::Result<()> {
425		let record = Record::from_data(
426			partition::PROPERTIES,
427			// NB: a single set of properties is stored, so no need for primary key
428			&[],
429			None,
430			properties,
431		)?;
432		self.inner.write().await.put(record).await
433	}
434
435	async fn read_properties(&self) -> anyhow::Result<Option<WalletProperties>> {
436		match self.inner.read().await.get(partition::PROPERTIES, &[]).await? {
437			Some(record) => Ok(Some(record.to_data()?)),
438			None => Ok(None),
439		}
440	}
441
442	async fn set_server_pubkey(&self, server_pubkey: PublicKey) -> anyhow::Result<()> {
443		let mut properties = match self.read_properties().await? {
444			Some(properties) => properties,
445			None => bail!("wallet not initialized"),
446		};
447
448		properties.server_pubkey = Some(server_pubkey);
449
450		let record = Record::from_data(partition::PROPERTIES, &[], None, &properties)?;
451		self.inner.write().await.put(record).await
452	}
453
454	async fn set_server_mailbox_pubkey(&self, server_mailbox_pubkey: PublicKey) -> anyhow::Result<()> {
455		let mut properties = match self.read_properties().await? {
456			Some(properties) => properties,
457			None => bail!("wallet not initialized"),
458		};
459
460		properties.server_mailbox_pubkey = Some(server_mailbox_pubkey);
461
462		let record = Record::from_data(partition::PROPERTIES, &[], None, &properties)?;
463		self.inner.write().await.put(record).await
464	}
465
466	#[cfg(feature = "onchain-bdk")]
467	async fn initialize_bdk_wallet(&self) -> anyhow::Result<ChangeSet> {
468		match self.inner.read().await.get(partition::BDK_CHANGESET, &[]).await? {
469			Some(record) => record.to_data(),
470			None => Ok(ChangeSet::default()),
471		}
472	}
473
474	#[cfg(feature = "onchain-bdk")]
475	async fn store_bdk_wallet_changeset(&self, changeset: &ChangeSet) -> anyhow::Result<()> {
476		let mut current = self.initialize_bdk_wallet().await?;
477		current.merge(changeset.clone());
478
479		let record = Record::from_data(
480			partition::BDK_CHANGESET,
481			// NB: a single changeset is stored, so no need for primary key
482			&[],
483			None,
484			&current,
485		)?;
486		self.inner.write().await.put(record).await
487	}
488
489	async fn create_new_movement(
490		&self,
491		status: MovementStatus,
492		subsystem: &MovementSubsystem,
493		time: DateTime<Local>,
494		action_id: Option<&str>,
495	) -> anyhow::Result<MovementId> {
496		let mut lock = self.inner.write().await;
497
498		let id = MovementId(lock.incremental_id(partition::MOVEMENT).await?);
499		let movement = Movement::new(id, status, subsystem, time);
500
501		let record = Record::from_data(
502			partition::MOVEMENT,
503			&id.to_bytes(),
504			Some(sort::movement_sort_key(&time)),
505			&movement,
506		)?;
507		lock.put(record).await?;
508
509		// Index by owning action so the movement can be reused on re-drive.
510		if let Some(action_id) = action_id {
511			let idx = Record::from_data(
512				partition::MOVEMENT_ACTION,
513				action_id.as_bytes(),
514				None,
515				&id.0,
516			)?;
517			lock.put(idx).await?;
518		}
519
520		Ok(id)
521	}
522
523	async fn get_or_create_movement_for_action(
524		&self,
525		subsystem: &MovementSubsystem,
526		time: DateTime<Local>,
527		action_id: &str,
528		update: MovementUpdate,
529	) -> anyhow::Result<(MovementId, bool)> {
530		let mut guard = self.inner.write().await;
531
532		// Reuse the movement already owned by this action, if any.
533		if let Some(rec) = guard.get(partition::MOVEMENT_ACTION, action_id.as_bytes()).await? {
534			let id = MovementId::new(
535				rec.to_data::<u32>().context("corrupt db: movement action index value")?);
536			return Ok((id, false));
537		}
538
539		let id = MovementId(guard.incremental_id(partition::MOVEMENT).await?);
540		let mut movement = Movement::new(id, MovementStatus::Pending, subsystem, time);
541		update.apply_to(&mut movement, time);
542		write_movement_records(&mut *guard, &movement).await?;
543
544		// Index by owning action so the movement can be reused on re-drive.
545		let idx = Record::from_data(
546			partition::MOVEMENT_ACTION,
547			action_id.as_bytes(),
548			None,
549			&id.0,
550		)?;
551		guard.put(idx).await?;
552
553		Ok((id, true))
554	}
555
556	async fn update_movement(&self, movement: &Movement) -> anyhow::Result<()> {
557		let mut guard = self.inner.write().await;
558		write_movement_records(&mut *guard, movement).await
559	}
560
561	async fn get_movement_by_id(&self, movement_id: MovementId) -> anyhow::Result<Movement> {
562		self.inner.read().await.get(partition::MOVEMENT, &movement_id.to_bytes())
563			.await?
564			.context("movement not found")?
565			.to_data()
566	}
567
568	async fn get_all_movements(&self) -> anyhow::Result<Vec<Movement>> {
569		let records = self.inner.read().await
570			.query_sorted(Query::new_full_range(partition::MOVEMENT)).await?;
571		records.into_iter().map(|r| r.to_data()).collect()
572	}
573
574	async fn get_movements_by_payment_method(
575		&self,
576		payment_method: &PaymentMethod,
577	) -> anyhow::Result<Vec<Movement>> {
578		let pm_bytes = serialize_payment_method(payment_method);
579		let sort_key = SortKey::from_bytes(pm_bytes);
580
581		let guard = self.inner.read().await;
582		let idx_recs = guard.query_sorted(Query::new(
583			partition::MOVEMENT_PAYMENT_METHOD,
584			sort_key.clone()..=sort_key,
585		)).await?;
586
587		let mut ret = Vec::with_capacity(idx_recs.len());
588		for idx_rec in idx_recs {
589			let id = MovementId::new(idx_rec.to_data::<u32>()
590				.context("corrupt db: movement payment method index value")?);
591
592			ret.push(
593				guard.get(partition::MOVEMENT, &id.to_bytes()).await?
594					.context("corrupt db: movement payment method entry for unknown movement")?
595					.to_data()
596					.context("corrupt db: invalid movement record")?
597			);
598		}
599		Ok(ret)
600	}
601
602	async fn store_round_state(&self, round_state: &RoundState) -> anyhow::Result<RoundStateId> {
603		let mut lock = self.inner.write().await;
604
605		let id = RoundStateId(lock.incremental_id(partition::ROUND_STATE).await?);
606		let serde_state = SerdeRoundState::from(round_state);
607		let record = Record::from_data(
608			partition::ROUND_STATE,
609			&id.to_bytes(),
610			Some(sort::SortKey::u32_asc(id.0)),
611			&serde_state,
612		)?;
613		lock.put(record).await?;
614
615		Ok(id)
616	}
617
618	async fn update_round_state(&self, round_state: &StoredRoundState) -> anyhow::Result<()> {
619		let serde_state = SerdeRoundState::from(round_state.state());
620		let record = Record::from_data(
621			partition::ROUND_STATE,
622			&round_state.id().to_bytes(),
623			Some(sort::SortKey::u32_asc(round_state.id().0)),
624			&serde_state,
625		)?;
626		self.inner.write().await.put(record).await
627	}
628
629	async fn remove_round_state(&self, round_state: &StoredRoundState) -> anyhow::Result<()> {
630		self.inner.write().await
631			.delete(partition::ROUND_STATE, &round_state.id().to_bytes()).await?;
632		Ok(())
633	}
634
635	async fn get_round_state_by_id(&self, _id: RoundStateId) -> anyhow::Result<Option<StoredRoundState<Unlocked>>> {
636		let record = self.inner.read().await
637			.get(partition::ROUND_STATE, &_id.to_bytes()).await?;
638		match record {
639			Some(r) => {
640				let pk_slice: [u8; 4] = r.pk[..4].try_into().expect("4 bytes shouldn't fail");
641				let id = RoundStateId(u32::from_be_bytes(pk_slice));
642				let state = r.to_data::<SerdeRoundState>()?.into();
643				Ok(Some(StoredRoundState::new(id, state)))
644			},
645			None => Ok(None),
646		}
647	}
648
649	async fn get_pending_round_state_ids(&self) -> anyhow::Result<Vec<RoundStateId>> {
650		let records = self.inner.read().await
651			.get_all(partition::ROUND_STATE).await?;
652		records.into_iter()
653			.map(|r| {
654				let pk_slice: [u8; 4] = r.pk[..4].try_into().expect("4 bytes shouldn't fail");
655				Ok(RoundStateId(u32::from_be_bytes(pk_slice)))
656			})
657			.collect()
658	}
659
660	async fn store_vtxos(&self, vtxos: &[(&Vtxo<Full>, &VtxoState)]) -> anyhow::Result<()> {
661		let mut lock = self.inner.write().await;
662
663		for (vtxo, state) in vtxos {
664			let serde_vtxo = SerdeVtxo {
665				vtxo: (*vtxo).clone(),
666				states: vec![(*state).clone()],
667				registered: false,
668			};
669
670			let sk = sort::vtxo_sort_key(
671				state.kind(), vtxo.expiry_height(), vtxo.amount(),
672			);
673			let record = Record::from_data(
674				partition::VTXO,
675				&vtxo.id().to_bytes(),
676				Some(sk),
677				&serde_vtxo,
678			)?;
679			lock.put(record).await?;
680		}
681		Ok(())
682	}
683
684	async fn get_wallet_vtxo(&self, id: VtxoId) -> anyhow::Result<Option<WalletVtxo>> {
685		let lock = self.inner.read().await;
686		match get_vtxo(&*lock, id).await? {
687			Some(serde_vtxo) => {
688				let state = serde_vtxo.current_state()
689					.context("vtxo has no state")?.clone();
690				Ok(Some(wallet_vtxo_from_full(
691					&serde_vtxo.vtxo, state, serde_vtxo.registered,
692				)))
693			},
694			None => Ok(None),
695		}
696	}
697
698	async fn get_wallet_vtxos(&self, ids: &[VtxoId]) -> anyhow::Result<Vec<WalletVtxo>> {
699		let lock = self.inner.read().await;
700		let mut out = Vec::with_capacity(ids.len());
701		for id in ids {
702			let serde_vtxo = get_vtxo(&*lock, *id).await?
703				.with_context(|| format!("vtxo {id} not found"))?;
704			let state = serde_vtxo.current_state()
705				.context("vtxo has no state")?.clone();
706			out.push(wallet_vtxo_from_full(
707				&serde_vtxo.vtxo, state, serde_vtxo.registered,
708			));
709		}
710		Ok(out)
711	}
712
713	async fn get_all_vtxos(&self) -> anyhow::Result<Vec<WalletVtxo>> {
714		let records = self.inner.read().await
715			.query_sorted(Query::new_full_range(partition::VTXO)).await?;
716
717		records
718			.into_iter()
719			.map(|r| {
720				let serde_vtxo = r.to_data::<SerdeVtxo>()?;
721				let state = serde_vtxo
722					.current_state()
723					.cloned()
724					.context("vtxo has no state")?;
725				Ok(wallet_vtxo_from_full(
726					&serde_vtxo.vtxo, state, serde_vtxo.registered,
727				))
728			})
729			.collect()
730	}
731
732	async fn get_vtxos_by_state(
733		&self,
734		states: &[VtxoStateKind],
735	) -> anyhow::Result<Vec<WalletVtxo>> {
736		let lock = self.inner.read().await;
737
738		let range = |state: VtxoStateKind| {
739			let start = sort::vtxo_sort_key(state, u32::MIN, Amount::ZERO);
740			let end = sort::vtxo_sort_key(state, u32::MAX, Amount::MAX);
741			(start, end)
742		};
743
744		let mut records = Vec::new();
745		for state in states {
746			let (start, end) = range(*state);
747			let query = Query::new(partition::VTXO, start..=end);
748
749			for record in lock.query_sorted(query).await? {
750				let serde_vtxo = record.to_data::<SerdeVtxo>()?;
751				let current_state = serde_vtxo.current_state()
752					.context("vtxo has no current state")?.clone();
753				debug_assert_eq!(current_state.kind(), *state);
754				records.push(wallet_vtxo_from_full(
755					&serde_vtxo.vtxo, current_state, serde_vtxo.registered,
756				));
757			}
758		}
759
760		// Each per-state range comes back in sort-key order, but
761		// concatenating the ranges groups results by state. Re-sort to the
762		// order the trait documents: expiry height ASC, amount DESC.
763		records.sort_by(|a, b| {
764			a.vtxo.expiry_height().cmp(&b.vtxo.expiry_height())
765				.then(b.vtxo.amount().cmp(&a.vtxo.amount()))
766		});
767
768		Ok(records)
769	}
770
771	async fn get_full_vtxo(&self, id: VtxoId) -> anyhow::Result<Option<Vtxo<Full>>> {
772		let lock = self.inner.read().await;
773		Ok(get_vtxo(&*lock, id).await?.map(|s| s.vtxo))
774	}
775
776	async fn get_full_vtxos(&self, ids: &[VtxoId]) -> anyhow::Result<Vec<Vtxo<Full>>> {
777		let lock = self.inner.read().await;
778		let mut out = Vec::with_capacity(ids.len());
779		for id in ids {
780			let serde_vtxo = get_vtxo(&*lock, *id).await?
781				.with_context(|| format!("vtxo {id} not found"))?;
782			out.push(serde_vtxo.vtxo);
783		}
784		Ok(out)
785	}
786
787	async fn remove_vtxo(&self, id: VtxoId) -> anyhow::Result<Option<Vtxo<Full>>> {
788		match self.inner.write().await.delete(partition::VTXO, &id.to_bytes()).await? {
789			Some(record) => Ok(Some(record.to_data::<SerdeVtxo>()?.vtxo)),
790			None => Ok(None),
791		}
792	}
793
794	async fn has_spent_vtxo(&self, id: VtxoId) -> anyhow::Result<bool> {
795		match self.get_wallet_vtxo(id).await? {
796			Some(vtxo) => Ok(vtxo.state.kind() == VtxoStateKind::Spent),
797			None => Ok(false),
798		}
799	}
800
801	async fn update_vtxo_state_checked(
802		&self,
803		vtxo_id: VtxoId,
804		new_state: VtxoState,
805		allowed_old_states: &[VtxoStateKind],
806	) -> anyhow::Result<WalletVtxo> {
807		let mut lock = self.inner.write().await;
808		update_vtxo_state_checked(&mut *lock, vtxo_id, new_state, allowed_old_states).await
809	}
810
811	async fn update_vtxo_states_checked(
812		&self,
813		vtxo_ids: &[VtxoId],
814		new_state: VtxoState,
815		allowed_old_states: &[VtxoStateKind],
816	) -> anyhow::Result<()> {
817		let mut lock = self.inner.write().await;
818		// Validate every vtxo before mutating anything, so a state-kind
819		// mismatch can't leave the batch half-applied. Concurrent batches
820		// are serialized by the write lock above. Storage errors during
821		// the put loop cannot be rolled back from here — that is a
822		// fundamental limitation of the adaptor.
823		for id in vtxo_ids {
824			get_check_vtxo_state(&*lock, *id, allowed_old_states).await?;
825		}
826		for id in vtxo_ids {
827			update_vtxo_state_checked(&mut *lock, *id, new_state.clone(), allowed_old_states).await?;
828		}
829		Ok(())
830	}
831
832	async fn mark_vtxos_registered(&self, vtxo_ids: &[VtxoId]) -> anyhow::Result<()> {
833		let mut lock = self.inner.write().await;
834		for id in vtxo_ids {
835			let mut serde_vtxo = get_vtxo(&*lock, *id).await?
836				.with_context(|| format!("vtxo {id} not found"))?;
837			if serde_vtxo.registered {
838				continue;
839			}
840			serde_vtxo.registered = true;
841
842			// Keep the record's sort key unchanged: it is derived from the
843			// current state, which this update doesn't touch.
844			let state = serde_vtxo.current_state().context("vtxo has no state")?;
845			let sk = sort::vtxo_sort_key(
846				state.kind(), serde_vtxo.vtxo.expiry_height(), serde_vtxo.vtxo.amount(),
847			);
848			let record = Record::from_data(
849				partition::VTXO,
850				&id.to_bytes(),
851				Some(sk),
852				&serde_vtxo,
853			)?;
854			lock.put(record).await?;
855		}
856		Ok(())
857	}
858
859	async fn get_unregistered_vtxo_ids(&self) -> anyhow::Result<Vec<VtxoId>> {
860		let records = self.inner.read().await
861			.query_sorted(Query::new_full_range(partition::VTXO)).await?;
862
863		let mut ids = Vec::new();
864		for record in records {
865			let serde_vtxo = record.to_data::<SerdeVtxo>()?;
866			if serde_vtxo.registered {
867				continue;
868			}
869			let state = serde_vtxo.current_state()
870				.context("vtxo has no state")?;
871			if state.kind() != VtxoStateKind::Spent {
872				ids.push(serde_vtxo.vtxo.id());
873			}
874		}
875		Ok(ids)
876	}
877
878	async fn store_vtxo_key(&self, index: u32, public_key: PublicKey) -> anyhow::Result<()> {
879		let vtxo_key = SerdeVtxoKey { index, public_key };
880		let record = Record::from_data(
881			partition::PUBLIC_KEY,
882			&public_key.serialize()[..],
883			Some(sort::SortKey::u64_desc(index as u64)),
884			&vtxo_key,
885		)?;
886		self.inner.write().await.put(record).await
887	}
888
889	async fn get_last_vtxo_key_index(&self) -> anyhow::Result<Option<u32>> {
890		// pks are sorted descending, so the first one is the highest index
891		let query = Query::new_full_range(partition::PUBLIC_KEY).limit(1);
892		let records = self.inner.read().await.query_sorted(query).await?;
893
894		match records.into_iter().next() {
895			Some(record) => {
896				let vtxo_key = record.to_data::<SerdeVtxoKey>()?;
897				Ok(Some(vtxo_key.index))
898			}
899			None => Ok(None),
900		}
901	}
902
903	async fn get_public_key_idx(&self, public_key: &PublicKey) -> anyhow::Result<Option<u32>> {
904		match self.inner.read().await
905			.get(partition::PUBLIC_KEY, &public_key.serialize()[..]).await?
906		{
907			Some(record) => {
908				let vtxo_key = record.to_data::<SerdeVtxoKey>()?;
909				Ok(Some(vtxo_key.index))
910			}
911			None => Ok(None),
912		}
913	}
914
915	async fn get_mailbox_checkpoint(&self) -> anyhow::Result<u64> {
916		match self.inner.read().await
917			.get(partition::MAILBOX_CHECKPOINT, &[]).await?
918		{
919			Some(record) => Ok(record.to_data::<u64>()?),
920			None => Ok(0),
921		}
922	}
923
924	async fn store_mailbox_checkpoint(&self, checkpoint: u64) -> anyhow::Result<()> {
925		let mut lock = self.inner.write().await;
926		let record = Record::from_data(
927			partition::MAILBOX_CHECKPOINT,
928			&[],
929			None,
930			&checkpoint,
931		)?;
932		lock.put(record).await?;
933		Ok(())
934	}
935
936	async fn upsert_wallet_action_checkpoint(
937		&self,
938		id: &WalletActionId,
939		checkpoint: &WalletActionCheckpoint,
940	) -> anyhow::Result<()> {
941		let record = Record::from_data(
942			partition::WALLET_ACTION_CHECKPOINT,
943			id.as_bytes(),
944			None,
945			checkpoint,
946		)?;
947		self.inner.write().await.put(record).await
948	}
949
950	async fn get_wallet_action_checkpoint(
951		&self,
952		id: &WalletActionId,
953	) -> anyhow::Result<Option<WalletActionCheckpoint>> {
954		match self.inner.read().await
955			.get(partition::WALLET_ACTION_CHECKPOINT, id.as_bytes()).await?
956		{
957			Some(record) => Ok(Some(record.to_data()?)),
958			None => Ok(None),
959		}
960	}
961
962	async fn get_all_wallet_action_checkpoints(
963		&self,
964	) -> anyhow::Result<Vec<WalletActionCheckpoint>> {
965		let records = self.inner.read().await
966			.get_all(partition::WALLET_ACTION_CHECKPOINT).await?;
967		records.into_iter().map(|r| r.to_data()).collect()
968	}
969
970	async fn remove_wallet_action_checkpoint(
971		&self,
972		id: &WalletActionId,
973	) -> anyhow::Result<()> {
974		self.inner.write().await
975			.delete(partition::WALLET_ACTION_CHECKPOINT, id.as_bytes()).await?;
976		Ok(())
977	}
978
979	async fn record_paid_invoice(
980		&self,
981		payment_hash: PaymentHash,
982		preimage: Preimage,
983	) -> anyhow::Result<()> {
984		let key = payment_hash.to_byte_array();
985		// Idempotent: preserve the original paid_at across retries.
986		let mut lock = self.inner.write().await;
987		if lock.get(partition::PAID_INVOICE, &key).await?.is_some() {
988			return Ok(());
989		}
990		let paid = PaidInvoice {
991			payment_hash,
992			preimage,
993			paid_at: chrono::Local::now(),
994		};
995		let record = Record::from_data(partition::PAID_INVOICE, &key, None, &paid)?;
996		lock.put(record).await
997	}
998
999	async fn get_paid_invoice(
1000		&self,
1001		payment_hash: PaymentHash,
1002	) -> anyhow::Result<Option<PaidInvoice>> {
1003		match self.inner.read().await
1004			.get(partition::PAID_INVOICE, &payment_hash.to_byte_array()).await?
1005		{
1006			Some(record) => Ok(Some(record.to_data()?)),
1007			None => Ok(None),
1008		}
1009	}
1010
1011	async fn record_settled_lightning_receive(
1012		&self,
1013		payment_hash: PaymentHash,
1014		preimage: Preimage,
1015		invoice: &Bolt11Invoice,
1016		amount: Amount,
1017	) -> anyhow::Result<()> {
1018		let key = payment_hash.to_byte_array();
1019
1020		let mut lock = self.inner.write().await;
1021		if lock.get(partition::SETTLED_LIGHTNING_RECEIVE, &key).await?.is_some() {
1022			return Ok(());
1023		}
1024		let settled = SettledLightningReceive {
1025			payment_hash,
1026			preimage,
1027			invoice: invoice.clone(),
1028			amount,
1029			settled_at: chrono::Local::now(),
1030		};
1031		let record = Record::from_data(partition::SETTLED_LIGHTNING_RECEIVE, &key, None, &settled)?;
1032		lock.put(record).await
1033	}
1034
1035	async fn get_settled_lightning_receive(
1036		&self,
1037		payment_hash: PaymentHash,
1038	) -> anyhow::Result<Option<SettledLightningReceive>> {
1039		match self.inner.read().await
1040			.get(partition::SETTLED_LIGHTNING_RECEIVE, &payment_hash.to_byte_array()).await?
1041		{
1042			Some(record) => Ok(Some(record.to_data()?)),
1043			None => Ok(None),
1044		}
1045	}
1046
1047
1048	async fn store_exit_vtxo_entry(&self, exit: &StoredExit) -> anyhow::Result<()> {
1049		let record = Record::from_data(
1050			partition::EXIT_VTXO,
1051			&exit.vtxo_id.to_bytes(),
1052			None,
1053			exit,
1054		)?;
1055		self.inner.write().await.put(record).await
1056	}
1057
1058	async fn remove_exit_vtxo_entry(&self, id: &VtxoId) -> anyhow::Result<()> {
1059		self.inner.write().await.delete(partition::EXIT_VTXO, &id.to_bytes()).await?;
1060		Ok(())
1061	}
1062
1063	async fn get_exit_vtxo_entries(&self) -> anyhow::Result<Vec<StoredExit>> {
1064		let records = self.inner.read().await.get_all(partition::EXIT_VTXO).await?;
1065		records.into_iter().map(|r| r.to_data()).collect()
1066	}
1067
1068	async fn get_exit_vtxo_entries_with_states(
1069		&self,
1070		states: &[ExitStateKind],
1071	) -> anyhow::Result<Vec<StoredExit>> {
1072		// The state lives inside the serialized record and adaptors only key by primary/sort key,
1073		// so there's no filter to push down — read the partition and filter here.
1074		let records = self.inner.read().await.get_all(partition::EXIT_VTXO).await?;
1075		records.into_iter()
1076			.map(|r| r.to_data::<StoredExit>())
1077			.filter(|e| e.as_ref().map_or(true, |e| states.contains(&e.state.kind())))
1078			.collect()
1079	}
1080
1081	async fn get_exit_vtxo_entry(&self, id: &VtxoId) -> anyhow::Result<Option<StoredExit>> {
1082		match self.inner.read().await.get(partition::EXIT_VTXO, &id.to_bytes()).await? {
1083			Some(record) => Ok(Some(record.to_data::<StoredExit>()?)),
1084			None => Ok(None),
1085		}
1086	}
1087
1088	async fn store_exit_child_tx(
1089		&self,
1090		exit_txid: Txid,
1091		child_tx: &Transaction,
1092		origin: ExitTxOrigin,
1093	) -> anyhow::Result<()> {
1094		let exit_child = SerdeExitChildTx {
1095			child_tx: child_tx.clone(),
1096			origin,
1097		};
1098		let record = Record::from_data(
1099			partition::EXIT_CHILD_TX,
1100			&exit_txid.to_byte_array(),
1101			None,
1102			&exit_child,
1103		)?;
1104		self.inner.write().await.put(record).await
1105	}
1106
1107	async fn get_exit_child_tx(
1108		&self,
1109		exit_txid: Txid,
1110	) -> anyhow::Result<Option<(Transaction, ExitTxOrigin)>> {
1111		match self.inner.read().await
1112			.get(partition::EXIT_CHILD_TX, &exit_txid.to_byte_array()).await?
1113		{
1114			Some(record) => {
1115				let exit_child = record.to_data::<SerdeExitChildTx>()?;
1116				Ok(Some((exit_child.child_tx, exit_child.origin)))
1117			}
1118			None => Ok(None),
1119		}
1120	}
1121}
1122
1123#[cfg(test)]
1124mod tests {
1125	use super::*;
1126
1127	#[test]
1128	fn storage_query_builder() {
1129		let query = Query::new_full_range(0).limit(10);
1130
1131		assert_eq!(query.partition, 0);
1132		assert_eq!(query.limit, Some(10));
1133		assert_eq!(query.range, ..);
1134	}
1135}
1136
1137/// This module provides comprehensive tests for all four methods of the
1138/// `StorageAdaptor` trait. Use these functions to validate custom implementations.
1139///
1140/// # Example
1141///
1142/// ```rust
1143/// use bark::persist::adaptor::memory::test_suite;
1144///
1145/// #[tokio::test]
1146/// async fn test_my_custom_adaptor() {
1147///     let storage = MyCustomStorageAdaptor::new();
1148///     test_suite::run_all(&storage).await;
1149/// }
1150/// ```
1151#[cfg(test)]
1152pub mod test_suite {
1153	use super::*;
1154	use super::partition::LAST_IDS;
1155	use super::sort::SortKey;
1156
1157	async fn clear_partitions<S: StorageAdaptor>(storage: &mut S, partitions: &[u8]) -> anyhow::Result<()> {
1158		for partition in partitions {
1159			let records = storage.get_all(*partition).await?;
1160			for record in records {
1161				storage.delete(record.partition, &record.pk).await?;
1162			}
1163		}
1164		Ok(())
1165	}
1166
1167	/// Runs all test suites against the given storage adaptor.
1168	pub async fn run_all<S: StorageAdaptor>(storage: &mut S) {
1169		// put tests
1170		test_put_insert(storage).await;
1171		test_put_upsert(storage).await;
1172		test_put_with_sort_key(storage).await;
1173		test_put_without_sort_key(storage).await;
1174		test_put_multiple_partitions(storage).await;
1175
1176		// get tests
1177		test_get_existing(storage).await;
1178		test_get_after_update(storage).await;
1179
1180		// delete tests
1181		test_delete_existing(storage).await;
1182		test_delete_nonexistent(storage).await;
1183		test_delete_idempotent(storage).await;
1184
1185		// query tests
1186		test_query_empty_partition(storage).await;
1187		test_query_returns_partition_records(storage).await;
1188		test_query_ordering(storage).await;
1189		test_query_with_limit(storage).await;
1190		test_query_null_sort_key_excluded(storage).await;
1191		test_query_partition_isolation(storage).await;
1192		test_query_range(storage).await;
1193		test_query_exclusive_end_range(storage).await;
1194		test_query_full_range_limit_one(storage).await;
1195
1196		// get_all tests
1197		test_get_all_empty_partition(storage).await;
1198		test_get_all_returns_all_records(storage).await;
1199		test_get_all_includes_records_without_sort_key(storage).await;
1200		test_get_all_partition_isolation(storage).await;
1201		test_get_all_after_delete(storage).await;
1202
1203		// incremental_id tests
1204		test_incremental_id_starts_at_one(storage).await;
1205		test_incremental_id_increments(storage).await;
1206		test_incremental_id_partition_isolation(storage).await;
1207		test_incremental_id_persists_across_operations(storage).await;
1208	}
1209
1210	/// Tests that put inserts a new record.
1211	pub async fn test_put_insert<S: StorageAdaptor>(storage: &mut S) {
1212		let record = Record {
1213			pk: "put_insert_1".into(),
1214			partition: 0,
1215			sort_key: None,
1216			data: b"test data".to_vec(),
1217		};
1218
1219		storage.put(record).await.expect("put should succeed");
1220
1221		let retrieved = storage
1222			.get(0, b"put_insert_1")
1223			.await
1224			.expect("get should succeed")
1225			.expect("record should exist");
1226
1227		assert_eq!(retrieved.pk, b"put_insert_1");
1228		assert_eq!(retrieved.partition, 0);
1229		assert_eq!(retrieved.data, b"test data");
1230	}
1231
1232	/// Tests that put updates an existing record (upsert behavior).
1233	pub async fn test_put_upsert<S: StorageAdaptor>(storage: &mut S) {
1234		let record1 = Record {
1235			pk: b"put_upsert_1".into(),
1236			partition: 0,
1237			sort_key: None,
1238			data: b"original".to_vec(),
1239		};
1240		storage.put(record1).await.expect("first put should succeed");
1241
1242		let record2 = Record {
1243			pk: "put_upsert_1".into(),
1244			partition: 0,
1245			sort_key: None,
1246			data: b"updated".to_vec(),
1247		};
1248		storage
1249			.put(record2)
1250			.await
1251			.expect("second put should succeed");
1252
1253		let retrieved = storage
1254			.get(0, b"put_upsert_1")
1255			.await
1256			.expect("get should succeed")
1257			.expect("record should exist");
1258
1259		assert_eq!(retrieved.data, b"updated", "data should be updated");
1260	}
1261
1262	/// Tests that put correctly stores the sort key.
1263	pub async fn test_put_with_sort_key<S: StorageAdaptor>(storage: &mut S) {
1264		let sort_key = SortKey::u32_asc(42);
1265		let record = Record {
1266			pk: b"put_sort_key_1".into(),
1267			partition: 0,
1268			sort_key: Some(sort_key.clone()),
1269			data: b"with sort key".to_vec(),
1270		};
1271
1272		storage.put(record).await.expect("put should succeed");
1273
1274		let retrieved = storage
1275			.get(0, b"put_sort_key_1")
1276			.await
1277			.expect("get should succeed")
1278			.expect("record should exist");
1279
1280		assert_eq!(retrieved.sort_key, Some(sort_key));
1281	}
1282
1283	/// Tests that put correctly handles records without sort keys.
1284	pub async fn test_put_without_sort_key<S: StorageAdaptor>(storage: &mut S) {
1285		let record = Record {
1286			pk: b"put_no_sort_key_1".into(),
1287			partition: 0,
1288			sort_key: None,
1289			data: b"no sort key".to_vec(),
1290		};
1291
1292		storage.put(record).await.expect("put should succeed");
1293
1294		let retrieved = storage
1295			.get(0, b"put_no_sort_key_1")
1296			.await
1297			.expect("get should succeed")
1298			.expect("record should exist");
1299
1300		assert!(retrieved.sort_key.is_none());
1301	}
1302
1303	/// Tests that put correctly handles multiple partitions.
1304	pub async fn test_put_multiple_partitions<S: StorageAdaptor>(storage: &mut S) {
1305		let record_a = Record {
1306			pk: "put_multi_a".into(),
1307			partition: 0,
1308			sort_key: None,
1309			data: b"in partition a".to_vec(),
1310		};
1311		let record_b = Record {
1312			pk: "put_multi_b".into(),
1313			partition: 1,
1314			sort_key: None,
1315			data: b"in partition b".to_vec(),
1316		};
1317
1318		storage.put(record_a).await.expect("put a should succeed");
1319		storage.put(record_b).await.expect("put b should succeed");
1320
1321		let retrieved_a = storage
1322			.get(0, b"put_multi_a")
1323			.await
1324			.expect("get should succeed")
1325			.expect("record a should exist");
1326		let retrieved_b = storage
1327			.get(1, b"put_multi_b")
1328			.await
1329			.expect("get should succeed")
1330			.expect("record b should exist");
1331
1332		assert_eq!(retrieved_a.partition, 0);
1333		assert_eq!(retrieved_b.partition, 1);
1334	}
1335
1336	/// Tests that get returns an existing record.
1337	pub async fn test_get_existing<S: StorageAdaptor>(storage: &mut S) {
1338		let record = Record {
1339			pk: b"get_existing_1".into(),
1340			partition: 0,
1341			sort_key: Some(SortKey::u32_asc(100)),
1342			data: b"test".to_vec(),
1343		};
1344		storage.put(record).await.expect("put should succeed");
1345
1346		let retrieved = storage
1347			.get(0, b"get_existing_1")
1348			.await
1349			.expect("get should succeed");
1350
1351		assert!(retrieved.is_some());
1352		let retrieved = retrieved.unwrap();
1353		assert_eq!(retrieved.pk, b"get_existing_1");
1354		assert_eq!(retrieved.partition, 0);
1355		assert_eq!(retrieved.data, b"test");
1356
1357		// superset of the key
1358		assert!(storage.get(0, b"get_existing_1_").await.unwrap().is_none());
1359		// subset of the key
1360		assert!(storage.get(0, b"get_existing_").await.unwrap().is_none());
1361
1362		// non-existent key
1363		assert!(storage.get(0, b"get_nonexistent_does_not_exist").await.unwrap().is_none());
1364	}
1365
1366	/// Tests that get returns updated data after put.
1367	pub async fn test_get_after_update<S: StorageAdaptor>(storage: &mut S) {
1368		let record1 = Record {
1369			pk: b"get_after_update_1".into(),
1370			partition: 0,
1371			sort_key: None,
1372			data: b"version1".to_vec(),
1373		};
1374		storage.put(record1).await.expect("put should succeed");
1375
1376		let record2 = Record {
1377			pk: b"get_after_update_1".into(),
1378			partition: 0,
1379			sort_key: None,
1380			data: b"version2".to_vec(),
1381		};
1382		storage.put(record2).await.expect("put should succeed");
1383
1384		let retrieved = storage
1385			.get(0, b"get_after_update_1")
1386			.await
1387			.expect("get should succeed")
1388			.expect("record should exist");
1389
1390		assert_eq!(retrieved.data, b"version2");
1391	}
1392
1393	/// Tests that delete removes an existing record and returns it.
1394	pub async fn test_delete_existing<S: StorageAdaptor>(storage: &mut S) {
1395		let record = Record {
1396			pk: b"delete_existing_1".into(),
1397			partition: 0,
1398			sort_key: None,
1399			data: b"to delete".to_vec(),
1400		};
1401		storage.put(record.clone()).await.expect("put should succeed");
1402
1403		let deleted_record = storage
1404			.delete(0, b"delete_existing_1")
1405			.await
1406			.expect("delete should succeed");
1407
1408		assert_eq!(deleted_record, Some(record));
1409
1410		let retrieved = storage
1411			.get(0, b"delete_existing_1")
1412			.await
1413			.expect("get should succeed");
1414		assert!(retrieved.is_none(), "record should no longer exist");
1415	}
1416
1417	/// Tests that delete returns None for non-existent records.
1418	pub async fn test_delete_nonexistent<S: StorageAdaptor>(storage: &mut S) {
1419		let deleted_record = storage
1420			.delete(0, b"delete_nonexistent_does_not_exist")
1421			.await
1422			.expect("delete should succeed");
1423
1424		assert!(
1425			deleted_record.is_none(),
1426			"delete should return None for non-existent record"
1427		);
1428	}
1429
1430	/// Tests that delete is idempotent (second delete returns None).
1431	pub async fn test_delete_idempotent<S: StorageAdaptor>(storage: &mut S) {
1432		let record = Record {
1433			pk: b"delete_idempotent_1".into(),
1434			partition: 0,
1435			sort_key: None,
1436			data: b"delete twice".to_vec(),
1437		};
1438		storage.put(record.clone()).await.expect("put should succeed");
1439
1440		let first_delete = storage
1441			.delete(0, b"delete_idempotent_1")
1442			.await
1443			.expect("first delete should succeed");
1444		let second_delete = storage
1445			.delete(0, b"delete_idempotent_1")
1446			.await
1447			.expect("second delete should succeed");
1448
1449		assert_eq!(first_delete, Some(record), "first delete should return the record");
1450		assert_eq!(second_delete, None, "second delete should return None");
1451	}
1452
1453	/// Tests that query returns empty results for empty partition.
1454	pub async fn test_query_empty_partition<S: StorageAdaptor>(storage: &mut S) {
1455		clear_partitions(storage, &[0]).await.unwrap();
1456		let results = storage
1457			.query_sorted(Query::new_full_range(0))
1458			.await
1459			.expect("query should succeed");
1460
1461		assert!(results.is_empty());
1462	}
1463
1464	/// Tests that query returns all records in a partition.
1465	pub async fn test_query_returns_partition_records<S: StorageAdaptor>(storage: &mut S) {
1466		clear_partitions(storage, &[0]).await.unwrap();
1467		for i in 0..3 {
1468			let record = Record {
1469				pk: format!("query_partition_{}", i).into(),
1470				partition: 0,
1471				sort_key: Some(SortKey::u32_asc(i)),
1472				data: format!("record_{}", i).as_bytes().to_vec(),
1473			};
1474			storage.put(record).await.expect("put should succeed");
1475		}
1476
1477		let results = storage
1478			.query_sorted(Query::new_full_range(0))
1479			.await
1480			.expect("query should succeed");
1481
1482		assert_eq!(results.len(), 3);
1483	}
1484
1485	/// Tests that query returns records in ascending sort key order by default.
1486	pub async fn test_query_ordering<S: StorageAdaptor>(storage: &mut S) {
1487		clear_partitions(storage, &[0]).await.unwrap();
1488		// Insert in non-sequential order
1489		for i in [5, 2, 8, 1, 9] {
1490			let record = Record {
1491				pk: format!("query_asc_{}", i).into(),
1492				partition: 0,
1493				sort_key: Some(SortKey::u32_asc(i)),
1494				data: format!("record_{}", i).as_bytes().to_vec(),
1495			};
1496			storage.put(record).await.expect("put should succeed");
1497		}
1498
1499		let results = storage
1500			.query_sorted(Query::new_full_range(0))
1501			.await
1502			.expect("query should succeed");
1503
1504		let values = results.iter().map(|r| r.data.clone()).collect::<Vec<_>>();
1505		assert_eq!(
1506			values,
1507			vec![b"record_1".to_vec(), b"record_2".to_vec(), b"record_5".to_vec(), b"record_8".to_vec(), b"record_9".to_vec()],
1508			"should be in ascending order"
1509		);
1510	}
1511
1512	/// Tests that query with limit returns at most N records.
1513	pub async fn test_query_with_limit<S: StorageAdaptor>(storage: &mut S) {
1514		clear_partitions(storage, &[0]).await.unwrap();
1515		for i in 0..10 {
1516			let record = Record {
1517				pk: format!("query_limit_{}", i).into(),
1518				partition: 0,
1519				sort_key: Some(SortKey::u32_asc(i)),
1520				data: format!("record_{}", i).as_bytes().to_vec(),
1521			};
1522			storage.put(record).await.expect("put should succeed");
1523		}
1524
1525		let results = storage
1526			.query_sorted(Query::new_full_range(0).limit(3))
1527			.await
1528			.expect("query should succeed");
1529
1530		assert_eq!(results.len(), 3);
1531		let values = results.iter().map(|r| r.data.clone()).collect::<Vec<_>>();
1532		assert_eq!(
1533			values,
1534			vec![b"record_0".to_vec(), b"record_1".to_vec(), b"record_2".to_vec()],
1535			"should return first 3 records"
1536		);
1537	}
1538
1539	/// Tests that records without sort keys don't appear in query
1540	pub async fn test_query_null_sort_key_excluded<S: StorageAdaptor>(storage: &mut S) {
1541		clear_partitions(storage, &[0]).await.unwrap();
1542		// Records with sort keys
1543		let with_key_1 = Record {
1544			pk: "query_null_with_1".into(),
1545			partition: 0,
1546			sort_key: Some(SortKey::u32_asc(1)),
1547			data: b"with_key_1".to_vec(),
1548		};
1549		let with_key_2 = Record {
1550			pk: "query_null_with_2".into(),
1551			partition: 0,
1552			sort_key: Some(SortKey::u32_asc(2)),
1553			data: b"with_key_2".to_vec(),
1554		};
1555
1556		// Record without sort key
1557		let without_key = Record {
1558			pk: "query_null_without".into(),
1559			partition: 0,
1560			sort_key: None,
1561			data: b"no_key".to_vec(),
1562		};
1563
1564		storage.put(with_key_1).await.expect("put should succeed");
1565		storage.put(without_key).await.expect("put should succeed");
1566		storage.put(with_key_2).await.expect("put should succeed");
1567
1568		// query should only return records with sort keys
1569		let results_query = storage.query_sorted(Query::new_full_range(0)).await
1570			.expect("query should succeed");
1571		assert_eq!(results_query.len(), 2, "query should only return records with sort keys");
1572		assert_eq!(results_query[0].data, b"with_key_1");
1573		assert_eq!(results_query[1].data, b"with_key_2");
1574
1575		// get_all should return all records
1576		let results_all = storage.get_all(0).await
1577			.expect("get_all should succeed");
1578		assert_eq!(results_all.len(), 3, "get_all should return all records including those without sort keys");
1579
1580		// Verify all three records are present (order not guaranteed for get_all)
1581		let has_with_key_1 = results_all.iter().any(|r| r.data == b"with_key_1");
1582		let has_with_key_2 = results_all.iter().any(|r| r.data == b"with_key_2");
1583		let has_without_key = results_all.iter().any(|r| r.data == b"no_key");
1584		assert!(has_with_key_1, "get_all should include with_key_1");
1585		assert!(has_with_key_2, "get_all should include with_key_2");
1586		assert!(has_without_key, "get_all should include record without sort key");
1587	}
1588
1589	/// Tests that query only returns records from the specified partition.
1590	pub async fn test_query_partition_isolation<S: StorageAdaptor>(storage: &mut S) {
1591		clear_partitions(storage, &[0, 1]).await.unwrap();
1592		// Records in partition A
1593		for i in 0..3 {
1594			let record = Record {
1595				pk: format!("query_iso_a_{}", i).into(),
1596				partition: 0,
1597				sort_key: Some(SortKey::u32_asc(i)),
1598				data: format!("record_{}", i).as_bytes().to_vec(),
1599			};
1600			storage.put(record).await.expect("put should succeed");
1601		}
1602
1603		// Records in partition B
1604		for i in 0..5 {
1605			let record = Record {
1606				pk: format!("query_iso_b_{}", i).into(),
1607				partition: 1,
1608				sort_key: Some(SortKey::u32_asc(i)),
1609				data: format!("record_{}", i + 100).as_bytes().to_vec(),
1610			};
1611			storage.put(record).await.expect("put should succeed");
1612		}
1613
1614		let results_a = storage
1615			.query_sorted(Query::new_full_range(0))
1616			.await
1617			.expect("query should succeed");
1618
1619		let results_b = storage
1620			.query_sorted(Query::new_full_range(1))
1621			.await
1622			.expect("query should succeed");
1623
1624		assert_eq!(results_a.len(), 3, "partition A should have 3 records");
1625		assert_eq!(results_b.len(), 5, "partition B should have 5 records");
1626
1627		// Verify all results_a are from partition A
1628		assert!(results_a
1629			.iter()
1630			.all(|r| r.partition == 0));
1631
1632		// Verify all results_b are from partition B
1633		assert!(results_b
1634			.iter()
1635			.all(|r| r.partition == 1));
1636	}
1637
1638	/// Tests that query with start and end keys returns only records within the range.
1639	pub async fn test_query_range<S: StorageAdaptor>(storage: &mut S) {
1640		clear_partitions(storage, &[0]).await.unwrap();
1641
1642		// Insert records with sort keys 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
1643		for i in 1..=10u32 {
1644			let record = Record {
1645				pk: format!("query_range_{}", i).into(),
1646				partition: 0,
1647				sort_key: Some(SortKey::u32_asc(i)),
1648				data: format!("record_{}", i).as_bytes().to_vec(),
1649			};
1650			storage.put(record).await.expect("put should succeed");
1651		}
1652
1653		// Query with start key only (>= 5)
1654		let results_start = storage
1655			.query_sorted(Query::new(0, SortKey::u32_asc(5)..))
1656			.await
1657			.expect("query should succeed");
1658
1659		assert_eq!(results_start.len(), 6, "should return records 5-10");
1660		let values: Vec<_> = results_start.iter().map(|r| r.data.clone()).collect();
1661		assert_eq!(
1662			values,
1663			vec![
1664				b"record_5".to_vec(),
1665				b"record_6".to_vec(),
1666				b"record_7".to_vec(),
1667				b"record_8".to_vec(),
1668				b"record_9".to_vec(),
1669				b"record_10".to_vec(),
1670			],
1671			"should return records from 5 onwards"
1672		);
1673
1674		// Query with end key only (<= 3)
1675		let results_end = storage
1676			.query_sorted(Query::new(0, ..=SortKey::u32_asc(3)))
1677			.await
1678			.expect("query should succeed");
1679
1680		assert_eq!(results_end.len(), 3, "should return records 1-3");
1681		let values: Vec<_> = results_end.iter().map(|r| r.data.clone()).collect();
1682		assert_eq!(
1683			values,
1684			vec![
1685				b"record_1".to_vec(),
1686				b"record_2".to_vec(),
1687				b"record_3".to_vec(),
1688			],
1689			"should return records up to 3"
1690		);
1691
1692		// Query with both start and end keys (3 <= x <= 7)
1693		let results_range = storage
1694			.query_sorted(Query::new(0, SortKey::u32_asc(3)..=SortKey::u32_asc(7)))
1695			.await
1696			.expect("query should succeed");
1697
1698		assert_eq!(results_range.len(), 5, "should return records 3-7");
1699		let values: Vec<_> = results_range.iter().map(|r| r.data.clone()).collect();
1700		assert_eq!(
1701			values,
1702			vec![
1703				b"record_3".to_vec(),
1704				b"record_4".to_vec(),
1705				b"record_5".to_vec(),
1706				b"record_6".to_vec(),
1707				b"record_7".to_vec(),
1708			],
1709			"should return records in range 3-7"
1710		);
1711
1712		// Query range with limit
1713		let results_range_limit = storage
1714			.query_sorted(Query::new(0, SortKey::u32_asc(2)..=SortKey::u32_asc(8)).limit(3))
1715			.await
1716			.expect("query should succeed");
1717
1718		assert_eq!(results_range_limit.len(), 3, "should return only 3 records due to limit");
1719		let values: Vec<_> = results_range_limit.iter().map(|r| r.data.clone()).collect();
1720		assert_eq!(
1721			values,
1722			vec![
1723				b"record_2".to_vec(),
1724				b"record_3".to_vec(),
1725				b"record_4".to_vec(),
1726			],
1727			"should return first 3 records in range"
1728		);
1729
1730		// Query range that matches no records
1731		let results_empty = storage
1732			.query_sorted(Query::new(0, SortKey::u32_asc(100)..=SortKey::u32_asc(200)))
1733			.await
1734			.expect("query should succeed");
1735
1736		assert!(results_empty.is_empty(), "should return no records for out-of-range query");
1737	}
1738
1739	/// Tests that exclusive end ranges (start..end) exclude the end bound.
1740	pub async fn test_query_exclusive_end_range<S: StorageAdaptor>(storage: &mut S) {
1741		clear_partitions(storage, &[0]).await.unwrap();
1742
1743		for i in 1..=10u32 {
1744			let record = Record {
1745				pk: format!("query_excl_{}", i).into(),
1746				partition: 0,
1747				sort_key: Some(SortKey::u32_asc(i)),
1748				data: format!("record_{}", i).as_bytes().to_vec(),
1749			};
1750			storage.put(record).await.expect("put should succeed");
1751		}
1752
1753		// Exclusive end: 3 <= x < 7 (should return records 3, 4, 5, 6)
1754		let results = storage
1755			.query_sorted(Query::new(0, SortKey::u32_asc(3)..SortKey::u32_asc(7)))
1756			.await
1757			.expect("query should succeed");
1758
1759		assert_eq!(results.len(), 4, "exclusive end should not include record_7");
1760		let values: Vec<_> = results.iter().map(|r| r.data.clone()).collect();
1761		assert_eq!(
1762			values,
1763			vec![
1764				b"record_3".to_vec(),
1765				b"record_4".to_vec(),
1766				b"record_5".to_vec(),
1767				b"record_6".to_vec(),
1768			],
1769		);
1770
1771		// Exclusive end only: x < 4 (should return records 1, 2, 3)
1772		let results = storage
1773			.query_sorted(Query::new(0, ..SortKey::u32_asc(4)))
1774			.await
1775			.expect("query should succeed");
1776
1777		assert_eq!(results.len(), 3, "exclusive upper bound should not include record_4");
1778		let values: Vec<_> = results.iter().map(|r| r.data.clone()).collect();
1779		assert_eq!(
1780			values,
1781			vec![
1782				b"record_1".to_vec(),
1783				b"record_2".to_vec(),
1784				b"record_3".to_vec(),
1785			],
1786		);
1787	}
1788
1789	/// Tests that full-range query with limit 1 returns the first sorted record.
1790	pub async fn test_query_full_range_limit_one<S: StorageAdaptor>(storage: &mut S) {
1791		clear_partitions(storage, &[0]).await.unwrap();
1792
1793		for i in [5u32, 2, 8, 1, 9] {
1794			let record = Record {
1795				pk: format!("query_limit1_{}", i).into(),
1796				partition: 0,
1797				sort_key: Some(SortKey::u32_asc(i)),
1798				data: format!("record_{}", i).as_bytes().to_vec(),
1799			};
1800			storage.put(record).await.expect("put should succeed");
1801		}
1802
1803		let results = storage
1804			.query_sorted(Query::new_full_range(0).limit(1))
1805			.await
1806			.expect("query should succeed");
1807
1808		assert_eq!(results.len(), 1);
1809		assert_eq!(results[0].data, b"record_1".to_vec(), "limit 1 should return the first record in sort order");
1810	}
1811
1812	/// Tests that incremental_id returns 1 for the first call on a partition.
1813	pub async fn test_incremental_id_starts_at_one<S: StorageAdaptor>(storage: &mut S) {
1814		// Clear the LAST_IDS entry for partition 0
1815		storage.delete(LAST_IDS, b"0").await.unwrap();
1816		let id = storage.incremental_id(0).await
1817			.expect("incremental_id should succeed");
1818
1819		assert_eq!(id, 1, "first id should be 1");
1820	}
1821
1822	/// Tests that incremental_id increments on subsequent calls.
1823	pub async fn test_incremental_id_increments<S: StorageAdaptor>(storage: &mut S) {
1824		clear_partitions(storage, &[0, LAST_IDS]).await.unwrap();
1825
1826		let id1 = storage.incremental_id(0).await
1827			.expect("incremental_id should succeed");
1828		let id2 = storage.incremental_id(0).await
1829			.expect("incremental_id should succeed");
1830		let id3 = storage.incremental_id(0).await
1831			.expect("incremental_id should succeed");
1832
1833		assert_eq!(id1, 1, "first id should be 1");
1834		assert_eq!(id2, 2, "second id should be 2");
1835		assert_eq!(id3, 3, "third id should be 3");
1836	}
1837
1838	/// Tests that incremental_id maintains separate sequences for different partitions.
1839	pub async fn test_incremental_id_partition_isolation<S: StorageAdaptor>(storage: &mut S) {
1840		clear_partitions(storage, &[0, 1, LAST_IDS]).await.unwrap();
1841
1842		// Generate IDs for partition A
1843		let a1 = storage.incremental_id(0).await
1844			.expect("incremental_id should succeed");
1845		let a2 = storage.incremental_id(0).await
1846			.expect("incremental_id should succeed");
1847		let a3 = storage.incremental_id(0).await
1848			.expect("incremental_id should succeed");
1849
1850		// Generate IDs for partition B
1851		let b1 = storage.incremental_id(1).await
1852			.expect("incremental_id should succeed");
1853		let b2 = storage.incremental_id(1).await
1854			.expect("incremental_id should succeed");
1855
1856		// Partition A should have 1, 2, 3
1857		assert_eq!(a1, 1);
1858		assert_eq!(a2, 2);
1859		assert_eq!(a3, 3);
1860
1861		// Partition B should have its own sequence starting at 1
1862		assert_eq!(b1, 1);
1863		assert_eq!(b2, 2);
1864
1865		// Generate more for A - should continue from 3
1866		let a4 = storage.incremental_id(0).await.expect("incremental_id should succeed");
1867		assert_eq!(a4, 4);
1868	}
1869
1870	/// Tests that incremental_id persists its state correctly.
1871	pub async fn test_incremental_id_persists_across_operations<S: StorageAdaptor>(storage: &mut S) {
1872		clear_partitions(storage, &[0, 1, LAST_IDS]).await.unwrap();
1873
1874		// Generate some IDs
1875		let id1 = storage.incremental_id(0).await
1876			.expect("incremental_id should succeed");
1877		let id2 = storage.incremental_id(0).await
1878			.expect("incremental_id should succeed");
1879		assert_eq!(id1, 1);
1880		assert_eq!(id2, 2);
1881
1882		// Verify the stored value can be retrieved directly
1883		let stored = storage
1884			.get(LAST_IDS, &[0])
1885			.await
1886			.expect("get should succeed")
1887			.expect("id record should exist");
1888		let stored_id: u32 = serde_json::from_slice(&stored.data).expect("should deserialize");
1889		assert_eq!(stored_id, 2, "stored id should be 2");
1890
1891		// Continue generating - should pick up where we left off
1892		let id3 = storage.incremental_id(0).await.expect("incremental_id should succeed");
1893		assert_eq!(id3, 3);
1894	}
1895
1896	/// Tests that get_all returns empty results for empty partition.
1897	pub async fn test_get_all_empty_partition<S: StorageAdaptor>(storage: &mut S) {
1898		clear_partitions(storage, &[0]).await.unwrap();
1899		let results = storage
1900			.get_all(0)
1901			.await
1902			.expect("get_all should succeed");
1903
1904		assert!(results.is_empty(), "get_all should return empty for empty partition");
1905	}
1906
1907	/// Tests that get_all returns all records in a partition.
1908	pub async fn test_get_all_returns_all_records<S: StorageAdaptor>(storage: &mut S) {
1909		clear_partitions(storage, &[0]).await.unwrap();
1910
1911		// Insert records with sort keys
1912		for i in 0..5 {
1913			let record = Record {
1914				pk: format!("get_all_{}", i).into(),
1915				partition: 0,
1916				sort_key: Some(SortKey::u32_asc(i)),
1917				data: format!("record_{}", i).as_bytes().to_vec(),
1918			};
1919			storage.put(record).await.expect("put should succeed");
1920		}
1921
1922		let results = storage
1923			.get_all(0)
1924			.await
1925			.expect("get_all should succeed");
1926
1927		assert_eq!(results.len(), 5, "get_all should return all 5 records");
1928
1929		// Verify all records are present (order not guaranteed)
1930		for i in 0..5 {
1931			let expected_data = format!("record_{}", i).as_bytes().to_vec();
1932			let found = results.iter().any(|r| r.data == expected_data);
1933			assert!(found, "get_all should include record_{}", i);
1934		}
1935	}
1936
1937	/// Tests that get_all includes records without sort keys.
1938	pub async fn test_get_all_includes_records_without_sort_key<S: StorageAdaptor>(storage: &mut S) {
1939		clear_partitions(storage, &[0]).await.unwrap();
1940
1941		// Insert records with sort keys
1942		let with_key_1 = Record {
1943			pk: "get_all_with_1".into(),
1944			partition: 0,
1945			sort_key: Some(SortKey::u32_asc(1)),
1946			data: b"with_key_1".to_vec(),
1947		};
1948		let with_key_2 = Record {
1949			pk: "get_all_with_2".into(),
1950			partition: 0,
1951			sort_key: Some(SortKey::u32_asc(2)),
1952			data: b"with_key_2".to_vec(),
1953		};
1954
1955		// Insert records without sort keys
1956		let without_key_1 = Record {
1957			pk: "get_all_without_1".into(),
1958			partition: 0,
1959			sort_key: None,
1960			data: b"without_key_1".to_vec(),
1961		};
1962		let without_key_2 = Record {
1963			pk: "get_all_without_2".into(),
1964			partition: 0,
1965			sort_key: None,
1966			data: b"without_key_2".to_vec(),
1967		};
1968
1969		storage.put(with_key_1).await.expect("put should succeed");
1970		storage.put(without_key_1).await.expect("put should succeed");
1971		storage.put(with_key_2).await.expect("put should succeed");
1972		storage.put(without_key_2).await.expect("put should succeed");
1973
1974		let results = storage
1975			.get_all(0)
1976			.await
1977			.expect("get_all should succeed");
1978
1979		assert_eq!(results.len(), 4, "get_all should return all 4 records");
1980
1981		// Verify all records are present
1982		let has_with_1 = results.iter().any(|r| r.data == b"with_key_1");
1983		let has_with_2 = results.iter().any(|r| r.data == b"with_key_2");
1984		let has_without_1 = results.iter().any(|r| r.data == b"without_key_1");
1985		let has_without_2 = results.iter().any(|r| r.data == b"without_key_2");
1986
1987		assert!(has_with_1, "get_all should include with_key_1");
1988		assert!(has_with_2, "get_all should include with_key_2");
1989		assert!(has_without_1, "get_all should include without_key_1");
1990		assert!(has_without_2, "get_all should include without_key_2");
1991
1992		// Verify that query excludes records without sort keys
1993		let query_results = storage
1994			.query_sorted(Query::new_full_range(0))
1995			.await
1996			.expect("query should succeed");
1997
1998		assert_eq!(query_results.len(), 2, "query should only return records with sort keys");
1999		let query_has_without = query_results.iter().any(|r| r.sort_key.is_none());
2000		assert!(!query_has_without, "query should not include records without sort keys");
2001	}
2002
2003	/// Tests that get_all only returns records from the specified partition.
2004	pub async fn test_get_all_partition_isolation<S: StorageAdaptor>(storage: &mut S) {
2005		clear_partitions(storage, &[0, 1]).await.unwrap();
2006
2007		// Insert records in partition 0
2008		for i in 0..3 {
2009			let record = Record {
2010				pk: format!("partition_0_{}", i).into(),
2011				partition: 0,
2012				sort_key: Some(SortKey::u32_asc(i)),
2013				data: format!("p0_record_{}", i).as_bytes().to_vec(),
2014			};
2015			storage.put(record).await.expect("put should succeed");
2016		}
2017
2018		// Insert records in partition 1
2019		for i in 0..2 {
2020			let record = Record {
2021				pk: format!("partition_1_{}", i).into(),
2022				partition: 1,
2023				sort_key: Some(SortKey::u32_asc(i)),
2024				data: format!("p1_record_{}", i).as_bytes().to_vec(),
2025			};
2026			storage.put(record).await.expect("put should succeed");
2027		}
2028
2029		// get_all partition 0
2030		let results_0 = storage.get_all(0).await.expect("get_all should succeed");
2031		assert_eq!(results_0.len(), 3, "partition 0 should have 3 records");
2032		assert!(results_0.iter().all(|r| r.partition == 0), "all records should be from partition 0");
2033
2034		// get_all partition 1
2035		let results_1 = storage.get_all(1).await.expect("get_all should succeed");
2036		assert_eq!(results_1.len(), 2, "partition 1 should have 2 records");
2037		assert!(results_1.iter().all(|r| r.partition == 1), "all records should be from partition 1");
2038	}
2039
2040	/// Tests that get_all reflects deletions.
2041	pub async fn test_get_all_after_delete<S: StorageAdaptor>(storage: &mut S) {
2042		clear_partitions(storage, &[0]).await.unwrap();
2043
2044		for i in 0..3u32 {
2045			let record = Record {
2046				pk: format!("get_all_del_{}", i).into(),
2047				partition: 0,
2048				sort_key: Some(SortKey::u32_asc(i)),
2049				data: format!("record_{}", i).as_bytes().to_vec(),
2050			};
2051			storage.put(record).await.expect("put should succeed");
2052		}
2053
2054		storage.delete(0, b"get_all_del_1").await.expect("delete should succeed");
2055
2056		let results = storage.get_all(0).await.expect("get_all should succeed");
2057		assert_eq!(results.len(), 2, "get_all should reflect the deletion");
2058
2059		let has_deleted = results.iter().any(|r| r.data == b"record_1".to_vec());
2060		assert!(!has_deleted, "deleted record should not appear");
2061	}
2062}