package validation import ( "math/big" "testing" ) func TestDefaultPriceImpactThresholds(t *testing.T) { thresholds := DefaultPriceImpactThresholds() tests := []struct { name string value float64 expected float64 }{ {"Low threshold", thresholds.LowThreshold, 0.5}, {"Medium threshold", thresholds.MediumThreshold, 2.0}, {"High threshold", thresholds.HighThreshold, 5.0}, {"Extreme threshold", thresholds.ExtremeThreshold, 10.0}, {"Max acceptable", thresholds.MaxAcceptable, 15.0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.value != tt.expected { t.Errorf("%s = %v, want %v", tt.name, tt.value, tt.expected) } }) } } func TestCategorizePriceImpact(t *testing.T) { validator := NewPriceImpactValidator(DefaultPriceImpactThresholds()) tests := []struct { name string priceImpact float64 expectedLevel PriceImpactRiskLevel }{ {"Negligible impact", 0.05, RiskLevelNegligible}, {"Low impact", 0.3, RiskLevelLow}, {"Medium impact", 1.0, RiskLevelMedium}, {"High impact", 3.0, RiskLevelHigh}, {"Extreme impact", 7.0, RiskLevelExtreme}, {"Unacceptable impact", 20.0, RiskLevelUnacceptable}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := validator.ValidatePriceImpact(tt.priceImpact) if result.RiskLevel != tt.expectedLevel { t.Errorf("Risk level = %v, want %v", result.RiskLevel, tt.expectedLevel) } }) } } func TestShouldRejectTrade(t *testing.T) { validator := NewPriceImpactValidator(DefaultPriceImpactThresholds()) tests := []struct { name string priceImpact float64 shouldReject bool }{ {"Low impact - accept", 0.5, false}, {"Medium impact - accept", 2.0, false}, {"High impact - accept", 5.0, false}, {"Extreme impact - accept", 10.0, false}, {"At max threshold - accept", 15.0, false}, {"Above max threshold - reject", 15.1, true}, {"Very high - reject", 30.0, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := validator.ShouldRejectTrade(tt.priceImpact) if result != tt.shouldReject { t.Errorf("ShouldRejectTrade(%v) = %v, want %v", tt.priceImpact, result, tt.shouldReject) } }) } } func TestShouldSplitTrade(t *testing.T) { validator := NewPriceImpactValidator(DefaultPriceImpactThresholds()) tests := []struct { name string priceImpact float64 shouldSplit bool }{ {"Negligible - no split", 0.1, false}, {"Low - no split", 0.5, false}, {"Just below medium - no split", 1.9, false}, {"At medium threshold - split", 2.0, true}, {"High - split", 5.0, true}, {"Extreme - split", 10.0, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := validator.ShouldSplitTrade(tt.priceImpact) if result != tt.shouldSplit { t.Errorf("ShouldSplitTrade(%v) = %v, want %v", tt.priceImpact, result, tt.shouldSplit) } }) } } func TestGetRecommendedSplitCount(t *testing.T) { validator := NewPriceImpactValidator(DefaultPriceImpactThresholds()) tests := []struct { name string priceImpact float64 expectedSplit int }{ {"Low impact - no split", 0.5, 1}, {"Medium impact - split in 2", 2.5, 2}, {"High impact - split in 4", 6.0, 4}, {"Extreme impact - split in 8", 12.0, 8}, {"Unacceptable - reject (0)", 20.0, 0}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := validator.GetRecommendedSplitCount(tt.priceImpact) if result != tt.expectedSplit { t.Errorf("GetRecommendedSplitCount(%v) = %v, want %v", tt.priceImpact, result, tt.expectedSplit) } }) } } func TestCalculateMaxTradeSize(t *testing.T) { validator := NewPriceImpactValidator(DefaultPriceImpactThresholds()) liquidity := big.NewInt(1000000) // 1M units of liquidity tests := []struct { name string liquidity *big.Int targetPriceImpact float64 expectedApproximate int64 // Approximate expected value }{ {"0.5% impact", liquidity, 0.5, 5025}, // ~0.5% of 1M {"1% impact", liquidity, 1.0, 10101}, // ~1% of 1M {"2% impact", liquidity, 2.0, 20408}, // ~2% of 1M {"5% impact", liquidity, 5.0, 52631}, // ~5% of 1M {"10% impact", liquidity, 10.0, 111111}, // ~10% of 1M } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := validator.CalculateMaxTradeSize(tt.liquidity, tt.targetPriceImpact) // Check if result is within 5% of expected value resultInt64 := result.Int64() lowerBound := int64(float64(tt.expectedApproximate) * 0.95) upperBound := int64(float64(tt.expectedApproximate) * 1.05) if resultInt64 < lowerBound || resultInt64 > upperBound { t.Errorf("CalculateMaxTradeSize() = %v, expected approximately %v (±5%%)", result, tt.expectedApproximate) } }) } } func TestValidatePriceImpactWithLiquidity(t *testing.T) { validator := NewPriceImpactValidator(DefaultPriceImpactThresholds()) liquidity := big.NewInt(1000000) // 1M units tests := []struct { name string tradeSize *big.Int liquidity *big.Int expectedRiskLevel PriceImpactRiskLevel }{ {"Small trade", big.NewInt(1000), liquidity, RiskLevelNegligible}, {"Medium trade", big.NewInt(20000), liquidity, RiskLevelMedium}, {"Large trade", big.NewInt(100000), liquidity, RiskLevelExtreme}, {"Very large trade", big.NewInt(500000), liquidity, RiskLevelUnacceptable}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := validator.ValidatePriceImpactWithLiquidity(tt.tradeSize, tt.liquidity) if result.RiskLevel != tt.expectedRiskLevel { t.Errorf("Risk level = %v, want %v (price impact: %.2f%%)", result.RiskLevel, tt.expectedRiskLevel, result.PriceImpact) } }) } } func TestConservativeThresholds(t *testing.T) { validator := NewPriceImpactValidator(ConservativePriceImpactThresholds()) // Test that conservative thresholds are more strict // With conservative: High=1.0%, Extreme=2.0% // So 1.0% exactly is at the boundary and goes to Extreme result := validator.ValidatePriceImpact(1.0) if result.RiskLevel != RiskLevelExtreme { t.Errorf("With conservative thresholds, 1%% should be Extreme risk, got %v", result.RiskLevel) } } func TestAggressiveThresholds(t *testing.T) { validator := NewPriceImpactValidator(AggressivePriceImpactThresholds()) // Test that aggressive thresholds are more lenient // With aggressive: Low=1.0%, Medium=3.0% // So 2.0% falls in the Medium range (between 1.0 and 3.0) result := validator.ValidatePriceImpact(2.0) if result.RiskLevel != RiskLevelMedium { t.Errorf("With aggressive thresholds, 2%% should be Medium risk, got %v", result.RiskLevel) } } func BenchmarkValidatePriceImpact(b *testing.B) { validator := NewPriceImpactValidator(DefaultPriceImpactThresholds()) b.ResetTimer() for i := 0; i < b.N; i++ { validator.ValidatePriceImpact(2.5) } } func BenchmarkValidatePriceImpactWithLiquidity(b *testing.B) { validator := NewPriceImpactValidator(DefaultPriceImpactThresholds()) tradeSize := big.NewInt(50000) liquidity := big.NewInt(1000000) b.ResetTimer() for i := 0; i < b.N; i++ { validator.ValidatePriceImpactWithLiquidity(tradeSize, liquidity) } }