refactor: improve imports, fix datetime usage, and reorganize cross-chain services
Some checks failed
Cross-Node Transaction Testing / transaction-test (push) Has been cancelled
Deploy to Testnet / deploy-testnet (push) Has been cancelled
Documentation Validation / validate-docs (push) Has been cancelled
Documentation Validation / validate-policies-strict (push) Has been cancelled
Integration Tests / test-service-integration (push) Has been cancelled
Multi-Node Stress Testing / stress-test (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Security Scanning / security-scan (push) Has been cancelled

- Added logger initialization to EventRouter in events.py
- Fixed datetime.timedelta references to use timedelta directly in security_hardening.py
- Fixed StateTransition timestamp default_factory to use lambda in state.py
- Fixed StateValidator.validate_transitions to only check source states exist
- Moved cross_chain_bridge_enhanced.py to cross_chain/bridge_enhanced.py
- Updated import paths in global_marketplace
This commit is contained in:
aitbc
2026-05-12 20:49:01 +02:00
parent c87806b68b
commit f4688aefbd
27 changed files with 5030 additions and 16 deletions

View File

@@ -0,0 +1,402 @@
"""
Tests for blockchain service layer
"""
import pytest
from unittest.mock import Mock, patch, MagicMock
from datetime import datetime
from aitbc.blockchain_service import (
Block,
Transaction,
Account,
BlockchainService,
RPCBlockchainService,
BlockchainServiceFactory,
)
class TestDataClasses:
"""Tests for blockchain data classes"""
def test_block_creation(self):
"""Test Block dataclass creation"""
block = Block(
height=100,
hash="0xabc123",
parent_hash="0xdef456",
timestamp=1234567890,
transactions=[{"hash": "0xtx1"}],
miner="0xminer",
gas_used=1000,
gas_limit=2000
)
assert block.height == 100
assert block.hash == "0xabc123"
assert block.parent_hash == "0xdef456"
assert block.transactions == [{"hash": "0xtx1"}]
def test_block_optional_fields(self):
"""Test Block with optional fields None"""
block = Block(
height=1,
hash="0xabc",
parent_hash="0xdef",
timestamp=0,
transactions=[]
)
assert block.miner is None
assert block.gas_used is None
assert block.gas_limit is None
def test_transaction_creation(self):
"""Test Transaction dataclass creation"""
tx = Transaction(
hash="0xtx123",
from_address="0xfrom",
to_address="0xto",
value="1000000000000000000",
nonce=1,
gas=21000,
gas_price="1000000000",
input_data="0xdata",
block_hash="0xblock",
block_number=100,
status="success"
)
assert tx.hash == "0xtx123"
assert tx.from_address == "0xfrom"
assert tx.to_address == "0xto"
def test_transaction_optional_fields(self):
"""Test Transaction with optional fields None"""
tx = Transaction(
hash="0xtx",
from_address="0xfrom",
to_address="0xto",
value="0",
nonce=0,
gas=0
)
assert tx.gas_price is None
assert tx.input_data is None
assert tx.block_hash is None
def test_account_creation(self):
"""Test Account dataclass creation"""
account = Account(
address="0xaccount123",
balance=1000000000000000000,
nonce=5
)
assert account.address == "0xaccount123"
assert account.balance == 1000000000000000000
assert account.nonce == 5
class TestRPCBlockchainService:
"""Tests for RPCBlockchainService"""
def test_initialization(self):
"""Test RPCBlockchainService initialization"""
with patch('aitbc.blockchain_service.AITBCHTTPClient') as mock_client_class:
service = RPCBlockchainService("http://localhost:8006", timeout=30)
assert service.rpc_url == "http://localhost:8006"
mock_client_class.assert_called_once_with(
base_url="http://localhost:8006",
timeout=30
)
def test_get_block_by_height(self):
"""Test get block by height"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {
"height": 100,
"hash": "0xblock100",
"parent_hash": "0xblock99",
"timestamp": 1234567890,
"transactions": [{"hash": "0xtx1"}],
"miner": "0xminer",
"gas_used": 1000,
"gas_limit": 2000
}
mock_client.get.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
block = service.get_block(100)
assert block.height == 100
assert block.hash == "0xblock100"
mock_client.get.assert_called_once_with("/rpc/blocks/100")
def test_get_block_by_hash(self):
"""Test get block by hash"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {
"height": 100,
"hash": "0xblockhash",
"parent_hash": "0xparent",
"timestamp": 1234567890,
"transactions": []
}
mock_client.get.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
block = service.get_block("0xblockhash")
assert block.height == 100
mock_client.get.assert_called_once_with("/rpc/block/0xblockhash")
def test_get_block_with_missing_fields(self):
"""Test get block handles missing fields with defaults"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {
"height": 100,
"hash": "0xblock",
"parent_hash": "0xparent",
"timestamp": 0
}
mock_client.get.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
block = service.get_block(100)
assert block.transactions == []
assert block.miner is None
assert block.gas_used is None
@patch('aitbc.blockchain_service.logger')
def test_get_block_error(self, mock_logger):
"""Test get block handles errors"""
mock_client = MagicMock()
mock_client.get.side_effect = Exception("Network error")
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
with pytest.raises(Exception):
service.get_block(100)
mock_logger.error.assert_called_once()
def test_get_head_block(self):
"""Test get head block"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {
"height": 200,
"hash": "0xhead",
"parent_hash": "0xprev",
"timestamp": 1234567890,
"transactions": []
}
mock_client.get.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
block = service.get_head_block()
assert block.height == 200
assert block.hash == "0xhead"
mock_client.get.assert_called_once_with("/rpc/head")
def test_get_transaction(self):
"""Test get transaction by hash"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {
"hash": "0xtx123",
"from": "0xfrom",
"to": "0xto",
"value": "1000000000000000000",
"nonce": 1,
"gas": 21000,
"gas_price": "1000000000",
"input": "0xdata",
"block_hash": "0xblock",
"block_number": 100,
"status": "success"
}
mock_client.get.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
tx = service.get_transaction("0xtx123")
assert tx.hash == "0xtx123"
assert tx.from_address == "0xfrom"
assert tx.to_address == "0xto"
mock_client.get.assert_called_once_with("/rpc/transaction/0xtx123")
def test_get_transaction_with_missing_fields(self):
"""Test get transaction handles missing fields"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {
"hash": "0xtx",
"from": "0xfrom",
"to": "0xto",
"value": "0",
"nonce": 0,
"gas": 0
}
mock_client.get.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
tx = service.get_transaction("0xtx")
assert tx.gas_price is None
assert tx.input_data is None
assert tx.block_number is None
def test_get_account_balance(self):
"""Test get account balance"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {
"balance": "1000000000000000000",
"nonce": 5
}
mock_client.get.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
account = service.get_account_balance("0xaccount")
assert account.address == "0xaccount"
assert account.balance == 1000000000000000000
assert account.nonce == 5
mock_client.get.assert_called_once_with("/rpc/account/0xaccount")
def test_get_account_balance_with_defaults(self):
"""Test get account balance with default values"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {}
mock_client.get.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
account = service.get_account_balance("0xaccount")
assert account.balance == 0
assert account.nonce == 0
def test_send_transaction(self):
"""Test send transaction"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {"hash": "0xtxhash"}
mock_client.post.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
tx_hash = service.send_transaction({"from": "0xfrom", "to": "0xto"})
assert tx_hash == "0xtxhash"
mock_client.post.assert_called_once_with("/rpc/sendTx", json={"from": "0xfrom", "to": "0xto"})
def test_send_transaction_with_tx_hash_key(self):
"""Test send transaction with tx_hash key in response"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {"tx_hash": "0xtxhash"}
mock_client.post.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
tx_hash = service.send_transaction({})
assert tx_hash == "0xtxhash"
def test_send_transaction_no_hash_error(self):
"""Test send transaction raises error when no hash in response"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {}
mock_client.post.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
with pytest.raises(ValueError, match="Transaction hash not found"):
service.send_transaction({})
def test_get_status(self):
"""Test get node status"""
mock_client = MagicMock()
mock_response = MagicMock()
mock_response.json.return_value = {
"status": "syncing",
"block_height": 100,
"peers": 5
}
mock_client.get.return_value = mock_response
with patch('aitbc.blockchain_service.AITBCHTTPClient', return_value=mock_client):
service = RPCBlockchainService("http://localhost:8006")
status = service.get_status()
assert status["status"] == "syncing"
assert status["block_height"] == 100
mock_client.get.assert_called_once_with("/rpc/status")
class TestBlockchainServiceFactory:
"""Tests for BlockchainServiceFactory"""
def test_create_rpc_service(self):
"""Test create RPC service"""
with patch('aitbc.blockchain_service.RPCBlockchainService') as mock_service_class:
factory = BlockchainServiceFactory()
service = factory.create_rpc_service("http://localhost:8006", timeout=60)
mock_service_class.assert_called_once_with("http://localhost:8006", 60)
def test_create_service_rpc(self):
"""Test create service with RPC type"""
with patch('aitbc.blockchain_service.BlockchainServiceFactory.create_rpc_service') as mock_create:
factory = BlockchainServiceFactory()
service = factory.create_service("rpc", rpc_url="http://localhost:8006")
mock_create.assert_called_once_with(rpc_url="http://localhost:8006")
def test_create_service_unknown_type(self):
"""Test create service with unknown type raises error"""
factory = BlockchainServiceFactory()
with pytest.raises(ValueError, match="Unknown service type"):
factory.create_service("unknown", rpc_url="http://localhost:8006")
def test_create_service_default_kwargs(self):
"""Test create service passes kwargs correctly"""
with patch('aitbc.blockchain_service.BlockchainServiceFactory.create_rpc_service') as mock_create:
factory = BlockchainServiceFactory()
service = factory.create_service("rpc", rpc_url="http://localhost:8006", timeout=45)
mock_create.assert_called_once_with(rpc_url="http://localhost:8006", timeout=45)
class TestBlockchainServiceAbstract:
"""Tests for BlockchainService abstract class"""
def test_blockchain_service_is_abstract(self):
"""Test BlockchainService cannot be instantiated directly"""
with pytest.raises(TypeError):
BlockchainService()
def test_blockchain_service_has_abstract_methods(self):
"""Test BlockchainService defines required abstract methods"""
assert hasattr(BlockchainService, 'get_block')
assert hasattr(BlockchainService, 'get_head_block')
assert hasattr(BlockchainService, 'get_transaction')
assert hasattr(BlockchainService, 'get_account_balance')
assert hasattr(BlockchainService, 'send_transaction')
assert hasattr(BlockchainService, 'get_status')