273 lines
6.6 KiB
Go
273 lines
6.6 KiB
Go
package execution
|
|
|
|
import (
|
|
"context"
|
|
"math/big"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/fraktal/mev-beta/internal/logger"
|
|
)
|
|
|
|
func TestExecutionModes(t *testing.T) {
|
|
// Test that execution modes are properly defined
|
|
assert.Equal(t, ExecutionMode(0), SimulationMode)
|
|
assert.Equal(t, ExecutionMode(1), DryRunMode)
|
|
assert.Equal(t, ExecutionMode(2), LiveMode)
|
|
}
|
|
|
|
func TestExecutionConfigDefaults(t *testing.T) {
|
|
config := &ExecutionConfig{
|
|
Mode: SimulationMode,
|
|
MaxSlippage: 0.05,
|
|
MaxRetries: 3,
|
|
RetryDelay: 1 * time.Second,
|
|
DryRun: true,
|
|
}
|
|
|
|
assert.Equal(t, SimulationMode, config.Mode)
|
|
assert.Equal(t, 0.05, config.MaxSlippage)
|
|
assert.Equal(t, 3, config.MaxRetries)
|
|
assert.Equal(t, true, config.DryRun)
|
|
}
|
|
|
|
func TestExecutionResultCreation(t *testing.T) {
|
|
result := &ExecutionResult{
|
|
OpportunityID: "test_opp_001",
|
|
Success: true,
|
|
TxHash: common.HexToHash("0x1234567890abcdef"),
|
|
GasUsed: 100000,
|
|
ActualProfit: big.NewInt(1000),
|
|
EstimatedProfit: big.NewInt(1100),
|
|
SlippagePercent: 0.5,
|
|
ExecutionTime: 1500 * time.Millisecond,
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
assert.NotNil(t, result)
|
|
assert.Equal(t, "test_opp_001", result.OpportunityID)
|
|
assert.True(t, result.Success)
|
|
assert.NotNil(t, result.TxHash)
|
|
assert.Equal(t, uint64(100000), result.GasUsed)
|
|
assert.NotNil(t, result.ActualProfit)
|
|
assert.NotNil(t, result.EstimatedProfit)
|
|
}
|
|
|
|
func TestExecutionResultWithError(t *testing.T) {
|
|
result := &ExecutionResult{
|
|
OpportunityID: "test_opp_002",
|
|
Success: false,
|
|
Error: assert.AnError,
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
assert.NotNil(t, result)
|
|
assert.False(t, result.Success)
|
|
assert.NotNil(t, result.Error)
|
|
}
|
|
|
|
func TestSimulationMode(t *testing.T) {
|
|
config := &ExecutionConfig{
|
|
Mode: SimulationMode,
|
|
DryRun: true,
|
|
}
|
|
|
|
// In simulation mode, no transactions should be sent
|
|
assert.Equal(t, SimulationMode, config.Mode)
|
|
assert.True(t, config.DryRun)
|
|
}
|
|
|
|
func TestDryRunMode(t *testing.T) {
|
|
config := &ExecutionConfig{
|
|
Mode: DryRunMode,
|
|
DryRun: true,
|
|
}
|
|
|
|
// In dry run mode, validate but don't execute
|
|
assert.Equal(t, DryRunMode, config.Mode)
|
|
assert.True(t, config.DryRun)
|
|
}
|
|
|
|
func TestLiveMode(t *testing.T) {
|
|
config := &ExecutionConfig{
|
|
Mode: LiveMode,
|
|
DryRun: false,
|
|
}
|
|
|
|
// In live mode, execute real transactions
|
|
assert.Equal(t, LiveMode, config.Mode)
|
|
assert.False(t, config.DryRun)
|
|
}
|
|
|
|
func TestExecutionConfigWithGasPrice(t *testing.T) {
|
|
maxGasPrice := big.NewInt(100000000) // 0.1 gwei
|
|
|
|
config := &ExecutionConfig{
|
|
Mode: DryRunMode,
|
|
MaxGasPrice: maxGasPrice,
|
|
MaxSlippage: 0.03,
|
|
}
|
|
|
|
assert.NotNil(t, config.MaxGasPrice)
|
|
assert.Equal(t, maxGasPrice, config.MaxGasPrice)
|
|
assert.Equal(t, 0.03, config.MaxSlippage)
|
|
}
|
|
|
|
func TestExecutionConfigWithMinProfit(t *testing.T) {
|
|
minProfit := big.NewInt(1000000000000000) // 0.001 ETH
|
|
|
|
config := &ExecutionConfig{
|
|
Mode: SimulationMode,
|
|
MinProfitThreshold: minProfit,
|
|
}
|
|
|
|
assert.NotNil(t, config.MinProfitThreshold)
|
|
assert.Equal(t, minProfit, config.MinProfitThreshold)
|
|
}
|
|
|
|
func TestExecutionFlashLoanConfig(t *testing.T) {
|
|
config := &ExecutionConfig{
|
|
Mode: LiveMode,
|
|
FlashLoanProvider: "balancer",
|
|
MaxRetries: 5,
|
|
RetryDelay: 500 * time.Millisecond,
|
|
}
|
|
|
|
assert.Equal(t, "balancer", config.FlashLoanProvider)
|
|
assert.Equal(t, 5, config.MaxRetries)
|
|
assert.Equal(t, 500*time.Millisecond, config.RetryDelay)
|
|
}
|
|
|
|
func TestExecutionParallelConfig(t *testing.T) {
|
|
config := &ExecutionConfig{
|
|
Mode: DryRunMode,
|
|
EnableParallelExec: true,
|
|
MaxRetries: 3,
|
|
}
|
|
|
|
assert.True(t, config.EnableParallelExec)
|
|
assert.Equal(t, 3, config.MaxRetries)
|
|
}
|
|
|
|
func TestExecutionTimestamp(t *testing.T) {
|
|
before := time.Now()
|
|
result := &ExecutionResult{
|
|
OpportunityID: "test_opp_003",
|
|
Success: true,
|
|
Timestamp: time.Now(),
|
|
}
|
|
after := time.Now()
|
|
|
|
assert.True(t, result.Timestamp.After(before) || result.Timestamp.Equal(before))
|
|
assert.True(t, result.Timestamp.Before(after) || result.Timestamp.Equal(after))
|
|
}
|
|
|
|
func TestMultipleExecutionResults(t *testing.T) {
|
|
results := make([]*ExecutionResult, 5)
|
|
|
|
for i := 0; i < 5; i++ {
|
|
results[i] = &ExecutionResult{
|
|
OpportunityID: "opp_" + string(rune(i)),
|
|
Success: i%2 == 0,
|
|
GasUsed: uint64(100000 + i*1000),
|
|
ActualProfit: big.NewInt(int64(1000 * (i + 1))),
|
|
ExecutionTime: time.Duration(1000*(i+1)) * time.Millisecond,
|
|
Timestamp: time.Now(),
|
|
}
|
|
}
|
|
|
|
assert.Equal(t, 5, len(results))
|
|
for i, result := range results {
|
|
assert.NotNil(t, result)
|
|
assert.NotEmpty(t, result.OpportunityID)
|
|
assert.Equal(t, i%2 == 0, result.Success)
|
|
}
|
|
}
|
|
|
|
func TestExecutionResultWithZeroProfit(t *testing.T) {
|
|
result := &ExecutionResult{
|
|
OpportunityID: "zero_profit_opp",
|
|
Success: true,
|
|
ActualProfit: big.NewInt(0),
|
|
EstimatedProfit: big.NewInt(100),
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
assert.NotNil(t, result)
|
|
assert.True(t, result.Success)
|
|
assert.Equal(t, int64(0), result.ActualProfit.Int64())
|
|
}
|
|
|
|
func TestExecutionResultWithNegativeProfit(t *testing.T) {
|
|
result := &ExecutionResult{
|
|
OpportunityID: "loss_opp",
|
|
Success: false,
|
|
ActualProfit: big.NewInt(-500),
|
|
EstimatedProfit: big.NewInt(100),
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
assert.NotNil(t, result)
|
|
assert.False(t, result.Success)
|
|
assert.True(t, result.ActualProfit.Sign() < 0)
|
|
}
|
|
|
|
func TestContextTimeout(t *testing.T) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
|
|
defer cancel()
|
|
|
|
config := &ExecutionConfig{
|
|
Mode: SimulationMode,
|
|
DryRun: true,
|
|
}
|
|
|
|
// Should handle context timeout gracefully
|
|
assert.NotNil(t, config)
|
|
<-ctx.Done()
|
|
assert.Error(t, ctx.Err())
|
|
}
|
|
|
|
func TestExecutionConfigValidation(t *testing.T) {
|
|
configs := []struct {
|
|
name string
|
|
config *ExecutionConfig
|
|
valid bool
|
|
}{
|
|
{
|
|
name: "Valid simulation config",
|
|
config: &ExecutionConfig{Mode: SimulationMode, DryRun: true},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "Valid dry run config",
|
|
config: &ExecutionConfig{Mode: DryRunMode, DryRun: true},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "Valid live config",
|
|
config: &ExecutionConfig{Mode: LiveMode, DryRun: false},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "Config with max gas price",
|
|
config: &ExecutionConfig{MaxGasPrice: big.NewInt(100000000)},
|
|
valid: true,
|
|
},
|
|
{
|
|
name: "Config with min profit",
|
|
config: &ExecutionConfig{MinProfitThreshold: big.NewInt(1000000000000000)},
|
|
valid: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range configs {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
assert.NotNil(t, tc.config)
|
|
assert.Equal(t, tc.valid, tc.valid)
|
|
})
|
|
}
|
|
}
|