feat: create v2-prep branch with comprehensive planning
Restructured project for V2 refactor: **Structure Changes:** - Moved all V1 code to orig/ folder (preserved with git mv) - Created docs/planning/ directory - Added orig/README_V1.md explaining V1 preservation **Planning Documents:** - 00_V2_MASTER_PLAN.md: Complete architecture overview - Executive summary of critical V1 issues - High-level component architecture diagrams - 5-phase implementation roadmap - Success metrics and risk mitigation - 07_TASK_BREAKDOWN.md: Atomic task breakdown - 99+ hours of detailed tasks - Every task < 2 hours (atomic) - Clear dependencies and success criteria - Organized by implementation phase **V2 Key Improvements:** - Per-exchange parsers (factory pattern) - Multi-layer strict validation - Multi-index pool cache - Background validation pipeline - Comprehensive observability **Critical Issues Addressed:** - Zero address tokens (strict validation + cache enrichment) - Parsing accuracy (protocol-specific parsers) - No audit trail (background validation channel) - Inefficient lookups (multi-index cache) - Stats disconnection (event-driven metrics) Next Steps: 1. Review planning documents 2. Begin Phase 1: Foundation (P1-001 through P1-010) 3. Implement parsers in Phase 2 4. Build cache system in Phase 3 5. Add validation pipeline in Phase 4 6. Migrate and test in Phase 5 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
346
tests/calculation-validation/validator_test.go
Normal file
346
tests/calculation-validation/validator_test.go
Normal file
@@ -0,0 +1,346 @@
|
||||
package calculation_validation
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidateThresholdCheck(t *testing.T) {
|
||||
validator := NewProfitValidator(1.0)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
check *ThresholdCheck
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "valid executable - profit above threshold",
|
||||
check: &ThresholdCheck{
|
||||
NetProfit: big.NewFloat(834.210302),
|
||||
MinThreshold: big.NewFloat(0.0001),
|
||||
Passed: true,
|
||||
},
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "valid rejection - profit below threshold",
|
||||
check: &ThresholdCheck{
|
||||
NetProfit: big.NewFloat(0.00001),
|
||||
MinThreshold: big.NewFloat(0.0001),
|
||||
Passed: false,
|
||||
},
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - should pass but marked failed",
|
||||
check: &ThresholdCheck{
|
||||
NetProfit: big.NewFloat(1.0),
|
||||
MinThreshold: big.NewFloat(0.0001),
|
||||
Passed: false, // Wrong!
|
||||
},
|
||||
wantValid: false,
|
||||
},
|
||||
{
|
||||
name: "edge case - profit equals threshold",
|
||||
check: &ThresholdCheck{
|
||||
NetProfit: big.NewFloat(0.0001),
|
||||
MinThreshold: big.NewFloat(0.0001),
|
||||
Passed: true,
|
||||
},
|
||||
wantValid: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
valid := validator.validateThresholdCheck(tt.check)
|
||||
if valid != tt.wantValid {
|
||||
t.Errorf("validateThresholdCheck() = %v, want %v", valid, tt.wantValid)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateThresholdComparison(t *testing.T) {
|
||||
validator := NewProfitValidator(1.0)
|
||||
minThresholdWei := big.NewInt(100000000000000) // 0.0001 ETH
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
netProfit *big.Float
|
||||
shouldExecute bool
|
||||
wantValid bool
|
||||
wantExecutable bool
|
||||
}{
|
||||
{
|
||||
name: "bug fix scenario - 834 ETH",
|
||||
netProfit: big.NewFloat(834.210302),
|
||||
shouldExecute: true,
|
||||
wantValid: true,
|
||||
wantExecutable: true,
|
||||
},
|
||||
{
|
||||
name: "below threshold",
|
||||
netProfit: big.NewFloat(0.00001),
|
||||
shouldExecute: false,
|
||||
wantValid: true,
|
||||
wantExecutable: false,
|
||||
},
|
||||
{
|
||||
name: "exactly at threshold",
|
||||
netProfit: big.NewFloat(0.0001),
|
||||
shouldExecute: true,
|
||||
wantValid: true,
|
||||
wantExecutable: true,
|
||||
},
|
||||
{
|
||||
name: "large profit",
|
||||
netProfit: big.NewFloat(1249.324868),
|
||||
shouldExecute: true,
|
||||
wantValid: true,
|
||||
wantExecutable: true,
|
||||
},
|
||||
{
|
||||
name: "buggy comparison - should reject but marked executable",
|
||||
netProfit: big.NewFloat(0.00001),
|
||||
shouldExecute: true, // Wrong!
|
||||
wantValid: false,
|
||||
wantExecutable: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := validator.ValidateThresholdComparison(tt.netProfit, minThresholdWei, tt.shouldExecute)
|
||||
|
||||
if result.IsValid != tt.wantValid {
|
||||
t.Errorf("ValidateThresholdComparison() valid = %v, want %v", result.IsValid, tt.wantValid)
|
||||
if len(result.Errors) > 0 {
|
||||
t.Logf("Errors: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateV3Calculation(t *testing.T) {
|
||||
validator := NewProfitValidator(1.0)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
calc SwapCalculation
|
||||
wantValid bool
|
||||
wantWarns bool
|
||||
}{
|
||||
{
|
||||
name: "valid calculation with 0.3% fee",
|
||||
calc: SwapCalculation{
|
||||
AmountIn: big.NewInt(1000000),
|
||||
AmountOut: big.NewInt(995000),
|
||||
Fee: 3000,
|
||||
FinalOut: big.NewInt(992015), // ~0.3% less
|
||||
},
|
||||
wantValid: true,
|
||||
wantWarns: false,
|
||||
},
|
||||
{
|
||||
name: "zero output",
|
||||
calc: SwapCalculation{
|
||||
AmountIn: big.NewInt(1000000),
|
||||
AmountOut: big.NewInt(0),
|
||||
Fee: 3000,
|
||||
FinalOut: big.NewInt(0),
|
||||
},
|
||||
wantValid: true,
|
||||
wantWarns: true,
|
||||
},
|
||||
{
|
||||
name: "invalid - finalOut > amountOut",
|
||||
calc: SwapCalculation{
|
||||
AmountIn: big.NewInt(1000000),
|
||||
AmountOut: big.NewInt(1000000),
|
||||
Fee: 3000,
|
||||
FinalOut: big.NewInt(1100000), // Impossible!
|
||||
},
|
||||
wantValid: false,
|
||||
wantWarns: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := validator.ValidateV3Calculation(tt.calc)
|
||||
|
||||
if result.IsValid != tt.wantValid {
|
||||
t.Errorf("ValidateV3Calculation() valid = %v, want %v", result.IsValid, tt.wantValid)
|
||||
if len(result.Errors) > 0 {
|
||||
t.Logf("Errors: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
|
||||
hasWarnings := len(result.Warnings) > 0
|
||||
if hasWarnings != tt.wantWarns {
|
||||
t.Errorf("ValidateV3Calculation() warnings = %v, want %v", hasWarnings, tt.wantWarns)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompareETHtoWei(t *testing.T) {
|
||||
validator := NewProfitValidator(1.0)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ethValue *big.Float
|
||||
weiValue *big.Int
|
||||
wantValid bool
|
||||
}{
|
||||
{
|
||||
name: "correct conversion - 1 ETH",
|
||||
ethValue: big.NewFloat(1.0),
|
||||
weiValue: big.NewInt(1e18),
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "correct conversion - 0.0001 ETH",
|
||||
ethValue: big.NewFloat(0.0001),
|
||||
weiValue: big.NewInt(1e14),
|
||||
wantValid: true,
|
||||
},
|
||||
{
|
||||
name: "incorrect conversion - off by factor of 1000",
|
||||
ethValue: big.NewFloat(1.0),
|
||||
weiValue: big.NewInt(1e15), // Wrong!
|
||||
wantValid: false,
|
||||
},
|
||||
{
|
||||
name: "bug scenario - using Int(nil) without scaling",
|
||||
ethValue: big.NewFloat(834.210302),
|
||||
weiValue: big.NewInt(834), // Buggy conversion!
|
||||
wantValid: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := validator.CompareETHtoWei(tt.ethValue, tt.weiValue)
|
||||
|
||||
if result.IsValid != tt.wantValid {
|
||||
t.Errorf("CompareETHtoWei() valid = %v, want %v", result.IsValid, tt.wantValid)
|
||||
if len(result.Errors) > 0 {
|
||||
t.Logf("Errors: %v", result.Errors)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCalculateStatistics(t *testing.T) {
|
||||
profits := []*big.Float{
|
||||
big.NewFloat(0.311819),
|
||||
big.NewFloat(1249.324868),
|
||||
big.NewFloat(2.166576),
|
||||
big.NewFloat(1363.860509),
|
||||
big.NewFloat(83.981698),
|
||||
}
|
||||
|
||||
total, average, max, min := CalculateStatistics(profits)
|
||||
|
||||
// Verify total
|
||||
expectedTotal := 2699.645470 // Sum of all values
|
||||
totalFloat, _ := total.Float64()
|
||||
if diff := totalFloat - expectedTotal; diff > 0.0001 || diff < -0.0001 {
|
||||
t.Errorf("Total = %.6f, want %.6f", totalFloat, expectedTotal)
|
||||
}
|
||||
|
||||
// Verify average
|
||||
expectedAvg := expectedTotal / 5.0
|
||||
avgFloat, _ := average.Float64()
|
||||
if diff := avgFloat - expectedAvg; diff > 0.0001 || diff < -0.0001 {
|
||||
t.Errorf("Average = %.6f, want %.6f", avgFloat, expectedAvg)
|
||||
}
|
||||
|
||||
// Verify max
|
||||
maxFloat, _ := max.Float64()
|
||||
if maxFloat != 1363.860509 {
|
||||
t.Errorf("Max = %.6f, want 1363.860509", maxFloat)
|
||||
}
|
||||
|
||||
// Verify min
|
||||
minFloat, _ := min.Float64()
|
||||
if minFloat != 0.311819 {
|
||||
t.Errorf("Min = %.6f, want 0.311819", minFloat)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateOpportunity(t *testing.T) {
|
||||
validator := NewProfitValidator(1.0)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
opp *OpportunityTestData
|
||||
wantValid bool
|
||||
wantErrs int
|
||||
wantWarns int
|
||||
}{
|
||||
{
|
||||
name: "valid executable opportunity",
|
||||
opp: &OpportunityTestData{
|
||||
ID: "test_1",
|
||||
NetProfitETH: big.NewFloat(1.0),
|
||||
IsExecutable: true,
|
||||
RejectReason: "",
|
||||
ThresholdCheck: &ThresholdCheck{
|
||||
NetProfit: big.NewFloat(1.0),
|
||||
MinThreshold: big.NewFloat(0.0001),
|
||||
Passed: true,
|
||||
},
|
||||
},
|
||||
wantValid: true,
|
||||
wantErrs: 0,
|
||||
wantWarns: 0,
|
||||
},
|
||||
{
|
||||
name: "invalid - executable but zero profit",
|
||||
opp: &OpportunityTestData{
|
||||
ID: "test_2",
|
||||
NetProfitETH: big.NewFloat(0.0),
|
||||
IsExecutable: true,
|
||||
RejectReason: "",
|
||||
},
|
||||
wantValid: false,
|
||||
wantErrs: 1,
|
||||
wantWarns: 0,
|
||||
},
|
||||
{
|
||||
name: "warning - executable but has reject reason",
|
||||
opp: &OpportunityTestData{
|
||||
ID: "test_3",
|
||||
NetProfitETH: big.NewFloat(1.0),
|
||||
IsExecutable: true,
|
||||
RejectReason: "some reason",
|
||||
},
|
||||
wantValid: true,
|
||||
wantErrs: 0,
|
||||
wantWarns: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := validator.ValidateOpportunity(tt.opp)
|
||||
|
||||
if result.IsValid != tt.wantValid {
|
||||
t.Errorf("ValidateOpportunity() valid = %v, want %v", result.IsValid, tt.wantValid)
|
||||
}
|
||||
|
||||
if len(result.Errors) != tt.wantErrs {
|
||||
t.Errorf("ValidateOpportunity() errors = %d, want %d: %v", len(result.Errors), tt.wantErrs, result.Errors)
|
||||
}
|
||||
|
||||
if len(result.Warnings) != tt.wantWarns {
|
||||
t.Errorf("ValidateOpportunity() warnings = %d, want %d: %v", len(result.Warnings), tt.wantWarns, result.Warnings)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user