Skip to content

Commit 743988d

Browse files
committed
storage tests moved into unit tests and dropped duplicated tests
1 parent a927a4c commit 743988d

File tree

8 files changed

+150
-1759
lines changed

8 files changed

+150
-1759
lines changed

dash-spv/src/storage/mod.rs

Lines changed: 149 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -394,134 +394,190 @@ impl masternode::MasternodeStateStorage for DiskStorageManager {
394394

395395
#[cfg(test)]
396396
mod tests {
397-
use crate::ChainState;
398-
399397
use super::*;
400398
use dashcore::Header as BlockHeader;
401399
use tempfile::{tempdir, TempDir};
402400

403401
#[tokio::test]
404-
async fn test_load_headers() -> Result<(), Box<dyn std::error::Error>> {
402+
async fn test_store_load_headers() -> Result<(), Box<dyn std::error::Error>> {
405403
// Create a temporary directory for the test
406404
let temp_dir = TempDir::new()?;
407-
let mut storage = DiskStorageManager::new(temp_dir.path().to_path_buf())
408-
.await
409-
.expect("Unable to create storage");
405+
let mut storage =
406+
DiskStorageManager::new(temp_dir.path()).await.expect("Unable to create storage");
410407

411-
// Create a test header
412-
let test_header = BlockHeader::dummy(1);
408+
let headers = BlockHeader::dummy_batch(0..60_000);
413409

414-
// Store just one header
415-
storage.store_headers(&[test_header]).await?;
410+
storage.store_headers(&headers[0..0]).await.expect("Should handle empty header batch");
411+
assert_eq!(storage.get_tip_height().await, None);
416412

413+
storage.store_headers(&headers[0..1]).await.expect("Failed to store headers");
417414
let loaded_headers = storage.load_headers(0..1).await?;
418-
419-
// Should only get back the one header we stored
420415
assert_eq!(loaded_headers.len(), 1);
421-
assert_eq!(loaded_headers[0], test_header);
416+
assert_eq!(loaded_headers[0], headers[0]);
417+
418+
storage.store_headers(&headers[1..100]).await.expect("Failed to store headers");
419+
let loaded_headers = storage.load_headers(50..60).await.unwrap();
420+
assert_eq!(loaded_headers.len(), 10);
421+
assert_eq!(&loaded_headers, &headers[50..60]);
422+
423+
storage.store_headers(&headers[100..headers.len()]).await.expect("Failed to store headers");
424+
425+
let tip_height = storage.get_tip_height().await.unwrap();
426+
let tip_header = storage.get_header(tip_height).await.unwrap().unwrap();
427+
let expected_header = &headers[headers.len() - 1];
428+
assert_eq!(tip_header, *expected_header);
429+
430+
let non_existing_height = tip_height + 1;
431+
let non_existing_header = storage.get_header(non_existing_height).await.unwrap();
432+
assert!(non_existing_header.is_none());
433+
434+
storage.shutdown().await;
435+
drop(storage);
436+
let storage =
437+
DiskStorageManager::new(temp_dir.path()).await.expect("Unable to open storage");
438+
439+
let loaded_headers = storage.load_headers(49_999..50_002).await.unwrap();
440+
assert_eq!(loaded_headers.len(), 3);
441+
assert_eq!(&loaded_headers, &headers[49_999..50_002]);
422442

423443
Ok(())
424444
}
425445

426446
#[tokio::test]
427447
async fn test_checkpoint_storage_indexing() -> StorageResult<()> {
428448
let temp_dir = tempdir().expect("Failed to create temp dir");
429-
let mut storage = DiskStorageManager::new(temp_dir.path().to_path_buf()).await?;
449+
let mut storage = DiskStorageManager::new(temp_dir.path()).await?;
430450

431451
// Create test headers starting from checkpoint height
432-
let checkpoint_height = 1_100_000;
433-
let headers = BlockHeader::dummy_batch(checkpoint_height..checkpoint_height + 100);
434-
435-
let mut base_state = ChainState::new();
436-
base_state.sync_base_height = checkpoint_height;
437-
storage.store_chain_state(&base_state).await?;
438-
439-
storage.store_headers_at_height(&headers, checkpoint_height).await?;
440-
assert_eq!(storage.get_stored_headers_len().await, headers.len() as u32);
441-
442-
// Verify headers are stored at correct blockchain heights
443-
let header_at_base = storage.get_header(checkpoint_height).await?;
444-
assert_eq!(
445-
header_at_base.expect("Header at base blockchain height should exist"),
446-
headers[0]
447-
);
448-
449-
let header_at_ending = storage.get_header(checkpoint_height + 99).await?;
450-
assert_eq!(
451-
header_at_ending.expect("Header at ending blockchain height should exist"),
452-
headers[99]
453-
);
454-
455-
// Test the reverse index (hash -> blockchain height)
456-
let hash_0 = headers[0].block_hash();
457-
let height_0 = storage.get_header_height_by_hash(&hash_0).await?;
458-
assert_eq!(
459-
height_0,
460-
Some(checkpoint_height),
461-
"Hash should map to blockchain height 1,100,000"
462-
);
463-
464-
let hash_99 = headers[99].block_hash();
465-
let height_99 = storage.get_header_height_by_hash(&hash_99).await?;
466-
assert_eq!(
467-
height_99,
468-
Some(checkpoint_height + 99),
469-
"Hash should map to blockchain height 1,100,099"
470-
);
471-
472-
// Store chain state to persist sync_base_height
473-
let mut chain_state = ChainState::new();
474-
chain_state.sync_base_height = checkpoint_height;
475-
storage.store_chain_state(&chain_state).await?;
476-
477-
// Force save to disk
478-
storage.persist().await;
452+
const CHECKPOINT_HEIGHT: u32 = 1_100_000;
453+
let headers: Vec<BlockHeader> = BlockHeader::dummy_batch(0..100);
454+
455+
storage.store_headers_at_height(&headers, CHECKPOINT_HEIGHT).await?;
456+
457+
check_storage(&storage, &headers).await?;
479458

459+
storage.shutdown().await;
480460
drop(storage);
481461

482-
// Create a new storage instance to test index rebuilding
483-
let storage2 = DiskStorageManager::new(temp_dir.path().to_path_buf()).await?;
484-
485-
// Verify the index was rebuilt correctly
486-
let height_after_rebuild = storage2.get_header_height_by_hash(&hash_0).await?;
487-
assert_eq!(
488-
height_after_rebuild,
489-
Some(checkpoint_height),
490-
"After index rebuild, hash should still map to blockchain height 1,100,000"
491-
);
492-
493-
// Verify header can still be retrieved by blockchain height after reload
494-
let header_after_reload = storage2.get_header(checkpoint_height).await?;
495-
assert!(
496-
header_after_reload.is_some(),
497-
"Header at base blockchain height should exist after reload"
498-
);
499-
assert_eq!(header_after_reload.unwrap(), headers[0]);
462+
let storage = DiskStorageManager::new(temp_dir.path()).await?;
500463

501-
Ok(())
464+
check_storage(&storage, &headers).await?;
465+
466+
return Ok(());
467+
468+
async fn check_storage(
469+
storage: &DiskStorageManager,
470+
headers: &[BlockHeader],
471+
) -> StorageResult<()> {
472+
assert_eq!(storage.get_stored_headers_len().await, headers.len() as u32);
473+
474+
let header_at_base = storage.get_header(CHECKPOINT_HEIGHT).await?;
475+
assert_eq!(header_at_base, Some(headers[0]));
476+
477+
let header_at_ending = storage.get_header(CHECKPOINT_HEIGHT + 99).await?;
478+
assert_eq!(header_at_ending, Some(headers[99]));
479+
480+
// Test the reverse index (hash -> blockchain height)
481+
let hash_0 = headers[0].block_hash();
482+
let height_0 = storage.get_header_height_by_hash(&hash_0).await?;
483+
assert_eq!(
484+
height_0,
485+
Some(CHECKPOINT_HEIGHT),
486+
"Hash should map to blockchain height 1,100,000"
487+
);
488+
489+
let hash_99 = headers[99].block_hash();
490+
let height_99 = storage.get_header_height_by_hash(&hash_99).await?;
491+
assert_eq!(
492+
height_99,
493+
Some(CHECKPOINT_HEIGHT + 99),
494+
"Hash should map to blockchain height 1,100,099"
495+
);
496+
497+
Ok(())
498+
}
502499
}
503500

504501
#[tokio::test]
505-
async fn test_shutdown_flushes_index() -> Result<(), Box<dyn std::error::Error>> {
506-
let temp_dir = TempDir::new()?;
507-
let base_path = temp_dir.path().to_path_buf();
508-
let headers = BlockHeader::dummy_batch(0..11_000);
509-
let last_hash = headers.last().unwrap().block_hash();
502+
async fn test_reverse_index_disk_storage() {
503+
let temp_dir = tempfile::tempdir().unwrap();
510504

511505
{
512-
let mut storage = DiskStorageManager::new(base_path.clone()).await?;
506+
let mut storage = DiskStorageManager::new(temp_dir.path()).await.unwrap();
507+
508+
// Create and store headers
509+
let headers = BlockHeader::dummy_batch(0..10);
513510

514-
storage.store_headers(&headers[..10_000]).await?;
515-
storage.persist().await;
511+
storage.store_headers(&headers).await.unwrap();
512+
513+
// Test reverse lookups
514+
for (i, header) in headers.iter().enumerate() {
515+
let hash = header.block_hash();
516+
let height = storage.get_header_height_by_hash(&hash).await.unwrap();
517+
assert_eq!(height, Some(i as u32), "Height mismatch for header {}", i);
518+
}
516519

517-
storage.store_headers(&headers[10_000..]).await?;
518520
storage.shutdown().await;
519521
}
520522

521-
let storage = DiskStorageManager::new(base_path).await?;
522-
let height = storage.get_header_height_by_hash(&last_hash).await?;
523-
assert_eq!(height, Some(10_999));
523+
// Test persistence - reload storage and verify index still works
524+
{
525+
let storage = DiskStorageManager::new(&temp_dir.path()).await.unwrap();
526+
527+
// The index should have been rebuilt from the loaded headers
528+
// We need to get the actual headers that were stored to test properly
529+
for i in 0..10 {
530+
let stored_header = storage.get_header(i).await.unwrap().unwrap();
531+
let hash = stored_header.block_hash();
532+
let height = storage.get_header_height_by_hash(&hash).await.unwrap();
533+
assert_eq!(height, Some(i), "Height mismatch after reload for header {}", i);
534+
}
535+
}
536+
}
524537

525-
Ok(())
538+
#[tokio::test]
539+
async fn test_clear_clears_index() {
540+
let mut storage =
541+
DiskStorageManager::with_temp_dir().await.expect("Failed to create tmp storage");
542+
543+
// Store some headers
544+
let header = BlockHeader::dummy_batch(0..1);
545+
storage.store_headers(&header).await.unwrap();
546+
547+
let hash = header[0].block_hash();
548+
assert!(storage.get_header_height_by_hash(&hash).await.unwrap().is_some());
549+
550+
// Clear storage
551+
storage.clear().await.unwrap();
552+
553+
// Verify index is cleared
554+
assert!(storage.get_header_height_by_hash(&hash).await.unwrap().is_none());
555+
}
556+
557+
#[tokio::test]
558+
async fn test_lock_lifecycle() {
559+
let temp_dir = TempDir::new().expect("Failed to create temp directory");
560+
let path = temp_dir.path().to_path_buf();
561+
let lock_path = {
562+
let mut lock_file = path.clone();
563+
lock_file.set_extension("lock");
564+
lock_file
565+
};
566+
567+
let mut storage1 = DiskStorageManager::new(&path).await.unwrap();
568+
assert!(lock_path.exists(), "Lock file should exist while storage is open");
569+
storage1.clear().await.expect("Failed to clear the storage");
570+
assert!(lock_path.exists(), "Lock file should exist after storage is cleared");
571+
572+
let storage2 = DiskStorageManager::new(&path).await;
573+
assert!(storage2.is_err(), "Second storage manager should fail");
574+
575+
// Lock file removed when storage drops
576+
drop(storage1);
577+
assert!(!lock_path.exists(), "Lock file should be removed after storage drops");
578+
579+
// Can reopen storage after previous one dropped
580+
let storage3 = DiskStorageManager::new(&path).await;
581+
assert!(storage3.is_ok(), "Should reopen after previous storage dropped");
526582
}
527583
}

0 commit comments

Comments
 (0)