1#[cfg(all(test, feature = "arbitrary"))]
101mod conformance;
102mod storage;
103
104use std::num::{NonZeroU64, NonZeroUsize};
105pub use storage::Ordinal;
106use thiserror::Error;
107
108#[derive(Debug, Error)]
110pub enum Error {
111 #[error("runtime error: {0}")]
112 Runtime(#[from] commonware_runtime::Error),
113 #[error("codec error: {0}")]
114 Codec(#[from] commonware_codec::Error),
115 #[error("invalid blob name: {0}")]
116 InvalidBlobName(String),
117 #[error("invalid record: {0}")]
118 InvalidRecord(u64),
119 #[error("missing record at {0}")]
120 MissingRecord(u64),
121}
122
123#[derive(Clone)]
125pub struct Config {
126 pub partition: String,
128
129 pub items_per_blob: NonZeroU64,
131
132 pub write_buffer: NonZeroUsize,
134
135 pub replay_buffer: NonZeroUsize,
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use crate::utils::bits_for_indices;
143 use commonware_codec::{FixedSize, Read, ReadExt, Write};
144 use commonware_cryptography::Crc32;
145 use commonware_formatting::hex;
146 use commonware_macros::{test_group, test_traced};
147 use commonware_runtime::{
148 deterministic, Blob, Buf, BufMut, Metrics as _, Runner, Storage, Supervisor as _,
149 };
150 use commonware_utils::{bitmap::BitMap, sequence::FixedBytes, NZUsize, NZU64};
151 use rand::Rng;
152 use std::collections::BTreeMap;
153
154 const DEFAULT_ITEMS_PER_BLOB: u64 = 1000;
155 const DEFAULT_WRITE_BUFFER: usize = 4096;
156 const DEFAULT_REPLAY_BUFFER: usize = 1024 * 1024;
157
158 #[test_traced]
159 fn test_put_get() {
160 let executor = deterministic::Runner::default();
162 executor.start(|context| async move {
163 let cfg = Config {
165 partition: "test-ordinal".into(),
166 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
167 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
168 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
169 };
170 let mut store =
171 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
172 .await
173 .expect("Failed to initialize store");
174
175 let value = FixedBytes::new([42u8; 32]);
176
177 assert!(!store.has(0));
179
180 store
182 .put(0, value.clone())
183 .await
184 .expect("Failed to put data");
185
186 assert!(store.has(0));
188
189 let retrieved = store
191 .get(0)
192 .await
193 .expect("Failed to get data")
194 .expect("Data not found");
195 assert_eq!(retrieved, value);
196
197 store.sync().await.expect("Failed to sync data");
199
200 let buffer = context.encode();
202 assert!(buffer.contains("gets_total 1"), "{}", buffer);
203 assert!(buffer.contains("puts_total 1"), "{}", buffer);
204 assert!(buffer.contains("has_total 2"), "{}", buffer);
205 assert!(buffer.contains("syncs_total 1"), "{}", buffer);
206 assert!(buffer.contains("pruned_total 0"), "{}", buffer);
207
208 let retrieved = store
210 .get(0)
211 .await
212 .expect("Failed to get data")
213 .expect("Data not found");
214 assert_eq!(retrieved, value);
215 });
216 }
217
218 #[test_traced]
219 fn test_sync_does_not_report_success_while_flush_fails() {
220 let executor = deterministic::Runner::default();
221 executor.start(|context| async move {
222 let cfg = Config {
223 partition: "test-ordinal".into(),
224 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
225 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
226 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
227 };
228 let mut store =
229 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
230 .await
231 .expect("Failed to initialize store");
232
233 store
234 .put(0, FixedBytes::new([42u8; 32]))
235 .await
236 .expect("Failed to put data");
237
238 let section = 0u64.to_be_bytes();
240 context
241 .remove(&cfg.partition, Some(§ion))
242 .await
243 .expect("Failed to remove blob");
244
245 assert!(store.sync().await.is_err(), "sync unexpectedly succeeded");
247 });
248 }
249
250 #[test_traced]
251 fn test_multiple_indices() {
252 let executor = deterministic::Runner::default();
254 executor.start(|context| async move {
255 let cfg = Config {
257 partition: "test-ordinal".into(),
258 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
259 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
260 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
261 };
262 let mut store =
263 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
264 .await
265 .expect("Failed to initialize store");
266
267 let indices = vec![
269 (0u64, FixedBytes::new([0u8; 32])),
270 (5u64, FixedBytes::new([5u8; 32])),
271 (10u64, FixedBytes::new([10u8; 32])),
272 (100u64, FixedBytes::new([100u8; 32])),
273 (1000u64, FixedBytes::new([200u8; 32])), ];
275
276 for (index, value) in &indices {
277 store
278 .put(*index, value.clone())
279 .await
280 .expect("Failed to put data");
281 }
282
283 store.sync().await.expect("Failed to sync");
285
286 for (index, value) in &indices {
288 let retrieved = store
289 .get(*index)
290 .await
291 .expect("Failed to get data")
292 .expect("Data not found");
293 assert_eq!(&retrieved, value);
294 }
295 });
296 }
297
298 #[test_traced]
299 fn test_sparse_indices() {
300 let executor = deterministic::Runner::default();
302 executor.start(|context| async move {
303 let cfg = Config {
305 partition: "test-ordinal".into(),
306 items_per_blob: NZU64!(100), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
308 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
309 };
310 let mut store =
311 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
312 .await
313 .expect("Failed to initialize store");
314
315 let indices = vec![
317 (0u64, FixedBytes::new([0u8; 32])),
318 (99u64, FixedBytes::new([99u8; 32])), (100u64, FixedBytes::new([100u8; 32])), (500u64, FixedBytes::new([200u8; 32])), ];
322
323 for (index, value) in &indices {
324 store
325 .put(*index, value.clone())
326 .await
327 .expect("Failed to put data");
328 }
329
330 assert!(!store.has(1));
332 assert!(!store.has(50));
333 assert!(!store.has(101));
334 assert!(!store.has(499));
335
336 store.sync().await.expect("Failed to sync");
338
339 for (index, value) in &indices {
340 let retrieved = store
341 .get(*index)
342 .await
343 .expect("Failed to get data")
344 .expect("Data not found");
345 assert_eq!(&retrieved, value);
346 }
347 });
348 }
349
350 #[test_traced]
351 fn test_next_gap() {
352 let executor = deterministic::Runner::default();
354 executor.start(|context| async move {
355 let cfg = Config {
357 partition: "test-ordinal".into(),
358 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
359 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
360 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
361 };
362 let mut store =
363 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
364 .await
365 .expect("Failed to initialize store");
366
367 store.put(1, FixedBytes::new([1u8; 32])).await.unwrap();
369 store.put(10, FixedBytes::new([10u8; 32])).await.unwrap();
370 store.put(11, FixedBytes::new([11u8; 32])).await.unwrap();
371 store.put(14, FixedBytes::new([14u8; 32])).await.unwrap();
372
373 let (current_end, start_next) = store.next_gap(0);
375 assert!(current_end.is_none());
376 assert_eq!(start_next, Some(1));
377
378 let (current_end, start_next) = store.next_gap(1);
379 assert_eq!(current_end, Some(1));
380 assert_eq!(start_next, Some(10));
381
382 let (current_end, start_next) = store.next_gap(10);
383 assert_eq!(current_end, Some(11));
384 assert_eq!(start_next, Some(14));
385
386 let (current_end, start_next) = store.next_gap(11);
387 assert_eq!(current_end, Some(11));
388 assert_eq!(start_next, Some(14));
389
390 let (current_end, start_next) = store.next_gap(12);
391 assert!(current_end.is_none());
392 assert_eq!(start_next, Some(14));
393
394 let (current_end, start_next) = store.next_gap(14);
395 assert_eq!(current_end, Some(14));
396 assert!(start_next.is_none());
397 });
398 }
399
400 #[test_traced]
401 fn test_missing_items() {
402 let executor = deterministic::Runner::default();
404 executor.start(|context| async move {
405 let cfg = Config {
407 partition: "test-ordinal".into(),
408 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
409 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
410 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
411 };
412 let mut store =
413 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
414 .await
415 .expect("Failed to initialize store");
416
417 assert_eq!(store.missing_items(0, 5), Vec::<u64>::new());
419 assert_eq!(store.missing_items(100, 10), Vec::<u64>::new());
420
421 store.put(1, FixedBytes::new([1u8; 32])).await.unwrap();
423 store.put(2, FixedBytes::new([2u8; 32])).await.unwrap();
424 store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
425 store.put(6, FixedBytes::new([6u8; 32])).await.unwrap();
426 store.put(10, FixedBytes::new([10u8; 32])).await.unwrap();
427
428 assert_eq!(store.missing_items(0, 5), vec![0, 3, 4, 7, 8]);
430 assert_eq!(store.missing_items(0, 6), vec![0, 3, 4, 7, 8, 9]);
431 assert_eq!(store.missing_items(0, 7), vec![0, 3, 4, 7, 8, 9]);
432
433 assert_eq!(store.missing_items(3, 3), vec![3, 4, 7]);
435 assert_eq!(store.missing_items(4, 2), vec![4, 7]);
436
437 assert_eq!(store.missing_items(1, 3), vec![3, 4, 7]);
439 assert_eq!(store.missing_items(2, 4), vec![3, 4, 7, 8]);
440 assert_eq!(store.missing_items(5, 2), vec![7, 8]);
441
442 assert_eq!(store.missing_items(11, 5), Vec::<u64>::new());
444 assert_eq!(store.missing_items(100, 10), Vec::<u64>::new());
445
446 store.put(1000, FixedBytes::new([100u8; 32])).await.unwrap();
448
449 let items = store.missing_items(11, 10);
451 assert_eq!(items, vec![11, 12, 13, 14, 15, 16, 17, 18, 19, 20]);
452
453 let items = store.missing_items(990, 15);
455 assert_eq!(
456 items,
457 vec![990, 991, 992, 993, 994, 995, 996, 997, 998, 999]
458 );
459
460 store.sync().await.unwrap();
462 assert_eq!(store.missing_items(0, 5), vec![0, 3, 4, 7, 8]);
463 assert_eq!(store.missing_items(3, 3), vec![3, 4, 7]);
464
465 store.put(9999, FixedBytes::new([99u8; 32])).await.unwrap();
467 store
468 .put(10001, FixedBytes::new([101u8; 32]))
469 .await
470 .unwrap();
471
472 let items = store.missing_items(9998, 5);
474 assert_eq!(items, vec![9998, 10000]);
475 });
476 }
477
478 #[test_traced]
479 fn test_restart() {
480 let executor = deterministic::Runner::default();
482 executor.start(|context| async move {
483 let cfg = Config {
484 partition: "test-ordinal".into(),
485 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
486 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
487 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
488 };
489
490 {
492 let mut store =
493 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
494 .await
495 .expect("Failed to initialize store");
496
497 let values = vec![
498 (0u64, FixedBytes::new([0u8; 32])),
499 (100u64, FixedBytes::new([100u8; 32])),
500 (1000u64, FixedBytes::new([200u8; 32])),
501 ];
502
503 for (index, value) in &values {
504 store
505 .put(*index, value.clone())
506 .await
507 .expect("Failed to put data");
508 }
509
510 store.sync().await.expect("Failed to sync store");
511 }
512
513 {
515 let mut bits0 = BitMap::zeroes(DEFAULT_ITEMS_PER_BLOB);
516 bits0.set(0, true);
517 bits0.set(100, true);
518 let mut bits1 = BitMap::zeroes(DEFAULT_ITEMS_PER_BLOB);
519 bits1.set(0, true);
520 let bits0 = Some(bits0);
521 let bits1 = Some(bits1);
522 let mut bits = BTreeMap::new();
523 bits.insert(0, &bits0);
524 bits.insert(1, &bits1);
525 let store = Ordinal::<_, FixedBytes<32>>::init(
526 context.child("second"),
527 cfg.clone(),
528 Some(bits),
529 )
530 .await
531 .expect("Failed to initialize store");
532
533 let values = vec![
534 (0u64, FixedBytes::new([0u8; 32])),
535 (100u64, FixedBytes::new([100u8; 32])),
536 (1000u64, FixedBytes::new([200u8; 32])),
537 ];
538
539 for (index, value) in &values {
540 let retrieved = store
541 .get(*index)
542 .await
543 .expect("Failed to get data")
544 .expect("Data not found");
545 assert_eq!(&retrieved, value);
546 }
547
548 let (current_end, start_next) = store.next_gap(0);
550 assert_eq!(current_end, Some(0));
551 assert_eq!(start_next, Some(100));
552 }
553 });
554 }
555
556 #[test_traced]
557 fn test_invalid_record() {
558 let executor = deterministic::Runner::default();
560 executor.start(|context| async move {
561 let cfg = Config {
562 partition: "test-ordinal".into(),
563 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
564 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
565 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
566 };
567
568 {
570 let mut store =
571 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
572 .await
573 .expect("Failed to initialize store");
574
575 store
576 .put(0, FixedBytes::new([42u8; 32]))
577 .await
578 .expect("Failed to put data");
579 store.sync().await.expect("Failed to sync store");
580 }
581
582 {
584 let (blob, _) = context
585 .open("test-ordinal", &0u64.to_be_bytes())
586 .await
587 .unwrap();
588 blob.write_at_sync(32, vec![0xFF]).await.unwrap();
590 }
591
592 {
594 let store =
595 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
596 .await
597 .expect("Failed to initialize store");
598
599 let result = store.get(0).await.unwrap();
600 assert!(result.is_none());
601
602 assert!(!store.has(0));
603 }
604 });
605 }
606
607 #[test_traced]
608 fn test_get_nonexistent() {
609 let executor = deterministic::Runner::default();
611 executor.start(|context| async move {
612 let cfg = Config {
614 partition: "test-ordinal".into(),
615 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
616 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
617 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
618 };
619 let store =
620 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
621 .await
622 .expect("Failed to initialize store");
623
624 let retrieved = store.get(999).await.expect("Failed to get data");
626 assert!(retrieved.is_none());
627
628 assert!(!store.has(999));
630 });
631 }
632
633 #[test_traced]
634 fn test_destroy() {
635 let executor = deterministic::Runner::default();
637 executor.start(|context| async move {
638 let cfg = Config {
639 partition: "test-ordinal".into(),
640 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
641 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
642 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
643 };
644
645 {
647 let mut store =
648 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
649 .await
650 .expect("Failed to initialize store");
651
652 store
653 .put(0, FixedBytes::new([0u8; 32]))
654 .await
655 .expect("Failed to put data");
656 store
657 .put(1000, FixedBytes::new([100u8; 32]))
658 .await
659 .expect("Failed to put data");
660
661 store.destroy().await.expect("Failed to destroy store");
663 }
664
665 {
667 let store =
668 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
669 .await
670 .expect("Failed to initialize store");
671
672 assert!(store.get(0).await.unwrap().is_none());
674 assert!(store.get(1000).await.unwrap().is_none());
675 assert!(!store.has(0));
676 assert!(!store.has(1000));
677 }
678 });
679 }
680
681 #[test_traced]
682 fn test_partial_record_write() {
683 let executor = deterministic::Runner::default();
685 executor.start(|context| async move {
686 let cfg = Config {
687 partition: "test-ordinal".into(),
688 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
689 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
690 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
691 };
692
693 {
695 let mut store =
696 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
697 .await
698 .expect("Failed to initialize store");
699
700 store
701 .put(0, FixedBytes::new([42u8; 32]))
702 .await
703 .expect("Failed to put data");
704 store
705 .put(1, FixedBytes::new([43u8; 32]))
706 .await
707 .expect("Failed to put data");
708 store.sync().await.expect("Failed to sync store");
709 }
710
711 {
713 let (blob, _) = context
714 .open("test-ordinal", &0u64.to_be_bytes())
715 .await
716 .unwrap();
717 blob.write_at_sync(36, vec![0xFF; 32]).await.unwrap();
719 }
720
721 {
723 let store =
724 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
725 .await
726 .expect("Failed to initialize store");
727
728 assert!(!store.has(0));
729 assert!(!store.has(1));
730
731 let mut store_mut = store;
733 store_mut.put(1, FixedBytes::new([44u8; 32])).await.unwrap();
734 assert_eq!(
735 store_mut.get(1).await.unwrap().unwrap(),
736 FixedBytes::new([44u8; 32])
737 );
738 }
739 });
740 }
741
742 #[test_traced]
743 fn test_corrupted_value() {
744 let executor = deterministic::Runner::default();
746 executor.start(|context| async move {
747 let cfg = Config {
748 partition: "test-ordinal".into(),
749 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
750 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
751 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
752 };
753
754 {
756 let mut store =
757 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
758 .await
759 .expect("Failed to initialize store");
760
761 store
762 .put(0, FixedBytes::new([42u8; 32]))
763 .await
764 .expect("Failed to put data");
765 store
766 .put(1, FixedBytes::new([43u8; 32]))
767 .await
768 .expect("Failed to put data");
769 store.sync().await.expect("Failed to sync store");
770 }
771
772 {
774 let (blob, _) = context
775 .open("test-ordinal", &0u64.to_be_bytes())
776 .await
777 .unwrap();
778 blob.write_at_sync(10, hex!("0xFFFFFFFF").to_vec())
780 .await
781 .unwrap();
782 }
783
784 {
786 let store =
787 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
788 .await
789 .expect("Failed to initialize store");
790
791 assert!(!store.has(0));
792 assert!(!store.has(1));
793 }
794 });
795 }
796
797 #[test_traced]
798 fn test_crc_corruptions() {
799 let executor = deterministic::Runner::default();
801 executor.start(|context| async move {
802 let cfg = Config {
803 partition: "test-ordinal".into(),
804 items_per_blob: NZU64!(10), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
806 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
807 };
808
809 {
811 let mut store =
812 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
813 .await
814 .expect("Failed to initialize store");
815
816 store.put(0, FixedBytes::new([0u8; 32])).await.unwrap();
818 store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
819 store.put(10, FixedBytes::new([10u8; 32])).await.unwrap();
820 store.put(15, FixedBytes::new([15u8; 32])).await.unwrap();
821 store.sync().await.expect("Failed to sync store");
822 }
823
824 {
826 let (blob, _) = context
828 .open("test-ordinal", &0u64.to_be_bytes())
829 .await
830 .unwrap();
831 blob.write_at_sync(32, vec![0xFF]).await.unwrap(); let (blob, _) = context
835 .open("test-ordinal", &1u64.to_be_bytes())
836 .await
837 .unwrap();
838 blob.write_at_sync(5, vec![0xFF; 4]).await.unwrap(); }
840
841 {
843 let store =
844 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
845 .await
846 .expect("Failed to initialize store");
847
848 assert!(!store.has(0));
849 assert!(!store.has(5));
850 assert!(!store.has(10));
851 assert!(!store.has(15));
852 }
853 });
854 }
855
856 #[test_traced]
857 fn test_extra_bytes_in_blob() {
858 let executor = deterministic::Runner::default();
860 executor.start(|context| async move {
861 let cfg = Config {
862 partition: "test-ordinal".into(),
863 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
864 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
865 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
866 };
867
868 {
870 let mut store =
871 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
872 .await
873 .expect("Failed to initialize store");
874
875 store
876 .put(0, FixedBytes::new([42u8; 32]))
877 .await
878 .expect("Failed to put data");
879 store
880 .put(1, FixedBytes::new([43u8; 32]))
881 .await
882 .expect("Failed to put data");
883 store.sync().await.expect("Failed to sync store");
884 }
885
886 {
888 let (blob, size) = context
889 .open("test-ordinal", &0u64.to_be_bytes())
890 .await
891 .unwrap();
892 let mut garbage = vec![0xFF; 32]; let invalid_crc = 0xDEADBEEFu32;
896 garbage.extend_from_slice(&invalid_crc.to_be_bytes());
897 assert_eq!(garbage.len(), 36); blob.write_at_sync(size, garbage).await.unwrap();
899 }
900
901 {
903 let store =
904 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
905 .await
906 .expect("Failed to initialize store");
907
908 assert!(!store.has(0));
909 assert!(!store.has(1));
910
911 let mut store_mut = store;
913 store_mut.put(2, FixedBytes::new([44u8; 32])).await.unwrap();
914 assert_eq!(
915 store_mut.get(2).await.unwrap().unwrap(),
916 FixedBytes::new([44u8; 32])
917 );
918 }
919 });
920 }
921
922 #[test_traced]
923 fn test_zero_filled_records() {
924 let executor = deterministic::Runner::default();
926 executor.start(|context| async move {
927 let cfg = Config {
928 partition: "test-ordinal".into(),
929 items_per_blob: NZU64!(DEFAULT_ITEMS_PER_BLOB),
930 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
931 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
932 };
933
934 {
936 let (blob, _) = context
937 .open("test-ordinal", &0u64.to_be_bytes())
938 .await
939 .unwrap();
940
941 let zeros = vec![0u8; 36 * 5]; blob.write_at_sync(0, zeros).await.unwrap();
944
945 let mut valid_record = vec![44u8; 32];
947 let crc = Crc32::checksum(&valid_record);
948 valid_record.extend_from_slice(&crc.to_be_bytes());
949 blob.write_at_sync(36 * 5, valid_record).await.unwrap();
950 }
951
952 {
954 let mut section = BitMap::zeroes(DEFAULT_ITEMS_PER_BLOB);
955 section.set(5, true);
956 let section = Some(section);
957 let mut bits = BTreeMap::new();
958 bits.insert(0, §ion);
959 let store = Ordinal::<_, FixedBytes<32>>::init(
960 context.child("storage"),
961 cfg.clone(),
962 Some(bits),
963 )
964 .await
965 .expect("Failed to initialize store");
966
967 for i in 0..5 {
969 assert!(!store.has(i));
970 }
971
972 assert!(store.has(5));
974 assert_eq!(
975 store.get(5).await.unwrap().unwrap(),
976 FixedBytes::new([44u8; 32])
977 );
978 }
979 });
980 }
981
982 fn test_operations_and_restart(num_values: usize) -> String {
983 let executor = deterministic::Runner::default();
985 executor.start(|mut context| async move {
986 let cfg = Config {
987 partition: "test-ordinal".into(),
988 items_per_blob: NZU64!(100), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
990 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
991 };
992
993 let mut store =
995 Ordinal::<_, FixedBytes<128>>::init(context.child("first"), cfg.clone(), None)
996 .await
997 .expect("Failed to initialize store");
998
999 let mut values = Vec::new();
1001 let mut rng_index = 0u64;
1002
1003 for _ in 0..num_values {
1004 let mut index_bytes = [0u8; 8];
1006 context.fill_bytes(&mut index_bytes);
1007 let index_offset = u64::from_be_bytes(index_bytes) % 1000;
1008 let index = rng_index + index_offset;
1009 rng_index = index + 1;
1010
1011 let mut value = [0u8; 128];
1013 context.fill_bytes(&mut value);
1014 let value = FixedBytes::<128>::new(value);
1015
1016 store
1017 .put(index, value.clone())
1018 .await
1019 .expect("Failed to put data");
1020 values.push((index, value));
1021 }
1022
1023 store.sync().await.expect("Failed to sync");
1025
1026 for (index, value) in &values {
1028 let retrieved = store
1029 .get(*index)
1030 .await
1031 .expect("Failed to get data")
1032 .expect("Data not found");
1033 assert_eq!(&retrieved, value);
1034 }
1035
1036 for i in 0..10 {
1038 let _ = store.next_gap(i * 100);
1039 }
1040
1041 store.sync().await.expect("Failed to sync store");
1043 drop(store);
1044
1045 let owned_bits = bits_for_indices(NZU64!(100), values.iter().map(|(index, _)| *index));
1047 let bits = owned_bits
1048 .iter()
1049 .map(|(section, bitmap)| (*section, bitmap))
1050 .collect();
1051 let mut store =
1052 Ordinal::<_, FixedBytes<128>>::init(context.child("second"), cfg, Some(bits))
1053 .await
1054 .expect("Failed to initialize store");
1055
1056 for (index, value) in &values {
1058 let retrieved = store
1059 .get(*index)
1060 .await
1061 .expect("Failed to get data")
1062 .expect("Data not found");
1063 assert_eq!(&retrieved, value);
1064 }
1065
1066 for _ in 0..10 {
1068 let mut index_bytes = [0u8; 8];
1069 context.fill_bytes(&mut index_bytes);
1070 let index = u64::from_be_bytes(index_bytes) % 10000;
1071
1072 let mut value = [0u8; 128];
1073 context.fill_bytes(&mut value);
1074 let value = FixedBytes::<128>::new(value);
1075
1076 store.put(index, value).await.expect("Failed to put data");
1077 }
1078
1079 store.sync().await.expect("Failed to sync");
1081
1082 context.auditor().state()
1084 })
1085 }
1086
1087 #[test_group("slow")]
1088 #[test_traced]
1089 fn test_determinism() {
1090 let state1 = test_operations_and_restart(100);
1091 let state2 = test_operations_and_restart(100);
1092 assert_eq!(state1, state2);
1093 }
1094
1095 #[test_traced]
1096 fn test_prune_basic() {
1097 let executor = deterministic::Runner::default();
1099 executor.start(|context| async move {
1100 let cfg = Config {
1101 partition: "test-ordinal".into(),
1102 items_per_blob: NZU64!(100), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1104 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1105 };
1106
1107 let mut store =
1108 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1109 .await
1110 .expect("Failed to initialize store");
1111
1112 let values = vec![
1114 (0u64, FixedBytes::new([0u8; 32])), (50u64, FixedBytes::new([50u8; 32])), (100u64, FixedBytes::new([100u8; 32])), (150u64, FixedBytes::new([150u8; 32])), (200u64, FixedBytes::new([200u8; 32])), (300u64, FixedBytes::new([44u8; 32])), ];
1121
1122 for (index, value) in &values {
1123 store
1124 .put(*index, value.clone())
1125 .await
1126 .expect("Failed to put data");
1127 }
1128 store.sync().await.unwrap();
1129
1130 for (index, value) in &values {
1132 assert_eq!(store.get(*index).await.unwrap().unwrap(), *value);
1133 }
1134
1135 store.prune(150).await.unwrap();
1137 let buffer = context.encode();
1138 assert!(buffer.contains("pruned_total 1"));
1139
1140 assert!(!store.has(0));
1142 assert!(!store.has(50));
1143 assert!(store.get(0).await.unwrap().is_none());
1144 assert!(store.get(50).await.unwrap().is_none());
1145
1146 assert!(store.has(100));
1148 assert!(store.has(150));
1149 assert!(store.has(200));
1150 assert!(store.has(300));
1151 assert_eq!(store.get(100).await.unwrap().unwrap(), values[2].1);
1152 assert_eq!(store.get(150).await.unwrap().unwrap(), values[3].1);
1153 assert_eq!(store.get(200).await.unwrap().unwrap(), values[4].1);
1154 assert_eq!(store.get(300).await.unwrap().unwrap(), values[5].1);
1155
1156 store.prune(250).await.unwrap();
1158 let buffer = context.encode();
1159 assert!(buffer.contains("pruned_total 2"));
1160
1161 assert!(!store.has(100));
1163 assert!(!store.has(150));
1164 assert!(store.get(100).await.unwrap().is_none());
1165 assert!(store.get(150).await.unwrap().is_none());
1166
1167 assert!(store.has(200));
1169 assert!(store.has(300));
1170 assert_eq!(store.get(200).await.unwrap().unwrap(), values[4].1);
1171 assert_eq!(store.get(300).await.unwrap().unwrap(), values[5].1);
1172 });
1173 }
1174
1175 #[test_traced]
1176 fn test_prune_with_gaps() {
1177 let executor = deterministic::Runner::default();
1179 executor.start(|context| async move {
1180 let cfg = Config {
1181 partition: "test-ordinal".into(),
1182 items_per_blob: NZU64!(100),
1183 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1184 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1185 };
1186
1187 let mut store =
1188 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1189 .await
1190 .expect("Failed to initialize store");
1191
1192 store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
1194 store.put(105, FixedBytes::new([105u8; 32])).await.unwrap();
1195 store.put(305, FixedBytes::new([49u8; 32])).await.unwrap();
1196 store.sync().await.unwrap();
1197
1198 let (current_end, next_start) = store.next_gap(0);
1200 assert!(current_end.is_none());
1201 assert_eq!(next_start, Some(5));
1202
1203 let (current_end, next_start) = store.next_gap(5);
1204 assert_eq!(current_end, Some(5));
1205 assert_eq!(next_start, Some(105));
1206
1207 store.prune(150).await.unwrap();
1209
1210 assert!(!store.has(5));
1212 assert!(store.get(5).await.unwrap().is_none());
1213
1214 assert!(store.has(105));
1216 assert!(store.has(305));
1217
1218 let (current_end, next_start) = store.next_gap(0);
1219 assert!(current_end.is_none());
1220 assert_eq!(next_start, Some(105));
1221
1222 let (current_end, next_start) = store.next_gap(105);
1223 assert_eq!(current_end, Some(105));
1224 assert_eq!(next_start, Some(305));
1225 });
1226 }
1227
1228 #[test_traced]
1229 fn test_prune_no_op() {
1230 let executor = deterministic::Runner::default();
1232 executor.start(|context| async move {
1233 let cfg = Config {
1234 partition: "test-ordinal".into(),
1235 items_per_blob: NZU64!(100),
1236 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1237 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1238 };
1239
1240 let mut store =
1241 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1242 .await
1243 .expect("Failed to initialize store");
1244
1245 store.put(100, FixedBytes::new([100u8; 32])).await.unwrap();
1247 store.put(200, FixedBytes::new([200u8; 32])).await.unwrap();
1248 store.sync().await.unwrap();
1249
1250 store.prune(50).await.unwrap();
1252
1253 assert!(store.has(100));
1255 assert!(store.has(200));
1256 let buffer = context.encode();
1257 assert!(buffer.contains("pruned_total 0"));
1258
1259 store.prune(100).await.unwrap();
1261
1262 assert!(store.has(100));
1264 assert!(store.has(200));
1265 let buffer = context.encode();
1266 assert!(buffer.contains("pruned_total 0"));
1267 });
1268 }
1269
1270 #[test_traced]
1271 fn test_prune_empty_store() {
1272 let executor = deterministic::Runner::default();
1274 executor.start(|context| async move {
1275 let cfg = Config {
1276 partition: "test-ordinal".into(),
1277 items_per_blob: NZU64!(100),
1278 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1279 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1280 };
1281
1282 let mut store =
1283 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1284 .await
1285 .expect("Failed to initialize store");
1286
1287 store.prune(1000).await.unwrap();
1289
1290 store.put(0, FixedBytes::new([0u8; 32])).await.unwrap();
1292 assert!(store.has(0));
1293 });
1294 }
1295
1296 #[test_traced]
1297 fn test_prune_after_restart() {
1298 let executor = deterministic::Runner::default();
1300 executor.start(|context| async move {
1301 let cfg = Config {
1302 partition: "test-ordinal".into(),
1303 items_per_blob: NZU64!(100),
1304 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1305 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1306 };
1307
1308 {
1310 let mut store =
1311 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1312 .await
1313 .expect("Failed to initialize store");
1314
1315 store.put(0, FixedBytes::new([0u8; 32])).await.unwrap();
1316 store.put(100, FixedBytes::new([100u8; 32])).await.unwrap();
1317 store.put(200, FixedBytes::new([200u8; 32])).await.unwrap();
1318 store.sync().await.unwrap();
1319 }
1320
1321 {
1323 let mut bits0 = BitMap::zeroes(100);
1324 bits0.set(0, true);
1325 let mut bits1 = BitMap::zeroes(100);
1326 bits1.set(0, true);
1327 let mut bits2 = BitMap::zeroes(100);
1328 bits2.set(0, true);
1329 let bits0 = Some(bits0);
1330 let bits1 = Some(bits1);
1331 let bits2 = Some(bits2);
1332 let mut bits = BTreeMap::new();
1333 bits.insert(0, &bits0);
1334 bits.insert(1, &bits1);
1335 bits.insert(2, &bits2);
1336 let mut store = Ordinal::<_, FixedBytes<32>>::init(
1337 context.child("second"),
1338 cfg.clone(),
1339 Some(bits),
1340 )
1341 .await
1342 .expect("Failed to initialize store");
1343
1344 assert!(store.has(0));
1346 assert!(store.has(100));
1347 assert!(store.has(200));
1348
1349 store.prune(150).await.unwrap();
1351
1352 assert!(!store.has(0));
1354 assert!(store.has(100));
1355 assert!(store.has(200));
1356
1357 store.sync().await.unwrap();
1358 }
1359
1360 {
1362 let mut bits1 = BitMap::zeroes(100);
1363 bits1.set(0, true);
1364 let mut bits2 = BitMap::zeroes(100);
1365 bits2.set(0, true);
1366 let bits1 = Some(bits1);
1367 let bits2 = Some(bits2);
1368 let mut bits = BTreeMap::new();
1369 bits.insert(1, &bits1);
1370 bits.insert(2, &bits2);
1371 let store = Ordinal::<_, FixedBytes<32>>::init(
1372 context.child("third"),
1373 cfg.clone(),
1374 Some(bits),
1375 )
1376 .await
1377 .expect("Failed to initialize store");
1378
1379 assert!(!store.has(0));
1380 assert!(store.has(100));
1381 assert!(store.has(200));
1382
1383 let (current_end, next_start) = store.next_gap(0);
1385 assert!(current_end.is_none());
1386 assert_eq!(next_start, Some(100));
1387 }
1388 });
1389 }
1390
1391 #[test_traced]
1392 fn test_prune_multiple_operations() {
1393 let executor = deterministic::Runner::default();
1395 executor.start(|context| async move {
1396 let cfg = Config {
1397 partition: "test-ordinal".into(),
1398 items_per_blob: NZU64!(50), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1400 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1401 };
1402
1403 let mut store =
1404 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1405 .await
1406 .expect("Failed to initialize store");
1407
1408 let mut values = Vec::new();
1410 for i in 0..10 {
1411 let index = i * 50 + 25; let value = FixedBytes::new([i as u8; 32]);
1413 store.put(index, value.clone()).await.unwrap();
1414 values.push((index, value));
1415 }
1416 store.sync().await.unwrap();
1417
1418 for i in 1..5 {
1420 let prune_index = i * 50 + 10;
1421 store.prune(prune_index).await.unwrap();
1422
1423 for (index, _) in &values {
1425 if *index < prune_index {
1426 assert!(!store.has(*index), "Index {index} should be pruned");
1427 } else {
1428 assert!(store.has(*index), "Index {index} should not be pruned");
1429 }
1430 }
1431 }
1432
1433 let buffer = context.encode();
1435 assert!(buffer.contains("pruned_total 4"));
1436
1437 for i in 4..10 {
1439 let index = i * 50 + 25;
1440 assert!(store.has(index));
1441 assert_eq!(
1442 store.get(index).await.unwrap().unwrap(),
1443 values[i as usize].1
1444 );
1445 }
1446 });
1447 }
1448
1449 #[test_traced]
1450 fn test_prune_blob_boundaries() {
1451 let executor = deterministic::Runner::default();
1453 executor.start(|context| async move {
1454 let cfg = Config {
1455 partition: "test-ordinal".into(),
1456 items_per_blob: NZU64!(100),
1457 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1458 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1459 };
1460
1461 let mut store =
1462 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1463 .await
1464 .expect("Failed to initialize store");
1465
1466 store.put(0, FixedBytes::new([0u8; 32])).await.unwrap(); store.put(99, FixedBytes::new([99u8; 32])).await.unwrap(); store.put(100, FixedBytes::new([100u8; 32])).await.unwrap(); store.put(199, FixedBytes::new([199u8; 32])).await.unwrap(); store.put(200, FixedBytes::new([200u8; 32])).await.unwrap(); store.sync().await.unwrap();
1473
1474 store.prune(100).await.unwrap();
1478 assert!(!store.has(0));
1479 assert!(!store.has(99));
1480 assert!(store.has(100));
1481 assert!(store.has(199));
1482 assert!(store.has(200));
1483
1484 store.prune(199).await.unwrap();
1486 assert!(store.has(100));
1487 assert!(store.has(199));
1488 assert!(store.has(200));
1489
1490 store.prune(200).await.unwrap();
1492 assert!(!store.has(100));
1493 assert!(!store.has(199));
1494 assert!(store.has(200));
1495
1496 let buffer = context.encode();
1497 assert!(buffer.contains("pruned_total 2"));
1498 });
1499 }
1500
1501 #[test_traced]
1502 fn test_prune_non_contiguous_sections() {
1503 let executor = deterministic::Runner::default();
1505 executor.start(|context| async move {
1506 let cfg = Config {
1507 partition: "test-ordinal".into(),
1508 items_per_blob: NZU64!(100),
1509 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1510 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1511 };
1512
1513 let mut store =
1514 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1515 .await
1516 .expect("Failed to initialize store");
1517
1518 store.put(0, FixedBytes::new([0u8; 32])).await.unwrap(); store.put(250, FixedBytes::new([50u8; 32])).await.unwrap(); store.put(500, FixedBytes::new([44u8; 32])).await.unwrap(); store.put(750, FixedBytes::new([45u8; 32])).await.unwrap(); store.sync().await.unwrap();
1524
1525 assert!(store.has(0));
1527 assert!(store.has(250));
1528 assert!(store.has(500));
1529 assert!(store.has(750));
1530
1531 store.prune(300).await.unwrap();
1533
1534 assert!(!store.has(0)); assert!(!store.has(250)); assert!(store.has(500)); assert!(store.has(750)); let buffer = context.encode();
1541 assert!(buffer.contains("pruned_total 2"));
1542
1543 store.prune(600).await.unwrap();
1545
1546 assert!(!store.has(500)); assert!(store.has(750)); let buffer = context.encode();
1551 assert!(buffer.contains("pruned_total 3"));
1552
1553 store.prune(1000).await.unwrap();
1555
1556 assert!(!store.has(750)); let buffer = context.encode();
1560 assert!(buffer.contains("pruned_total 4"));
1561 });
1562 }
1563
1564 #[test_traced]
1565 fn test_prune_removes_correct_pending() {
1566 let executor = deterministic::Runner::default();
1568 executor.start(|context| async move {
1569 let cfg = Config {
1570 partition: "test-ordinal".into(),
1571 items_per_blob: NZU64!(100),
1572 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1573 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1574 };
1575 let mut store =
1576 Ordinal::<_, FixedBytes<32>>::init(context.child("storage"), cfg.clone(), None)
1577 .await
1578 .expect("Failed to initialize store");
1579
1580 store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
1582 store.sync().await.unwrap();
1583
1584 store.put(10, FixedBytes::new([10u8; 32])).await.unwrap(); store.put(110, FixedBytes::new([110u8; 32])).await.unwrap(); assert!(store.has(5));
1590 assert!(store.has(10));
1591 assert!(store.has(110));
1592
1593 store.prune(150).await.unwrap();
1595
1596 assert!(!store.has(5));
1598 assert!(!store.has(10));
1599
1600 assert!(store.has(110));
1602 assert_eq!(
1603 store.get(110).await.unwrap().unwrap(),
1604 FixedBytes::new([110u8; 32])
1605 );
1606
1607 store.sync().await.unwrap();
1609 assert!(store.has(110));
1610 assert_eq!(
1611 store.get(110).await.unwrap().unwrap(),
1612 FixedBytes::new([110u8; 32])
1613 );
1614 });
1615 }
1616
1617 #[test_traced]
1618 fn test_init_without_bits_deletes_existing_data() {
1619 let executor = deterministic::Runner::default();
1621 executor.start(|context| async move {
1622 let cfg = Config {
1623 partition: "test-ordinal".into(),
1624 items_per_blob: NZU64!(10), write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1626 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1627 };
1628
1629 {
1631 let mut store =
1632 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1633 .await
1634 .expect("Failed to initialize store");
1635
1636 store.put(0, FixedBytes::new([0u8; 32])).await.unwrap();
1638 store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
1639 store.put(9, FixedBytes::new([9u8; 32])).await.unwrap();
1640
1641 store.put(10, FixedBytes::new([10u8; 32])).await.unwrap();
1643 store.put(15, FixedBytes::new([15u8; 32])).await.unwrap();
1644
1645 store.put(25, FixedBytes::new([25u8; 32])).await.unwrap();
1647
1648 store.sync().await.unwrap();
1649 }
1650
1651 {
1653 let store =
1654 Ordinal::<_, FixedBytes<32>>::init(context.child("second"), cfg.clone(), None)
1655 .await
1656 .expect("Failed to initialize store");
1657
1658 assert!(!store.has(0));
1659 assert!(!store.has(5));
1660 assert!(!store.has(9));
1661 assert!(!store.has(10));
1662 assert!(!store.has(15));
1663 assert!(!store.has(25));
1664 assert!(!store.has(1));
1665 assert!(!store.has(11));
1666 assert!(!store.has(20));
1667 }
1668 });
1669 }
1670
1671 #[test_traced]
1672 fn test_init_empty_hashmap() {
1673 let executor = deterministic::Runner::default();
1675 executor.start(|context| async move {
1676 let cfg = Config {
1677 partition: "test-ordinal".into(),
1678 items_per_blob: NZU64!(10),
1679 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1680 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1681 };
1682
1683 {
1685 let mut store =
1686 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1687 .await
1688 .expect("Failed to initialize store");
1689
1690 store.put(0, FixedBytes::new([0u8; 32])).await.unwrap();
1691 store.put(10, FixedBytes::new([10u8; 32])).await.unwrap();
1692 store.put(20, FixedBytes::new([20u8; 32])).await.unwrap();
1693
1694 store.sync().await.unwrap();
1695 }
1696
1697 {
1699 let bits: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
1700 let store = Ordinal::<_, FixedBytes<32>>::init(
1701 context.child("second"),
1702 cfg.clone(),
1703 Some(bits),
1704 )
1705 .await
1706 .expect("Failed to initialize store with bits");
1707
1708 assert!(!store.has(0));
1710 assert!(!store.has(10));
1711 assert!(!store.has(20));
1712 }
1713
1714 {
1716 let mut section = BitMap::zeroes(10);
1717 section.set(0, true);
1718 let section = Some(section);
1719 let mut bits = BTreeMap::new();
1720 bits.insert(0, §ion);
1721 let result = Ordinal::<_, FixedBytes<32>>::init(
1722 context.child("third"),
1723 cfg.clone(),
1724 Some(bits),
1725 )
1726 .await;
1727 assert!(matches!(result, Err(Error::MissingRecord(0))));
1728 }
1729 });
1730 }
1731
1732 #[test_traced]
1733 fn test_init_selective_sections() {
1734 let executor = deterministic::Runner::default();
1736 executor.start(|context| async move {
1737 let cfg = Config {
1738 partition: "test-ordinal".into(),
1739 items_per_blob: NZU64!(10),
1740 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1741 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1742 };
1743
1744 {
1746 let mut store =
1747 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1748 .await
1749 .expect("Failed to initialize store");
1750
1751 for i in 0..10 {
1753 store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1754 }
1755
1756 for i in 10..20 {
1758 store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1759 }
1760
1761 for i in 20..30 {
1763 store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1764 }
1765
1766 store.sync().await.unwrap();
1767 }
1768
1769 {
1771 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
1772
1773 let mut bitmap = BitMap::zeroes(10);
1775 bitmap.set(2, true); bitmap.set(5, true); bitmap.set(8, true); let bitmap_option = Some(bitmap);
1779
1780 bits_map.insert(1, &bitmap_option);
1781
1782 let store = Ordinal::<_, FixedBytes<32>>::init(
1783 context.child("second"),
1784 cfg.clone(),
1785 Some(bits_map),
1786 )
1787 .await
1788 .expect("Failed to initialize store with bits");
1789
1790 assert!(store.has(12));
1792 assert!(store.has(15));
1793 assert!(store.has(18));
1794
1795 assert!(!store.has(10));
1797 assert!(!store.has(11));
1798 assert!(!store.has(13));
1799 assert!(!store.has(14));
1800 assert!(!store.has(16));
1801 assert!(!store.has(17));
1802 assert!(!store.has(19));
1803
1804 for i in 0..10 {
1806 assert!(!store.has(i));
1807 }
1808 for i in 20..30 {
1809 assert!(!store.has(i));
1810 }
1811
1812 assert_eq!(
1814 store.get(12).await.unwrap().unwrap(),
1815 FixedBytes::new([12u8; 32])
1816 );
1817 assert_eq!(
1818 store.get(15).await.unwrap().unwrap(),
1819 FixedBytes::new([15u8; 32])
1820 );
1821 assert_eq!(
1822 store.get(18).await.unwrap().unwrap(),
1823 FixedBytes::new([18u8; 32])
1824 );
1825 }
1826
1827 {
1829 let mut bitmap = BitMap::zeroes(10);
1830 bitmap.set(0, true); let bitmap_option = Some(bitmap);
1832 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
1833 bits_map.insert(1, &bitmap_option);
1834 let result = Ordinal::<_, FixedBytes<32>>::init(
1835 context.child("third"),
1836 cfg.clone(),
1837 Some(bits_map),
1838 )
1839 .await;
1840 assert!(matches!(result, Err(Error::MissingRecord(10))));
1841 }
1842 });
1843 }
1844
1845 #[test_traced]
1846 fn test_init_none_option_all_records_exist() {
1847 let executor = deterministic::Runner::default();
1849 executor.start(|context| async move {
1850 let cfg = Config {
1851 partition: "test-ordinal".into(),
1852 items_per_blob: NZU64!(5),
1853 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1854 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1855 };
1856
1857 {
1859 let mut store =
1860 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1861 .await
1862 .expect("Failed to initialize store");
1863
1864 for i in 5..10 {
1866 store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1867 }
1868
1869 store.sync().await.unwrap();
1870 }
1871
1872 {
1874 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
1875 let none_option: Option<BitMap> = None;
1876 bits_map.insert(1, &none_option);
1877
1878 let store = Ordinal::<_, FixedBytes<32>>::init(
1879 context.child("second"),
1880 cfg.clone(),
1881 Some(bits_map),
1882 )
1883 .await
1884 .expect("Failed to initialize store with bits");
1885
1886 for i in 5..10 {
1888 assert!(store.has(i));
1889 assert_eq!(
1890 store.get(i).await.unwrap().unwrap(),
1891 FixedBytes::new([i as u8; 32])
1892 );
1893 }
1894 }
1895 });
1896 }
1897
1898 #[test_traced]
1899 #[should_panic(expected = "Failed to initialize store with bits: MissingRecord(6)")]
1900 fn test_init_none_option_missing_record_panics() {
1901 let executor = deterministic::Runner::default();
1903 executor.start(|context| async move {
1904 let cfg = Config {
1905 partition: "test-ordinal".into(),
1906 items_per_blob: NZU64!(5),
1907 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1908 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1909 };
1910
1911 {
1913 let mut store =
1914 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1915 .await
1916 .expect("Failed to initialize store");
1917
1918 store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
1920 store.put(7, FixedBytes::new([7u8; 32])).await.unwrap();
1922 store.put(8, FixedBytes::new([8u8; 32])).await.unwrap();
1923 store.put(9, FixedBytes::new([9u8; 32])).await.unwrap();
1924
1925 store.sync().await.unwrap();
1926 }
1927
1928 {
1931 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
1932 let none_option: Option<BitMap> = None;
1933 bits_map.insert(1, &none_option);
1934
1935 let _store = Ordinal::<_, FixedBytes<32>>::init(
1936 context.child("second"),
1937 cfg.clone(),
1938 Some(bits_map),
1939 )
1940 .await
1941 .expect("Failed to initialize store with bits");
1942 }
1943 });
1944 }
1945
1946 #[test_traced]
1947 fn test_init_mixed_sections() {
1948 let executor = deterministic::Runner::default();
1950 executor.start(|context| async move {
1951 let cfg = Config {
1952 partition: "test-ordinal".into(),
1953 items_per_blob: NZU64!(5),
1954 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
1955 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
1956 };
1957
1958 {
1960 let mut store =
1961 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
1962 .await
1963 .expect("Failed to initialize store");
1964
1965 for i in 0..5 {
1967 store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1968 }
1969
1970 store.put(5, FixedBytes::new([5u8; 32])).await.unwrap();
1972 store.put(7, FixedBytes::new([7u8; 32])).await.unwrap();
1973 store.put(9, FixedBytes::new([9u8; 32])).await.unwrap();
1974
1975 for i in 10..15 {
1977 store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
1978 }
1979
1980 store.sync().await.unwrap();
1981 }
1982
1983 {
1985 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
1986
1987 let none_option: Option<BitMap> = None;
1989 bits_map.insert(0, &none_option);
1990
1991 let mut bitmap1 = BitMap::zeroes(5);
1993 bitmap1.set(0, true); bitmap1.set(2, true); let bitmap1_option = Some(bitmap1);
1997 bits_map.insert(1, &bitmap1_option);
1998
1999 let store = Ordinal::<_, FixedBytes<32>>::init(
2002 context.child("second"),
2003 cfg.clone(),
2004 Some(bits_map),
2005 )
2006 .await
2007 .expect("Failed to initialize store with bits");
2008
2009 for i in 0..5 {
2011 assert!(store.has(i));
2012 assert_eq!(
2013 store.get(i).await.unwrap().unwrap(),
2014 FixedBytes::new([i as u8; 32])
2015 );
2016 }
2017
2018 assert!(store.has(5));
2020 assert!(store.has(7));
2021 assert!(!store.has(6));
2022 assert!(!store.has(8));
2023 assert!(!store.has(9)); for i in 10..15 {
2027 assert!(!store.has(i));
2028 }
2029 }
2030 });
2031 }
2032
2033 #[test_traced]
2034 #[should_panic(expected = "Failed to initialize store with bits: MissingRecord(2)")]
2035 fn test_init_corrupted_records() {
2036 let executor = deterministic::Runner::default();
2038 executor.start(|context| async move {
2039 let cfg = Config {
2040 partition: "test-ordinal".into(),
2041 items_per_blob: NZU64!(5),
2042 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
2043 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
2044 };
2045
2046 {
2048 let mut store =
2049 Ordinal::<_, FixedBytes<32>>::init(context.child("first"), cfg.clone(), None)
2050 .await
2051 .expect("Failed to initialize store");
2052
2053 for i in 0..5 {
2055 store.put(i, FixedBytes::new([i as u8; 32])).await.unwrap();
2056 }
2057
2058 store.sync().await.unwrap();
2059 }
2060
2061 {
2063 let (blob, _) = context
2064 .open("test-ordinal", &0u64.to_be_bytes())
2065 .await
2066 .unwrap();
2067 let offset = 2 * 36 + 32; blob.write_at_sync(offset, vec![0xFF]).await.unwrap();
2070 }
2071
2072 {
2074 let mut bits_map: BTreeMap<u64, &Option<BitMap>> = BTreeMap::new();
2075
2076 let mut bitmap = BitMap::zeroes(5);
2078 bitmap.set(0, true); bitmap.set(2, true); bitmap.set(4, true); let bitmap_option = Some(bitmap);
2082 bits_map.insert(0, &bitmap_option);
2083
2084 let _store = Ordinal::<_, FixedBytes<32>>::init(
2085 context.child("second"),
2086 cfg.clone(),
2087 Some(bits_map),
2088 )
2089 .await
2090 .expect("Failed to initialize store with bits");
2091 }
2092 });
2093 }
2094
2095 #[derive(Debug, PartialEq, Eq)]
2097 pub struct DummyValue {
2098 pub value: u64,
2099 }
2100
2101 impl Write for DummyValue {
2102 fn write(&self, buf: &mut impl BufMut) {
2103 self.value.write(buf);
2104 }
2105 }
2106
2107 impl Read for DummyValue {
2108 type Cfg = ();
2109
2110 fn read_cfg(buf: &mut impl Buf, _: &Self::Cfg) -> Result<Self, commonware_codec::Error> {
2111 let value = u64::read(buf)?;
2112 if value == 0 {
2113 return Err(commonware_codec::Error::Invalid(
2114 "DummyValue",
2115 "value must be non-zero",
2116 ));
2117 }
2118 Ok(Self { value })
2119 }
2120 }
2121
2122 impl FixedSize for DummyValue {
2123 const SIZE: usize = u64::SIZE;
2124 }
2125
2126 #[test_traced]
2127 fn test_init_skip_unparseable_record() {
2128 let executor = deterministic::Runner::default();
2130 executor.start(|context| async move {
2131 let cfg = Config {
2132 partition: "test-ordinal".into(),
2133 items_per_blob: NZU64!(1),
2134 write_buffer: NZUsize!(DEFAULT_WRITE_BUFFER),
2135 replay_buffer: NZUsize!(DEFAULT_REPLAY_BUFFER),
2136 };
2137
2138 {
2140 let mut store =
2141 Ordinal::<_, DummyValue>::init(context.child("first"), cfg.clone(), None)
2142 .await
2143 .expect("Failed to initialize store");
2144
2145 store.put(1, DummyValue { value: 1 }).await.unwrap();
2147 store.put(2, DummyValue { value: 0 }).await.unwrap(); store.put(4, DummyValue { value: 4 }).await.unwrap();
2149
2150 store.sync().await.unwrap();
2151 }
2152
2153 {
2155 let store =
2156 Ordinal::<_, DummyValue>::init(context.child("second"), cfg.clone(), None)
2157 .await
2158 .expect("Failed to initialize store");
2159
2160 assert!(!store.has(1));
2161 assert!(!store.has(2));
2162 assert!(!store.has(4));
2163 }
2164 });
2165 }
2166}