Some checks failed
CLI Tests / test-cli (push) Failing after 6s
Cross-Node Transaction Testing / transaction-test (push) Successful in 3s
Deploy to Testnet / deploy-testnet (push) Successful in 1m20s
Documentation Validation / validate-docs (push) Failing after 10s
Documentation Validation / validate-policies-strict (push) Successful in 4s
Multi-Node Stress Testing / stress-test (push) Successful in 3s
Node Failover Simulation / failover-test (push) Successful in 7s
Python Tests / test-python (push) Failing after 1m12s
Security Scanning / security-scan (push) Successful in 28s
Replaced direct AITBC CLI command execution with OpenClaw agent-based execution that respects the allowlist: - Changed openclaw_training_operations to execute commands via `openclaw agent --message` instead of direct CLI calls - Removed operation-specific command building logic (wallet_create, genesis_init, etc.) - Simplified execution flow to single OpenClaw agent invocation with prompt message - Added prerequisites
410 lines
14 KiB
Python
410 lines
14 KiB
Python
"""
|
|
Minimal conftest for pytest discovery without complex imports
|
|
"""
|
|
|
|
import pytest
|
|
import sys
|
|
import os
|
|
from pathlib import Path
|
|
from unittest.mock import Mock
|
|
|
|
# Configure Python path for test discovery
|
|
project_root = Path(__file__).parent.parent
|
|
sys.path.insert(0, str(project_root))
|
|
|
|
# Add aitbc package to sys.path for centralized utilities
|
|
sys.path.insert(0, str(project_root / "aitbc"))
|
|
|
|
# Import aitbc utilities for conftest
|
|
from aitbc.constants import DATA_DIR, LOG_DIR
|
|
|
|
# Import new testing utilities
|
|
from aitbc.testing import MockFactory, TestDataGenerator, MockResponse, MockDatabase, MockCache
|
|
|
|
# Import training setup utilities
|
|
from aitbc.training_setup import TrainingEnvironment, TrainingSetupError
|
|
|
|
# Add necessary source paths
|
|
sys.path.insert(0, str(project_root / "packages" / "py" / "aitbc-core" / "src"))
|
|
sys.path.insert(0, str(project_root / "packages" / "py" / "aitbc-crypto" / "src"))
|
|
sys.path.insert(0, str(project_root / "packages" / "py" / "aitbc-p2p" / "src"))
|
|
sys.path.insert(0, str(project_root / "packages" / "py" / "aitbc-sdk" / "src"))
|
|
sys.path.insert(0, str(project_root / "apps" / "coordinator-api" / "src"))
|
|
sys.path.insert(0, str(project_root / "apps" / "wallet-daemon" / "src"))
|
|
sys.path.insert(0, str(project_root / "apps" / "blockchain-node" / "src"))
|
|
sys.path.insert(0, str(project_root / "apps" / "monitor"))
|
|
sys.path.insert(0, str(project_root / "apps" / "ai-engine" / "src"))
|
|
sys.path.insert(0, str(project_root / "apps" / "simple-explorer"))
|
|
sys.path.insert(0, str(project_root / "apps" / "zk-circuits"))
|
|
sys.path.insert(0, str(project_root / "apps" / "exchange-integration"))
|
|
sys.path.insert(0, str(project_root / "apps" / "compliance-service"))
|
|
sys.path.insert(0, str(project_root / "apps" / "plugin-registry"))
|
|
sys.path.insert(0, str(project_root / "apps" / "trading-engine"))
|
|
sys.path.insert(0, str(project_root / "apps" / "plugin-security"))
|
|
sys.path.insert(0, str(project_root / "apps" / "plugin-analytics"))
|
|
sys.path.insert(0, str(project_root / "apps" / "global-infrastructure"))
|
|
sys.path.insert(0, str(project_root / "apps" / "plugin-marketplace"))
|
|
sys.path.insert(0, str(project_root / "apps" / "multi-region-load-balancer"))
|
|
sys.path.insert(0, str(project_root / "apps" / "global-ai-agents"))
|
|
sys.path.insert(0, str(project_root / "apps" / "miner"))
|
|
sys.path.insert(0, str(project_root / "apps" / "marketplace"))
|
|
sys.path.insert(0, str(project_root / "apps" / "agent-services" / "agent-registry" / "src"))
|
|
sys.path.insert(0, str(project_root / "apps" / "blockchain-explorer"))
|
|
sys.path.insert(0, str(project_root / "apps" / "exchange"))
|
|
sys.path.insert(0, str(project_root / "apps" / "blockchain-event-bridge"))
|
|
sys.path.insert(0, str(project_root / "apps" / "coordinator-api"))
|
|
|
|
# Set up test environment
|
|
os.environ["TEST_MODE"] = "true"
|
|
os.environ["AUDIT_LOG_DIR"] = str(LOG_DIR / "audit")
|
|
os.environ["TEST_DATABASE_URL"] = "sqlite:///:memory:"
|
|
os.environ["DATA_DIR"] = str(DATA_DIR)
|
|
|
|
# Mock missing optional dependencies
|
|
sys.modules['slowapi'] = Mock()
|
|
sys.modules['slowapi.util'] = Mock()
|
|
sys.modules['slowapi.limiter'] = Mock()
|
|
sys.modules['web3'] = Mock()
|
|
|
|
# Mock aitbc_crypto only when package import is unavailable
|
|
try:
|
|
import aitbc_crypto as _aitbc_crypto_pkg # type: ignore
|
|
except Exception:
|
|
_aitbc_crypto_pkg = Mock()
|
|
sys.modules['aitbc_crypto'] = _aitbc_crypto_pkg
|
|
|
|
# Mock aitbc_crypto functions
|
|
def mock_encrypt_data(data, key):
|
|
return f"encrypted_{data}"
|
|
|
|
def mock_decrypt_data(data, key):
|
|
return data.replace("encrypted_", "")
|
|
|
|
def mock_generate_viewing_key():
|
|
return "test_viewing_key"
|
|
|
|
_aitbc_crypto_pkg.encrypt_data = mock_encrypt_data
|
|
_aitbc_crypto_pkg.decrypt_data = mock_decrypt_data
|
|
_aitbc_crypto_pkg.generate_viewing_key = mock_generate_viewing_key
|
|
|
|
# Provide minimal submodules used by coordinator imports
|
|
signing_mod = Mock()
|
|
|
|
class _ReceiptSigner:
|
|
def verify_receipt(self, payload, signature):
|
|
return True
|
|
|
|
signing_mod.ReceiptSigner = _ReceiptSigner
|
|
sys.modules['aitbc_crypto.signing'] = signing_mod
|
|
|
|
|
|
@pytest.fixture
|
|
def coordinator_client():
|
|
"""Create a test client for coordinator API"""
|
|
from fastapi.testclient import TestClient
|
|
|
|
try:
|
|
# Import the coordinator app specifically
|
|
import sys
|
|
# Ensure coordinator-api path is first
|
|
coordinator_path = str(project_root / "apps" / "coordinator-api" / "src")
|
|
if coordinator_path not in sys.path[:1]:
|
|
sys.path.insert(0, coordinator_path)
|
|
|
|
from app.main import app as coordinator_app
|
|
print("✅ Using real coordinator API client")
|
|
return TestClient(coordinator_app)
|
|
except ImportError as e:
|
|
# Create a mock client if imports fail
|
|
print(f"Warning: Using mock coordinator_client due to import error: {e}")
|
|
|
|
# Use new MockResponse from aitbc.testing
|
|
mock_response = MockResponse(
|
|
status_code=201,
|
|
json_data={
|
|
"job_id": "test-job-123",
|
|
"state": "QUEUED",
|
|
"assigned_miner_id": None,
|
|
"requested_at": "2026-01-26T18:00:00.000000",
|
|
"expires_at": "2026-01-26T18:15:00.000000",
|
|
"error": None,
|
|
"payment_id": "test-payment-456",
|
|
"payment_status": "escrowed"
|
|
}
|
|
)
|
|
|
|
mock_client = Mock()
|
|
mock_client.post.return_value = mock_response
|
|
|
|
# Use TestDataGenerator for consistent test data
|
|
mock_get_response = MockResponse(
|
|
status_code=200,
|
|
json_data={
|
|
"job_id": "test-job-123",
|
|
"state": "QUEUED",
|
|
"assigned_miner_id": None,
|
|
"requested_at": "2026-01-26T18:00:00.000000",
|
|
"expires_at": "2026-01-26T18:15:00.000000",
|
|
"error": None,
|
|
"payment_id": "test-payment-456",
|
|
"payment_status": "escrowed"
|
|
}
|
|
)
|
|
mock_client.get.return_value = mock_get_response
|
|
|
|
# Mock for receipts
|
|
mock_receipts_response = MockResponse(
|
|
status_code=200,
|
|
json_data={
|
|
"items": [],
|
|
"total": 0
|
|
}
|
|
)
|
|
|
|
def mock_get_side_effect(url, headers=None):
|
|
if "receipts" in url:
|
|
return mock_receipts_response
|
|
elif "/docs" in url or "/openapi.json" in url:
|
|
return MockResponse(status_code=200, text='{"openapi": "3.0.0", "info": {"title": "AITBC Coordinator API"}}')
|
|
elif "/v1/health" in url:
|
|
return MockResponse(status_code=200, json_data={"status": "ok", "env": "dev"})
|
|
elif "/payment" in url:
|
|
return MockResponse(
|
|
status_code=200,
|
|
json_data={
|
|
"job_id": "test-job-123",
|
|
"payment_id": "test-payment-456",
|
|
"amount": 100,
|
|
"currency": "AITBC",
|
|
"status": "escrowed",
|
|
"payment_method": "aitbc_token",
|
|
"escrow_address": "test-escrow-id",
|
|
"created_at": "2026-01-26T18:00:00.000000",
|
|
"updated_at": "2026-01-26T18:00:00.000000"
|
|
}
|
|
)
|
|
return mock_get_response
|
|
|
|
mock_client.get.side_effect = mock_get_side_effect
|
|
mock_client.patch.return_value = MockResponse(status_code=200, json_data={"status": "updated"})
|
|
return mock_client
|
|
|
|
|
|
@pytest.fixture
|
|
def wallet_client():
|
|
"""Create a test client for wallet daemon"""
|
|
from fastapi.testclient import TestClient
|
|
try:
|
|
from apps.wallet_daemon.src.app.main import app
|
|
return TestClient(app)
|
|
except ImportError:
|
|
# Create a mock client if imports fail
|
|
from unittest.mock import Mock
|
|
mock_client = Mock()
|
|
|
|
# Mock response objects
|
|
mock_response = Mock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {
|
|
"id": "wallet-123",
|
|
"address": "0x1234567890abcdef",
|
|
"balance": "1000.0"
|
|
}
|
|
|
|
mock_client.post.return_value = mock_response
|
|
mock_client.get.return_value = mock_response
|
|
mock_client.patch.return_value = mock_response
|
|
return mock_client
|
|
|
|
|
|
@pytest.fixture
|
|
def blockchain_client():
|
|
"""Create a test client for blockchain node"""
|
|
from fastapi.testclient import TestClient
|
|
try:
|
|
from apps.blockchain_node.src.aitbc_chain.node import BlockchainNode
|
|
node = BlockchainNode()
|
|
return TestClient(node.app)
|
|
except ImportError:
|
|
# Create a mock client if imports fail
|
|
from unittest.mock import Mock
|
|
mock_client = Mock()
|
|
|
|
# Mock response objects
|
|
mock_response = Mock()
|
|
mock_response.status_code = 200
|
|
mock_response.json.return_value = {
|
|
"block_number": 100,
|
|
"hash": "0xblock123",
|
|
"transaction_hash": "0xtx456"
|
|
}
|
|
|
|
mock_client.post.return_value = mock_response
|
|
mock_client.get.return_value = mock_response
|
|
return mock_client
|
|
|
|
|
|
@pytest.fixture
|
|
def marketplace_client():
|
|
"""Create a test client for marketplace"""
|
|
from fastapi.testclient import TestClient
|
|
try:
|
|
from apps.marketplace.src.app.main import app
|
|
return TestClient(app)
|
|
except ImportError:
|
|
# Create a mock client if imports fail
|
|
from unittest.mock import Mock
|
|
mock_client = Mock()
|
|
|
|
# Mock response objects
|
|
mock_response = Mock()
|
|
mock_response.status_code = 201
|
|
mock_response.json.return_value = {
|
|
"id": "service-123",
|
|
"name": "Test Service",
|
|
"status": "active"
|
|
}
|
|
|
|
mock_client.post.return_value = mock_response
|
|
mock_client.get.return_value = Mock(
|
|
status_code=200,
|
|
json=lambda: {"items": [], "total": 0}
|
|
)
|
|
return mock_client
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_tenant():
|
|
"""Create a sample tenant for testing using TestDataGenerator"""
|
|
return TestDataGenerator.generate_user_data(
|
|
id="tenant-123",
|
|
first_name="Test",
|
|
last_name="Tenant",
|
|
is_active=True
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def sample_job_data():
|
|
"""Sample job creation data using TestDataGenerator"""
|
|
return {
|
|
"job_type": "ai_inference",
|
|
"parameters": {
|
|
"model": "gpt-4",
|
|
"prompt": "Test prompt",
|
|
"max_tokens": 100,
|
|
"temperature": 0.7
|
|
},
|
|
"priority": "normal",
|
|
"timeout": 300
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_db():
|
|
"""Create a mock database for testing"""
|
|
return MockDatabase()
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_cache():
|
|
"""Create a mock cache for testing"""
|
|
return MockCache()
|
|
|
|
|
|
@pytest.fixture
|
|
def test_user_data():
|
|
"""Generate test user data using TestDataGenerator"""
|
|
return TestDataGenerator.generate_user_data()
|
|
|
|
|
|
@pytest.fixture
|
|
def test_transaction_data():
|
|
"""Generate test transaction data using TestDataGenerator"""
|
|
return TestDataGenerator.generate_transaction_data()
|
|
|
|
|
|
@pytest.fixture
|
|
def test_wallet_data():
|
|
"""Generate test wallet data using TestDataGenerator"""
|
|
return TestDataGenerator.generate_wallet_data()
|
|
|
|
|
|
@pytest.fixture
|
|
def test_ethereum_address():
|
|
"""Generate a test Ethereum address using MockFactory"""
|
|
return MockFactory.generate_ethereum_address()
|
|
|
|
|
|
# Training environment setup fixtures
|
|
@pytest.fixture(scope="session")
|
|
def training_env():
|
|
"""
|
|
Session-scoped fixture for training environment setup.
|
|
Sets up the training environment once per test session.
|
|
"""
|
|
env = TrainingEnvironment()
|
|
try:
|
|
# Check prerequisites only, don't do full setup in tests
|
|
env.check_prerequisites()
|
|
yield env
|
|
except TrainingSetupError as e:
|
|
pytest.skip(f"Training prerequisites not met: {e}")
|
|
|
|
|
|
@pytest.fixture
|
|
def training_env_mock():
|
|
"""
|
|
Function-scoped fixture for mocked training environment.
|
|
Uses mocked subprocess calls for faster, isolated tests.
|
|
"""
|
|
from unittest.mock import patch, MagicMock
|
|
|
|
env = TrainingEnvironment()
|
|
|
|
# Mock subprocess.run to avoid actual CLI calls
|
|
def mock_subprocess_run(*args, **kwargs):
|
|
mock_result = MagicMock()
|
|
mock_result.returncode = 0
|
|
mock_result.stdout = "success"
|
|
mock_result.stderr = ""
|
|
return mock_result
|
|
|
|
with patch('subprocess.run', side_effect=mock_subprocess_run):
|
|
yield env
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_faucet_response():
|
|
"""Mock faucet API response for testing"""
|
|
return {
|
|
"status": "success",
|
|
"address": "ait1testaddress",
|
|
"amount": 1000,
|
|
"transaction_id": "tx_test123",
|
|
"timestamp": "2026-05-05T12:00:00"
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def training_stage_data():
|
|
"""Sample training stage data for testing"""
|
|
return {
|
|
"stage": "stage1_foundation",
|
|
"agent_type": "general",
|
|
"training_data": {
|
|
"prerequisites": {
|
|
"description": "Test prerequisites",
|
|
"setup_script": "/opt/aitbc/scripts/training/setup_training_env.sh",
|
|
"requirements": ["AITBC node running", "Funded accounts"]
|
|
},
|
|
"operations": [
|
|
{
|
|
"operation": "wallet_create",
|
|
"parameters": {"name": "test-wallet"},
|
|
"expected_result": {"status": "success"}
|
|
}
|
|
]
|
|
}
|
|
}
|