Files
aitbc/apps/blockchain-node/tests/contracts/test_escrow.py
aitbc c876b0aa20 feat: implement AITBC mesh network deployment infrastructure
 Phase 0: Pre-implementation checklist completed
- Environment configurations (dev/staging/production)
- Directory structure setup (logs, backups, monitoring)
- Virtual environment with dependencies

 Master deployment script created
- Single command deployment with validation
- Progress tracking and rollback capability
- Health checks and deployment reporting

 Validation script created
- Module import validation
- Basic functionality testing
- Configuration and script verification

 Implementation fixes
- Fixed dataclass import in consensus keys
- Fixed async function syntax in tests
- Updated deployment script for virtual environment

🚀 Ready for deployment: ./scripts/deploy-mesh-network.sh dev
2026-04-02 12:08:15 +02:00

403 lines
14 KiB
Python

"""
Tests for Escrow System
"""
import pytest
import asyncio
import time
from decimal import Decimal
from unittest.mock import Mock, patch
from aitbc_chain.contracts.escrow import EscrowManager, EscrowState, DisputeReason
class TestEscrowManager:
"""Test cases for escrow manager"""
def setup_method(self):
"""Setup test environment"""
self.escrow_manager = EscrowManager()
def test_create_contract(self):
"""Test escrow contract creation"""
success, message, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="job_001",
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0')
)
)
assert success, f"Contract creation failed: {message}"
assert contract_id is not None
# Check contract details
contract = asyncio.run(self.escrow_manager.get_contract_info(contract_id))
assert contract is not None
assert contract.job_id == "job_001"
assert contract.client_address == "0x1234567890123456789012345678901234567890"
assert contract.agent_address == "0x2345678901234567890123456789012345678901"
assert contract.amount > Decimal('100.0') # Includes platform fee
assert contract.state == EscrowState.CREATED
def test_create_contract_invalid_inputs(self):
"""Test contract creation with invalid inputs"""
success, message, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="", # Empty job ID
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0')
)
)
assert not success
assert contract_id is None
assert "invalid" in message.lower()
def test_create_contract_with_milestones(self):
"""Test contract creation with milestones"""
milestones = [
{
'milestone_id': 'milestone_1',
'description': 'Initial setup',
'amount': Decimal('30.0')
},
{
'milestone_id': 'milestone_2',
'description': 'Main work',
'amount': Decimal('50.0')
},
{
'milestone_id': 'milestone_3',
'description': 'Final delivery',
'amount': Decimal('20.0')
}
]
success, message, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="job_002",
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0'),
milestones=milestones
)
)
assert success
assert contract_id is not None
# Check milestones
contract = asyncio.run(self.escrow_manager.get_contract_info(contract_id))
assert len(contract.milestones) == 3
assert contract.milestones[0]['amount'] == Decimal('30.0')
assert contract.milestones[1]['amount'] == Decimal('50.0')
assert contract.milestones[2]['amount'] == Decimal('20.0')
def test_create_contract_invalid_milestones(self):
"""Test contract creation with invalid milestones"""
milestones = [
{
'milestone_id': 'milestone_1',
'description': 'Setup',
'amount': Decimal('30.0')
},
{
'milestone_id': 'milestone_2',
'description': 'Main work',
'amount': Decimal('80.0') # Total exceeds contract amount
}
]
success, message, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="job_003",
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0'),
milestones=milestones
)
)
assert not success
assert "milestones" in message.lower()
def test_fund_contract(self):
"""Test funding contract"""
# Create contract first
success, _, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="job_004",
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0')
)
)
assert success
# Fund contract
success, message = asyncio.run(
self.escrow_manager.fund_contract(contract_id, "tx_hash_001")
)
assert success, f"Contract funding failed: {message}"
# Check state
contract = asyncio.run(self.escrow_manager.get_contract_info(contract_id))
assert contract.state == EscrowState.FUNDED
def test_fund_already_funded_contract(self):
"""Test funding already funded contract"""
# Create and fund contract
success, _, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="job_005",
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0')
)
)
asyncio.run(self.escrow_manager.fund_contract(contract_id, "tx_hash_001"))
# Try to fund again
success, message = asyncio.run(
self.escrow_manager.fund_contract(contract_id, "tx_hash_002")
)
assert not success
assert "state" in message.lower()
def test_start_job(self):
"""Test starting job"""
# Create and fund contract
success, _, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="job_006",
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0')
)
)
asyncio.run(self.escrow_manager.fund_contract(contract_id, "tx_hash_001"))
# Start job
success, message = asyncio.run(self.escrow_manager.start_job(contract_id))
assert success, f"Job start failed: {message}"
# Check state
contract = asyncio.run(self.escrow_manager.get_contract_info(contract_id))
assert contract.state == EscrowState.JOB_STARTED
def test_complete_milestone(self):
"""Test completing milestone"""
milestones = [
{
'milestone_id': 'milestone_1',
'description': 'Setup',
'amount': Decimal('50.0')
},
{
'milestone_id': 'milestone_2',
'description': 'Delivery',
'amount': Decimal('50.0')
}
]
# Create contract with milestones
success, _, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="job_007",
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0'),
milestones=milestones
)
)
asyncio.run(self.escrow_manager.fund_contract(contract_id, "tx_hash_001"))
asyncio.run(self.escrow_manager.start_job(contract_id))
# Complete milestone
success, message = asyncio.run(
self.escrow_manager.complete_milestone(contract_id, "milestone_1")
)
assert success, f"Milestone completion failed: {message}"
# Check milestone status
contract = asyncio.run(self.escrow_manager.get_contract_info(contract_id))
milestone = contract.milestones[0]
assert milestone['completed']
assert milestone['completed_at'] is not None
def test_verify_milestone(self):
"""Test verifying milestone"""
milestones = [
{
'milestone_id': 'milestone_1',
'description': 'Setup',
'amount': Decimal('50.0')
}
]
# Create contract with milestone
success, _, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="job_008",
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0'),
milestones=milestones
)
)
asyncio.run(self.escrow_manager.fund_contract(contract_id, "tx_hash_001"))
asyncio.run(self.escrow_manager.start_job(contract_id))
asyncio.run(self.escrow_manager.complete_milestone(contract_id, "milestone_1"))
# Verify milestone
success, message = asyncio.run(
self.escrow_manager.verify_milestone(contract_id, "milestone_1", True, "Work completed successfully")
)
assert success, f"Milestone verification failed: {message}"
# Check verification status
contract = asyncio.run(self.escrow_manager.get_contract_info(contract_id))
milestone = contract.milestones[0]
assert milestone['verified']
assert milestone['verification_feedback'] == "Work completed successfully"
def test_create_dispute(self):
"""Test creating dispute"""
# Create and fund contract
success, _, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="job_009",
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0')
)
)
asyncio.run(self.escrow_manager.fund_contract(contract_id, "tx_hash_001"))
asyncio.run(self.escrow_manager.start_job(contract_id))
# Create dispute
evidence = [
{
'type': 'screenshot',
'description': 'Poor quality work',
'timestamp': time.time()
}
]
success, message = asyncio.run(
self.escrow_manager.create_dispute(
contract_id, DisputeReason.QUALITY_ISSUES, "Work quality is poor", evidence
)
)
assert success, f"Dispute creation failed: {message}"
# Check dispute status
contract = asyncio.run(self.escrow_manager.get_contract_info(contract_id))
assert contract.state == EscrowState.DISPUTED
assert contract.dispute_reason == DisputeReason.QUALITY_ISSUES
def test_resolve_dispute(self):
"""Test resolving dispute"""
# Create and fund contract
success, _, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="job_010",
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0')
)
)
asyncio.run(self.escrow_manager.fund_contract(contract_id, "tx_hash_001"))
asyncio.run(self.escrow_manager.start_job(contract_id))
# Create dispute
asyncio.run(
self.escrow_manager.create_dispute(
contract_id, DisputeReason.QUALITY_ISSUES, "Quality issues"
)
)
# Resolve dispute
resolution = {
'winner': 'client',
'client_refund': 0.8, # 80% refund
'agent_payment': 0.2 # 20% payment
}
success, message = asyncio.run(
self.escrow_manager.resolve_dispute(contract_id, resolution)
)
assert success, f"Dispute resolution failed: {message}"
# Check resolution
contract = asyncio.run(self.escrow_manager.get_contract_info(contract_id))
assert contract.state == EscrowState.RESOLVED
assert contract.resolution == resolution
def test_refund_contract(self):
"""Test refunding contract"""
# Create and fund contract
success, _, contract_id = asyncio.run(
self.escrow_manager.create_contract(
job_id="job_011",
client_address="0x1234567890123456789012345678901234567890",
agent_address="0x2345678901234567890123456789012345678901",
amount=Decimal('100.0')
)
)
asyncio.run(self.escrow_manager.fund_contract(contract_id, "tx_hash_001"))
# Refund contract
success, message = asyncio.run(
self.escrow_manager.refund_contract(contract_id, "Client requested refund")
)
assert success, f"Refund failed: {message}"
# Check refund status
contract = asyncio.run(self.escrow_manager.get_contract_info(contract_id))
assert contract.state == EscrowState.REFUNDED
assert contract.refunded_amount > 0
def test_get_escrow_statistics(self):
"""Test getting escrow statistics"""
# Create multiple contracts
for i in range(5):
asyncio.run(
self.escrow_manager.create_contract(
job_id=f"job_{i:03d}",
client_address=f"0x123456789012345678901234567890123456789{i}",
agent_address=f"0x234567890123456789012345678901234567890{i}",
amount=Decimal('100.0')
)
)
stats = asyncio.run(self.escrow_manager.get_escrow_statistics())
assert 'total_contracts' in stats
assert 'active_contracts' in stats
assert 'disputed_contracts' in stats
assert 'state_distribution' in stats
assert 'total_amount' in stats
assert stats['total_contracts'] >= 5
if __name__ == "__main__":
pytest.main([__file__])