//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() } }