|
| 1 | +//! Integration test for FFI event callbacks. |
| 2 | +//! |
| 3 | +//! This test verifies all three callback structs work correctly in a real sync scenario: |
| 4 | +//! - FFISyncEventCallbacks |
| 5 | +//! - FFINetworkEventCallbacks |
| 6 | +//! - FFIWalletEventCallbacks |
| 7 | +
|
| 8 | +mod ffi_test_common; |
| 9 | + |
| 10 | +use ffi_test_common::{ |
| 11 | + create_full_sync_callbacks, create_network_callbacks, create_wallet_callbacks, FFINodeContext, |
| 12 | + FFITestContext, |
| 13 | +}; |
| 14 | +use std::sync::atomic::Ordering; |
| 15 | + |
| 16 | +#[test] |
| 17 | +#[ignore] // Run with: cargo test --test callback_integration_test -- --ignored |
| 18 | +fn test_all_callbacks_during_sync() { |
| 19 | + unsafe { |
| 20 | + dash_spv_ffi::dash_spv_ffi_init_logging(c"info".as_ptr(), true, std::ptr::null(), 0); |
| 21 | + } |
| 22 | + |
| 23 | + let Some(mut node_ctx) = FFINodeContext::new() else { |
| 24 | + return; |
| 25 | + }; |
| 26 | + |
| 27 | + unsafe { |
| 28 | + let ctx = FFITestContext::new(node_ctx.addr); |
| 29 | + let tracker = ctx.tracker.clone(); |
| 30 | + |
| 31 | + ctx.add_wallet(&node_ctx.light_wallet.mnemonic); |
| 32 | + tracing::info!("Added wallet from mnemonic via FFI"); |
| 33 | + |
| 34 | + // Set up all three callback types |
| 35 | + let sync_callbacks = create_full_sync_callbacks(&tracker); |
| 36 | + let network_callbacks = create_network_callbacks(&tracker); |
| 37 | + let wallet_callbacks = create_wallet_callbacks(&tracker); |
| 38 | + |
| 39 | + let result = |
| 40 | + dash_spv_ffi::dash_spv_ffi_client_set_sync_event_callbacks(ctx.client, sync_callbacks); |
| 41 | + assert_eq!(result, 0, "Failed to set sync callbacks"); |
| 42 | + |
| 43 | + let result = dash_spv_ffi::dash_spv_ffi_client_set_network_event_callbacks( |
| 44 | + ctx.client, |
| 45 | + network_callbacks, |
| 46 | + ); |
| 47 | + assert_eq!(result, 0, "Failed to set network callbacks"); |
| 48 | + |
| 49 | + let result = dash_spv_ffi::dash_spv_ffi_client_set_wallet_event_callbacks( |
| 50 | + ctx.client, |
| 51 | + wallet_callbacks, |
| 52 | + ); |
| 53 | + assert_eq!(result, 0, "Failed to set wallet callbacks"); |
| 54 | + |
| 55 | + let result = dash_spv_ffi::dash_spv_ffi_client_run(ctx.client); |
| 56 | + assert_eq!(result, 0, "Failed to run FFI client"); |
| 57 | + tracing::info!("FFI client running with all callback types"); |
| 58 | + |
| 59 | + ctx.wait_for_sync(node_ctx.expected_height, 180); |
| 60 | + |
| 61 | + // Log callback invocation summary |
| 62 | + let sync_start = tracker.sync_start_count.load(Ordering::SeqCst); |
| 63 | + let headers_stored = tracker.block_headers_stored_count.load(Ordering::SeqCst); |
| 64 | + let header_complete = tracker.block_header_sync_complete_count.load(Ordering::SeqCst); |
| 65 | + let filter_headers_stored = tracker.filter_headers_stored_count.load(Ordering::SeqCst); |
| 66 | + let filter_header_complete = |
| 67 | + tracker.filter_headers_sync_complete_count.load(Ordering::SeqCst); |
| 68 | + let sync_complete = tracker.sync_complete_count.load(Ordering::SeqCst); |
| 69 | + let peer_connected = tracker.peer_connected_count.load(Ordering::SeqCst); |
| 70 | + let peers_updated = tracker.peers_updated_count.load(Ordering::SeqCst); |
| 71 | + |
| 72 | + tracing::info!("=== Callback Summary ==="); |
| 73 | + tracing::info!( |
| 74 | + "Sync: start={}, headers_stored={}, header_complete={}, filter_headers={}, filter_complete={}, sync_complete={}", |
| 75 | + sync_start, headers_stored, header_complete, filter_headers_stored, filter_header_complete, sync_complete |
| 76 | + ); |
| 77 | + tracing::info!( |
| 78 | + "Network: peer_connected={}, peers_updated={}", |
| 79 | + peer_connected, peers_updated |
| 80 | + ); |
| 81 | + tracing::info!( |
| 82 | + "Wallet: tx_received={}", |
| 83 | + tracker.transaction_received_count.load(Ordering::SeqCst) |
| 84 | + ); |
| 85 | + |
| 86 | + // Validate sync callbacks |
| 87 | + assert!(sync_start > 0, "on_sync_start should have been called"); |
| 88 | + assert!(headers_stored > 0, "on_block_headers_stored should have been called"); |
| 89 | + assert_eq!(header_complete, 1, "on_block_header_sync_complete should be called once"); |
| 90 | + assert!( |
| 91 | + filter_headers_stored > 0, |
| 92 | + "on_filter_headers_stored should have been called" |
| 93 | + ); |
| 94 | + assert_eq!( |
| 95 | + filter_header_complete, 1, |
| 96 | + "on_filter_headers_sync_complete should be called once" |
| 97 | + ); |
| 98 | + assert_eq!(sync_complete, 1, "on_sync_complete should be called once"); |
| 99 | + |
| 100 | + // Validate network callbacks |
| 101 | + assert!(peer_connected > 0, "on_peer_connected should have been called"); |
| 102 | + assert!(peers_updated > 0, "on_peers_updated should have been called"); |
| 103 | + |
| 104 | + // Validate final state |
| 105 | + let final_header = tracker.last_header_tip.load(Ordering::SeqCst); |
| 106 | + assert_eq!(final_header, node_ctx.expected_height, "Final header tip mismatch"); |
| 107 | + |
| 108 | + { |
| 109 | + let errors = tracker.errors.lock().unwrap(); |
| 110 | + if !errors.is_empty() { |
| 111 | + tracing::warn!("Errors during sync: {:?}", *errors); |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + node_ctx.stop(); |
| 116 | + ctx.cleanup(); |
| 117 | + } |
| 118 | + |
| 119 | + tracing::info!("Callback integration test completed successfully"); |
| 120 | +} |
0 commit comments