1use async_trait::async_trait;
4use std::collections::HashMap;
5use std::sync::{Arc, RwLock};
6
7use crate::types::{to_hex, Hash};
8
9#[derive(Debug, Clone, Default)]
11pub struct StoreStats {
12 pub count: u64,
14 pub bytes: u64,
16 pub pinned_count: u64,
18 pub pinned_bytes: u64,
20}
21
22#[async_trait]
24pub trait Store: Send + Sync {
25 async fn put(&self, hash: Hash, data: Vec<u8>) -> Result<bool, StoreError>;
28
29 async fn put_many(&self, items: Vec<(Hash, Vec<u8>)>) -> Result<usize, StoreError> {
32 let mut inserted = 0usize;
33 for (hash, data) in items {
34 if self.put(hash, data).await? {
35 inserted += 1;
36 }
37 }
38 Ok(inserted)
39 }
40
41 async fn get(&self, hash: &Hash) -> Result<Option<Vec<u8>>, StoreError>;
44
45 async fn has(&self, hash: &Hash) -> Result<bool, StoreError>;
47
48 async fn delete(&self, hash: &Hash) -> Result<bool, StoreError>;
51
52 fn set_max_bytes(&self, _max: u64) {}
58
59 fn max_bytes(&self) -> Option<u64> {
61 None
62 }
63
64 async fn stats(&self) -> StoreStats {
66 StoreStats::default()
67 }
68
69 async fn evict_if_needed(&self) -> Result<u64, StoreError> {
72 Ok(0)
73 }
74
75 async fn pin(&self, _hash: &Hash) -> Result<(), StoreError> {
81 Ok(())
82 }
83
84 async fn unpin(&self, _hash: &Hash) -> Result<(), StoreError> {
86 Ok(())
87 }
88
89 fn pin_count(&self, _hash: &Hash) -> u32 {
91 0
92 }
93
94 fn is_pinned(&self, hash: &Hash) -> bool {
96 self.pin_count(hash) > 0
97 }
98}
99
100#[derive(Debug, thiserror::Error)]
102pub enum StoreError {
103 #[error("IO error: {0}")]
104 Io(#[from] std::io::Error),
105 #[error("Store error: {0}")]
106 Other(String),
107}
108
109#[derive(Debug, Default)]
110struct BufferedStoreInner {
111 pending: HashMap<Hash, Vec<u8>>,
112 order: Vec<Hash>,
113}
114
115#[derive(Debug, Clone)]
117pub struct BufferedStore<S: Store> {
118 base: Arc<S>,
119 inner: Arc<RwLock<BufferedStoreInner>>,
120}
121
122impl<S: Store> BufferedStore<S> {
123 pub fn new(base: Arc<S>) -> Self {
124 Self {
125 base,
126 inner: Arc::new(RwLock::new(BufferedStoreInner::default())),
127 }
128 }
129
130 pub async fn flush(&self) -> Result<usize, StoreError> {
131 let items = {
132 let mut inner = self.inner.write().unwrap();
133 if inner.order.is_empty() {
134 return Ok(0);
135 }
136
137 let order = std::mem::take(&mut inner.order);
138 let mut items = Vec::with_capacity(order.len());
139 for hash in order {
140 if let Some(data) = inner.pending.remove(&hash) {
141 items.push((hash, data));
142 }
143 }
144 items
145 };
146
147 self.base.put_many(items).await
148 }
149}
150
151#[async_trait]
152impl<S: Store> Store for BufferedStore<S> {
153 async fn put(&self, hash: Hash, data: Vec<u8>) -> Result<bool, StoreError> {
154 {
155 let inner = self.inner.read().unwrap();
156 if inner.pending.contains_key(&hash) {
157 return Ok(false);
158 }
159 }
160
161 if self.base.has(&hash).await? {
162 return Ok(false);
163 }
164
165 let mut inner = self.inner.write().unwrap();
166 if inner.pending.contains_key(&hash) {
167 return Ok(false);
168 }
169 inner.order.push(hash);
170 inner.pending.insert(hash, data);
171 Ok(true)
172 }
173
174 async fn put_many(&self, items: Vec<(Hash, Vec<u8>)>) -> Result<usize, StoreError> {
175 let mut inserted = 0usize;
176 for (hash, data) in items {
177 if self.put(hash, data).await? {
178 inserted += 1;
179 }
180 }
181 Ok(inserted)
182 }
183
184 async fn get(&self, hash: &Hash) -> Result<Option<Vec<u8>>, StoreError> {
185 if let Some(data) = self.inner.read().unwrap().pending.get(hash).cloned() {
186 return Ok(Some(data));
187 }
188 self.base.get(hash).await
189 }
190
191 async fn has(&self, hash: &Hash) -> Result<bool, StoreError> {
192 if self.inner.read().unwrap().pending.contains_key(hash) {
193 return Ok(true);
194 }
195 self.base.has(hash).await
196 }
197
198 async fn delete(&self, hash: &Hash) -> Result<bool, StoreError> {
199 let removed = {
200 let mut inner = self.inner.write().unwrap();
201 let removed = inner.pending.remove(hash).is_some();
202 if removed {
203 inner.order.retain(|queued| queued != hash);
204 }
205 removed
206 };
207
208 if removed {
209 return Ok(true);
210 }
211
212 self.base.delete(hash).await
213 }
214
215 async fn stats(&self) -> StoreStats {
216 let mut stats = self.base.stats().await;
217 let pending_bytes = self
218 .inner
219 .read()
220 .unwrap()
221 .pending
222 .values()
223 .map(|data| data.len() as u64)
224 .sum::<u64>();
225 stats.count += self.inner.read().unwrap().pending.len() as u64;
226 stats.bytes += pending_bytes;
227 stats
228 }
229
230 async fn evict_if_needed(&self) -> Result<u64, StoreError> {
231 self.base.evict_if_needed().await
232 }
233
234 async fn pin(&self, hash: &Hash) -> Result<(), StoreError> {
235 self.base.pin(hash).await
236 }
237
238 async fn unpin(&self, hash: &Hash) -> Result<(), StoreError> {
239 self.base.unpin(hash).await
240 }
241
242 fn pin_count(&self, hash: &Hash) -> u32 {
243 self.base.pin_count(hash)
244 }
245}
246
247#[derive(Debug, Clone)]
249struct MemoryEntry {
250 data: Vec<u8>,
251 order: u64,
253}
254
255#[derive(Debug, Default)]
257struct MemoryStoreInner {
258 data: HashMap<String, MemoryEntry>,
259 pins: HashMap<String, u32>,
260 next_order: u64,
261 max_bytes: Option<u64>,
262}
263
264#[derive(Debug, Clone, Default)]
266pub struct MemoryStore {
267 inner: Arc<RwLock<MemoryStoreInner>>,
268}
269
270impl MemoryStore {
271 pub fn new() -> Self {
272 Self {
273 inner: Arc::new(RwLock::new(MemoryStoreInner::default())),
274 }
275 }
276
277 pub fn with_max_bytes(max_bytes: u64) -> Self {
279 Self {
280 inner: Arc::new(RwLock::new(MemoryStoreInner {
281 max_bytes: if max_bytes > 0 { Some(max_bytes) } else { None },
282 ..Default::default()
283 })),
284 }
285 }
286
287 pub fn size(&self) -> usize {
289 self.inner.read().unwrap().data.len()
290 }
291
292 pub fn total_bytes(&self) -> usize {
294 self.inner
295 .read()
296 .unwrap()
297 .data
298 .values()
299 .map(|e| e.data.len())
300 .sum()
301 }
302
303 pub fn clear(&self) {
305 self.inner.write().unwrap().data.clear();
306 }
307
308 pub fn keys(&self) -> Vec<Hash> {
310 self.inner
311 .read()
312 .unwrap()
313 .data
314 .keys()
315 .filter_map(|hex| {
316 let bytes = hex::decode(hex).ok()?;
317 if bytes.len() != 32 {
318 return None;
319 }
320 let mut hash = [0u8; 32];
321 hash.copy_from_slice(&bytes);
322 Some(hash)
323 })
324 .collect()
325 }
326
327 fn evict_to_target(&self, target_bytes: u64) -> u64 {
329 let mut inner = self.inner.write().unwrap();
330
331 let current_bytes: u64 = inner.data.values().map(|e| e.data.len() as u64).sum();
332 if current_bytes <= target_bytes {
333 return 0;
334 }
335
336 let mut unpinned: Vec<(String, u64, u64)> = inner
338 .data
339 .iter()
340 .filter(|(key, _)| inner.pins.get(*key).copied().unwrap_or(0) == 0)
341 .map(|(key, entry)| (key.clone(), entry.order, entry.data.len() as u64))
342 .collect();
343
344 unpinned.sort_by_key(|(_, order, _)| *order);
345
346 let mut freed = 0u64;
347 let to_free = current_bytes - target_bytes;
348
349 for (key, _, size) in unpinned {
350 if freed >= to_free {
351 break;
352 }
353 inner.data.remove(&key);
354 freed += size;
355 }
356
357 freed
358 }
359}
360
361#[async_trait]
362impl Store for MemoryStore {
363 async fn put(&self, hash: Hash, data: Vec<u8>) -> Result<bool, StoreError> {
364 let key = to_hex(&hash);
365 let mut inner = self.inner.write().unwrap();
366 if inner.data.contains_key(&key) {
367 return Ok(false);
368 }
369 let order = inner.next_order;
370 inner.next_order += 1;
371 inner.data.insert(key, MemoryEntry { data, order });
372 Ok(true)
373 }
374
375 async fn put_many(&self, items: Vec<(Hash, Vec<u8>)>) -> Result<usize, StoreError> {
376 let mut inserted = 0usize;
377 let mut inner = self.inner.write().unwrap();
378 for (hash, data) in items {
379 let key = to_hex(&hash);
380 if inner.data.contains_key(&key) {
381 continue;
382 }
383 let order = inner.next_order;
384 inner.next_order += 1;
385 inner.data.insert(key, MemoryEntry { data, order });
386 inserted += 1;
387 }
388 Ok(inserted)
389 }
390
391 async fn get(&self, hash: &Hash) -> Result<Option<Vec<u8>>, StoreError> {
392 let key = to_hex(hash);
393 let inner = self.inner.read().unwrap();
394 Ok(inner.data.get(&key).map(|e| e.data.clone()))
395 }
396
397 async fn has(&self, hash: &Hash) -> Result<bool, StoreError> {
398 let key = to_hex(hash);
399 Ok(self.inner.read().unwrap().data.contains_key(&key))
400 }
401
402 async fn delete(&self, hash: &Hash) -> Result<bool, StoreError> {
403 let key = to_hex(hash);
404 let mut inner = self.inner.write().unwrap();
405 inner.pins.remove(&key);
407 Ok(inner.data.remove(&key).is_some())
408 }
409
410 fn set_max_bytes(&self, max: u64) {
411 self.inner.write().unwrap().max_bytes = if max > 0 { Some(max) } else { None };
412 }
413
414 fn max_bytes(&self) -> Option<u64> {
415 self.inner.read().unwrap().max_bytes
416 }
417
418 async fn stats(&self) -> StoreStats {
419 let inner = self.inner.read().unwrap();
420 let mut count = 0u64;
421 let mut bytes = 0u64;
422 let mut pinned_count = 0u64;
423 let mut pinned_bytes = 0u64;
424
425 for (key, entry) in &inner.data {
426 count += 1;
427 bytes += entry.data.len() as u64;
428 if inner.pins.get(key).copied().unwrap_or(0) > 0 {
429 pinned_count += 1;
430 pinned_bytes += entry.data.len() as u64;
431 }
432 }
433
434 StoreStats {
435 count,
436 bytes,
437 pinned_count,
438 pinned_bytes,
439 }
440 }
441
442 async fn evict_if_needed(&self) -> Result<u64, StoreError> {
443 let max = match self.inner.read().unwrap().max_bytes {
444 Some(m) => m,
445 None => return Ok(0), };
447
448 let current: u64 = self
449 .inner
450 .read()
451 .unwrap()
452 .data
453 .values()
454 .map(|e| e.data.len() as u64)
455 .sum();
456
457 if current <= max {
458 return Ok(0);
459 }
460
461 let target = max * 9 / 10;
463 Ok(self.evict_to_target(target))
464 }
465
466 async fn pin(&self, hash: &Hash) -> Result<(), StoreError> {
467 let key = to_hex(hash);
468 let mut inner = self.inner.write().unwrap();
469 *inner.pins.entry(key).or_insert(0) += 1;
470 Ok(())
471 }
472
473 async fn unpin(&self, hash: &Hash) -> Result<(), StoreError> {
474 let key = to_hex(hash);
475 let mut inner = self.inner.write().unwrap();
476 if let Some(count) = inner.pins.get_mut(&key) {
477 if *count > 0 {
478 *count -= 1;
479 }
480 if *count == 0 {
481 inner.pins.remove(&key);
482 }
483 }
484 Ok(())
485 }
486
487 fn pin_count(&self, hash: &Hash) -> u32 {
488 let key = to_hex(hash);
489 self.inner
490 .read()
491 .unwrap()
492 .pins
493 .get(&key)
494 .copied()
495 .unwrap_or(0)
496 }
497}
498
499#[cfg(test)]
500mod tests {
501 use super::*;
502 use crate::hash::sha256;
503
504 #[tokio::test]
505 async fn test_put_returns_true_for_new() {
506 let store = MemoryStore::new();
507 let data = vec![1u8, 2, 3];
508 let hash = sha256(&data);
509
510 let result = store.put(hash, data).await.unwrap();
511 assert!(result);
512 }
513
514 #[tokio::test]
515 async fn test_put_returns_false_for_duplicate() {
516 let store = MemoryStore::new();
517 let data = vec![1u8, 2, 3];
518 let hash = sha256(&data);
519
520 store.put(hash, data.clone()).await.unwrap();
521 let result = store.put(hash, data).await.unwrap();
522 assert!(!result);
523 }
524
525 #[tokio::test]
526 async fn test_put_many_counts_only_new_items() {
527 let store = MemoryStore::new();
528 let data1 = vec![1u8, 2, 3];
529 let data2 = vec![4u8, 5, 6];
530 let hash1 = sha256(&data1);
531 let hash2 = sha256(&data2);
532
533 store.put(hash1, data1.clone()).await.unwrap();
534 let inserted = store
535 .put_many(vec![(hash1, data1), (hash2, data2.clone())])
536 .await
537 .unwrap();
538
539 assert_eq!(inserted, 1);
540 assert_eq!(store.get(&hash2).await.unwrap(), Some(data2));
541 }
542
543 #[tokio::test]
544 async fn test_buffered_store_flushes_pending_writes() {
545 let base = std::sync::Arc::new(MemoryStore::new());
546 let buffered = BufferedStore::new(std::sync::Arc::clone(&base));
547 let data = vec![9u8, 8, 7];
548 let hash = sha256(&data);
549
550 assert!(buffered.put(hash, data.clone()).await.unwrap());
551 assert_eq!(buffered.get(&hash).await.unwrap(), Some(data.clone()));
552 assert_eq!(base.get(&hash).await.unwrap(), None);
553
554 let flushed = buffered.flush().await.unwrap();
555
556 assert_eq!(flushed, 1);
557 assert_eq!(base.get(&hash).await.unwrap(), Some(data));
558 }
559
560 #[tokio::test]
561 async fn test_get_returns_data() {
562 let store = MemoryStore::new();
563 let data = vec![1u8, 2, 3];
564 let hash = sha256(&data);
565
566 store.put(hash, data.clone()).await.unwrap();
567 let result = store.get(&hash).await.unwrap();
568
569 assert_eq!(result, Some(data));
570 }
571
572 #[tokio::test]
573 async fn test_get_returns_none_for_missing() {
574 let store = MemoryStore::new();
575 let hash = [0u8; 32];
576
577 let result = store.get(&hash).await.unwrap();
578 assert!(result.is_none());
579 }
580
581 #[tokio::test]
582 async fn test_has_returns_true() {
583 let store = MemoryStore::new();
584 let data = vec![1u8, 2, 3];
585 let hash = sha256(&data);
586
587 store.put(hash, data).await.unwrap();
588 assert!(store.has(&hash).await.unwrap());
589 }
590
591 #[tokio::test]
592 async fn test_has_returns_false() {
593 let store = MemoryStore::new();
594 let hash = [0u8; 32];
595
596 assert!(!store.has(&hash).await.unwrap());
597 }
598
599 #[tokio::test]
600 async fn test_delete_returns_true() {
601 let store = MemoryStore::new();
602 let data = vec![1u8, 2, 3];
603 let hash = sha256(&data);
604
605 store.put(hash, data).await.unwrap();
606 let result = store.delete(&hash).await.unwrap();
607
608 assert!(result);
609 assert!(!store.has(&hash).await.unwrap());
610 }
611
612 #[tokio::test]
613 async fn test_delete_returns_false() {
614 let store = MemoryStore::new();
615 let hash = [0u8; 32];
616
617 let result = store.delete(&hash).await.unwrap();
618 assert!(!result);
619 }
620
621 #[tokio::test]
622 async fn test_size() {
623 let store = MemoryStore::new();
624 assert_eq!(store.size(), 0);
625
626 let data1 = vec![1u8];
627 let data2 = vec![2u8];
628 let hash1 = sha256(&data1);
629 let hash2 = sha256(&data2);
630
631 store.put(hash1, data1).await.unwrap();
632 store.put(hash2, data2).await.unwrap();
633
634 assert_eq!(store.size(), 2);
635 }
636
637 #[tokio::test]
638 async fn test_total_bytes() {
639 let store = MemoryStore::new();
640 assert_eq!(store.total_bytes(), 0);
641
642 let data1 = vec![1u8, 2, 3];
643 let data2 = vec![4u8, 5];
644 let hash1 = sha256(&data1);
645 let hash2 = sha256(&data2);
646
647 store.put(hash1, data1).await.unwrap();
648 store.put(hash2, data2).await.unwrap();
649
650 assert_eq!(store.total_bytes(), 5);
651 }
652
653 #[tokio::test]
654 async fn test_clear() {
655 let store = MemoryStore::new();
656 let data = vec![1u8, 2, 3];
657 let hash = sha256(&data);
658
659 store.put(hash, data).await.unwrap();
660 store.clear();
661
662 assert_eq!(store.size(), 0);
663 assert!(!store.has(&hash).await.unwrap());
664 }
665
666 #[tokio::test]
667 async fn test_keys() {
668 let store = MemoryStore::new();
669 assert!(store.keys().is_empty());
670
671 let data1 = vec![1u8];
672 let data2 = vec![2u8];
673 let hash1 = sha256(&data1);
674 let hash2 = sha256(&data2);
675
676 store.put(hash1, data1).await.unwrap();
677 store.put(hash2, data2).await.unwrap();
678
679 let keys = store.keys();
680 assert_eq!(keys.len(), 2);
681
682 let mut hex_keys: Vec<_> = keys.iter().map(to_hex).collect();
683 hex_keys.sort();
684 let mut expected: Vec<_> = vec![to_hex(&hash1), to_hex(&hash2)];
685 expected.sort();
686 assert_eq!(hex_keys, expected);
687 }
688
689 #[tokio::test]
690 async fn test_pin_and_unpin() {
691 let store = MemoryStore::new();
692 let data = vec![1u8, 2, 3];
693 let hash = sha256(&data);
694
695 store.put(hash, data).await.unwrap();
696
697 assert!(!store.is_pinned(&hash));
699 assert_eq!(store.pin_count(&hash), 0);
700
701 store.pin(&hash).await.unwrap();
703 assert!(store.is_pinned(&hash));
704 assert_eq!(store.pin_count(&hash), 1);
705
706 store.unpin(&hash).await.unwrap();
708 assert!(!store.is_pinned(&hash));
709 assert_eq!(store.pin_count(&hash), 0);
710 }
711
712 #[tokio::test]
713 async fn test_pin_count_ref_counting() {
714 let store = MemoryStore::new();
715 let data = vec![1u8, 2, 3];
716 let hash = sha256(&data);
717
718 store.put(hash, data).await.unwrap();
719
720 store.pin(&hash).await.unwrap();
722 store.pin(&hash).await.unwrap();
723 store.pin(&hash).await.unwrap();
724 assert_eq!(store.pin_count(&hash), 3);
725
726 store.unpin(&hash).await.unwrap();
728 assert_eq!(store.pin_count(&hash), 2);
729 assert!(store.is_pinned(&hash));
730
731 store.unpin(&hash).await.unwrap();
733 store.unpin(&hash).await.unwrap();
734 assert_eq!(store.pin_count(&hash), 0);
735 assert!(!store.is_pinned(&hash));
736
737 store.unpin(&hash).await.unwrap();
739 assert_eq!(store.pin_count(&hash), 0);
740 }
741
742 #[tokio::test]
743 async fn test_stats() {
744 let store = MemoryStore::new();
745
746 let data1 = vec![1u8, 2, 3]; let data2 = vec![4u8, 5]; let hash1 = sha256(&data1);
749 let hash2 = sha256(&data2);
750
751 store.put(hash1, data1).await.unwrap();
752 store.put(hash2, data2).await.unwrap();
753
754 store.pin(&hash1).await.unwrap();
756
757 let stats = store.stats().await;
758 assert_eq!(stats.count, 2);
759 assert_eq!(stats.bytes, 5);
760 assert_eq!(stats.pinned_count, 1);
761 assert_eq!(stats.pinned_bytes, 3);
762 }
763
764 #[tokio::test]
765 async fn test_max_bytes() {
766 let store = MemoryStore::new();
767 assert!(store.max_bytes().is_none());
768
769 store.set_max_bytes(1000);
770 assert_eq!(store.max_bytes(), Some(1000));
771
772 store.set_max_bytes(0);
774 assert!(store.max_bytes().is_none());
775 }
776
777 #[tokio::test]
778 async fn test_with_max_bytes() {
779 let store = MemoryStore::with_max_bytes(500);
780 assert_eq!(store.max_bytes(), Some(500));
781
782 let store_unlimited = MemoryStore::with_max_bytes(0);
783 assert!(store_unlimited.max_bytes().is_none());
784 }
785
786 #[tokio::test]
787 async fn test_eviction_respects_pins() {
788 let store = MemoryStore::with_max_bytes(10);
790
791 let data1 = vec![1u8, 1, 1]; let data2 = vec![2u8, 2, 2];
794 let data3 = vec![3u8, 3, 3]; let hash1 = sha256(&data1);
796 let hash2 = sha256(&data2);
797 let hash3 = sha256(&data3);
798
799 store.put(hash1, data1).await.unwrap();
800 store.put(hash2, data2).await.unwrap();
801 store.put(hash3, data3).await.unwrap();
802
803 store.pin(&hash1).await.unwrap();
805
806 let data4 = vec![4u8, 4, 4];
808 let hash4 = sha256(&data4);
809 store.put(hash4, data4).await.unwrap();
810
811 let freed = store.evict_if_needed().await.unwrap();
813 assert!(freed > 0);
814
815 assert!(store.has(&hash1).await.unwrap());
817 assert!(!store.has(&hash2).await.unwrap());
819 assert!(store.has(&hash3).await.unwrap());
821 assert!(store.has(&hash4).await.unwrap());
822 }
823
824 #[tokio::test]
825 async fn test_eviction_lru_order() {
826 let store = MemoryStore::with_max_bytes(15);
828
829 let data1 = vec![1u8; 5]; let data2 = vec![2u8; 5];
832 let data3 = vec![3u8; 5];
833 let data4 = vec![4u8; 5]; let hash1 = sha256(&data1);
835 let hash2 = sha256(&data2);
836 let hash3 = sha256(&data3);
837 let hash4 = sha256(&data4);
838
839 store.put(hash1, data1).await.unwrap();
840 store.put(hash2, data2).await.unwrap();
841 store.put(hash3, data3).await.unwrap();
842 store.put(hash4, data4).await.unwrap();
843
844 assert_eq!(store.total_bytes(), 20);
846
847 let freed = store.evict_if_needed().await.unwrap();
849 assert!(freed >= 5); assert!(!store.has(&hash1).await.unwrap());
853 assert!(store.has(&hash4).await.unwrap());
855 }
856
857 #[tokio::test]
858 async fn test_no_eviction_when_under_limit() {
859 let store = MemoryStore::with_max_bytes(100);
860
861 let data = vec![1u8, 2, 3];
862 let hash = sha256(&data);
863 store.put(hash, data).await.unwrap();
864
865 let freed = store.evict_if_needed().await.unwrap();
866 assert_eq!(freed, 0);
867 assert!(store.has(&hash).await.unwrap());
868 }
869
870 #[tokio::test]
871 async fn test_no_eviction_without_limit() {
872 let store = MemoryStore::new();
873
874 for i in 0..100u8 {
876 let data = vec![i; 100];
877 let hash = sha256(&data);
878 store.put(hash, data).await.unwrap();
879 }
880
881 let freed = store.evict_if_needed().await.unwrap();
882 assert_eq!(freed, 0);
883 assert_eq!(store.size(), 100);
884 }
885
886 #[tokio::test]
887 async fn test_delete_removes_pin() {
888 let store = MemoryStore::new();
889 let data = vec![1u8, 2, 3];
890 let hash = sha256(&data);
891
892 store.put(hash, data).await.unwrap();
893 store.pin(&hash).await.unwrap();
894 assert!(store.is_pinned(&hash));
895
896 store.delete(&hash).await.unwrap();
897 assert_eq!(store.pin_count(&hash), 0);
899 }
900}