Integration Testing Guide [AIR-3][AIS-3][AIT-3][RES-3]¶
Comprehensive integration testing for Anya-core extensions, validating cross-system compatibility between Bitcoin networks, Web5 protocols, and ML systems.
Overview¶
Integration tests ensure that Anya-core extensions work correctly across system boundaries, testing real-world scenarios with Bitcoin testnet/mainnet, Web5 decentralized networks, and ML model inference pipelines. These tests validate BIP compliance in production-like environments.
Integration Test Architecture¶
Test Categories¶
- Bitcoin Network Integration: Testnet/mainnet connectivity
- Web5 Protocol Integration: DWN, DID, and VC interactions
- ML Pipeline Integration: Model training and inference workflows
- Cross-System Integration: Bitcoin + Web5 + ML combined operations
- External Service Integration: Third-party API compatibility
- Database Integration: Persistent storage validation
Test Environment Setup¶
# Integration test environment
export BITCOIN_NETWORK=testnet
export WEB5_ENDPOINT=https://dwn.testnet.web5.com
export ML_INFERENCE_URL=http://localhost:8080/predict
export DATABASE_URL=postgresql://test:test@localhost/anya_test
export REDIS_URL=redis://localhost:6379/1
Bitcoin Network Integration¶
Testnet Integration Tests¶
#[cfg(test)]
mod bitcoin_integration_tests {
use super::*;
use bitcoin::{Network, Address, Transaction};
use bitcoincore_rpc::{Client, Auth, RpcApi};
#[tokio::test]
#[ignore] // Run with: cargo test --ignored
async fn test_testnet_transaction_broadcast() {
let client = Client::new(
"http://localhost:18332",
Auth::UserPass("testuser".to_string(), "testpass".to_string())
).unwrap();
// Create and sign transaction
let tx = create_test_transaction_with_real_inputs(&client).await;
let signed_tx = sign_transaction(&tx, &get_test_private_key()).unwrap();
// Broadcast to testnet
let txid = client.send_raw_transaction(&signed_tx).unwrap();
// Wait for confirmation
let confirmed_tx = wait_for_confirmation(&client, &txid, 6).await.unwrap();
assert_eq!(confirmed_tx.txid(), txid);
}
#[tokio::test]
async fn test_bitcoin_address_validation_mainnet() {
let validator = AddressValidator::new(Network::Bitcoin);
// Test various address formats
let p2pkh = Address::from_str("1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2").unwrap();
assert!(validator.validate(&p2pkh).is_ok());
let p2sh = Address::from_str("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy").unwrap();
assert!(validator.validate(&p2sh).is_ok());
let bech32 = Address::from_str("bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq").unwrap();
assert!(validator.validate(&bech32).is_ok());
}
#[tokio::test]
async fn test_lightning_network_integration() {
let ln_client = LightningClient::connect("localhost:10009").await.unwrap();
// Test channel creation
let channel_request = OpenChannelRequest {
node_pubkey: "03...".to_string(),
local_funding_amount: 100_000,
push_sat: 50_000,
};
let channel = ln_client.open_channel(channel_request).await.unwrap();
assert!(channel.is_active());
// Test payment
let payment_request = PaymentRequest {
payment_hash: "abc123...".to_string(),
amount_msat: 1000,
destination: "03...".to_string(),
};
let payment_result = ln_client.send_payment(payment_request).await.unwrap();
assert!(payment_result.is_successful());
}
}
Block Processing Integration¶
#[tokio::test]
async fn test_block_processing_pipeline() {
let block_processor = BlockProcessor::new(Network::Testnet);
let bitcoin_client = BitcoinClient::new_testnet().await.unwrap();
// Get latest block
let block_hash = bitcoin_client.get_best_block_hash().await.unwrap();
let block = bitcoin_client.get_block(&block_hash).await.unwrap();
// Process block through pipeline
let processed_block = block_processor.process(&block).await.unwrap();
// Validate processed data
assert_eq!(processed_block.hash, block.block_hash());
assert_eq!(processed_block.transactions.len(), block.txdata.len());
// Verify BIP compliance
for tx in &processed_block.transactions {
assert!(tx.is_bip_compliant());
}
}
Web5 Integration Testing¶
DID Resolution Integration¶
#[cfg(test)]
mod web5_integration_tests {
use super::*;
use web5::{DID, DidResolver, DWN, VerifiableCredential};
#[tokio::test]
async fn test_did_resolution_across_networks() {
let resolver = DidResolver::new_with_endpoints(vec![
"https://dwn.testnet.web5.com",
"https://did.testnet.ion.msidentity.com",
]);
// Test Web5 DID resolution
let web5_did = DID::parse("did:web5:testnet:alice").unwrap();
let document = resolver.resolve(&web5_did).await.unwrap();
assert_eq!(document.id(), &web5_did);
assert!(!document.verification_methods().is_empty());
// Test ION DID resolution
let ion_did = DID::parse("did:ion:EiDyOQbbZAa3aiRzeCkV7LOx3SERjjH93EXoIM3UoN4oWg").unwrap();
let ion_document = resolver.resolve(&ion_did).await.unwrap();
assert_eq!(ion_document.id(), &ion_did);
}
#[tokio::test]
async fn test_dwn_message_flow() {
let dwn_client = DWNClient::new("https://dwn.testnet.web5.com").await.unwrap();
let did = create_test_did().await;
// Write message to DWN
let message = Message::builder()
.protocol("https://example.com/chat")
.schema("https://example.com/schemas/message")
.data(b"Hello, Web5!")
.build()
.unwrap();
let write_result = dwn_client.write(&did, message.clone()).await.unwrap();
assert!(write_result.is_successful());
// Read message from DWN
let query = QueryBuilder::new()
.protocol("https://example.com/chat")
.build();
let messages = dwn_client.query(&did, query).await.unwrap();
assert_eq!(messages.len(), 1);
assert_eq!(messages[0].data(), message.data());
}
#[tokio::test]
async fn test_verifiable_credential_lifecycle() {
let issuer_did = create_test_did().await;
let subject_did = create_test_did().await;
// Issue credential
let credential = VerifiableCredential::builder()
.issuer(issuer_did.clone())
.subject(subject_did.clone())
.credential_type("UniversityDegree")
.claim("degree", "Bachelor of Computer Science")
.claim("university", "Test University")
.build()
.unwrap();
let signed_credential = credential.sign(&get_issuer_key()).await.unwrap();
// Verify credential
let verifier = CredentialVerifier::new();
let verification_result = verifier.verify(&signed_credential).await.unwrap();
assert!(verification_result.is_valid());
assert_eq!(verification_result.issuer(), &issuer_did);
assert_eq!(verification_result.subject(), &subject_did);
// Store credential in DWN
let dwn_client = DWNClient::new("https://dwn.testnet.web5.com").await.unwrap();
let storage_result = dwn_client.store_credential(&subject_did, &signed_credential).await.unwrap();
assert!(storage_result.is_successful());
}
}
ML Pipeline Integration¶
Model Training Integration¶
#[cfg(test)]
mod ml_integration_tests {
use super::*;
use ml::{Model, TrainingPipeline, InferencePipeline};
#[tokio::test]
async fn test_bitcoin_price_prediction_pipeline() {
let training_pipeline = TrainingPipeline::new()
.with_data_source("https://api.coingecko.com/api/v3/coins/bitcoin/market_chart")
.with_model_type(ModelType::LSTM)
.with_features(vec!["price", "volume", "market_cap"])
.with_target("price_next_hour");
// Train model with historical data
let model = training_pipeline.train().await.unwrap();
assert!(model.accuracy() > 0.7);
// Test real-time prediction
let inference_pipeline = InferencePipeline::new(model);
let current_data = fetch_current_bitcoin_data().await.unwrap();
let prediction = inference_pipeline.predict(¤t_data).await.unwrap();
assert!(prediction.confidence() > 0.5);
assert!(prediction.value() > 0.0);
}
#[tokio::test]
async fn test_transaction_anomaly_detection() {
let anomaly_detector = AnomalyDetector::load_from_file("models/tx_anomaly.json").unwrap();
// Test with normal transaction
let normal_tx = create_normal_transaction();
let normal_score = anomaly_detector.score(&normal_tx).await.unwrap();
assert!(normal_score < 0.5); // Low anomaly score
// Test with suspicious transaction
let suspicious_tx = create_suspicious_transaction();
let suspicious_score = anomaly_detector.score(&suspicious_tx).await.unwrap();
assert!(suspicious_score > 0.8); // High anomaly score
}
#[tokio::test]
async fn test_did_reputation_scoring() {
let reputation_model = ReputationModel::load_from_file("models/did_reputation.json").unwrap();
// Test established DID
let established_did = DID::parse("did:web5:established:alice").unwrap();
let established_score = reputation_model.calculate_score(&established_did).await.unwrap();
assert!(established_score > 0.7);
// Test new DID
let new_did = DID::parse("did:web5:new:bob").unwrap();
let new_score = reputation_model.calculate_score(&new_did).await.unwrap();
assert!(new_score < 0.5);
}
}
Cross-System Integration¶
Bitcoin + Web5 Integration¶
#[tokio::test]
async fn test_bitcoin_web5_identity_verification() {
// Create Web5 identity
let did = create_test_did().await;
let identity_key = generate_identity_key();
// Create Bitcoin address from same key
let bitcoin_key = derive_bitcoin_key_from_identity(&identity_key);
let bitcoin_address = Address::p2wpkh(&bitcoin_key.public_key(), Network::Testnet).unwrap();
// Create proof of ownership
let message = format!("I control Bitcoin address {} with DID {}", bitcoin_address, did);
let signature = bitcoin_key.sign_message(&message).unwrap();
// Store proof in DWN
let dwn_client = DWNClient::new("https://dwn.testnet.web5.com").await.unwrap();
let proof_message = Message::builder()
.protocol("https://bitcoin.org/address-proof")
.data(serde_json::to_vec(&AddressProof {
address: bitcoin_address.to_string(),
message,
signature: signature.to_string(),
}).unwrap())
.build()
.unwrap();
let storage_result = dwn_client.write(&did, proof_message).await.unwrap();
assert!(storage_result.is_successful());
// Verify proof
let verifier = AddressProofVerifier::new();
let verification_result = verifier.verify_dwn_proof(&did, &bitcoin_address).await.unwrap();
assert!(verification_result.is_valid());
}
Web5 + ML Integration¶
#[tokio::test]
async fn test_web5_ml_personalization() {
let did = create_test_did().await;
let personalization_model = PersonalizationModel::new();
// Store user preferences in DWN
let preferences = UserPreferences {
interests: vec!["bitcoin", "defi", "privacy"],
risk_tolerance: 0.7,
investment_horizon: "long_term",
};
let dwn_client = DWNClient::new("https://dwn.testnet.web5.com").await.unwrap();
let prefs_message = Message::builder()
.protocol("https://anya.org/user-preferences")
.data(serde_json::to_vec(&preferences).unwrap())
.build()
.unwrap();
dwn_client.write(&did, prefs_message).await.unwrap();
// Generate personalized recommendations
let user_data = dwn_client.read_user_data(&did).await.unwrap();
let recommendations = personalization_model.generate_recommendations(&user_data).await.unwrap();
assert!(!recommendations.is_empty());
assert!(recommendations.iter().any(|r| r.category == "bitcoin"));
}
Database Integration¶
Persistent Storage Tests¶
#[tokio::test]
async fn test_database_transaction_consistency() {
let db_pool = create_test_database_pool().await;
let transaction_store = TransactionStore::new(db_pool.clone());
// Start database transaction
let mut db_tx = db_pool.begin().await.unwrap();
// Store Bitcoin transaction
let bitcoin_tx = create_test_transaction();
transaction_store.store_bitcoin_transaction(&mut db_tx, &bitcoin_tx).await.unwrap();
// Store Web5 message
let web5_message = create_test_message();
transaction_store.store_web5_message(&mut db_tx, &web5_message).await.unwrap();
// Store ML prediction
let prediction = create_test_prediction();
transaction_store.store_ml_prediction(&mut db_tx, &prediction).await.unwrap();
// Commit transaction
db_tx.commit().await.unwrap();
// Verify all data is stored consistently
let stored_bitcoin_tx = transaction_store.get_bitcoin_transaction(&bitcoin_tx.txid()).await.unwrap();
assert_eq!(stored_bitcoin_tx.txid(), bitcoin_tx.txid());
let stored_web5_message = transaction_store.get_web5_message(&web5_message.id()).await.unwrap();
assert_eq!(stored_web5_message.id(), web5_message.id());
let stored_prediction = transaction_store.get_ml_prediction(&prediction.id()).await.unwrap();
assert_eq!(stored_prediction.id(), prediction.id());
}
Performance Integration Tests¶
Load Testing¶
#[tokio::test]
async fn test_concurrent_transaction_processing() {
let processor = TransactionProcessor::new();
let transaction_count = 1000;
let transactions: Vec<_> = (0..transaction_count)
.map(|_| create_test_transaction())
.collect();
let start_time = Instant::now();
// Process transactions concurrently
let results: Vec<_> = futures::future::join_all(
transactions.iter().map(|tx| processor.process_transaction(tx))
).await;
let duration = start_time.elapsed();
// Verify all transactions processed successfully
for result in results {
assert!(result.is_ok());
}
// Verify performance requirements
let throughput = transaction_count as f64 / duration.as_secs_f64();
assert!(throughput > 100.0); // At least 100 TPS
}
Test Environment Management¶
Docker Test Environment¶
# docker-compose.test.yml
version: '3.8'
services:
bitcoin-testnet:
image: ruimarinho/bitcoin-core:latest
command: >
bitcoind
-testnet=1
-rpcuser=testuser
-rpcpassword=testpass
-rpcallowip=0.0.0.0/0
-server=1
ports:
- "18332:18332"
postgres:
image: postgres:14
environment:
POSTGRES_DB: anya_test
POSTGRES_USER: test
POSTGRES_PASSWORD: test
ports:
- "5432:5432"
redis:
image: redis:alpine
ports:
- "6379:6379"
web5-dwn:
image: tbd54566975/dwn-server:latest
ports:
- "3000:3000"
Test Setup and Teardown¶
use testcontainers::{Docker, Container, Image};
pub struct TestEnvironment {
bitcoin_container: Container<'static, Docker, BitcoinImage>,
postgres_container: Container<'static, Docker, PostgresImage>,
redis_container: Container<'static, Docker, RedisImage>,
}
impl TestEnvironment {
pub async fn new() -> Self {
let docker = Docker::default();
let bitcoin_container = docker.run(BitcoinImage::default());
let postgres_container = docker.run(PostgresImage::default());
let redis_container = docker.run(RedisImage::default());
// Wait for services to be ready
wait_for_bitcoin_ready(&bitcoin_container).await;
wait_for_postgres_ready(&postgres_container).await;
wait_for_redis_ready(&redis_container).await;
Self {
bitcoin_container,
postgres_container,
redis_container,
}
}
pub fn bitcoin_rpc_url(&self) -> String {
format!("http://localhost:{}", self.bitcoin_container.get_host_port(18332))
}
pub fn postgres_url(&self) -> String {
format!("postgresql://test:test@localhost:{}/anya_test",
self.postgres_container.get_host_port(5432))
}
}
Continuous Integration¶
CI Integration Test Pipeline¶
# .github/workflows/integration-tests.yml
name: Integration Tests
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
integration-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:14
env:
POSTGRES_PASSWORD: test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Start Bitcoin Testnet
run: |
docker run -d --name bitcoin-testnet \
-p 18332:18332 \
ruimarinho/bitcoin-core:latest \
bitcoind -testnet=1 -rpcuser=test -rpcpassword=test
- name: Run Integration Tests
run: cargo test --features integration-tests --ignored
env:
DATABASE_URL: postgresql://postgres:test@localhost/test
REDIS_URL: redis://localhost:6379
BITCOIN_RPC_URL: http://test:test@localhost:18332
Best Practices¶
Test Isolation¶
- Clean State: Each test starts with a fresh environment
- Independent: Tests don't depend on execution order
- Idempotent: Tests can be run multiple times safely
- Atomic: Each test validates one integration scenario
Error Handling¶
#[tokio::test]
async fn test_network_failure_recovery() {
let client = BitcoinClient::new_with_retries(3);
// Simulate network failure
let result = client.get_block_height_with_simulated_failure().await;
match result {
Ok(height) => assert!(height > 0),
Err(e) => assert!(e.is_retryable()),
}
}
Test Data Management¶
lazy_static! {
static ref TEST_DATA: TestDataManager = TestDataManager::new();
}
impl TestDataManager {
pub fn get_test_transaction(&self, scenario: &str) -> Transaction {
match scenario {
"valid_p2pkh" => self.load_fixture("valid_p2pkh_tx.json"),
"multisig_2_of_3" => self.load_fixture("multisig_2_of_3_tx.json"),
_ => panic!("Unknown test scenario: {}", scenario),
}
}
}
Resources¶
- Bitcoin Testnet Guide
- Web5 Testing Documentation
- Testcontainers Rust
- Tokio Testing
- Unit Testing Guide
- Performance Testing Guide
Last updated: June 7, 2025