saving in place
This commit is contained in:
814
test/stress/benchmarks.go
Normal file
814
test/stress/benchmarks.go
Normal file
@@ -0,0 +1,814 @@
|
||||
package stress_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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/pools"
|
||||
"github.com/fraktal/mev-beta/pkg/profitcalc"
|
||||
"github.com/fraktal/mev-beta/pkg/scanner/market"
|
||||
"github.com/fraktal/mev-beta/pkg/trading"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// BenchmarkStressTestSuite runs benchmark tests for the stress test suite
|
||||
func BenchmarkStressTestSuite(b *testing.B) {
|
||||
// Create test logger
|
||||
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
||||
|
||||
// Create test components
|
||||
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
||||
poolCache := pools.NewPoolCache(10000, time.Hour)
|
||||
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
||||
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
||||
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
||||
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
||||
slippageProtector := trading.NewSlippageProtection(nil, log)
|
||||
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
||||
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
||||
|
||||
// Create stress test suite
|
||||
suite := NewStressTestSuite(
|
||||
log,
|
||||
protocolRegistry,
|
||||
poolCache,
|
||||
marketDiscovery,
|
||||
strategyEngine,
|
||||
profitCalculator,
|
||||
mevAnalyzer,
|
||||
slippageProtector,
|
||||
capitalOptimizer,
|
||||
profitTracker,
|
||||
)
|
||||
|
||||
// Run benchmark tests
|
||||
b.Run("MarketScannerStressTest", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := suite.RunMarketScannerStressTest()
|
||||
if !result.Passed {
|
||||
b.Fatalf("Market scanner stress test failed: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("SwapAnalyzerStressTest", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := suite.RunSwapAnalyzerStressTest()
|
||||
if !result.Passed {
|
||||
b.Fatalf("Swap analyzer stress test failed: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("PoolDiscoveryStressTest", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := suite.RunPoolDiscoveryStressTest()
|
||||
if !result.Passed {
|
||||
b.Fatalf("Pool discovery stress test failed: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("ArbitrageEngineStressTest", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := suite.RunArbitrageEngineStressTest()
|
||||
if !result.Passed {
|
||||
b.Fatalf("Arbitrage engine stress test failed: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("EventProcessingStressTest", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := suite.RunEventProcessingStressTest()
|
||||
if !result.Passed {
|
||||
b.Fatalf("Event processing stress test failed: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("ProfitCalculationStressTest", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := suite.RunProfitCalculationStressTest()
|
||||
if !result.Passed {
|
||||
b.Fatalf("Profit calculation stress test failed: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("ConcurrencyStressTest", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := suite.RunConcurrencyStressTest()
|
||||
if !result.Passed {
|
||||
b.Fatalf("Concurrency stress test failed: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("MemoryStressTest", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := suite.RunMemoryStressTest()
|
||||
if !result.Passed {
|
||||
b.Fatalf("Memory stress test failed: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("PerformanceRegressionTest", func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := suite.RunPerformanceRegressionTest()
|
||||
if !result.Passed {
|
||||
b.Fatalf("Performance regression test failed: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkConcurrentMarketScanning benchmarks concurrent market scanning performance
|
||||
func BenchmarkConcurrentMarketScanning(b *testing.B) {
|
||||
// Create test logger
|
||||
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
||||
|
||||
// Create test components
|
||||
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
||||
poolCache := pools.NewPoolCache(10000, time.Hour)
|
||||
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
||||
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
||||
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
||||
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
||||
slippageProtector := trading.NewSlippageProtection(nil, log)
|
||||
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
||||
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
||||
|
||||
// Create stress test suite
|
||||
suite := NewStressTestSuite(
|
||||
log,
|
||||
protocolRegistry,
|
||||
poolCache,
|
||||
marketDiscovery,
|
||||
strategyEngine,
|
||||
profitCalculator,
|
||||
mevAnalyzer,
|
||||
slippageProtector,
|
||||
capitalOptimizer,
|
||||
profitTracker,
|
||||
)
|
||||
|
||||
// Generate test data
|
||||
testPools := suite.generateTestPools(1000)
|
||||
testEvents := suite.generateTestEvents(10000)
|
||||
|
||||
b.ResetTimer()
|
||||
b.Run("ConcurrentPoolScanning", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var wg sync.WaitGroup
|
||||
errorCount := int64(0)
|
||||
|
||||
// Process pools concurrently
|
||||
for _, pool := range testPools {
|
||||
wg.Add(1)
|
||||
go func(p *market.CachedData) {
|
||||
defer wg.Done()
|
||||
|
||||
// Simulate pool scanning
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Mock pool scanning operation
|
||||
err := suite.mockPoolScan(ctx, p)
|
||||
if err != nil {
|
||||
atomic.AddInt64(&errorCount, 1)
|
||||
}
|
||||
}(pool)
|
||||
}
|
||||
|
||||
// Wait for all operations to complete
|
||||
wg.Wait()
|
||||
|
||||
if errorCount > 0 {
|
||||
b.Fatalf("Concurrent pool scanning failed with %d errors", errorCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
b.Run("ConcurrentEventProcessing", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
var wg sync.WaitGroup
|
||||
errorCount := int64(0)
|
||||
|
||||
// Process events concurrently
|
||||
for _, event := range testEvents {
|
||||
wg.Add(1)
|
||||
go func(e events.Event) {
|
||||
defer wg.Done()
|
||||
|
||||
// Simulate event processing
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Mock event processing operation
|
||||
err := suite.mockEventProcessing(ctx, e)
|
||||
if err != nil {
|
||||
atomic.AddInt64(&errorCount, 1)
|
||||
}
|
||||
}(event)
|
||||
}
|
||||
|
||||
// Wait for all operations to complete
|
||||
wg.Wait()
|
||||
|
||||
if errorCount > 0 {
|
||||
b.Fatalf("Concurrent event processing failed with %d errors", errorCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkMemoryAllocation benchmarks memory allocation performance
|
||||
func BenchmarkMemoryAllocation(b *testing.B) {
|
||||
// Create test logger
|
||||
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
||||
|
||||
// Create test components
|
||||
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
||||
poolCache := pools.NewPoolCache(10000, time.Hour)
|
||||
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
||||
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
||||
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
||||
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
||||
slippageProtector := trading.NewSlippageProtection(nil, log)
|
||||
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
||||
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
||||
|
||||
// Create stress test suite
|
||||
suite := NewStressTestSuite(
|
||||
log,
|
||||
protocolRegistry,
|
||||
poolCache,
|
||||
marketDiscovery,
|
||||
strategyEngine,
|
||||
profitCalculator,
|
||||
mevAnalyzer,
|
||||
slippageProtector,
|
||||
capitalOptimizer,
|
||||
profitTracker,
|
||||
)
|
||||
|
||||
b.ResetTimer()
|
||||
b.Run("LargeDataStructures", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Generate large test data sets
|
||||
dataSets := suite.generateLargeTestDataSets(10000)
|
||||
|
||||
// Process large data sets
|
||||
var wg sync.WaitGroup
|
||||
errorCount := int64(0)
|
||||
|
||||
for _, dataSet := range dataSets {
|
||||
wg.Add(1)
|
||||
go func(ds []*market.CachedData) {
|
||||
defer wg.Done()
|
||||
|
||||
// Simulate memory-intensive processing
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Mock memory-intensive operation
|
||||
err := suite.mockMemoryIntensiveProcessing(ctx, ds)
|
||||
if err != nil {
|
||||
atomic.AddInt64(&errorCount, 1)
|
||||
}
|
||||
}(dataSet)
|
||||
}
|
||||
|
||||
// Wait for all operations to complete
|
||||
wg.Wait()
|
||||
|
||||
if errorCount > 0 {
|
||||
b.Fatalf("Memory-intensive processing failed with %d errors", errorCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkCPUUtilization benchmarks CPU utilization performance
|
||||
func BenchmarkCPUUtilization(b *testing.B) {
|
||||
// Create test logger
|
||||
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
||||
|
||||
// Create test components
|
||||
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
||||
poolCache := pools.NewPoolCache(10000, time.Hour)
|
||||
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
||||
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
||||
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
||||
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
||||
slippageProtector := trading.NewSlippageProtection(nil, log)
|
||||
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
||||
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
||||
|
||||
// Create stress test suite
|
||||
suite := NewStressTestSuite(
|
||||
log,
|
||||
protocolRegistry,
|
||||
poolCache,
|
||||
marketDiscovery,
|
||||
strategyEngine,
|
||||
profitCalculator,
|
||||
mevAnalyzer,
|
||||
slippageProtector,
|
||||
capitalOptimizer,
|
||||
profitTracker,
|
||||
)
|
||||
|
||||
b.ResetTimer()
|
||||
b.Run("CPUIntensiveCalculations", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Generate test data
|
||||
testProfits := suite.generateTestProfits(1000)
|
||||
|
||||
// Process profits concurrently
|
||||
var wg sync.WaitGroup
|
||||
errorCount := int64(0)
|
||||
|
||||
for _, profit := range testProfits {
|
||||
wg.Add(1)
|
||||
go func(p *arbitrum.ArbitrageOpportunityDetailed) {
|
||||
defer wg.Done()
|
||||
|
||||
// Simulate CPU-intensive calculations
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Mock profit calculation operation
|
||||
err := suite.mockProfitCalculation(ctx, p)
|
||||
if err != nil {
|
||||
atomic.AddInt64(&errorCount, 1)
|
||||
}
|
||||
}(profit)
|
||||
}
|
||||
|
||||
// Wait for all operations to complete
|
||||
wg.Wait()
|
||||
|
||||
if errorCount > 0 {
|
||||
b.Fatalf("CPU-intensive calculations failed with %d errors", errorCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkNetworkLatency benchmarks network latency handling
|
||||
func BenchmarkNetworkLatency(b *testing.B) {
|
||||
// Create test logger
|
||||
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
||||
|
||||
// Create test components
|
||||
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
||||
poolCache := pools.NewPoolCache(10000, time.Hour)
|
||||
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
||||
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
||||
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
||||
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
||||
slippageProtector := trading.NewSlippageProtection(nil, log)
|
||||
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
||||
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
||||
|
||||
// Create stress test suite
|
||||
suite := NewStressTestSuite(
|
||||
log,
|
||||
protocolRegistry,
|
||||
poolCache,
|
||||
marketDiscovery,
|
||||
strategyEngine,
|
||||
profitCalculator,
|
||||
mevAnalyzer,
|
||||
slippageProtector,
|
||||
capitalOptimizer,
|
||||
profitTracker,
|
||||
)
|
||||
|
||||
b.ResetTimer()
|
||||
b.Run("NetworkRequestHandling", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Generate test data
|
||||
testPools := suite.generateTestPools(100)
|
||||
|
||||
// Process pools with simulated network delays
|
||||
var wg sync.WaitGroup
|
||||
errorCount := int64(0)
|
||||
|
||||
for _, pool := range testPools {
|
||||
wg.Add(1)
|
||||
go func(p *market.CachedData) {
|
||||
defer wg.Done()
|
||||
|
||||
// Simulate network delay
|
||||
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
|
||||
|
||||
// Simulate pool scanning with network request
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Mock pool scanning operation
|
||||
err := suite.mockPoolScan(ctx, p)
|
||||
if err != nil {
|
||||
atomic.AddInt64(&errorCount, 1)
|
||||
}
|
||||
}(pool)
|
||||
}
|
||||
|
||||
// Wait for all operations to complete
|
||||
wg.Wait()
|
||||
|
||||
if errorCount > 0 {
|
||||
b.Fatalf("Network request handling failed with %d errors", errorCount)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BenchmarkErrorHandling benchmarks error handling performance
|
||||
func BenchmarkErrorHandling(b *testing.B) {
|
||||
// Create test logger
|
||||
log := logger.New("warn", "text", "") // Use warn level to minimize logging overhead
|
||||
|
||||
// Create test components
|
||||
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
||||
poolCache := pools.NewPoolCache(10000, time.Hour)
|
||||
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
||||
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
||||
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
||||
mevAnalyzer := arbitrum.NewMEVAnalyzer(log)
|
||||
slippageProtector := trading.NewSlippageProtection(nil, log)
|
||||
capitalOptimizer := arbitrum.NewCapitalOptimizer(log)
|
||||
profitTracker := arbitrum.NewProfitabilityTracker(log)
|
||||
|
||||
// Create stress test suite
|
||||
suite := NewStressTestSuite(
|
||||
log,
|
||||
protocolRegistry,
|
||||
poolCache,
|
||||
marketDiscovery,
|
||||
strategyEngine,
|
||||
profitCalculator,
|
||||
mevAnalyzer,
|
||||
slippageProtector,
|
||||
capitalOptimizer,
|
||||
profitTracker,
|
||||
)
|
||||
|
||||
b.ResetTimer()
|
||||
b.Run("ErrorHandlingPerformance", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
// Generate test data with errors
|
||||
testEvents := suite.generateTestEventsWithErrorRate(1000, 0.05) // 5% error rate
|
||||
|
||||
// Process events with error handling
|
||||
var wg sync.WaitGroup
|
||||
errorCount := int64(0)
|
||||
successCount := int64(0)
|
||||
|
||||
for _, event := range testEvents {
|
||||
wg.Add(1)
|
||||
go func(e events.Event) {
|
||||
defer wg.Done()
|
||||
|
||||
// Simulate event processing
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Mock event processing operation
|
||||
err := suite.mockEventProcessing(ctx, e)
|
||||
if err != nil {
|
||||
atomic.AddInt64(&errorCount, 1)
|
||||
suite.recordError(fmt.Sprintf("EventProcessingError: %v", err))
|
||||
} else {
|
||||
atomic.AddInt64(&successCount, 1)
|
||||
}
|
||||
}(event)
|
||||
}
|
||||
|
||||
// Wait for all operations to complete
|
||||
wg.Wait()
|
||||
|
||||
// Validate error handling worked correctly
|
||||
if errorCount+successCount != int64(len(testEvents)) {
|
||||
b.Fatalf("Error handling count mismatch: %d errors + %d successes != %d total", errorCount, successCount, len(testEvents))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// generateTestEventsWithErrorRate generates test events with a specific error rate
|
||||
func (suite *StressTestSuite) generateTestEventsWithErrorRate(count int, errorRate float64) []events.Event {
|
||||
events := suite.generateTestEvents(count)
|
||||
|
||||
// Mark some events to cause errors based on error rate
|
||||
errorEvents := int(float64(count) * errorRate)
|
||||
for i := 0; i < errorEvents && i < count; i++ {
|
||||
// Mark event to cause error
|
||||
events[i].Type = events.Unknown // Invalid event type to cause processing errors
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
// generateTestPools generates test pools for benchmarking
|
||||
func (suite *StressTestSuite) generateTestPools(count int) []*market.CachedData {
|
||||
pools := make([]*market.CachedData, count)
|
||||
|
||||
// Known token addresses for testing
|
||||
wethAddr := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
|
||||
usdcAddr := common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831")
|
||||
usdtAddr := common.HexToAddress("0xff970a61a04b1ca14834a43f5de4533ebddb5cc8")
|
||||
wbtcAddr := common.HexToAddress("0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f")
|
||||
|
||||
tokens := []common.Address{wethAddr, usdcAddr, usdtAddr, wbtcAddr}
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
// Select random tokens for the pool
|
||||
token0 := tokens[rand.Intn(len(tokens))]
|
||||
token1 := tokens[rand.Intn(len(tokens))]
|
||||
for token0 == token1 {
|
||||
token1 = tokens[rand.Intn(len(tokens))]
|
||||
}
|
||||
|
||||
// Create deterministic pool address based on index
|
||||
poolAddr := common.BigToAddress(big.NewInt(int64(i + 1000000)))
|
||||
|
||||
// Generate deterministic liquidity and price values
|
||||
liquidity := uint256.NewInt(uint64(1000000 + i*1000)) // Increasing liquidity
|
||||
sqrtPrice := uint256.NewInt(uint64(1000000000000000000 + i*100000000000000)) // Increasing price
|
||||
|
||||
pools[i] = &market.CachedData{
|
||||
Address: poolAddr,
|
||||
Token0: token0,
|
||||
Token1: token1,
|
||||
Fee: int64(3000 + (i%4)*500), // Varying fees (0.05%, 0.3%, 0.5%, 1%)
|
||||
Liquidity: liquidity,
|
||||
SqrtPriceX96: sqrtPrice,
|
||||
Tick: int(74959 + i), // Varying ticks
|
||||
TickSpacing: 60,
|
||||
Protocol: fmt.Sprintf("uniswap_v%d", 2+(i%2)), // Alternating V2/V3
|
||||
LastUpdated: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
return pools
|
||||
}
|
||||
|
||||
// generateTestEvents generates test events for benchmarking
|
||||
func (suite *StressTestSuite) generateTestEvents(count int) []events.Event {
|
||||
events := make([]events.Event, count)
|
||||
|
||||
// Known token addresses for testing
|
||||
wethAddr := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
|
||||
usdcAddr := common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831")
|
||||
usdtAddr := common.HexToAddress("0xff970a61a04b1ca14834a43f5de4533ebddb5cc8")
|
||||
wbtcAddr := common.HexToAddress("0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f")
|
||||
|
||||
tokens := []common.Address{wethAddr, usdcAddr, usdtAddr, wbtcAddr}
|
||||
protocols := []string{"uniswap_v2", "uniswap_v3", "sushiswap", "camelot_v2", "camelot_v3", "balancer_v2", "curve", "algebra"}
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
// Select random tokens for the event
|
||||
token0 := tokens[rand.Intn(len(tokens))]
|
||||
token1 := tokens[rand.Intn(len(tokens))]
|
||||
for token0 == token1 {
|
||||
token1 = tokens[rand.Intn(len(tokens))]
|
||||
}
|
||||
|
||||
// Select random protocol
|
||||
protocol := protocols[rand.Intn(len(protocols))]
|
||||
|
||||
// Create deterministic pool address based on index
|
||||
poolAddr := common.BigToAddress(big.NewInt(int64(i + 2000000)))
|
||||
|
||||
// Generate deterministic amounts
|
||||
amount0 := big.NewInt(int64(100000000000000000 + int64(i)*10000000000000)) // Varying amounts
|
||||
amount1 := big.NewInt(int64(200000000000000000 + int64(i)*20000000000000)) // Varying amounts
|
||||
|
||||
// Generate deterministic liquidity and price values
|
||||
liquidity := uint256.NewInt(uint64(500000 + i*500)) // Increasing liquidity
|
||||
sqrtPrice := uint256.NewInt(uint64(500000000000000000 + i*50000000000000)) // Increasing price
|
||||
|
||||
events[i] = events.Event{
|
||||
Timestamp: time.Now(),
|
||||
BlockNumber: uint64(10000000 + i),
|
||||
TransactionHash: common.BigToHash(big.NewInt(int64(i + 3000000))),
|
||||
LogIndex: uint(i % 100),
|
||||
Type: events.Swap, // Default to swap events
|
||||
Protocol: protocol,
|
||||
PoolAddress: poolAddr,
|
||||
Token0: token0,
|
||||
Token1: token1,
|
||||
Amount0: amount0,
|
||||
Amount1: amount1,
|
||||
Liquidity: liquidity,
|
||||
SqrtPriceX96: sqrtPrice,
|
||||
Tick: int32(74959 + i%1000), // Varying ticks
|
||||
}
|
||||
}
|
||||
|
||||
return events
|
||||
}
|
||||
|
||||
// generateTestProfits generates test profits for benchmarking
|
||||
func (suite *StressTestSuite) generateTestProfits(count int) []*arbitrum.ArbitrageOpportunityDetailed {
|
||||
profits := make([]*arbitrum.ArbitrageOpportunityDetailed, count)
|
||||
|
||||
// Known token addresses for testing
|
||||
wethAddr := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
|
||||
usdcAddr := common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831")
|
||||
usdtAddr := common.HexToAddress("0xff970a61a04b1ca14834a43f5de4533ebddb5cc8")
|
||||
wbtcAddr := common.HexToAddress("0x2f2a2543b76a4166549f7aab2e75bef0aefc5b0f")
|
||||
|
||||
tokens := []common.Address{wethAddr, usdcAddr, usdtAddr, wbtcAddr}
|
||||
exchanges := []string{"uniswap_v2", "uniswap_v3", "sushiswap", "camelot_v2", "camelot_v3", "balancer_v2", "curve", "algebra"}
|
||||
|
||||
for i := 0; i < count; i++ {
|
||||
// Select random tokens for the arbitrage
|
||||
tokenIn := tokens[rand.Intn(len(tokens))]
|
||||
tokenOut := tokens[rand.Intn(len(tokens))]
|
||||
for tokenIn == tokenOut {
|
||||
tokenOut = tokens[rand.Intn(len(tokens))]
|
||||
}
|
||||
|
||||
// Select random exchanges
|
||||
exchangeA := exchanges[rand.Intn(len(exchanges))]
|
||||
exchangeB := exchanges[rand.Intn(len(exchanges))]
|
||||
for exchangeA == exchangeB {
|
||||
exchangeB = exchanges[rand.Intn(len(exchanges))]
|
||||
}
|
||||
|
||||
// Create deterministic pool addresses based on index
|
||||
poolA := common.BigToAddress(big.NewInt(int64(i + 4000000)))
|
||||
poolB := common.BigToAddress(big.NewInt(int64(i + 5000000)))
|
||||
|
||||
// Generate deterministic amounts
|
||||
amountIn := big.NewInt(int64(100000000000000000 + int64(i)*10000000000000)) // Varying amounts
|
||||
expectedAmountOut := big.NewInt(int64(105000000000000000 + int64(i)*10500000000000)) // 5% profit
|
||||
actualAmountOut := big.NewInt(int64(104000000000000000 + int64(i)*10400000000000)) // 4% profit
|
||||
profit := big.NewInt(int64(4000000000000000 + int64(i)*400000000000)) // Varying profits
|
||||
gasCost := big.NewInt(int64(1000000000000000 + int64(i)*100000000000)) // Varying gas costs
|
||||
netProfit := new(big.Int).Sub(profit, gasCost)
|
||||
|
||||
profits[i] = &arbitrum.ArbitrageOpportunityDetailed{
|
||||
ID: fmt.Sprintf("arb_%d_%d", time.Now().Unix(), i+1000000),
|
||||
Type: "arbitrage",
|
||||
TokenIn: tokenIn,
|
||||
TokenOut: tokenOut,
|
||||
AmountIn: amountIn,
|
||||
ExpectedAmountOut: expectedAmountOut,
|
||||
ActualAmountOut: actualAmountOut,
|
||||
Profit: profit,
|
||||
ProfitUSD: 50.0 + float64(i%1000)*0.05, // Varying USD profits
|
||||
ProfitMargin: 0.04 + float64(i%100)*0.0001, // Varying margins (4-5%)
|
||||
GasCost: gasCost,
|
||||
NetProfit: netProfit,
|
||||
ExchangeA: exchangeA,
|
||||
ExchangeB: exchangeB,
|
||||
PoolA: poolA,
|
||||
PoolB: poolB,
|
||||
PriceImpactA: 0.005 + float64(i%1000)*0.000005, // Varying price impacts
|
||||
PriceImpactB: 0.003 + float64(i%1000)*0.000003, // Varying price impacts
|
||||
CapitalRequired: 100.0 + float64(i%10000)*0.01, // Varying capital requirements
|
||||
GasCostUSD: 5.0 + float64(i%100)*0.05, // Varying gas costs in USD
|
||||
Confidence: 0.8 + float64(i%20)*0.01, // Varying confidence (80-100%)
|
||||
RiskScore: 0.2 + float64(i%50)*0.01, // Varying risk scores (20-70%)
|
||||
ExecutionTime: time.Duration(15+i%10) * time.Second, // Varying execution times
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
}
|
||||
|
||||
return profits
|
||||
}
|
||||
|
||||
// generateLargeTestDataSets generates large test data sets for memory benchmarking
|
||||
func (suite *StressTestSuite) generateLargeTestDataSets(count int) [][]*market.CachedData {
|
||||
// Create batches of test data
|
||||
batchSize := 1000
|
||||
batchCount := count / batchSize
|
||||
if count%batchSize > 0 {
|
||||
batchCount++
|
||||
}
|
||||
|
||||
dataSets := make([][]*market.CachedData, batchCount)
|
||||
|
||||
for i := 0; i < batchCount; i++ {
|
||||
dataSets[i] = suite.generateTestPools(batchSize)
|
||||
}
|
||||
|
||||
return dataSets
|
||||
}
|
||||
|
||||
// mockPoolScan simulates pool scanning for benchmarking
|
||||
func (suite *StressTestSuite) mockPoolScan(ctx context.Context, pool *market.CachedData) error {
|
||||
atomic.AddUint64(&suite.metrics.totalPoolsScanned, 1)
|
||||
atomic.AddUint64(&suite.metrics.totalTestsRun, 1)
|
||||
|
||||
// Simulate work
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(time.Duration(rand.Intn(10)) * time.Millisecond):
|
||||
// Simulate occasional errors
|
||||
if rand.Intn(1000) < 5 { // 0.5% error rate
|
||||
atomic.AddUint64(&suite.metrics.testsFailed, 1)
|
||||
return fmt.Errorf("simulated pool scan error")
|
||||
}
|
||||
|
||||
atomic.AddUint64(&suite.metrics.testsPassed, 1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// mockEventProcessing simulates event processing for benchmarking
|
||||
func (suite *StressTestSuite) mockEventProcessing(ctx context.Context, event events.Event) error {
|
||||
atomic.AddUint64(&suite.metrics.totalTransactions, 1)
|
||||
atomic.AddUint64(&suite.metrics.totalTestsRun, 1)
|
||||
|
||||
// Simulate work
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(time.Duration(rand.Intn(5)) * time.Millisecond):
|
||||
// Simulate occasional errors
|
||||
if rand.Intn(1000) < 3 { // 0.3% error rate
|
||||
atomic.AddUint64(&suite.metrics.testsFailed, 1)
|
||||
return fmt.Errorf("simulated event processing error")
|
||||
}
|
||||
|
||||
atomic.AddUint64(&suite.metrics.testsPassed, 1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// mockMemoryIntensiveProcessing simulates memory-intensive processing for benchmarking
|
||||
func (suite *StressTestSuite) mockMemoryIntensiveProcessing(ctx context.Context, dataSet []*market.CachedData) error {
|
||||
atomic.AddUint64(&suite.metrics.totalTestsRun, 1)
|
||||
|
||||
// Simulate memory-intensive work
|
||||
// Create temporary data structures to consume memory
|
||||
tempData := make([]*market.CachedData, len(dataSet))
|
||||
copy(tempData, dataSet)
|
||||
|
||||
// Simulate work
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(time.Duration(rand.Intn(20)) * time.Millisecond):
|
||||
// Clear temporary data
|
||||
tempData = nil
|
||||
|
||||
// Simulate occasional errors
|
||||
if rand.Intn(1000) < 8 { // 0.8% error rate
|
||||
atomic.AddUint64(&suite.metrics.testsFailed, 1)
|
||||
return fmt.Errorf("simulated memory-intensive processing error")
|
||||
}
|
||||
|
||||
atomic.AddUint64(&suite.metrics.testsPassed, 1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// mockProfitCalculation simulates profit calculation for benchmarking
|
||||
func (suite *StressTestSuite) mockProfitCalculation(ctx context.Context, profit *arbitrum.ArbitrageOpportunityDetailed) error {
|
||||
atomic.AddUint64(&suite.metrics.totalArbitrageOps, 1)
|
||||
atomic.AddUint64(&suite.metrics.totalTestsRun, 1)
|
||||
|
||||
// Simulate work
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-time.After(time.Duration(rand.Intn(15)) * time.Millisecond):
|
||||
// Simulate occasional errors
|
||||
if rand.Intn(1000) < 6 { // 0.6% error rate
|
||||
atomic.AddUint64(&suite.metrics.testsFailed, 1)
|
||||
return fmt.Errorf("simulated profit calculation error")
|
||||
}
|
||||
|
||||
atomic.AddUint64(&suite.metrics.testsPassed, 1)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
326
test/stress/stress_test_runner.go
Normal file
326
test/stress/stress_test_runner.go
Normal file
@@ -0,0 +1,326 @@
|
||||
package stress_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/signal"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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/marketdata"
|
||||
"github.com/fraktal/mev-beta/pkg/pools"
|
||||
"github.com/fraktal/mev-beta/pkg/profitcalc"
|
||||
"github.com/fraktal/mev-beta/pkg/scanner/market"
|
||||
"github.com/fraktal/mev-beta/pkg/scanner/swap"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// StressTestRunner runs comprehensive stress tests for the MEV bot
|
||||
type StressTestRunner struct {
|
||||
suite *StressTestSuite
|
||||
logger *logger.Logger
|
||||
wg sync.WaitGroup
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
shutdown chan struct{}
|
||||
}
|
||||
|
||||
// NewStressTestRunner creates a new stress test runner
|
||||
func NewStressTestRunner() *StressTestRunner {
|
||||
// Initialize logger
|
||||
log := logger.New("debug", "text", "logs/stress_test.log")
|
||||
|
||||
// Create test components
|
||||
protocolRegistry := arbitrum.NewArbitrumProtocolRegistry(log)
|
||||
poolCache := pools.NewPoolCache(10000, time.Hour)
|
||||
marketDiscovery := market.NewMarketDiscovery(nil, log, "")
|
||||
strategyEngine := arbitrum.NewMEVStrategyEngine(log, protocolRegistry)
|
||||
profitCalculator := profitcalc.NewProfitCalculatorWithClient(log, nil)
|
||||
marketDataLogger := marketdata.NewMarketDataLogger(log, nil)
|
||||
swapAnalyzer := swap.NewSwapAnalyzer(log, marketDataLogger, profitCalculator, nil)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
runner := &StressTestRunner{
|
||||
logger: log,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
shutdown: make(chan struct{}),
|
||||
}
|
||||
|
||||
// Create stress test suite
|
||||
runner.suite = NewStressTestSuite(
|
||||
log,
|
||||
protocolRegistry,
|
||||
poolCache,
|
||||
marketDiscovery,
|
||||
strategyEngine,
|
||||
profitCalculator,
|
||||
nil, // mevAnalyzer
|
||||
nil, // slippageProtector
|
||||
nil, // capitalOptimizer
|
||||
nil, // profitTracker
|
||||
)
|
||||
|
||||
return runner
|
||||
}
|
||||
|
||||
// RunAllStressTests runs all stress tests
|
||||
func (str *StressTestRunner) RunAllStressTests() {
|
||||
str.logger.Info("🚀 Starting comprehensive stress tests...")
|
||||
|
||||
// Set up graceful shutdown
|
||||
str.setupGracefulShutdown()
|
||||
|
||||
// Run individual stress tests
|
||||
tests := []struct {
|
||||
name string
|
||||
fn func() *StressTestResult
|
||||
}{
|
||||
{"Market Scanner Stress Test", str.suite.RunMarketScannerStressTest},
|
||||
{"Swap Analyzer Stress Test", str.suite.RunSwapAnalyzerStressTest},
|
||||
{"Pool Discovery Stress Test", str.suite.RunPoolDiscoveryStressTest},
|
||||
{"Arbitrage Engine Stress Test", str.suite.RunArbitrageEngineStressTest},
|
||||
{"Event Processing Stress Test", str.suite.RunEventProcessingStressTest},
|
||||
{"Profit Calculation Stress Test", str.suite.RunProfitCalculationStressTest},
|
||||
{"Concurrency Stress Test", str.suite.RunConcurrencyStressTest},
|
||||
{"Memory Stress Test", str.suite.RunMemoryStressTest},
|
||||
{"Performance Regression Test", str.suite.RunPerformanceRegressionTest},
|
||||
}
|
||||
|
||||
results := make([]*StressTestResult, len(tests))
|
||||
|
||||
// Run tests concurrently
|
||||
for i, test := range tests {
|
||||
str.wg.Add(1)
|
||||
go func(idx int, t struct {
|
||||
name string
|
||||
fn func() *StressTestResult
|
||||
}) {
|
||||
defer str.wg.Done()
|
||||
|
||||
str.logger.Info(fmt.Sprintf("🧪 Running %s...", t.name))
|
||||
result := t.fn()
|
||||
results[idx] = result
|
||||
|
||||
if result.Passed {
|
||||
str.logger.Info(fmt.Sprintf("✅ %s PASSED", t.name))
|
||||
} else {
|
||||
str.logger.Error(fmt.Sprintf("❌ %s FAILED", t.name))
|
||||
}
|
||||
}(i, test)
|
||||
}
|
||||
|
||||
// Wait for all tests to complete
|
||||
str.wg.Wait()
|
||||
|
||||
// Generate and log summary report
|
||||
str.generateSummaryReport(results)
|
||||
|
||||
// Close resources
|
||||
close(str.shutdown)
|
||||
}
|
||||
|
||||
// generateSummaryReport generates and logs a summary report of stress test results
|
||||
func (str *StressTestRunner) generateSummaryReport(results []*StressTestResult) {
|
||||
str.logger.Info("📊 STRESS TEST SUMMARY REPORT")
|
||||
str.logger.Info("================================")
|
||||
|
||||
passed := 0
|
||||
failed := 0
|
||||
totalTests := len(results)
|
||||
|
||||
for _, result := range results {
|
||||
if result.Passed {
|
||||
passed++
|
||||
} else {
|
||||
failed++
|
||||
}
|
||||
|
||||
status := "✅ PASS"
|
||||
if !result.Passed {
|
||||
status = "❌ FAIL"
|
||||
}
|
||||
|
||||
str.logger.Info(fmt.Sprintf("%s %s - Score: %.1f%%", status, result.TestName, result.PerformanceScore))
|
||||
}
|
||||
|
||||
str.logger.Info("================================")
|
||||
str.logger.Info(fmt.Sprintf("TOTAL: %d tests, %d passed, %d failed", totalTests, passed, failed))
|
||||
str.logger.Info(fmt.Sprintf("SUCCESS RATE: %.1f%%", float64(passed)/float64(totalTests)*100))
|
||||
|
||||
if failed == 0 {
|
||||
str.logger.Info("🎉 ALL STRESS TESTS PASSED!")
|
||||
} else {
|
||||
str.logger.Warn(fmt.Sprintf("⚠️ %d STRESS TESTS FAILED - REVIEW RESULTS", failed))
|
||||
}
|
||||
}
|
||||
|
||||
// setupGracefulShutdown sets up signal handling for graceful shutdown
|
||||
func (str *StressTestRunner) setupGracefulShutdown() {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
||||
|
||||
go func() {
|
||||
<-c
|
||||
str.logger.Info("🛑 Shutdown signal received, stopping stress tests...")
|
||||
str.cancel()
|
||||
close(str.shutdown)
|
||||
}()
|
||||
}
|
||||
|
||||
// generateTestLoad generates synthetic load for stress testing
|
||||
func (str *StressTestRunner) generateTestLoad(duration time.Duration, transactionsPerSecond int) {
|
||||
str.logger.Info(fmt.Sprintf("💥 Generating synthetic load: %d TPS for %v", transactionsPerSecond, duration))
|
||||
|
||||
// Calculate interval between transactions for precise TPS
|
||||
interval := time.Duration(int64(time.Second) / int64(transactionsPerSecond))
|
||||
if interval == 0 {
|
||||
interval = time.Nanosecond // Minimum interval for very high TPS
|
||||
}
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
|
||||
startTime := time.Now()
|
||||
transactionCount := uint64(0)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-str.ctx.Done():
|
||||
str.logger.Info(fmt.Sprintf("⏹️ Load generation stopped. Generated %d transactions in %v", transactionCount, time.Since(startTime)))
|
||||
return
|
||||
case <-ticker.C:
|
||||
// Generate synthetic transaction
|
||||
str.generateSyntheticTransaction()
|
||||
atomic.AddUint64(&transactionCount, 1)
|
||||
|
||||
// Check if duration has elapsed
|
||||
if time.Since(startTime) >= duration {
|
||||
str.logger.Info(fmt.Sprintf("⏰ Load generation completed. Generated %d transactions in %v", transactionCount, time.Since(startTime)))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// generateSyntheticTransaction generates a synthetic transaction for stress testing
|
||||
func (str *StressTestRunner) generateSyntheticTransaction() {
|
||||
// Generate random transaction data
|
||||
txHash := common.BigToHash(big.NewInt(rand.Int63()))
|
||||
blockNumber := uint64(rand.Int63n(100000000) + 100000000)
|
||||
poolAddr := common.BigToAddress(big.NewInt(rand.Int63()))
|
||||
token0 := common.BigToAddress(big.NewInt(rand.Int63()))
|
||||
token1 := common.BigToAddress(big.NewInt(rand.Int63()))
|
||||
amount0 := big.NewInt(rand.Int63n(1000000000000000000)) // Up to 1 ETH
|
||||
amount1 := big.NewInt(rand.Int63n(1000000000000000000)) // Up to 1 ETH
|
||||
|
||||
// Create event with random protocol
|
||||
protocols := []string{"uniswap_v2", "uniswap_v3", "sushiswap", "camelot_v2", "camelot_v3", "balancer_v2", "curve", "algebra"}
|
||||
protocol := protocols[rand.Intn(len(protocols))]
|
||||
|
||||
event := events.Event{
|
||||
Timestamp: time.Now(),
|
||||
BlockNumber: blockNumber,
|
||||
TransactionHash: txHash,
|
||||
LogIndex: uint(rand.Intn(100)),
|
||||
Type: events.Swap,
|
||||
Protocol: protocol,
|
||||
PoolAddress: poolAddr,
|
||||
Token0: token0,
|
||||
Token1: token1,
|
||||
Amount0: amount0,
|
||||
Amount1: amount1,
|
||||
Liquidity: uint256.NewInt(uint64(rand.Int63n(1000000000000000000))), // Up to 1 ETH equivalent
|
||||
SqrtPriceX96: uint256.NewInt(uint64(rand.Int63n(1000000000000000000))), // Random sqrt price
|
||||
Tick: int32(rand.Int31n(100000) - 50000), // Random tick between -50000 and 50000
|
||||
}
|
||||
|
||||
// Process event through the system
|
||||
// Note: In a real implementation, this would call the actual processing methods
|
||||
// For stress testing, we'll just simulate the processing
|
||||
time.Sleep(time.Duration(rand.Intn(5)) * time.Millisecond)
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("🚀 MEV Bot Stress Test Runner")
|
||||
fmt.Println("=============================")
|
||||
|
||||
// Create stress test runner
|
||||
runner := NewStressTestRunner()
|
||||
defer runner.logger.Close()
|
||||
|
||||
// Parse command line arguments
|
||||
args := os.Args[1:]
|
||||
if len(args) == 0 {
|
||||
fmt.Println("Usage: stress-test-runner [options]")
|
||||
fmt.Println("Options:")
|
||||
fmt.Println(" --load-test Run load generation test")
|
||||
fmt.Println(" --full-suite Run full stress test suite")
|
||||
fmt.Println(" --duration <sec> Set test duration (default: 60)")
|
||||
fmt.Println(" --tps <count> Set transactions per second (default: 1000)")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Parse arguments
|
||||
duration := 60 * time.Second
|
||||
tps := 1000
|
||||
runLoadTest := false
|
||||
runFullSuite := false
|
||||
|
||||
for i := 0; i < len(args); i++ {
|
||||
switch args[i] {
|
||||
case "--duration":
|
||||
if i+1 < len(args) {
|
||||
if sec, err := fmt.Sscanf(args[i+1], "%d", &duration); err == nil && sec == 1 {
|
||||
duration = time.Duration(sec) * time.Second
|
||||
}
|
||||
i++
|
||||
}
|
||||
case "--tps":
|
||||
if i+1 < len(args) {
|
||||
if count, err := fmt.Sscanf(args[i+1], "%d", &tps); err == nil && count == 1 {
|
||||
tps = int(count)
|
||||
}
|
||||
i++
|
||||
}
|
||||
case "--load-test":
|
||||
runLoadTest = true
|
||||
case "--full-suite":
|
||||
runFullSuite = true
|
||||
}
|
||||
}
|
||||
|
||||
// Run selected tests
|
||||
if runLoadTest {
|
||||
fmt.Printf("🏃 Running load test for %v at %d TPS...\n", duration, tps)
|
||||
go runner.generateTestLoad(duration, tps)
|
||||
|
||||
// Wait for completion or shutdown
|
||||
select {
|
||||
case <-runner.shutdown:
|
||||
fmt.Println("🛑 Load test interrupted")
|
||||
case <-time.After(duration + 5*time.Second):
|
||||
fmt.Println("✅ Load test completed")
|
||||
}
|
||||
}
|
||||
|
||||
if runFullSuite {
|
||||
fmt.Println("🧪 Running full stress test suite...")
|
||||
runner.RunAllStressTests()
|
||||
fmt.Println("✅ Full stress test suite completed")
|
||||
}
|
||||
|
||||
if !runLoadTest && !runFullSuite {
|
||||
fmt.Println("❌ No test specified. Use --load-test or --full-suite")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
1689
test/stress/stress_test_suite.go
Normal file
1689
test/stress/stress_test_suite.go
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user