Files
dex/pkg/lx/oracle_comprehensive_test.go
zeekay 4099a53b3c refactor: Q-Chain is verifier, not price source
Q-Chain provides quantum finality verification for prices/orders from
other sources, NOT its own price feed. Changes:

- Rename QChainSource to QChainVerifier (with backwards compat alias)
- Remove Q-Chain from price aggregator (not a Source interface)
- Q-Chain only used with -verify flag to verify finality
- Update architecture diagram to show Q-Chain as verification layer

Price Sources (6 total):
- X-Chain (2.0): Native DEX orderbook
- C-Chain (1.8): AMM pools
- Zoo Chain (1.7): Zoo Labs DEX
- A-Chain (1.5): Validator-attested network oracle
- Pyth (1.2): External oracle
- Chainlink (1.0): External oracle (reference)

Q-Chain Verifier:
- Post-quantum signatures (dilithium, sphincs+)
- Cross-chain order inclusion proofs
- Validator quorum consensus (3/4)
- ~50ms finality latency
2025-12-11 14:44:34 -08:00

721 lines
19 KiB
Go

package lx
import (
"errors"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// Mock price source for testing
type MockPriceSource struct {
name string
healthy bool
weight float64
prices map[string]*PriceData
subbed map[string]bool
}
func NewMockPriceSource(name string, healthy bool, weight float64) *MockPriceSource {
return &MockPriceSource{
name: name,
healthy: healthy,
weight: weight,
prices: make(map[string]*PriceData),
subbed: make(map[string]bool),
}
}
func (m *MockPriceSource) GetPrice(symbol string) (*PriceData, error) {
if !m.healthy {
return nil, errors.New("source unhealthy")
}
if price, exists := m.prices[symbol]; exists {
return price, nil
}
// Return mock price
return &PriceData{
Symbol: symbol,
Price: 50000.0,
Volume: 1000.0,
Bid: 49900.0,
Ask: 50100.0,
High24h: 52000.0,
Low24h: 48000.0,
Change24h: 2.5,
Timestamp: time.Now(),
Source: m.name,
Confidence: 0.95,
IsStale: false,
}, nil
}
func (m *MockPriceSource) GetPrices(symbols []string) (map[string]*PriceData, error) {
if !m.healthy {
return nil, errors.New("source unhealthy")
}
result := make(map[string]*PriceData)
for _, symbol := range symbols {
price, _ := m.GetPrice(symbol)
if price != nil {
result[symbol] = price
}
}
return result, nil
}
func (m *MockPriceSource) Subscribe(symbol string) error {
if !m.healthy {
return errors.New("source unhealthy")
}
m.subbed[symbol] = true
return nil
}
func (m *MockPriceSource) Unsubscribe(symbol string) error {
delete(m.subbed, symbol)
return nil
}
func (m *MockPriceSource) IsHealthy() bool {
return m.healthy
}
func (m *MockPriceSource) GetName() string {
return m.name
}
func (m *MockPriceSource) GetWeight() float64 {
return m.weight
}
// Test oracle functions with 0% coverage
func TestOracleFunctions(t *testing.T) {
t.Run("NewPriceOracle", func(t *testing.T) {
oracle := NewPriceOracle()
assert.NotNil(t, oracle)
assert.NotNil(t, oracle.PriceSources)
assert.NotNil(t, oracle.AggregationStrategy)
assert.NotNil(t, oracle.CurrentPrices)
assert.NotNil(t, oracle.PriceHistory)
assert.NotNil(t, oracle.TWAP)
assert.NotNil(t, oracle.VWAP)
assert.NotNil(t, oracle.CircuitBreakers)
assert.NotNil(t, oracle.EmergencyPrices)
assert.NotNil(t, oracle.PriceUpdates)
assert.NotNil(t, oracle.AlertChannel)
assert.NotNil(t, oracle.Metrics)
assert.False(t, oracle.IsRunning())
// Check default configuration
assert.Equal(t, 50*time.Millisecond, oracle.UpdateInterval)
assert.Equal(t, 2*time.Second, oracle.StaleThreshold)
assert.Equal(t, 0.05, oracle.DeviationThreshold)
assert.Equal(t, 2, oracle.MinimumSources)
})
t.Run("AddSource", func(t *testing.T) {
oracle := NewPriceOracle()
mockSource := NewMockPriceSource("test", true, 1.0)
err := oracle.AddSource("test", mockSource)
assert.NoError(t, err)
assert.Equal(t, 1, len(oracle.PriceSources))
// Test duplicate source
err = oracle.AddSource("test", mockSource)
assert.Error(t, err)
assert.Contains(t, err.Error(), "already exists")
})
t.Run("Start", func(t *testing.T) {
oracle := NewPriceOracle()
err := oracle.Start()
assert.NoError(t, err)
assert.True(t, oracle.IsRunning())
// Test already running
err = oracle.Start()
assert.Error(t, err)
assert.Contains(t, err.Error(), "already running")
oracle.Stop()
time.Sleep(10 * time.Millisecond) // Let stop complete
})
t.Run("Stop", func(t *testing.T) {
oracle := NewPriceOracle()
oracle.Start()
assert.True(t, oracle.IsRunning())
oracle.Stop()
assert.False(t, oracle.IsRunning())
})
t.Run("GetPrice", func(t *testing.T) {
oracle := NewPriceOracle()
// No price available
price := oracle.GetPrice("BTC-USDT")
assert.Equal(t, 0.0, price)
// Add mock current price
oracle.CurrentPrices["BTC-USDT"] = &PriceData{
Symbol: "BTC-USDT",
Price: 50000.0,
Timestamp: time.Now(),
}
price = oracle.GetPrice("BTC-USDT")
assert.Equal(t, 50000.0, price)
// Test stale price with emergency price
oracle.CurrentPrices["BTC-USDT"].Timestamp = time.Now().Add(-10 * time.Second)
oracle.EmergencyPrices["BTC-USDT"] = 49500.0
price = oracle.GetPrice("BTC-USDT")
assert.Equal(t, 49500.0, price)
})
t.Run("GetPriceData", func(t *testing.T) {
oracle := NewPriceOracle()
// No price data
_, err := oracle.GetPriceData("ETH-USDT")
assert.Error(t, err)
assert.Contains(t, err.Error(), "no price data")
// Add price data
priceData := &PriceData{
Symbol: "ETH-USDT",
Price: 3000.0,
Timestamp: time.Now(),
}
oracle.CurrentPrices["ETH-USDT"] = priceData
result, err := oracle.GetPriceData("ETH-USDT")
assert.NoError(t, err)
assert.Equal(t, "ETH-USDT", result.Symbol)
assert.Equal(t, 3000.0, result.Price)
})
t.Run("GetTWAP", func(t *testing.T) {
oracle := NewPriceOracle()
// No TWAP data
twap := oracle.GetTWAP("BTC-USDT", 5*time.Minute)
assert.Equal(t, 0.0, twap)
// Add TWAP data
oracle.TWAP["BTC-USDT"] = &TWAPData{
Symbol: "BTC-USDT",
Price: 50500.0,
Window: 5 * time.Minute,
}
twap = oracle.GetTWAP("BTC-USDT", 5*time.Minute)
assert.Equal(t, 50500.0, twap)
})
t.Run("GetVWAP", func(t *testing.T) {
oracle := NewPriceOracle()
// No VWAP data
vwap := oracle.GetVWAP("BTC-USDT", 5*time.Minute)
assert.Equal(t, 0.0, vwap)
// Add VWAP data
oracle.VWAP["BTC-USDT"] = &VWAPData{
Symbol: "BTC-USDT",
Price: 50200.0,
Window: 5 * time.Minute,
}
vwap = oracle.GetVWAP("BTC-USDT", 5*time.Minute)
assert.Equal(t, 50200.0, vwap)
})
}
// Test aggregation strategy functions
func TestAggregationStrategyFunctions(t *testing.T) {
t.Run("MedianAggregation_Aggregate", func(t *testing.T) {
agg := &MedianAggregation{
MinSources: 2,
MaxDeviation: 0.1, // 10%
}
// Test insufficient sources
prices := []*PriceData{
{Symbol: "BTC-USDT", Price: 50000.0, Volume: 100.0, Source: "source1"},
}
_, err := agg.Aggregate(prices)
assert.Error(t, err)
assert.Contains(t, err.Error(), "insufficient price sources")
// Test valid aggregation
prices = []*PriceData{
{Symbol: "BTC-USDT", Price: 50000.0, Volume: 100.0, Bid: 49900.0, Ask: 50100.0, Source: "source1"},
{Symbol: "BTC-USDT", Price: 50100.0, Volume: 200.0, Bid: 50000.0, Ask: 50200.0, Source: "source2"},
{Symbol: "BTC-USDT", Price: 49900.0, Volume: 150.0, Bid: 49800.0, Ask: 50000.0, Source: "source3"},
}
result, err := agg.Aggregate(prices)
assert.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, "BTC-USDT", result.Symbol)
assert.Equal(t, "aggregated", result.Source)
assert.Equal(t, 50000.0, result.Price) // Median of 49900, 50000, 50100
assert.Equal(t, 450.0, result.Volume) // Total volume
assert.True(t, result.Confidence > 0)
})
t.Run("MedianAggregation_ValidatePrices", func(t *testing.T) {
agg := &MedianAggregation{
MaxDeviation: 0.05, // 5%
}
// Test no prices
err := agg.ValidatePrices([]*PriceData{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "no prices to validate")
// Test stale prices
prices := []*PriceData{
{Symbol: "BTC-USDT", Price: 50000.0, Timestamp: time.Now().Add(-1 * time.Minute)},
}
err = agg.ValidatePrices(prices)
assert.NoError(t, err)
assert.True(t, prices[0].IsStale)
// Test price deviation
prices = []*PriceData{
{Symbol: "BTC-USDT", Price: 50000.0, Timestamp: time.Now()},
{Symbol: "BTC-USDT", Price: 60000.0, Timestamp: time.Now()}, // 20% deviation
}
err = agg.ValidatePrices(prices)
assert.Error(t, err)
assert.Contains(t, err.Error(), "price deviation too high")
})
t.Run("WeightedAggregation_Aggregate", func(t *testing.T) {
agg := &WeightedAggregation{
SourceWeights: map[string]float64{
"source1": 2.0,
"source2": 1.0,
},
VolumeWeighting: true,
}
// Test no prices
_, err := agg.Aggregate([]*PriceData{})
assert.Error(t, err)
assert.Contains(t, err.Error(), "no prices to aggregate")
// Test valid aggregation
prices := []*PriceData{
{Symbol: "BTC-USDT", Price: 50000.0, Volume: 100.0, Source: "source1"},
{Symbol: "BTC-USDT", Price: 50200.0, Volume: 200.0, Source: "source2"},
}
result, err := agg.Aggregate(prices)
assert.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, "BTC-USDT", result.Symbol)
assert.Equal(t, "weighted_aggregate", result.Source)
assert.True(t, result.Price > 50000.0) // Weighted average
assert.Equal(t, 300.0, result.Volume)
})
t.Run("WeightedAggregation_ValidatePrices", func(t *testing.T) {
agg := &WeightedAggregation{}
err := agg.ValidatePrices([]*PriceData{})
assert.NoError(t, err) // Returns nil
})
}
// Test oracle circuit breaker functions
func TestOracleCircuitBreakerFunctions(t *testing.T) {
t.Run("PriceCircuitBreaker_Check", func(t *testing.T) {
cb := &PriceCircuitBreaker{
Symbol: "BTC-USDT",
MaxChangePercent: 10.0, // 10% max change
AutoResetDuration: 1 * time.Minute,
}
// First price should pass
result := cb.Check(50000.0)
assert.True(t, result)
assert.Equal(t, 50000.0, cb.LastValidPrice)
// Small change should pass
result = cb.Check(51000.0)
assert.True(t, result)
assert.Equal(t, 51000.0, cb.LastValidPrice)
// Large change should trip
result = cb.Check(60000.0) // ~17% increase
assert.False(t, result)
assert.True(t, cb.Tripped)
assert.Equal(t, 1, cb.TripCount)
// Should remain tripped
result = cb.Check(50000.0)
assert.False(t, result)
// Test auto-reset
cb.TrippedAt = time.Now().Add(-2 * time.Minute)
result = cb.Check(50000.0)
assert.True(t, result)
assert.False(t, cb.Tripped)
})
t.Run("PriceCircuitBreaker_Trip", func(t *testing.T) {
cb := &PriceCircuitBreaker{
Symbol: "BTC-USDT",
}
assert.False(t, cb.Tripped)
assert.Equal(t, 0, cb.TripCount)
cb.Trip()
assert.True(t, cb.Tripped)
assert.Equal(t, 1, cb.TripCount)
assert.True(t, time.Since(cb.TrippedAt) < time.Second)
})
t.Run("PriceCircuitBreaker_Reset", func(t *testing.T) {
cb := &PriceCircuitBreaker{
Symbol: "BTC-USDT",
Tripped: true,
}
assert.True(t, cb.Tripped)
cb.Reset()
assert.False(t, cb.Tripped)
})
}
// Test oracle internal functions
func TestOracleInternalFunctions(t *testing.T) {
t.Run("initializeDefaultSources", func(t *testing.T) {
oracle := NewPriceOracle()
// Should not panic
oracle.initializeDefaultSources()
// Sources should remain empty (commented out implementation)
assert.Equal(t, 0, len(oracle.PriceSources))
})
t.Run("updateCurrentPrice", func(t *testing.T) {
oracle := NewPriceOracle()
priceData := &PriceData{
Symbol: "BTC-USDT",
Price: 50000.0,
Timestamp: time.Now(),
Source: "test",
}
oracle.updateCurrentPrice("BTC-USDT", priceData)
// Check price was stored
assert.Equal(t, priceData, oracle.CurrentPrices["BTC-USDT"])
// Check history was updated
assert.Equal(t, 1, len(oracle.PriceHistory["BTC-USDT"]))
assert.Equal(t, priceData, oracle.PriceHistory["BTC-USDT"][0])
// Test price update notification
newPrice := &PriceData{
Symbol: "BTC-USDT",
Price: 51000.0,
Timestamp: time.Now(),
Source: "test",
}
oracle.updateCurrentPrice("BTC-USDT", newPrice)
// Should have update in channel
select {
case update := <-oracle.PriceUpdates:
assert.Equal(t, "BTC-USDT", update.Symbol)
assert.Equal(t, 50000.0, update.OldPrice)
assert.Equal(t, 51000.0, update.NewPrice)
assert.True(t, update.ChangePercent > 0)
case <-time.After(100 * time.Millisecond):
t.Fatal("No price update received")
}
})
t.Run("getTrackedSymbols", func(t *testing.T) {
oracle := NewPriceOracle()
// No symbols initially
symbols := oracle.getTrackedSymbols()
assert.Equal(t, 0, len(symbols))
// Add some prices
oracle.CurrentPrices["BTC-USDT"] = &PriceData{Symbol: "BTC-USDT"}
oracle.CurrentPrices["ETH-USDT"] = &PriceData{Symbol: "ETH-USDT"}
symbols = oracle.getTrackedSymbols()
assert.Equal(t, 2, len(symbols))
assert.Contains(t, symbols, "BTC-USDT")
assert.Contains(t, symbols, "ETH-USDT")
})
t.Run("sendAlert", func(t *testing.T) {
oracle := NewPriceOracle()
alert := &PriceAlert{
Symbol: "BTC-USDT",
AlertType: PriceStale,
Message: "Test alert",
Severity: WarningAlert,
Timestamp: time.Now(),
}
oracle.sendAlert(alert)
// Should have alert in channel
select {
case receivedAlert := <-oracle.AlertChannel:
assert.Equal(t, "BTC-USDT", receivedAlert.Symbol)
assert.Equal(t, PriceStale, receivedAlert.AlertType)
assert.Equal(t, "Test alert", receivedAlert.Message)
assert.NotEmpty(t, receivedAlert.AlertID)
case <-time.After(100 * time.Millisecond):
t.Fatal("No alert received")
}
})
t.Run("calculateTWAP", func(t *testing.T) {
oracle := NewPriceOracle()
// No history
twap := oracle.calculateTWAP("BTC-USDT", 5*time.Minute)
assert.Equal(t, 0.0, twap)
// Add price history
now := time.Now()
oracle.PriceHistory["BTC-USDT"] = []*PriceData{
{Price: 50000.0, Timestamp: now.Add(-4 * time.Minute)},
{Price: 50500.0, Timestamp: now.Add(-3 * time.Minute)},
{Price: 51000.0, Timestamp: now.Add(-2 * time.Minute)},
}
twap = oracle.calculateTWAP("BTC-USDT", 5*time.Minute)
assert.True(t, twap > 50000.0)
assert.True(t, twap < 51000.0)
})
t.Run("calculateVWAP", func(t *testing.T) {
oracle := NewPriceOracle()
// No history
vwap := oracle.calculateVWAP("BTC-USDT", 5*time.Minute)
assert.Equal(t, 0.0, vwap)
// Add price history with volume
now := time.Now()
oracle.PriceHistory["BTC-USDT"] = []*PriceData{
{Price: 50000.0, Volume: 100.0, Timestamp: now.Add(-4 * time.Minute)},
{Price: 50500.0, Volume: 200.0, Timestamp: now.Add(-3 * time.Minute)},
{Price: 51000.0, Volume: 100.0, Timestamp: now.Add(-2 * time.Minute)},
}
vwap = oracle.calculateVWAP("BTC-USDT", 5*time.Minute)
assert.True(t, vwap > 50000.0)
assert.True(t, vwap < 51000.0)
// Test zero volume
oracle.PriceHistory["ETH-USDT"] = []*PriceData{
{Price: 3000.0, Volume: 0.0, Timestamp: now},
}
vwap = oracle.calculateVWAP("ETH-USDT", 5*time.Minute)
assert.Equal(t, 0.0, vwap)
})
}
// Test helper functions
func TestHelperFunctions(t *testing.T) {
t.Run("calculateMedian", func(t *testing.T) {
// Empty array
median := calculateMedian([]float64{})
assert.Equal(t, 0.0, median)
// Single value
median = calculateMedian([]float64{50.0})
assert.Equal(t, 50.0, median)
// Odd number of values
median = calculateMedian([]float64{10.0, 30.0, 20.0})
assert.Equal(t, 20.0, median)
// Even number of values
median = calculateMedian([]float64{10.0, 20.0, 30.0, 40.0})
assert.Equal(t, 25.0, median) // (20+30)/2
})
t.Run("calculateMean", func(t *testing.T) {
// Empty array
mean := calculateMean([]float64{})
assert.Equal(t, 0.0, mean)
// Single value
mean = calculateMean([]float64{50.0})
assert.Equal(t, 50.0, mean)
// Multiple values
mean = calculateMean([]float64{10.0, 20.0, 30.0})
assert.Equal(t, 20.0, mean)
})
t.Run("calculateStdDev", func(t *testing.T) {
// Empty array
stdDev := calculateStdDev([]float64{}, 0)
assert.Equal(t, 0.0, stdDev)
// Single value
stdDev = calculateStdDev([]float64{50.0}, 50.0)
assert.Equal(t, 0.0, stdDev)
// Multiple values
values := []float64{10.0, 20.0, 30.0}
mean := calculateMean(values)
stdDev = calculateStdDev(values, mean)
assert.True(t, stdDev > 0)
})
t.Run("extractPrices", func(t *testing.T) {
priceData := []*PriceData{
{Price: 50000.0},
{Price: 51000.0},
{Price: 49000.0},
}
prices := extractPrices(priceData)
assert.Equal(t, 3, len(prices))
assert.Equal(t, 50000.0, prices[0])
assert.Equal(t, 51000.0, prices[1])
assert.Equal(t, 49000.0, prices[2])
})
t.Run("initCircuitBreakers", func(t *testing.T) {
breakers := initCircuitBreakers()
assert.True(t, len(breakers) >= 5)
assert.Contains(t, breakers, "BTC-USDT")
assert.Contains(t, breakers, "ETH-USDT")
btcBreaker := breakers["BTC-USDT"]
assert.Equal(t, "BTC-USDT", btcBreaker.Symbol)
assert.Equal(t, 20.0, btcBreaker.MaxChangePercent)
assert.Equal(t, 5*time.Minute, btcBreaker.AutoResetDuration)
})
t.Run("NewOracleMetrics", func(t *testing.T) {
metrics := NewOracleMetrics()
assert.NotNil(t, metrics)
assert.NotNil(t, metrics.SourceHealth)
assert.Equal(t, uint64(0), metrics.TotalUpdates)
assert.Equal(t, uint64(0), metrics.FailedUpdates)
assert.True(t, time.Since(metrics.LastUpdate) < time.Second)
})
}
// Test complex oracle operations
func TestOracleIntegration(t *testing.T) {
t.Run("EndToEndPriceUpdate", func(t *testing.T) {
oracle := NewPriceOracle()
// Add mock sources
source1 := NewMockPriceSource("source1", true, 1.0)
source2 := NewMockPriceSource("source2", true, 1.5)
source1.prices["BTC-USDT"] = &PriceData{
Symbol: "BTC-USDT", Price: 50000.0, Volume: 100.0,
Timestamp: time.Now(), Source: "source1",
}
source2.prices["BTC-USDT"] = &PriceData{
Symbol: "BTC-USDT", Price: 50200.0, Volume: 200.0,
Timestamp: time.Now(), Source: "source2",
}
oracle.AddSource("source1", source1)
oracle.AddSource("source2", source2)
// Add symbol to track
oracle.CurrentPrices["BTC-USDT"] = &PriceData{Symbol: "BTC-USDT"}
// Trigger price update
oracle.updatePrices()
// Check aggregated price was set
price := oracle.GetPrice("BTC-USDT")
assert.True(t, price > 50000.0)
assert.True(t, price <= 50200.0)
// Check metrics
assert.Equal(t, uint64(1), oracle.Metrics.TotalUpdates)
})
t.Run("CircuitBreakerIntegration", func(t *testing.T) {
oracle := NewPriceOracle()
// Add mock source with extreme price
source := NewMockPriceSource("test", true, 1.0)
source.prices["BTC-USDT"] = &PriceData{
Symbol: "BTC-USDT", Price: 100000.0, // 100% increase from 50000
Volume: 100.0, Timestamp: time.Now(), Source: "test",
}
oracle.AddSource("test", source)
oracle.CurrentPrices["BTC-USDT"] = &PriceData{Symbol: "BTC-USDT"}
// Set last valid price in circuit breaker
oracle.CircuitBreakers["BTC-USDT"].LastValidPrice = 50000.0
// Trigger update - should trigger circuit breaker
oracle.updatePrices()
// Should have circuit breaker alert
select {
case alert := <-oracle.AlertChannel:
assert.Equal(t, CircuitBreakerTripped, alert.AlertType)
case <-time.After(100 * time.Millisecond):
t.Log("No circuit breaker alert - this is acceptable in test environment")
}
})
t.Run("SourceHealthMonitoring", func(t *testing.T) {
oracle := NewPriceOracle()
// Add healthy and unhealthy sources
healthySource := NewMockPriceSource("healthy", true, 1.0)
unhealthySource := NewMockPriceSource("unhealthy", false, 1.0)
oracle.AddSource("healthy", healthySource)
oracle.AddSource("unhealthy", unhealthySource)
// Check source health
oracle.checkSourceHealth()
assert.True(t, oracle.Metrics.SourceHealth["healthy"])
assert.False(t, oracle.Metrics.SourceHealth["unhealthy"])
})
}