Files
mev-beta/orig/test/integration_arbitrum_test.go
Administrator c54c569f30 refactor: move all remaining files to orig/ directory
Completed clean root directory structure:
- Root now contains only: .git, .env, docs/, orig/
- Moved all remaining files and directories to orig/:
  - Config files (.claude, .dockerignore, .drone.yml, etc.)
  - All .env variants (except active .env)
  - Git config (.gitconfig, .github, .gitignore, etc.)
  - Tool configs (.golangci.yml, .revive.toml, etc.)
  - Documentation (*.md files, @prompts)
  - Build files (Dockerfiles, Makefile, go.mod, go.sum)
  - Docker compose files
  - All source directories (scripts, tests, tools, etc.)
  - Runtime directories (logs, monitoring, reports)
  - Dependency files (node_modules, lib, cache)
  - Special files (--delete)

- Removed empty runtime directories (bin/, data/)

V2 structure is now clean:
- docs/planning/ - V2 planning documents
- orig/ - Complete V1 codebase preserved
- .env - Active environment config (not in git)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-10 10:53:05 +01:00

875 lines
26 KiB
Go

//go:build integration && legacy && forked
// +build integration,legacy,forked
package test_main
import (
"context"
"fmt"
"math/big"
"os"
"strings"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/fraktal/mev-beta/internal/logger"
"github.com/fraktal/mev-beta/pkg/arbitrum"
"github.com/fraktal/mev-beta/pkg/events"
"github.com/fraktal/mev-beta/pkg/oracle"
)
// IntegrationTestSuite manages live Arbitrum integration testing
type IntegrationTestSuite struct {
rpcClient *rpc.Client
ethClient *ethclient.Client
l2Parser *arbitrum.ArbitrumL2Parser
eventParser *events.EventParser
logger *logger.Logger
oracle *oracle.PriceOracle
rpcEndpoint string
testConfig *IntegrationConfig
}
// IntegrationConfig contains configuration for integration tests
type IntegrationConfig struct {
RPCEndpoint string `json:"rpc_endpoint"`
WSEndpoint string `json:"ws_endpoint"`
TestTimeout time.Duration `json:"test_timeout"`
MaxBlocksToTest int `json:"max_blocks_to_test"`
MinBlockNumber uint64 `json:"min_block_number"`
MaxBlockNumber uint64 `json:"max_block_number"`
KnownTxHashes []string `json:"known_tx_hashes"`
HighValueTxHashes []string `json:"high_value_tx_hashes"`
MEVTxHashes []string `json:"mev_tx_hashes"`
EnableLiveValidation bool `json:"enable_live_validation"`
ValidateGasEstimates bool `json:"validate_gas_estimates"`
ValidatePriceData bool `json:"validate_price_data"`
}
// LiveTransactionData represents validated transaction data from Arbitrum
type LiveTransactionData struct {
Hash common.Hash `json:"hash"`
BlockNumber uint64 `json:"block_number"`
BlockHash common.Hash `json:"block_hash"`
TransactionIndex uint `json:"transaction_index"`
From common.Address `json:"from"`
To *common.Address `json:"to"`
Value *big.Int `json:"value"`
GasLimit uint64 `json:"gas_limit"`
GasUsed uint64 `json:"gas_used"`
GasPrice *big.Int `json:"gas_price"`
Data []byte `json:"data"`
Logs []*types.Log `json:"logs"`
Status uint64 `json:"status"`
// Parsed DEX data
ParsedDEX *arbitrum.DEXTransaction `json:"parsed_dex,omitempty"`
ParsedEvents []*events.Event `json:"parsed_events,omitempty"`
ValidationErrors []string `json:"validation_errors,omitempty"`
}
func NewIntegrationTestSuite() *IntegrationTestSuite {
config := &IntegrationConfig{
RPCEndpoint: getEnvOrDefault("ARBITRUM_RPC_ENDPOINT", "https://arb1.arbitrum.io/rpc"),
WSEndpoint: getEnvOrDefault("ARBITRUM_WS_ENDPOINT", "wss://arb1.arbitrum.io/ws"),
TestTimeout: 30 * time.Second,
MaxBlocksToTest: 10,
MinBlockNumber: 150000000, // Recent Arbitrum blocks
MaxBlockNumber: 0, // Will be set to latest
EnableLiveValidation: getEnvOrDefault("ENABLE_LIVE_VALIDATION", "false") == "true",
ValidateGasEstimates: true,
ValidatePriceData: false, // Requires price oracle setup
// Known high-activity DEX transactions for validation
KnownTxHashes: []string{
// These would be real Arbitrum transaction hashes
"0x1234567890123456789012345678901234567890123456789012345678901234",
},
HighValueTxHashes: []string{
// High-value swap transactions
"0x2345678901234567890123456789012345678901234567890123456789012345",
},
MEVTxHashes: []string{
// Known MEV transactions
"0x3456789012345678901234567890123456789012345678901234567890123456",
},
}
return &IntegrationTestSuite{
testConfig: config,
}
}
func TestArbitrumIntegration(t *testing.T) {
if testing.Short() {
t.Skip("Skipping integration tests in short mode")
}
// Check if live testing is enabled
if os.Getenv("ENABLE_LIVE_TESTING") != "true" {
t.Skip("Live integration testing disabled. Set ENABLE_LIVE_TESTING=true to run")
}
suite := NewIntegrationTestSuite()
// Setup test suite
t.Run("Setup", func(t *testing.T) {
suite.setupIntegrationTest(t)
})
// Test RPC connectivity
t.Run("RPC_Connectivity", func(t *testing.T) {
suite.testRPCConnectivity(t)
})
// Test block retrieval and parsing
t.Run("Block_Retrieval", func(t *testing.T) {
suite.testBlockRetrieval(t)
})
// Test transaction parsing with live data
t.Run("Live_Transaction_Parsing", func(t *testing.T) {
suite.testLiveTransactionParsing(t)
})
// Test known high-value transactions
t.Run("High_Value_Transactions", func(t *testing.T) {
suite.testHighValueTransactions(t)
})
// Test MEV transaction detection
t.Run("MEV_Detection", func(t *testing.T) {
suite.testMEVDetection(t)
})
// Test parser accuracy with known transactions
t.Run("Parser_Accuracy", func(t *testing.T) {
suite.testParserAccuracy(t)
})
// Test real-time block monitoring
t.Run("Real_Time_Monitoring", func(t *testing.T) {
suite.testRealTimeMonitoring(t)
})
// Performance test with live data
t.Run("Live_Performance", func(t *testing.T) {
suite.testLivePerformance(t)
})
// Cleanup
t.Run("Cleanup", func(t *testing.T) {
suite.cleanup(t)
})
}
func (suite *IntegrationTestSuite) setupIntegrationTest(t *testing.T) {
// Setup logger
suite.logger = logger.NewLogger(logger.Config{
Level: "info",
Format: "json",
})
// Create RPC client
var err error
suite.rpcClient, err = rpc.Dial(suite.testConfig.RPCEndpoint)
require.NoError(t, err, "Failed to connect to Arbitrum RPC")
// Create Ethereum client
suite.ethClient, err = ethclient.Dial(suite.testConfig.RPCEndpoint)
require.NoError(t, err, "Failed to create Ethereum client")
// Setup oracle (mock for integration tests)
suite.oracle, err = oracle.NewPriceOracle(&oracle.Config{
Providers: []oracle.Provider{
{Name: "mock", Type: "mock"},
},
}, suite.logger)
require.NoError(t, err, "Failed to create price oracle")
// Create parsers
suite.l2Parser, err = arbitrum.NewArbitrumL2Parser(suite.testConfig.RPCEndpoint, suite.logger, suite.oracle)
require.NoError(t, err, "Failed to create L2 parser")
suite.eventParser = events.NewEventParser()
// Get latest block number
if suite.testConfig.MaxBlockNumber == 0 {
latestHeader, err := suite.ethClient.HeaderByNumber(context.Background(), nil)
require.NoError(t, err, "Failed to get latest block header")
suite.testConfig.MaxBlockNumber = latestHeader.Number.Uint64()
}
t.Logf("Integration test setup complete. Testing blocks %d to %d",
suite.testConfig.MinBlockNumber, suite.testConfig.MaxBlockNumber)
}
func (suite *IntegrationTestSuite) testRPCConnectivity(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), suite.testConfig.TestTimeout)
defer cancel()
// Test basic RPC call
var blockNumber string
err := suite.rpcClient.CallContext(ctx, &blockNumber, "eth_blockNumber")
require.NoError(t, err, "Failed to call eth_blockNumber")
assert.NotEmpty(t, blockNumber, "Block number should not be empty")
// Test eth client
latestBlock, err := suite.ethClient.BlockNumber(ctx)
require.NoError(t, err, "Failed to get latest block number")
assert.Greater(t, latestBlock, uint64(0), "Latest block should be greater than 0")
// Test WebSocket connection if available
if suite.testConfig.WSEndpoint != "" {
wsClient, err := rpc.Dial(suite.testConfig.WSEndpoint)
if err == nil {
defer wsClient.Close()
var wsBlockNumber string
err = wsClient.CallContext(ctx, &wsBlockNumber, "eth_blockNumber")
assert.NoError(t, err, "WebSocket RPC call should succeed")
} else {
t.Logf("WebSocket connection failed (optional): %v", err)
}
}
t.Logf("RPC connectivity test passed. Latest block: %d", latestBlock)
}
func (suite *IntegrationTestSuite) testBlockRetrieval(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), suite.testConfig.TestTimeout)
defer cancel()
// Test retrieving recent blocks
testBlocks := []uint64{
suite.testConfig.MaxBlockNumber - 1,
suite.testConfig.MaxBlockNumber - 2,
suite.testConfig.MaxBlockNumber - 10,
}
for _, blockNumber := range testBlocks {
t.Run(fmt.Sprintf("Block_%d", blockNumber), func(t *testing.T) {
// Retrieve block using eth client
block, err := suite.ethClient.BlockByNumber(ctx, big.NewInt(int64(blockNumber)))
require.NoError(t, err, "Failed to retrieve block %d", blockNumber)
assert.NotNil(t, block, "Block should not be nil")
// Validate block structure
assert.Equal(t, blockNumber, block.Number().Uint64(), "Block number mismatch")
assert.NotEqual(t, common.Hash{}, block.Hash(), "Block hash should not be empty")
assert.NotNil(t, block.Transactions(), "Block transactions should not be nil")
// Test parsing block transactions
txCount := len(block.Transactions())
if txCount > 0 {
dexTxCount := 0
for _, tx := range block.Transactions() {
if suite.eventParser.IsDEXInteraction(tx) {
dexTxCount++
}
}
t.Logf("Block %d: %d transactions, %d DEX interactions",
blockNumber, txCount, dexTxCount)
}
})
}
}
func (suite *IntegrationTestSuite) testLiveTransactionParsing(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), suite.testConfig.TestTimeout*2)
defer cancel()
// Find recent blocks with DEX activity
var testTransactions []*LiveTransactionData
for i := 0; i < suite.testConfig.MaxBlocksToTest && len(testTransactions) < 20; i++ {
blockNumber := suite.testConfig.MaxBlockNumber - uint64(i)
block, err := suite.ethClient.BlockByNumber(ctx, big.NewInt(int64(blockNumber)))
if err != nil {
t.Logf("Failed to retrieve block %d: %v", blockNumber, err)
continue
}
for _, tx := range block.Transactions() {
if suite.eventParser.IsDEXInteraction(tx) {
// Get transaction receipt
receipt, err := suite.ethClient.TransactionReceipt(ctx, tx.Hash())
if err != nil {
continue
}
// Create live transaction data
liveData := &LiveTransactionData{
Hash: tx.Hash(),
BlockNumber: blockNumber,
BlockHash: block.Hash(),
TransactionIndex: receipt.TransactionIndex,
From: getSender(tx),
To: tx.To(),
Value: tx.Value(),
GasLimit: tx.Gas(),
GasUsed: receipt.GasUsed,
GasPrice: tx.GasPrice(),
Data: tx.Data(),
Logs: receipt.Logs,
Status: receipt.Status,
}
testTransactions = append(testTransactions, liveData)
if len(testTransactions) >= 20 {
break
}
}
}
}
require.Greater(t, len(testTransactions), 0, "No DEX transactions found in recent blocks")
t.Logf("Testing parsing of %d live DEX transactions", len(testTransactions))
// Parse transactions and validate
successCount := 0
errorCount := 0
for i, liveData := range testTransactions {
t.Run(fmt.Sprintf("Tx_%s", liveData.Hash.Hex()[:10]), func(t *testing.T) {
// Convert to RawL2Transaction format
rawTx := arbitrum.RawL2Transaction{
Hash: liveData.Hash.Hex(),
From: liveData.From.Hex(),
To: liveData.To.Hex(),
Value: liveData.Value.String(),
Input: common.Bytes2Hex(liveData.Data),
}
// Test L2 parser
parsed, err := suite.l2Parser.ParseDEXTransaction(rawTx)
if err != nil {
liveData.ValidationErrors = append(liveData.ValidationErrors,
fmt.Sprintf("L2 parser error: %v", err))
errorCount++
} else if parsed != nil {
liveData.ParsedDEX = parsed
successCount++
// Validate parsed data
suite.validateParsedTransaction(t, liveData, parsed)
}
// Test event parser
tx := types.NewTransaction(0, *liveData.To, liveData.Value, liveData.GasLimit,
liveData.GasPrice, liveData.Data)
parsedEvents, err := suite.eventParser.ParseTransaction(tx, liveData.BlockNumber, uint64(time.Now().Unix()))
if err != nil {
liveData.ValidationErrors = append(liveData.ValidationErrors,
fmt.Sprintf("Event parser error: %v", err))
} else {
liveData.ParsedEvents = parsedEvents
}
})
// Progress logging
if (i+1)%5 == 0 {
t.Logf("Progress: %d/%d transactions processed", i+1, len(testTransactions))
}
}
successRate := float64(successCount) / float64(len(testTransactions)) * 100
t.Logf("Live transaction parsing: %d/%d successful (%.2f%%)",
successCount, len(testTransactions), successRate)
// Validate success rate
assert.Greater(t, successRate, 80.0,
"Parser success rate (%.2f%%) should be above 80%%", successRate)
}
func (suite *IntegrationTestSuite) testHighValueTransactions(t *testing.T) {
if len(suite.testConfig.HighValueTxHashes) == 0 {
t.Skip("No high-value transaction hashes configured")
}
ctx, cancel := context.WithTimeout(context.Background(), suite.testConfig.TestTimeout)
defer cancel()
for _, txHashStr := range suite.testConfig.HighValueTxHashes {
t.Run(fmt.Sprintf("HighValue_%s", txHashStr[:10]), func(t *testing.T) {
txHash := common.HexToHash(txHashStr)
// Get transaction
tx, isPending, err := suite.ethClient.TransactionByHash(ctx, txHash)
if err != nil {
t.Skipf("Failed to retrieve transaction %s: %v", txHashStr, err)
return
}
assert.False(t, isPending, "Transaction should not be pending")
// Get receipt
receipt, err := suite.ethClient.TransactionReceipt(ctx, txHash)
require.NoError(t, err, "Failed to retrieve transaction receipt")
// Validate transaction succeeded
assert.Equal(t, uint64(1), receipt.Status, "High-value transaction should have succeeded")
// Test parsing
if suite.eventParser.IsDEXInteraction(tx) {
rawTx := arbitrum.RawL2Transaction{
Hash: tx.Hash().Hex(),
From: getSender(tx).Hex(),
To: tx.To().Hex(),
Value: tx.Value().String(),
Input: common.Bytes2Hex(tx.Data()),
}
parsed, err := suite.l2Parser.ParseDEXTransaction(rawTx)
assert.NoError(t, err, "High-value transaction should parse successfully")
assert.NotNil(t, parsed, "Parsed result should not be nil")
if parsed != nil {
t.Logf("High-value transaction: Protocol=%s, Function=%s, Value=%s ETH",
parsed.Protocol, parsed.FunctionName,
new(big.Float).Quo(new(big.Float).SetInt(parsed.Value), big.NewFloat(1e18)).String())
}
}
})
}
}
func (suite *IntegrationTestSuite) testMEVDetection(t *testing.T) {
if len(suite.testConfig.MEVTxHashes) == 0 {
t.Skip("No MEV transaction hashes configured")
}
ctx, cancel := context.WithTimeout(context.Background(), suite.testConfig.TestTimeout)
defer cancel()
for _, txHashStr := range suite.testConfig.MEVTxHashes {
t.Run(fmt.Sprintf("MEV_%s", txHashStr[:10]), func(t *testing.T) {
txHash := common.HexToHash(txHashStr)
// Get transaction
tx, isPending, err := suite.ethClient.TransactionByHash(ctx, txHash)
if err != nil {
t.Skipf("Failed to retrieve MEV transaction %s: %v", txHashStr, err)
return
}
assert.False(t, isPending, "Transaction should not be pending")
// Get receipt
receipt, err := suite.ethClient.TransactionReceipt(ctx, txHash)
require.NoError(t, err, "Failed to retrieve transaction receipt")
// Test parsing
if suite.eventParser.IsDEXInteraction(tx) {
rawTx := arbitrum.RawL2Transaction{
Hash: tx.Hash().Hex(),
From: getSender(tx).Hex(),
To: tx.To().Hex(),
Value: tx.Value().String(),
Input: common.Bytes2Hex(tx.Data()),
}
parsed, err := suite.l2Parser.ParseDEXTransaction(rawTx)
if err == nil && parsed != nil {
// Analyze for MEV characteristics
mevScore := suite.calculateMEVScore(tx, receipt, parsed)
t.Logf("MEV transaction analysis: Score=%.2f, Protocol=%s, GasPrice=%s gwei",
mevScore, parsed.Protocol,
new(big.Float).Quo(new(big.Float).SetInt(tx.GasPrice()), big.NewFloat(1e9)).String())
// MEV transactions typically have high gas prices or specific patterns
assert.True(t, mevScore > 0.5 || tx.GasPrice().Cmp(big.NewInt(1e10)) > 0,
"Transaction should show MEV characteristics")
}
}
})
}
}
func (suite *IntegrationTestSuite) testParserAccuracy(t *testing.T) {
// Test parser accuracy by comparing against known on-chain data
ctx, cancel := context.WithTimeout(context.Background(), suite.testConfig.TestTimeout)
defer cancel()
// Find blocks with diverse DEX activity
accuracyTests := []struct {
name string
blockNumber uint64
expectedTxs int
}{
{"Recent_High_Activity", suite.testConfig.MaxBlockNumber - 5, 10},
{"Recent_Medium_Activity", suite.testConfig.MaxBlockNumber - 15, 5},
{"Earlier_Block", suite.testConfig.MaxBlockNumber - 100, 3},
}
for _, test := range accuracyTests {
t.Run(test.name, func(t *testing.T) {
block, err := suite.ethClient.BlockByNumber(ctx, big.NewInt(int64(test.blockNumber)))
if err != nil {
t.Skipf("Failed to retrieve block %d: %v", test.blockNumber, err)
return
}
dexTransactions := []*types.Transaction{}
for _, tx := range block.Transactions() {
if suite.eventParser.IsDEXInteraction(tx) {
dexTransactions = append(dexTransactions, tx)
}
}
if len(dexTransactions) == 0 {
t.Skip("No DEX transactions found in block")
return
}
// Test parsing accuracy
correctParses := 0
totalParses := 0
for _, tx := range dexTransactions[:minInt(len(dexTransactions), test.expectedTxs)] {
rawTx := arbitrum.RawL2Transaction{
Hash: tx.Hash().Hex(),
From: getSender(tx).Hex(),
To: tx.To().Hex(),
Value: tx.Value().String(),
Input: common.Bytes2Hex(tx.Data()),
}
parsed, err := suite.l2Parser.ParseDEXTransaction(rawTx)
totalParses++
if err == nil && parsed != nil {
// Validate against on-chain data
if suite.validateAgainstOnChainData(ctx, tx, parsed) {
correctParses++
}
}
}
accuracy := float64(correctParses) / float64(totalParses) * 100
t.Logf("Parser accuracy for %s: %d/%d correct (%.2f%%)",
test.name, correctParses, totalParses, accuracy)
// Require high accuracy
assert.Greater(t, accuracy, 85.0,
"Parser accuracy (%.2f%%) should be above 85%%", accuracy)
})
}
}
func (suite *IntegrationTestSuite) testRealTimeMonitoring(t *testing.T) {
if suite.testConfig.WSEndpoint == "" {
t.Skip("WebSocket endpoint not configured")
}
// Test real-time block monitoring (short duration for testing)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
wsClient, err := rpc.Dial(suite.testConfig.WSEndpoint)
if err != nil {
t.Skipf("Failed to connect to WebSocket: %v", err)
return
}
defer wsClient.Close()
// Subscribe to new heads
ch := make(chan *types.Header)
sub, err := suite.ethClient.SubscribeNewHead(ctx, ch)
if err != nil {
t.Skipf("Failed to subscribe to new heads: %v", err)
return
}
defer sub.Unsubscribe()
blocksReceived := 0
dexTransactionsFound := 0
t.Log("Starting real-time monitoring...")
for {
select {
case err := <-sub.Err():
t.Logf("Subscription error: %v", err)
return
case header := <-ch:
blocksReceived++
t.Logf("Received new block: %d (hash: %s)",
header.Number.Uint64(), header.Hash().Hex()[:10])
// Get full block and check for DEX transactions
block, err := suite.ethClient.BlockByHash(ctx, header.Hash())
if err != nil {
t.Logf("Failed to retrieve block: %v", err)
continue
}
dexTxCount := 0
for _, tx := range block.Transactions() {
if suite.eventParser.IsDEXInteraction(tx) {
dexTxCount++
}
}
if dexTxCount > 0 {
dexTransactionsFound += dexTxCount
t.Logf("Block %d: %d DEX transactions found",
header.Number.Uint64(), dexTxCount)
}
case <-ctx.Done():
t.Logf("Real-time monitoring complete: %d blocks, %d DEX transactions",
blocksReceived, dexTransactionsFound)
return
}
}
}
func (suite *IntegrationTestSuite) testLivePerformance(t *testing.T) {
// Performance test with live Arbitrum data
ctx, cancel := context.WithTimeout(context.Background(), suite.testConfig.TestTimeout)
defer cancel()
// Get recent high-activity block
block, err := suite.ethClient.BlockByNumber(ctx,
big.NewInt(int64(suite.testConfig.MaxBlockNumber-1)))
require.NoError(t, err, "Failed to retrieve block for performance test")
dexTransactions := []*types.Transaction{}
for _, tx := range block.Transactions() {
if suite.eventParser.IsDEXInteraction(tx) {
dexTransactions = append(dexTransactions, tx)
if len(dexTransactions) >= 50 { // Limit for performance test
break
}
}
}
if len(dexTransactions) == 0 {
t.Skip("No DEX transactions found for performance test")
}
t.Logf("Performance testing with %d live DEX transactions", len(dexTransactions))
// Measure parsing performance
startTime := time.Now()
successCount := 0
for _, tx := range dexTransactions {
rawTx := arbitrum.RawL2Transaction{
Hash: tx.Hash().Hex(),
From: getSender(tx).Hex(),
To: tx.To().Hex(),
Value: tx.Value().String(),
Input: common.Bytes2Hex(tx.Data()),
}
_, err := suite.l2Parser.ParseDEXTransaction(rawTx)
if err == nil {
successCount++
}
}
totalTime := time.Since(startTime)
throughput := float64(len(dexTransactions)) / totalTime.Seconds()
t.Logf("Live performance: %d transactions in %v (%.2f tx/s), success=%d/%d",
len(dexTransactions), totalTime, throughput, successCount, len(dexTransactions))
// Validate performance meets requirements
assert.Greater(t, throughput, 100.0,
"Live throughput (%.2f tx/s) should be above 100 tx/s", throughput)
assert.Greater(t, float64(successCount)/float64(len(dexTransactions))*100, 80.0,
"Live parsing success rate should be above 80%%")
}
func (suite *IntegrationTestSuite) cleanup(t *testing.T) {
if suite.l2Parser != nil {
suite.l2Parser.Close()
}
if suite.rpcClient != nil {
suite.rpcClient.Close()
}
if suite.ethClient != nil {
suite.ethClient.Close()
}
t.Log("Integration test cleanup complete")
}
// Helper functions
func (suite *IntegrationTestSuite) validateParsedTransaction(t *testing.T, liveData *LiveTransactionData, parsed *arbitrum.DEXTransaction) {
// Validate parsed data against live transaction data
assert.Equal(t, liveData.Hash.Hex(), parsed.Hash,
"Transaction hash should match")
if parsed.Value != nil {
assert.Equal(t, liveData.Value, parsed.Value,
"Transaction value should match")
}
// Validate protocol identification
assert.NotEmpty(t, parsed.Protocol, "Protocol should be identified")
assert.NotEmpty(t, parsed.FunctionName, "Function name should be identified")
// Validate amounts if present
if parsed.SwapDetails != nil && parsed.SwapDetails.AmountIn != nil {
assert.True(t, parsed.SwapDetails.AmountIn.Cmp(big.NewInt(0)) > 0,
"Amount in should be positive")
}
}
func (suite *IntegrationTestSuite) calculateMEVScore(tx *types.Transaction, receipt *types.Receipt, parsed *arbitrum.DEXTransaction) float64 {
score := 0.0
// High gas price indicates MEV
gasPrice := new(big.Float).SetInt(tx.GasPrice())
gasPriceGwei := new(big.Float).Quo(gasPrice, big.NewFloat(1e9))
gasPriceFloat, _ := gasPriceGwei.Float64()
if gasPriceFloat > 50 {
score += 0.3
}
if gasPriceFloat > 100 {
score += 0.2
}
// Large transaction values indicate potential MEV
if tx.Value().Cmp(big.NewInt(1e18)) > 0 { // > 1 ETH
score += 0.2
}
// Complex function calls (multicall, aggregators)
if strings.Contains(parsed.FunctionName, "multicall") ||
strings.Contains(parsed.Protocol, "1Inch") {
score += 0.3
}
return score
}
func (suite *IntegrationTestSuite) validateAgainstOnChainData(ctx context.Context, tx *types.Transaction, parsed *arbitrum.DEXTransaction) bool {
// This would implement validation against actual on-chain data
// For now, perform basic consistency checks
if parsed.Value == nil || parsed.Value.Cmp(tx.Value()) != 0 {
return false
}
if parsed.Hash != tx.Hash().Hex() {
return false
}
// Additional validation would compare swap amounts, tokens, etc.
// against actual transaction logs and state changes
return true
}
func getSender(tx *types.Transaction) common.Address {
// This would typically require signature recovery
// For integration tests, we'll use a placeholder or skip sender validation
return common.Address{}
}
func getEnvOrDefault(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func minInt(a, b int) int {
if a < b {
return a
}
return b
}
// Additional test for API rate limiting and error handling
func TestRPCRateLimiting(t *testing.T) {
if testing.Short() || os.Getenv("ENABLE_LIVE_TESTING") != "true" {
t.Skip("Skipping RPC rate limiting test")
}
suite := NewIntegrationTestSuite()
suite.setupIntegrationTest(t)
defer suite.cleanup(t)
// Test rapid consecutive calls
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
successCount := 0
rateLimitCount := 0
for i := 0; i < 100; i++ {
var blockNumber string
err := suite.rpcClient.CallContext(ctx, &blockNumber, "eth_blockNumber")
if err != nil {
if strings.Contains(err.Error(), "rate limit") ||
strings.Contains(err.Error(), "429") {
rateLimitCount++
} else {
t.Logf("Unexpected error: %v", err)
}
} else {
successCount++
}
// Small delay to avoid overwhelming the endpoint
time.Sleep(10 * time.Millisecond)
}
t.Logf("Rate limiting test: %d successful, %d rate limited",
successCount, rateLimitCount)
// Should handle rate limiting gracefully
assert.Greater(t, successCount, 50, "Should have some successful calls")
}
// Test for handling network issues
func TestNetworkResilience(t *testing.T) {
if testing.Short() || os.Getenv("ENABLE_LIVE_TESTING") != "true" {
t.Skip("Skipping network resilience test")
}
// Test with invalid endpoint
invalidSuite := &IntegrationTestSuite{
testConfig: &IntegrationConfig{
RPCEndpoint: "https://invalid-endpoint.example.com",
TestTimeout: 5 * time.Second,
},
}
// Should handle connection failures gracefully
logger := logger.NewLogger(logger.Config{Level: "error"})
_, err := arbitrum.NewArbitrumL2Parser(invalidSuite.testConfig.RPCEndpoint, logger, nil)
assert.Error(t, err, "Should fail to connect to invalid endpoint")
// Test timeout handling
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Millisecond)
defer cancel()
client, err := rpc.DialContext(ctx, "https://arb1.arbitrum.io/rpc")
if err != nil {
t.Logf("Expected timeout error: %v", err)
} else {
client.Close()
}
}