- Change SQLite database path from `/home/oib/windsurf/aitbc/data/` to `/opt/data/` - Fix foreign key references to use correct table names (users, wallets, gpu_registry) - Replace governance router with new governance and community routers - Add multi-modal RL router to main application - Simplify DEPLOYMENT_READINESS_REPORT.md to focus on production deployment status - Update governance router with decentralized DAO voting
785 lines
29 KiB
Python
785 lines
29 KiB
Python
"""
|
|
P2P Trading System Integration Tests
|
|
Comprehensive testing for agent-to-agent trading, matching, negotiation, and settlement
|
|
"""
|
|
|
|
import pytest
|
|
import asyncio
|
|
from datetime import datetime, timedelta
|
|
from uuid import uuid4
|
|
from typing import Dict, Any
|
|
|
|
from sqlmodel import Session, select
|
|
from sqlalchemy.exc import SQLAlchemyError
|
|
|
|
from apps.coordinator_api.src.app.services.trading_service import (
|
|
P2PTradingProtocol, MatchingEngine, NegotiationSystem, SettlementLayer
|
|
)
|
|
from apps.coordinator_api.src.app.domain.trading import (
|
|
TradeRequest, TradeMatch, TradeNegotiation, TradeAgreement, TradeSettlement,
|
|
TradeStatus, TradeType, NegotiationStatus, SettlementType
|
|
)
|
|
|
|
|
|
class TestMatchingEngine:
|
|
"""Test matching engine algorithms"""
|
|
|
|
@pytest.fixture
|
|
def matching_engine(self):
|
|
return MatchingEngine()
|
|
|
|
@pytest.fixture
|
|
def sample_buyer_request(self):
|
|
return TradeRequest(
|
|
request_id="req_001",
|
|
buyer_agent_id="buyer_001",
|
|
trade_type=TradeType.AI_POWER,
|
|
title="AI Model Training Service",
|
|
description="Need GPU resources for model training",
|
|
requirements={
|
|
"specifications": {
|
|
"cpu_cores": 8,
|
|
"memory_gb": 32,
|
|
"gpu_count": 2,
|
|
"gpu_memory_gb": 16
|
|
},
|
|
"timing": {
|
|
"start_time": datetime.utcnow() + timedelta(hours=2),
|
|
"duration_hours": 12
|
|
}
|
|
},
|
|
specifications={
|
|
"cpu_cores": 8,
|
|
"memory_gb": 32,
|
|
"gpu_count": 2,
|
|
"gpu_memory_gb": 16
|
|
},
|
|
budget_range={"min": 0.1, "max": 0.2},
|
|
preferred_regions=["us-east", "us-west"],
|
|
service_level_required="premium"
|
|
)
|
|
|
|
def test_price_compatibility_calculation(self, matching_engine):
|
|
"""Test price compatibility calculation"""
|
|
|
|
# Test perfect match
|
|
buyer_budget = {"min": 0.1, "max": 0.2}
|
|
seller_price = 0.15
|
|
|
|
score = matching_engine.calculate_price_compatibility(buyer_budget, seller_price)
|
|
assert 0 <= score <= 100
|
|
assert score > 50 # Should be good match
|
|
|
|
# Test below minimum
|
|
seller_price_low = 0.05
|
|
score_low = matching_engine.calculate_price_compatibility(buyer_budget, seller_price_low)
|
|
assert score_low == 0.0
|
|
|
|
# Test above maximum
|
|
seller_price_high = 0.25
|
|
score_high = matching_engine.calculate_price_compatibility(buyer_budget, seller_price_high)
|
|
assert score_high == 0.0
|
|
|
|
# Test infinite budget
|
|
buyer_budget_inf = {"min": 0.1, "max": float('inf')}
|
|
score_inf = matching_engine.calculate_price_compatibility(buyer_budget_inf, seller_price)
|
|
assert score_inf == 100.0
|
|
|
|
def test_specification_compatibility_calculation(self, matching_engine):
|
|
"""Test specification compatibility calculation"""
|
|
|
|
# Test perfect match
|
|
buyer_specs = {"cpu_cores": 8, "memory_gb": 32, "gpu_count": 2}
|
|
seller_specs = {"cpu_cores": 8, "memory_gb": 32, "gpu_count": 2}
|
|
|
|
score = matching_engine.calculate_specification_compatibility(buyer_specs, seller_specs)
|
|
assert score == 100.0
|
|
|
|
# Test partial match
|
|
seller_partial = {"cpu_cores": 8, "memory_gb": 64, "gpu_count": 2}
|
|
score_partial = matching_engine.calculate_specification_compatibility(buyer_specs, seller_partial)
|
|
assert score_partial == 100.0 # Seller offers more
|
|
|
|
# Test insufficient match
|
|
seller_insufficient = {"cpu_cores": 4, "memory_gb": 16, "gpu_count": 1}
|
|
score_insufficient = matching_engine.calculate_specification_compatibility(buyer_specs, seller_insufficient)
|
|
assert score_insufficient < 100.0
|
|
assert score_insufficient > 0.0
|
|
|
|
# Test no overlap
|
|
buyer_no_overlap = {"cpu_cores": 8}
|
|
seller_no_overlap = {"memory_gb": 32}
|
|
score_no_overlap = matching_engine.calculate_specification_compatibility(buyer_no_overlap, seller_no_overlap)
|
|
assert score_no_overlap == 50.0 # Neutral score
|
|
|
|
def test_timing_compatibility_calculation(self, matching_engine):
|
|
"""Test timing compatibility calculation"""
|
|
|
|
# Test perfect overlap
|
|
buyer_timing = {
|
|
"start_time": datetime.utcnow() + timedelta(hours=2),
|
|
"end_time": datetime.utcnow() + timedelta(hours=14)
|
|
}
|
|
seller_timing = {
|
|
"start_time": datetime.utcnow() + timedelta(hours=2),
|
|
"end_time": datetime.utcnow() + timedelta(hours=14)
|
|
}
|
|
|
|
score = matching_engine.calculate_timing_compatibility(buyer_timing, seller_timing)
|
|
assert score == 100.0
|
|
|
|
# Test partial overlap
|
|
seller_partial = {
|
|
"start_time": datetime.utcnow() + timedelta(hours=4),
|
|
"end_time": datetime.utcnow() + timedelta(hours=10)
|
|
}
|
|
score_partial = matching_engine.calculate_timing_compatibility(buyer_timing, seller_partial)
|
|
assert 0 < score_partial < 100
|
|
|
|
# Test no overlap
|
|
seller_no_overlap = {
|
|
"start_time": datetime.utcnow() + timedelta(hours=20),
|
|
"end_time": datetime.utcnow() + timedelta(hours=30)
|
|
}
|
|
score_no_overlap = matching_engine.calculate_timing_compatibility(buyer_timing, seller_no_overlap)
|
|
assert score_no_overlap == 0.0
|
|
|
|
def test_geographic_compatibility_calculation(self, matching_engine):
|
|
"""Test geographic compatibility calculation"""
|
|
|
|
# Test perfect match
|
|
buyer_regions = ["us-east", "us-west"]
|
|
seller_regions = ["us-east", "us-west", "eu-central"]
|
|
|
|
score = matching_engine.calculate_geographic_compatibility(buyer_regions, seller_regions)
|
|
assert score == 100.0
|
|
|
|
# Test partial match
|
|
seller_partial = ["us-east", "eu-central"]
|
|
score_partial = matching_engine.calculate_geographic_compatibility(buyer_regions, seller_partial)
|
|
assert 0 < score_partial < 100
|
|
|
|
# Test no match
|
|
seller_no_match = ["eu-central", "ap-southeast"]
|
|
score_no_match = matching_engine.calculate_geographic_compatibility(buyer_regions, seller_no_match)
|
|
assert score_no_match == 20.0 # Low score
|
|
|
|
# Test excluded regions
|
|
buyer_excluded = ["eu-central"]
|
|
seller_excluded = ["eu-central", "ap-southeast"]
|
|
score_excluded = matching_engine.calculate_geographic_compatibility(
|
|
buyer_regions, seller_regions, buyer_excluded, seller_excluded
|
|
)
|
|
assert score_excluded == 0.0
|
|
|
|
def test_overall_match_score_calculation(self, matching_engine, sample_buyer_request):
|
|
"""Test overall match score calculation"""
|
|
|
|
seller_offer = {
|
|
"agent_id": "seller_001",
|
|
"price": 0.15,
|
|
"specifications": {
|
|
"cpu_cores": 8,
|
|
"memory_gb": 32,
|
|
"gpu_count": 2,
|
|
"gpu_memory_gb": 16
|
|
},
|
|
"timing": {
|
|
"start_time": datetime.utcnow() + timedelta(hours=2),
|
|
"duration_hours": 12
|
|
},
|
|
"regions": ["us-east", "us-west"],
|
|
"service_level": "premium"
|
|
}
|
|
|
|
seller_reputation = 750.0
|
|
|
|
result = matching_engine.calculate_overall_match_score(
|
|
sample_buyer_request, seller_offer, seller_reputation
|
|
)
|
|
|
|
# Verify result structure
|
|
assert "overall_score" in result
|
|
assert "price_compatibility" in result
|
|
assert "specification_compatibility" in result
|
|
assert "timing_compatibility" in result
|
|
assert "reputation_compatibility" in result
|
|
assert "geographic_compatibility" in result
|
|
assert "confidence_level" in result
|
|
|
|
# Verify score ranges
|
|
assert 0 <= result["overall_score"] <= 100
|
|
assert 0 <= result["confidence_level"] <= 1
|
|
|
|
# Should be a good match
|
|
assert result["overall_score"] > 60 # Above minimum threshold
|
|
|
|
def test_find_matches(self, matching_engine, sample_buyer_request):
|
|
"""Test finding matches for a trade request"""
|
|
|
|
seller_offers = [
|
|
{
|
|
"agent_id": "seller_001",
|
|
"price": 0.15,
|
|
"specifications": {"cpu_cores": 8, "memory_gb": 32, "gpu_count": 2},
|
|
"timing": {"start_time": datetime.utcnow() + timedelta(hours=2), "duration_hours": 12},
|
|
"regions": ["us-east", "us-west"],
|
|
"service_level": "premium"
|
|
},
|
|
{
|
|
"agent_id": "seller_002",
|
|
"price": 0.25,
|
|
"specifications": {"cpu_cores": 4, "memory_gb": 16, "gpu_count": 1},
|
|
"timing": {"start_time": datetime.utcnow() + timedelta(hours=4), "duration_hours": 8},
|
|
"regions": ["eu-central"],
|
|
"service_level": "standard"
|
|
},
|
|
{
|
|
"agent_id": "seller_003",
|
|
"price": 0.12,
|
|
"specifications": {"cpu_cores": 16, "memory_gb": 64, "gpu_count": 4},
|
|
"timing": {"start_time": datetime.utcnow() + timedelta(hours=1), "duration_hours": 24},
|
|
"regions": ["us-east", "us-west", "ap-southeast"],
|
|
"service_level": "premium"
|
|
}
|
|
]
|
|
|
|
seller_reputations = {
|
|
"seller_001": 750.0,
|
|
"seller_002": 600.0,
|
|
"seller_003": 850.0
|
|
}
|
|
|
|
matches = matching_engine.find_matches(
|
|
sample_buyer_request, seller_offers, seller_reputations
|
|
)
|
|
|
|
# Should find matches above threshold
|
|
assert len(matches) > 0
|
|
assert len(matches) <= matching_engine.max_matches_per_request
|
|
|
|
# Should be sorted by score (descending)
|
|
for i in range(len(matches) - 1):
|
|
assert matches[i]["match_score"] >= matches[i + 1]["match_score"]
|
|
|
|
# All matches should be above minimum threshold
|
|
for match in matches:
|
|
assert match["match_score"] >= matching_engine.min_match_score
|
|
|
|
|
|
class TestNegotiationSystem:
|
|
"""Test negotiation system functionality"""
|
|
|
|
@pytest.fixture
|
|
def negotiation_system(self):
|
|
return NegotiationSystem()
|
|
|
|
@pytest.fixture
|
|
def sample_buyer_request(self):
|
|
return TradeRequest(
|
|
request_id="req_001",
|
|
buyer_agent_id="buyer_001",
|
|
trade_type=TradeType.AI_POWER,
|
|
title="AI Model Training Service",
|
|
budget_range={"min": 0.1, "max": 0.2},
|
|
specifications={"cpu_cores": 8, "memory_gb": 32, "gpu_count": 2},
|
|
start_time=datetime.utcnow() + timedelta(hours=2),
|
|
duration_hours=12,
|
|
service_level_required="premium"
|
|
)
|
|
|
|
@pytest.fixture
|
|
def sample_seller_offer(self):
|
|
return {
|
|
"agent_id": "seller_001",
|
|
"price": 0.15,
|
|
"specifications": {"cpu_cores": 8, "memory_gb": 32, "gpu_count": 2},
|
|
"timing": {"start_time": datetime.utcnow() + timedelta(hours=2), "duration_hours": 12},
|
|
"regions": ["us-east", "us-west"],
|
|
"service_level": "premium",
|
|
"terms": {"settlement_type": "escrow", "delivery_guarantee": True}
|
|
}
|
|
|
|
def test_generate_initial_offer(self, negotiation_system, sample_buyer_request, sample_seller_offer):
|
|
"""Test initial offer generation"""
|
|
|
|
initial_offer = negotiation_system.generate_initial_offer(
|
|
sample_buyer_request, sample_seller_offer
|
|
)
|
|
|
|
# Verify offer structure
|
|
assert "price" in initial_offer
|
|
assert "specifications" in initial_offer
|
|
assert "timing" in initial_offer
|
|
assert "service_level" in initial_offer
|
|
assert "payment_terms" in initial_offer
|
|
assert "delivery_terms" in initial_offer
|
|
|
|
# Price should be between buyer budget and seller price
|
|
assert sample_buyer_request.budget_range["min"] <= initial_offer["price"] <= sample_seller_offer["price"]
|
|
|
|
# Service level should be appropriate
|
|
assert initial_offer["service_level"] in ["basic", "standard", "premium"]
|
|
|
|
# Payment terms should include escrow
|
|
assert initial_offer["payment_terms"]["settlement_type"] == "escrow"
|
|
|
|
def test_merge_specifications(self, negotiation_system):
|
|
"""Test specification merging"""
|
|
|
|
buyer_specs = {"cpu_cores": 8, "memory_gb": 32, "gpu_count": 2, "storage_gb": 100}
|
|
seller_specs = {"cpu_cores": 8, "memory_gb": 64, "gpu_count": 2, "gpu_memory_gb": 16}
|
|
|
|
merged = negotiation_system.merge_specifications(buyer_specs, seller_specs)
|
|
|
|
# Should include all buyer requirements
|
|
assert merged["cpu_cores"] == 8
|
|
assert merged["memory_gb"] == 32
|
|
assert merged["gpu_count"] == 2
|
|
assert merged["storage_gb"] == 100
|
|
|
|
# Should include additional seller capabilities
|
|
assert merged["gpu_memory_gb"] == 16
|
|
assert merged["memory_gb"] >= 32 # Should keep higher value
|
|
|
|
def test_negotiate_timing(self, negotiation_system):
|
|
"""Test timing negotiation"""
|
|
|
|
buyer_timing = {
|
|
"start_time": datetime.utcnow() + timedelta(hours=2),
|
|
"end_time": datetime.utcnow() + timedelta(hours=14),
|
|
"duration_hours": 12
|
|
}
|
|
|
|
seller_timing = {
|
|
"start_time": datetime.utcnow() + timedelta(hours=3),
|
|
"end_time": datetime.utcnow() + timedelta(hours=15),
|
|
"duration_hours": 10
|
|
}
|
|
|
|
negotiated = negotiation_system.negotiate_timing(buyer_timing, seller_timing)
|
|
|
|
# Should use later start time
|
|
assert negotiated["start_time"] == seller_timing["start_time"]
|
|
|
|
# Should use shorter duration
|
|
assert negotiated["duration_hours"] == seller_timing["duration_hours"]
|
|
|
|
def test_calculate_concession(self, negotiation_system):
|
|
"""Test concession calculation"""
|
|
|
|
current_offer = {"price": 0.15, "specifications": {"cpu_cores": 8}}
|
|
previous_offer = {"price": 0.18, "specifications": {"cpu_cores": 8}}
|
|
|
|
# Test balanced strategy
|
|
concession = negotiation_system.calculate_concession(
|
|
current_offer, previous_offer, "balanced", 1
|
|
)
|
|
|
|
# Should move price towards buyer preference
|
|
assert concession["price"] < current_offer["price"]
|
|
assert concession["specifications"] == current_offer["specifications"]
|
|
|
|
def test_evaluate_offer(self, negotiation_system):
|
|
"""Test offer evaluation"""
|
|
|
|
requirements = {
|
|
"budget_range": {"min": 0.1, "max": 0.2},
|
|
"specifications": {"cpu_cores": 8, "memory_gb": 32}
|
|
}
|
|
|
|
# Test acceptable offer
|
|
acceptable_offer = {
|
|
"price": 0.15,
|
|
"specifications": {"cpu_cores": 8, "memory_gb": 32}
|
|
}
|
|
|
|
result = negotiation_system.evaluate_offer(acceptable_offer, requirements, "balanced")
|
|
assert result["should_accept"] is True
|
|
|
|
# Test unacceptable offer (too expensive)
|
|
expensive_offer = {
|
|
"price": 0.25,
|
|
"specifications": {"cpu_cores": 8, "memory_gb": 32}
|
|
}
|
|
|
|
result_expensive = negotiation_system.evaluate_offer(expensive_offer, requirements, "balanced")
|
|
assert result_expensive["should_accept"] is False
|
|
assert result_expensive["reason"] == "price_above_maximum"
|
|
|
|
|
|
class TestSettlementLayer:
|
|
"""Test settlement layer functionality"""
|
|
|
|
@pytest.fixture
|
|
def settlement_layer(self):
|
|
return SettlementLayer()
|
|
|
|
@pytest.fixture
|
|
def sample_agreement(self):
|
|
return TradeAgreement(
|
|
agreement_id="agree_001",
|
|
buyer_agent_id="buyer_001",
|
|
seller_agent_id="seller_001",
|
|
trade_type=TradeType.AI_POWER,
|
|
title="AI Model Training Service",
|
|
agreed_terms={"delivery_date": "2026-02-27"},
|
|
total_price=0.15,
|
|
currency="AITBC",
|
|
service_level_agreement={"escrow_conditions": {"delivery_confirmed": True}}
|
|
)
|
|
|
|
def test_create_settlement(self, settlement_layer, sample_agreement):
|
|
"""Test settlement creation"""
|
|
|
|
# Test escrow settlement
|
|
settlement = settlement_layer.create_settlement(sample_agreement, SettlementType.ESCROW)
|
|
|
|
# Verify settlement structure
|
|
assert "settlement_id" in settlement
|
|
assert "agreement_id" in settlement
|
|
assert "settlement_type" in settlement
|
|
assert "total_amount" in settlement
|
|
assert "requires_escrow" in settlement
|
|
assert "platform_fee" in settlement
|
|
assert "net_amount_seller" in settlement
|
|
|
|
# Verify escrow configuration
|
|
assert settlement["requires_escrow"] is True
|
|
assert "escrow_config" in settlement
|
|
assert "escrow_address" in settlement["escrow_config"]
|
|
|
|
# Verify fee calculation
|
|
expected_fee = sample_agreement.total_price * 0.02 # 2% for escrow
|
|
assert settlement["platform_fee"] == expected_fee
|
|
assert settlement["net_amount_seller"] == sample_agreement.total_price - expected_fee
|
|
|
|
def test_process_payment(self, settlement_layer, sample_agreement):
|
|
"""Test payment processing"""
|
|
|
|
settlement = settlement_layer.create_settlement(sample_agreement, SettlementType.IMMEDIATE)
|
|
|
|
payment_result = settlement_layer.process_payment(settlement, "blockchain")
|
|
|
|
# Verify payment result
|
|
assert "transaction_id" in payment_result
|
|
assert "transaction_hash" in payment_result
|
|
assert "status" in payment_result
|
|
assert "amount" in payment_result
|
|
assert "fee" in payment_result
|
|
assert "net_amount" in payment_result
|
|
|
|
# Verify transaction details
|
|
assert payment_result["status"] == "processing"
|
|
assert payment_result["amount"] == settlement["total_amount"]
|
|
assert payment_result["fee"] == settlement["platform_fee"]
|
|
|
|
def test_release_escrow(self, settlement_layer, sample_agreement):
|
|
"""Test escrow release"""
|
|
|
|
settlement = settlement_layer.create_settlement(sample_agreement, SettlementType.ESCROW)
|
|
|
|
# Test successful release
|
|
release_result = settlement_layer.release_escrow(
|
|
settlement, "delivery_confirmed", release_conditions_met=True
|
|
)
|
|
|
|
# Verify release result
|
|
assert release_result["conditions_met"] is True
|
|
assert release_result["status"] == "released"
|
|
assert "transaction_id" in release_result
|
|
assert "amount_released" in release_result
|
|
|
|
# Test failed release
|
|
release_failed = settlement_layer.release_escrow(
|
|
settlement, "delivery_not_confirmed", release_conditions_met=False
|
|
)
|
|
|
|
assert release_failed["conditions_met"] is False
|
|
assert release_failed["status"] == "held"
|
|
assert "hold_reason" in release_failed
|
|
|
|
def test_handle_dispute(self, settlement_layer, sample_agreement):
|
|
"""Test dispute handling"""
|
|
|
|
settlement = settlement_layer.create_settlement(sample_agreement, SettlementType.ESCROW)
|
|
|
|
dispute_details = {
|
|
"type": "quality_issue",
|
|
"reason": "Service quality not as expected",
|
|
"initiated_by": "buyer_001"
|
|
}
|
|
|
|
dispute_result = settlement_layer.handle_dispute(settlement, dispute_details)
|
|
|
|
# Verify dispute result
|
|
assert "dispute_id" in dispute_result
|
|
assert "dispute_type" in dispute_result
|
|
assert "dispute_reason" in dispute_result
|
|
assert "initiated_by" in dispute_result
|
|
assert "status" in dispute_result
|
|
|
|
# Verify escrow hold
|
|
assert dispute_result["escrow_status"] == "held_pending_resolution"
|
|
assert dispute_result["escrow_release_blocked"] is True
|
|
|
|
|
|
class TestP2PTradingProtocol:
|
|
"""Test P2P trading protocol functionality"""
|
|
|
|
@pytest.fixture
|
|
def mock_session(self):
|
|
"""Mock database session"""
|
|
class MockSession:
|
|
def __init__(self):
|
|
self.data = {}
|
|
self.committed = False
|
|
|
|
def exec(self, query):
|
|
# Mock query execution
|
|
if hasattr(query, 'where'):
|
|
return []
|
|
return []
|
|
|
|
def add(self, obj):
|
|
self.data[obj.id if hasattr(obj, 'id') else 'temp'] = obj
|
|
|
|
def commit(self):
|
|
self.committed = True
|
|
|
|
def refresh(self, obj):
|
|
pass
|
|
|
|
return MockSession()
|
|
|
|
@pytest.fixture
|
|
def trading_protocol(self, mock_session):
|
|
return P2PTradingProtocol(mock_session)
|
|
|
|
def test_create_trade_request(self, trading_protocol, mock_session):
|
|
"""Test creating a trade request"""
|
|
|
|
agent_id = "buyer_001"
|
|
trade_type = TradeType.AI_POWER
|
|
title = "AI Model Training Service"
|
|
description = "Need GPU resources for model training"
|
|
requirements = {
|
|
"specifications": {"cpu_cores": 8, "memory_gb": 32, "gpu_count": 2},
|
|
"timing": {"duration_hours": 12}
|
|
}
|
|
budget_range = {"min": 0.1, "max": 0.2}
|
|
|
|
# Create trade request
|
|
trade_request = asyncio.run(
|
|
trading_protocol.create_trade_request(
|
|
buyer_agent_id=agent_id,
|
|
trade_type=trade_type,
|
|
title=title,
|
|
description=description,
|
|
requirements=requirements,
|
|
budget_range=budget_range
|
|
)
|
|
)
|
|
|
|
# Verify request creation
|
|
assert trade_request.buyer_agent_id == agent_id
|
|
assert trade_request.trade_type == trade_type
|
|
assert trade_request.title == title
|
|
assert trade_request.description == description
|
|
assert trade_request.requirements == requirements
|
|
assert trade_request.budget_range == budget_range
|
|
assert trade_request.status == TradeStatus.OPEN
|
|
assert mock_session.committed
|
|
|
|
def test_find_matches(self, trading_protocol, mock_session):
|
|
"""Test finding matches for a trade request"""
|
|
|
|
# Mock session to return trade request
|
|
mock_request = TradeRequest(
|
|
request_id="req_001",
|
|
buyer_agent_id="buyer_001",
|
|
trade_type=TradeType.AI_POWER,
|
|
requirements={"specifications": {"cpu_cores": 8}},
|
|
budget_range={"min": 0.1, "max": 0.2}
|
|
)
|
|
|
|
mock_session.exec = lambda query: [mock_request] if hasattr(query, 'where') else []
|
|
mock_session.add = lambda obj: None
|
|
mock_session.commit = lambda: None
|
|
|
|
# Mock available sellers
|
|
async def mock_get_sellers(request):
|
|
return [
|
|
{
|
|
"agent_id": "seller_001",
|
|
"price": 0.15,
|
|
"specifications": {"cpu_cores": 8, "memory_gb": 32},
|
|
"timing": {"start_time": datetime.utcnow(), "duration_hours": 12},
|
|
"regions": ["us-east"],
|
|
"service_level": "premium"
|
|
}
|
|
]
|
|
|
|
async def mock_get_reputations(seller_ids):
|
|
return {"seller_001": 750.0}
|
|
|
|
trading_protocol.get_available_sellers = mock_get_sellers
|
|
trading_protocol.get_seller_reputations = mock_get_reputations
|
|
|
|
# Find matches
|
|
matches = asyncio.run(trading_protocol.find_matches("req_001"))
|
|
|
|
# Verify matches
|
|
assert isinstance(matches, list)
|
|
assert len(matches) > 0
|
|
assert "seller_001" in matches
|
|
|
|
def test_initiate_negotiation(self, trading_protocol, mock_session):
|
|
"""Test initiating negotiation"""
|
|
|
|
# Mock trade match and request
|
|
mock_match = TradeMatch(
|
|
match_id="match_001",
|
|
request_id="req_001",
|
|
buyer_agent_id="buyer_001",
|
|
seller_agent_id="seller_001",
|
|
seller_offer={"price": 0.15, "specifications": {"cpu_cores": 8}}
|
|
)
|
|
|
|
mock_request = TradeRequest(
|
|
request_id="req_001",
|
|
buyer_agent_id="buyer_001",
|
|
requirements={"specifications": {"cpu_cores": 8}},
|
|
budget_range={"min": 0.1, "max": 0.2}
|
|
)
|
|
|
|
mock_session.exec = lambda query: [mock_match] if "match_id" in str(query) else [mock_request]
|
|
mock_session.add = lambda obj: None
|
|
mock_session.commit = lambda: None
|
|
|
|
# Initiate negotiation
|
|
negotiation = asyncio.run(
|
|
trading_protocol.initiate_negotiation("match_001", "buyer", "balanced")
|
|
)
|
|
|
|
# Verify negotiation creation
|
|
assert negotiation.match_id == "match_001"
|
|
assert negotiation.buyer_agent_id == "buyer_001"
|
|
assert negotiation.seller_agent_id == "seller_001"
|
|
assert negotiation.status == NegotiationStatus.PENDING
|
|
assert negotiation.negotiation_strategy == "balanced"
|
|
assert "current_terms" in negotiation
|
|
assert "initial_terms" in negotiation
|
|
|
|
def test_get_trading_summary(self, trading_protocol, mock_session):
|
|
"""Test getting trading summary"""
|
|
|
|
# Mock session to return empty lists
|
|
mock_session.exec = lambda query: []
|
|
|
|
# Get summary
|
|
summary = asyncio.run(trading_protocol.get_trading_summary("agent_001"))
|
|
|
|
# Verify summary structure
|
|
assert "agent_id" in summary
|
|
assert "trade_requests" in summary
|
|
assert "trade_matches" in summary
|
|
assert "negotiations" in summary
|
|
assert "agreements" in summary
|
|
assert "success_rate" in summary
|
|
assert "total_trade_volume" in summary
|
|
assert "recent_activity" in summary
|
|
|
|
# Verify values for empty data
|
|
assert summary["agent_id"] == "agent_001"
|
|
assert summary["trade_requests"] == 0
|
|
assert summary["trade_matches"] == 0
|
|
assert summary["negotiations"] == 0
|
|
assert summary["agreements"] == 0
|
|
assert summary["success_rate"] == 0.0
|
|
assert summary["total_trade_volume"] == 0.0
|
|
|
|
|
|
# Performance Tests
|
|
class TestTradingPerformance:
|
|
"""Performance tests for trading system"""
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_bulk_matching_performance(self):
|
|
"""Test performance of bulk matching operations"""
|
|
|
|
# Test matching performance with many requests and sellers
|
|
# Should complete within acceptable time limits
|
|
|
|
pass
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_negotiation_performance(self):
|
|
"""Test negotiation system performance"""
|
|
|
|
# Test negotiation performance with multiple concurrent negotiations
|
|
# Should complete within acceptable time limits
|
|
|
|
pass
|
|
|
|
|
|
# Utility Functions
|
|
def create_test_trade_request(**kwargs) -> Dict[str, Any]:
|
|
"""Create test trade request data"""
|
|
|
|
defaults = {
|
|
"buyer_agent_id": "test_buyer_001",
|
|
"trade_type": TradeType.AI_POWER,
|
|
"title": "Test AI Service",
|
|
"description": "Test description",
|
|
"requirements": {
|
|
"specifications": {"cpu_cores": 4, "memory_gb": 16},
|
|
"timing": {"duration_hours": 8}
|
|
},
|
|
"budget_range": {"min": 0.05, "max": 0.1},
|
|
"urgency_level": "normal",
|
|
"preferred_regions": ["us-east"],
|
|
"service_level_required": "standard"
|
|
}
|
|
|
|
defaults.update(kwargs)
|
|
return defaults
|
|
|
|
|
|
def create_test_seller_offer(**kwargs) -> Dict[str, Any]:
|
|
"""Create test seller offer data"""
|
|
|
|
defaults = {
|
|
"agent_id": "test_seller_001",
|
|
"price": 0.075,
|
|
"specifications": {"cpu_cores": 4, "memory_gb": 16, "gpu_count": 1},
|
|
"timing": {"start_time": datetime.utcnow(), "duration_hours": 8},
|
|
"regions": ["us-east"],
|
|
"service_level": "standard",
|
|
"terms": {"settlement_type": "escrow"}
|
|
}
|
|
|
|
defaults.update(kwargs)
|
|
return defaults
|
|
|
|
|
|
# Test Configuration
|
|
@pytest.fixture(scope="session")
|
|
def test_config():
|
|
"""Test configuration for trading system tests"""
|
|
|
|
return {
|
|
"test_agent_count": 100,
|
|
"test_request_count": 500,
|
|
"test_match_count": 1000,
|
|
"performance_threshold_ms": 2000,
|
|
"memory_threshold_mb": 150
|
|
}
|
|
|
|
|
|
# Test Markers
|
|
pytest.mark.unit = pytest.mark.unit
|
|
pytest.mark.integration = pytest.mark.integration
|
|
pytest.mark.performance = pytest.mark.performance
|
|
pytest.mark.slow = pytest.mark.slow
|