Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions dash-spv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,5 @@ name = "dash_spv"
path = "src/lib.rs"

[features]
# Gate incomplete mock-dependent tests to avoid "unexpected cfg" warnings.
skip_mock_implementation_incomplete = []

# Terminal UI feature (off by default, for use by binary only)
terminal-ui = ["dep:crossterm"]
76 changes: 69 additions & 7 deletions dash-spv/src/chain/chainlock_test.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
#[cfg(test)]
mod tests {
use super::super::*;
use crate::{storage::DiskStorageManager, types::ChainState};
use dashcore::{ChainLock, Network};
use crate::{
storage::{BlockHeaderStorage, DiskStorageManager},
types::ChainState,
};
use dashcore::{constants::genesis_block, ChainLock, Network};
use dashcore_test_utils::fixtures::test_block_hash;

/// Create a test ChainLock with minimal valid data
fn create_test_chainlock(height: u32, block_hash: BlockHash) -> ChainLock {
ChainLock {
block_height: height,
block_hash,
signature: dashcore::bls_sig_utils::BLSSignature::from([0u8; 96]), // BLS signature placeholder
}
}

#[tokio::test]
async fn test_chainlock_processing() {
// Create storage and ChainLock manager
Expand Down Expand Up @@ -47,11 +59,8 @@ mod tests {
let chain_state = ChainState::new_for_network(Network::Testnet);

// Process first ChainLock at height 1000
let chainlock1 = ChainLock {
block_height: 1000,
block_hash: test_block_hash(1),
signature: dashcore::bls_sig_utils::BLSSignature::from([0; 96]),
};
let chainlock1 = create_test_chainlock(1000, test_block_hash(1));

chainlock_manager
.process_chain_lock(chainlock1.clone(), &chain_state, &mut storage)
.await
Expand Down Expand Up @@ -104,4 +113,57 @@ mod tests {
assert!(chainlock_manager.would_violate_chain_lock(1500, 2500)); // Would reorg ChainLock at 2000
assert!(!chainlock_manager.would_violate_chain_lock(3001, 4000)); // After ChainLocks - OK
}

#[tokio::test]
async fn test_chainlock_queue_and_process_flow() {
let chainlock_manager = ChainLockManager::new(true);

// Queue multiple ChainLocks
let chain_lock1 = create_test_chainlock(100, BlockHash::from([1u8; 32]));
let chain_lock2 = create_test_chainlock(200, BlockHash::from([2u8; 32]));
let chain_lock3 = create_test_chainlock(300, BlockHash::from([3u8; 32]));

chainlock_manager.queue_pending_chainlock(chain_lock1).unwrap();
chainlock_manager.queue_pending_chainlock(chain_lock2).unwrap();
chainlock_manager.queue_pending_chainlock(chain_lock3).unwrap();

// Verify all are queued
{
// Note: pending_chainlocks is private, can't access directly
let pending = chainlock_manager.pending_chainlocks.read().unwrap();
assert_eq!(pending.len(), 3);
assert_eq!(pending[0].block_height, 100);
assert_eq!(pending[1].block_height, 200);
assert_eq!(pending[2].block_height, 300);
}
}

#[tokio::test]
async fn test_chainlock_manager_cache_operations() {
let mut storage = DiskStorageManager::with_temp_dir().await.unwrap();

let chainlock_manager = ChainLockManager::new(true);

// Add test headers
let genesis = genesis_block(Network::Dash).header;
storage.store_headers_at_height(&[genesis], 0).await.unwrap();

// Create and process a ChainLock
let chain_lock = create_test_chainlock(0, genesis.block_hash());
let chain_state = ChainState::new();
let _ = chainlock_manager
.process_chain_lock(chain_lock.clone(), &chain_state, &mut storage)
.await;
Comment on lines +148 to +156
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Verify network consistency and check process result.

Two concerns:

  1. The test uses genesis_block(Network::Dash) but ChainState::new() instead of ChainState::new_for_network(Network::Dash). If ChainState::new() defaults to a different network, this could cause subtle test failures or false positives.

  2. The result of process_chain_lock is discarded with let _ = .... Consider asserting success like other tests do, or at least documenting why the result is intentionally ignored.

🔧 Proposed fix
         // Create and process a ChainLock
         let chain_lock = create_test_chainlock(0, genesis.block_hash());
-        let chain_state = ChainState::new();
-        let _ = chainlock_manager
+        let chain_state = ChainState::new_for_network(Network::Dash);
+        chainlock_manager
             .process_chain_lock(chain_lock.clone(), &chain_state, &mut storage)
-            .await;
+            .await
+            .expect("ChainLock processing should succeed");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let genesis = genesis_block(Network::Dash).header;
storage.store_headers_at_height(&[genesis], 0).await.unwrap();
// Create and process a ChainLock
let chain_lock = create_test_chainlock(0, genesis.block_hash());
let chain_state = ChainState::new();
let _ = chainlock_manager
.process_chain_lock(chain_lock.clone(), &chain_state, &mut storage)
.await;
let genesis = genesis_block(Network::Dash).header;
storage.store_headers_at_height(&[genesis], 0).await.unwrap();
// Create and process a ChainLock
let chain_lock = create_test_chainlock(0, genesis.block_hash());
let chain_state = ChainState::new_for_network(Network::Dash);
chainlock_manager
.process_chain_lock(chain_lock.clone(), &chain_state, &mut storage)
.await
.expect("ChainLock processing should succeed");
🤖 Prompt for AI Agents
In @dash-spv/src/chain/chainlock_test.rs around lines 148 - 156, The test
currently builds a genesis header with genesis_block(Network::Dash) but
constructs a generic ChainState via ChainState::new() and discards the process
result; fix by creating the chain state for Dash (use
ChainState::new_for_network(Network::Dash) or equivalent) to ensure network
consistency with genesis_block, and replace the discarded result of
chainlock_manager.process_chain_lock(...) with an assertion that the call
succeeded (e.g., assert matches Ok/expected variant) so the test fails on errors
instead of hiding them.


// Test cache operations
assert!(chainlock_manager.has_chain_lock_at_height(0));

let entry = chainlock_manager.get_chain_lock_by_height(0);
assert!(entry.is_some());
assert_eq!(entry.unwrap().chain_lock.block_height, 0);

let entry_by_hash = chainlock_manager.get_chain_lock_by_hash(&genesis.block_hash());
assert!(entry_by_hash.is_some());
assert_eq!(entry_by_hash.unwrap().chain_lock.block_height, 0);
}
}
16 changes: 0 additions & 16 deletions dash-spv/tests/block_download_test.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
//! Tests for block downloading on filter match functionality.
//!
//! NOTE: This test file is currently disabled due to incomplete mock NetworkManager implementation.
//! TODO: Re-enable once NetworkManager trait methods are fully implemented.

#![cfg(feature = "skip_mock_implementation_incomplete")]

//! Tests for block downloading on filter match functionality.

use std::collections::HashSet;
Expand Down Expand Up @@ -159,7 +152,6 @@ fn create_test_filter_match(block_hash: BlockHash, height: u32) -> FilterMatch {
}
}

#[ignore = "mock implementation incomplete"]
#[tokio::test]
async fn test_filter_sync_manager_creation() {
let config = create_test_config();
Expand All @@ -171,7 +163,6 @@ async fn test_filter_sync_manager_creation() {
assert_eq!(filter_sync.pending_download_count(), 0);
}

#[ignore = "mock implementation incomplete"]
#[tokio::test]
async fn test_request_block_download() {
let config = create_test_config();
Expand Down Expand Up @@ -209,7 +200,6 @@ async fn test_request_block_download() {
assert_eq!(filter_sync.pending_download_count(), 1);
}

#[ignore = "mock implementation incomplete"]
#[tokio::test]
async fn test_duplicate_block_request_prevention() {
let config = create_test_config();
Expand All @@ -233,7 +223,6 @@ async fn test_duplicate_block_request_prevention() {
assert_eq!(filter_sync.pending_download_count(), 1);
}

#[ignore = "mock implementation incomplete"]
#[tokio::test]
async fn test_handle_downloaded_block() {
let config = create_test_config();
Expand Down Expand Up @@ -264,7 +253,6 @@ async fn test_handle_downloaded_block() {
assert_eq!(filter_sync.pending_download_count(), 0);
}

#[ignore = "mock implementation incomplete"]
#[tokio::test]
async fn test_handle_unexpected_block() {
let config = create_test_config();
Expand All @@ -281,7 +269,6 @@ async fn test_handle_unexpected_block() {
assert!(result.is_none());
}

#[ignore = "mock implementation incomplete"]
#[tokio::test]
async fn test_process_multiple_filter_matches() {
let config = create_test_config();
Expand Down Expand Up @@ -332,11 +319,9 @@ async fn test_process_multiple_filter_matches() {
assert_eq!(filter_sync.pending_download_count(), 3);
}

#[ignore = "mock implementation incomplete"]
#[tokio::test]
async fn test_sync_manager_integration() {}

#[ignore = "mock implementation incomplete"]
#[tokio::test]
async fn test_filter_match_and_download_workflow() {
let config = create_test_config();
Expand Down Expand Up @@ -369,7 +354,6 @@ async fn test_filter_match_and_download_workflow() {
assert!(filter_sync.has_pending_downloads());
}

#[ignore = "mock implementation incomplete"]
#[tokio::test]
async fn test_reset_clears_download_state() {
let config = create_test_config();
Expand Down
Loading
Loading