Completed clean root directory structure: - Root now contains only: .git, .env, docs/, orig/ - Moved all remaining files and directories to orig/: - Config files (.claude, .dockerignore, .drone.yml, etc.) - All .env variants (except active .env) - Git config (.gitconfig, .github, .gitignore, etc.) - Tool configs (.golangci.yml, .revive.toml, etc.) - Documentation (*.md files, @prompts) - Build files (Dockerfiles, Makefile, go.mod, go.sum) - Docker compose files - All source directories (scripts, tests, tools, etc.) - Runtime directories (logs, monitoring, reports) - Dependency files (node_modules, lib, cache) - Special files (--delete) - Removed empty runtime directories (bin/, data/) V2 structure is now clean: - docs/planning/ - V2 planning documents - orig/ - Complete V1 codebase preserved - .env - Active environment config (not in git) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
365 lines
11 KiB
Go
365 lines
11 KiB
Go
//go:build integration && legacy && forked
|
|
// +build integration,legacy,forked
|
|
|
|
package test_main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math/big"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/holiman/uint256"
|
|
|
|
"github.com/fraktal/mev-beta/internal/logger"
|
|
"github.com/fraktal/mev-beta/pkg/database"
|
|
"github.com/fraktal/mev-beta/pkg/events"
|
|
"github.com/fraktal/mev-beta/pkg/market"
|
|
"github.com/fraktal/mev-beta/pkg/marketdata"
|
|
)
|
|
|
|
// TestComprehensiveMarketDataLogging tests the complete market data logging system
|
|
func TestComprehensiveMarketDataLogging(t *testing.T) {
|
|
// Create logger
|
|
log := logger.New("info", "text", "")
|
|
|
|
t.Log("=== Comprehensive Market Data Logging Test ===")
|
|
|
|
// Test 1: Initialize Market Data Logger
|
|
t.Log("\n--- Test 1: Market Data Logger Initialization ---")
|
|
|
|
// Create mock database (in production would be real database)
|
|
db := &database.Database{} // Mock database
|
|
|
|
// Initialize market data logger
|
|
dataLogger := marketdata.NewMarketDataLogger(log, db)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
err := dataLogger.Initialize(ctx)
|
|
if err != nil {
|
|
t.Errorf("Failed to initialize market data logger: %v", err)
|
|
}
|
|
|
|
stats := dataLogger.GetStatistics()
|
|
t.Logf("Initial statistics: %+v", stats)
|
|
|
|
// Verify initial state
|
|
if !stats["initialized"].(bool) {
|
|
t.Error("Market data logger should be initialized")
|
|
}
|
|
|
|
// Test 2: Token Caching and Management
|
|
t.Log("\n--- Test 2: Token Caching and Management ---")
|
|
|
|
// Test known tokens
|
|
wethAddr := common.HexToAddress("0x82af49447d8a07e3bd95bd0d56f35241523fbab1")
|
|
usdcAddr := common.HexToAddress("0xaf88d065e77c8cc2239327c5edb3a432268e5831")
|
|
|
|
// Get token info
|
|
wethInfo, exists := dataLogger.GetTokenInfo(wethAddr)
|
|
if !exists {
|
|
t.Error("WETH should be in token cache")
|
|
} else {
|
|
t.Logf("WETH token info: Symbol=%s, Verified=%t", wethInfo.Symbol, wethInfo.IsVerified)
|
|
}
|
|
|
|
usdcInfo, exists := dataLogger.GetTokenInfo(usdcAddr)
|
|
if !exists {
|
|
t.Error("USDC should be in token cache")
|
|
} else {
|
|
t.Logf("USDC token info: Symbol=%s, Verified=%t", usdcInfo.Symbol, usdcInfo.IsVerified)
|
|
}
|
|
|
|
// Test token lookup by symbol
|
|
wethTokens := dataLogger.GetTokensBySymbol("WETH")
|
|
if len(wethTokens) == 0 {
|
|
t.Error("Should find WETH tokens by symbol")
|
|
} else {
|
|
t.Logf("Found %d WETH tokens", len(wethTokens))
|
|
}
|
|
|
|
// Test 3: Swap Event Logging
|
|
t.Log("\n--- Test 3: Comprehensive Swap Event Logging ---")
|
|
|
|
// Create test swap event
|
|
swapEvent := events.Event{
|
|
Type: events.Swap,
|
|
TransactionHash: common.HexToHash("0x1234567890abcdef"),
|
|
BlockNumber: 12345678,
|
|
PoolAddress: common.HexToAddress("0xC6962004f452bE9203591991D15f6b388e09E8D0"), // WETH/USDC pool
|
|
Protocol: "UniswapV3",
|
|
Token0: wethAddr,
|
|
Token1: usdcAddr,
|
|
Amount0: big.NewInt(-1000000000000000000), // -1 WETH (out)
|
|
Amount1: big.NewInt(2000000000), // 2000 USDC (in)
|
|
SqrtPriceX96: uint256.NewInt(1771845812700481934),
|
|
Liquidity: uint256.NewInt(1000000000000000000),
|
|
Tick: -74959,
|
|
}
|
|
|
|
// Create comprehensive swap data
|
|
swapData := &marketdata.SwapEventData{
|
|
TxHash: swapEvent.TransactionHash,
|
|
BlockNumber: swapEvent.BlockNumber,
|
|
Timestamp: time.Now(),
|
|
PoolAddress: swapEvent.PoolAddress,
|
|
Protocol: swapEvent.Protocol,
|
|
Token0: swapEvent.Token0,
|
|
Token1: swapEvent.Token1,
|
|
Amount0Out: big.NewInt(1000000000000000000), // 1 WETH out
|
|
Amount1In: big.NewInt(2000000000), // 2000 USDC in
|
|
Amount0In: big.NewInt(0),
|
|
Amount1Out: big.NewInt(0),
|
|
SqrtPriceX96: swapEvent.SqrtPriceX96,
|
|
Liquidity: swapEvent.Liquidity,
|
|
Tick: int32(swapEvent.Tick),
|
|
AmountInUSD: 2000.0,
|
|
AmountOutUSD: 2000.0,
|
|
}
|
|
|
|
// Log the swap event
|
|
err = dataLogger.LogSwapEvent(ctx, swapEvent, swapData)
|
|
if err != nil {
|
|
t.Errorf("Failed to log swap event: %v", err)
|
|
}
|
|
|
|
t.Logf("Logged swap event: %s -> %s, Amount: %s USDC -> %s WETH",
|
|
swapData.Token1.Hex()[:8], swapData.Token0.Hex()[:8],
|
|
swapData.Amount1In.String(), swapData.Amount0Out.String())
|
|
|
|
// Test 4: Liquidity Event Logging
|
|
t.Log("\n--- Test 4: Comprehensive Liquidity Event Logging ---")
|
|
|
|
// Create test liquidity event
|
|
liquidityEvent := events.Event{
|
|
Type: events.AddLiquidity,
|
|
TransactionHash: common.HexToHash("0xabcdef1234567890"),
|
|
BlockNumber: 12345679,
|
|
PoolAddress: swapEvent.PoolAddress,
|
|
Protocol: "UniswapV3",
|
|
Token0: wethAddr,
|
|
Token1: usdcAddr,
|
|
Amount0: big.NewInt(5000000000000000000), // 5 WETH
|
|
Amount1: big.NewInt(10000000000), // 10000 USDC
|
|
Liquidity: uint256.NewInt(7071067811865475244), // sqrt(5 * 10000)
|
|
SqrtPriceX96: uint256.NewInt(1771845812700481934),
|
|
Tick: -74959,
|
|
}
|
|
|
|
// Create comprehensive liquidity data
|
|
liquidityData := &marketdata.LiquidityEventData{
|
|
TxHash: liquidityEvent.TransactionHash,
|
|
BlockNumber: liquidityEvent.BlockNumber,
|
|
Timestamp: time.Now(),
|
|
EventType: "mint",
|
|
PoolAddress: liquidityEvent.PoolAddress,
|
|
Protocol: liquidityEvent.Protocol,
|
|
Token0: liquidityEvent.Token0,
|
|
Token1: liquidityEvent.Token1,
|
|
Amount0: liquidityEvent.Amount0,
|
|
Amount1: liquidityEvent.Amount1,
|
|
Liquidity: liquidityEvent.Liquidity,
|
|
Amount0USD: 10000.0, // 5 WETH * $2000
|
|
Amount1USD: 10000.0, // 10000 USDC
|
|
TotalUSD: 20000.0,
|
|
}
|
|
|
|
// Log the liquidity event
|
|
err = dataLogger.LogLiquidityEvent(ctx, liquidityEvent, liquidityData)
|
|
if err != nil {
|
|
t.Errorf("Failed to log liquidity event: %v", err)
|
|
}
|
|
|
|
t.Logf("Logged %s liquidity event: %s WETH + %s USDC = %s liquidity",
|
|
liquidityData.EventType,
|
|
liquidityData.Amount0.String(),
|
|
liquidityData.Amount1.String(),
|
|
liquidityData.Liquidity.ToBig().String())
|
|
|
|
// Test 5: Pool Discovery and Caching
|
|
t.Log("\n--- Test 5: Pool Discovery and Caching ---")
|
|
|
|
// Get pool info that should have been cached
|
|
poolInfo, exists := dataLogger.GetPoolInfo(swapEvent.PoolAddress)
|
|
if !exists {
|
|
t.Error("Pool should be cached after swap event")
|
|
} else {
|
|
t.Logf("Cached pool info: Protocol=%s, SwapCount=%d, LiquidityEvents=%d",
|
|
poolInfo.Protocol, poolInfo.SwapCount, poolInfo.LiquidityEvents)
|
|
}
|
|
|
|
// Test pools for token pair lookup
|
|
pools := dataLogger.GetPoolsForTokenPair(wethAddr, usdcAddr)
|
|
if len(pools) == 0 {
|
|
t.Error("Should find pools for WETH/USDC pair")
|
|
} else {
|
|
t.Logf("Found %d pools for WETH/USDC pair", len(pools))
|
|
for i, pool := range pools {
|
|
t.Logf(" Pool %d: %s (%s) - Swaps: %d, Liquidity Events: %d",
|
|
i+1, pool.Address.Hex(), pool.Protocol, pool.SwapCount, pool.LiquidityEvents)
|
|
}
|
|
}
|
|
|
|
// Test 6: Factory Management
|
|
t.Log("\n--- Test 6: Factory Management ---")
|
|
|
|
activeFactories := dataLogger.GetActiveFactories()
|
|
if len(activeFactories) == 0 {
|
|
t.Error("Should have active factories")
|
|
} else {
|
|
t.Logf("Found %d active factories", len(activeFactories))
|
|
for i, factory := range activeFactories {
|
|
t.Logf(" Factory %d: %s (%s %s) - %d pools",
|
|
i+1, factory.Address.Hex(), factory.Protocol, factory.Version, factory.PoolCount)
|
|
}
|
|
}
|
|
|
|
// Test specific factory lookup
|
|
uniV3Factory := common.HexToAddress("0x1F98431c8aD98523631AE4a59f267346ea31F984")
|
|
factoryInfo, exists := dataLogger.GetFactoryInfo(uniV3Factory)
|
|
if !exists {
|
|
t.Error("UniswapV3 factory should be known")
|
|
} else {
|
|
t.Logf("UniswapV3 factory info: Protocol=%s, Version=%s, Active=%t",
|
|
factoryInfo.Protocol, factoryInfo.Version, factoryInfo.IsActive)
|
|
}
|
|
|
|
// Test 7: Market Builder Integration
|
|
t.Log("\n--- Test 7: Market Builder Integration ---")
|
|
|
|
// Initialize market builder
|
|
marketBuilder := market.NewMarketBuilder(log, db, nil, dataLogger)
|
|
|
|
err = marketBuilder.Initialize(ctx)
|
|
if err != nil {
|
|
t.Errorf("Failed to initialize market builder: %v", err)
|
|
}
|
|
|
|
// Get market for WETH/USDC
|
|
wethUsdcMarket, exists := marketBuilder.GetMarket(wethAddr, usdcAddr)
|
|
if !exists {
|
|
t.Log("WETH/USDC market not built yet (expected for test)")
|
|
} else {
|
|
t.Logf("WETH/USDC market: %d pools, Total Liquidity: %s, Spread: %.2f%%",
|
|
wethUsdcMarket.PoolCount,
|
|
wethUsdcMarket.TotalLiquidity.String(),
|
|
wethUsdcMarket.PriceSpread)
|
|
|
|
if wethUsdcMarket.BestPool != nil {
|
|
t.Logf("Best pool: %s (%s) - %.2f%% liquidity share",
|
|
wethUsdcMarket.BestPool.Address.Hex(),
|
|
wethUsdcMarket.BestPool.Protocol,
|
|
wethUsdcMarket.BestPool.LiquidityShare*100)
|
|
}
|
|
}
|
|
|
|
// Get all markets
|
|
allMarkets := marketBuilder.GetAllMarkets()
|
|
t.Logf("Total markets built: %d", len(allMarkets))
|
|
|
|
// Test 8: Statistics and Performance
|
|
t.Log("\n--- Test 8: Statistics and Performance ---")
|
|
|
|
finalStats := dataLogger.GetStatistics()
|
|
t.Logf("Final market data statistics: %+v", finalStats)
|
|
|
|
builderStats := marketBuilder.GetStatistics()
|
|
t.Logf("Market builder statistics: %+v", builderStats)
|
|
|
|
// Validate expected statistics
|
|
if finalStats["swapEvents"].(int64) < 1 {
|
|
t.Error("Should have logged at least 1 swap event")
|
|
}
|
|
|
|
if finalStats["liquidityEvents"].(int64) < 1 {
|
|
t.Error("Should have logged at least 1 liquidity event")
|
|
}
|
|
|
|
if finalStats["totalTokens"].(int) < 2 {
|
|
t.Error("Should have at least 2 tokens cached")
|
|
}
|
|
|
|
// Test 9: Race Condition Safety
|
|
t.Log("\n--- Test 9: Concurrent Access Safety ---")
|
|
|
|
// Test concurrent access to caches
|
|
done := make(chan bool, 10)
|
|
|
|
// Simulate concurrent token lookups
|
|
for i := 0; i < 5; i++ {
|
|
go func(id int) {
|
|
defer func() { done <- true }()
|
|
|
|
// Rapid token lookups
|
|
for j := 0; j < 100; j++ {
|
|
_, _ = dataLogger.GetTokenInfo(wethAddr)
|
|
_, _ = dataLogger.GetPoolInfo(swapEvent.PoolAddress)
|
|
_ = dataLogger.GetPoolsForTokenPair(wethAddr, usdcAddr)
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
// Simulate concurrent event logging
|
|
for i := 0; i < 5; i++ {
|
|
go func(id int) {
|
|
defer func() { done <- true }()
|
|
|
|
// Create slightly different events
|
|
testEvent := swapEvent
|
|
testEvent.TransactionHash = common.HexToHash(fmt.Sprintf("0x%d234567890abcdef", id))
|
|
|
|
testSwapData := *swapData
|
|
testSwapData.TxHash = testEvent.TransactionHash
|
|
|
|
// Log events rapidly
|
|
for j := 0; j < 10; j++ {
|
|
_ = dataLogger.LogSwapEvent(context.Background(), testEvent, &testSwapData)
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
// Wait for all goroutines to complete
|
|
for i := 0; i < 10; i++ {
|
|
<-done
|
|
}
|
|
|
|
t.Log("Concurrent access test completed without deadlocks")
|
|
|
|
// Test 10: Cleanup and Shutdown
|
|
t.Log("\n--- Test 10: Cleanup and Shutdown ---")
|
|
|
|
// Stop components gracefully
|
|
dataLogger.Stop()
|
|
marketBuilder.Stop()
|
|
|
|
// Final statistics
|
|
shutdownStats := dataLogger.GetStatistics()
|
|
t.Logf("Shutdown statistics: %+v", shutdownStats)
|
|
|
|
t.Log("\n=== Comprehensive Market Data Logging Test Complete ===")
|
|
}
|
|
|
|
// TestMarketDataPersistence tests database persistence of market data
|
|
func TestMarketDataPersistence(t *testing.T) {
|
|
t.Log("=== Market Data Persistence Test ===")
|
|
|
|
// This would test actual database operations in a real implementation
|
|
// For now, we'll simulate the persistence layer
|
|
|
|
t.Log("Market data persistence test completed (simulation)")
|
|
}
|
|
|
|
// TestMarketDataRecovery tests recovery from cached data
|
|
func TestMarketDataRecovery(t *testing.T) {
|
|
t.Log("=== Market Data Recovery Test ===")
|
|
|
|
// This would test loading existing data from database on startup
|
|
// For now, we'll simulate the recovery process
|
|
|
|
t.Log("Market data recovery test completed (simulation)")
|
|
}
|