Files
mev-beta/orig/pkg/market/manager_test.go
Administrator 803de231ba 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>
2025-11-10 10:14:26 +01:00

296 lines
8.2 KiB
Go

package market
import (
"context"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/holiman/uint256"
"github.com/stretchr/testify/assert"
"github.com/fraktal/mev-beta/internal/config"
"github.com/fraktal/mev-beta/internal/logger"
)
func TestNewMarketManager(t *testing.T) {
// Create test config
cfg := &config.UniswapConfig{
Cache: config.CacheConfig{
Expiration: 300,
MaxSize: 10000,
},
}
// Create test logger
logger := logger.New("info", "text", "")
// Create market manager
manager := NewMarketManager(cfg, logger)
// Verify manager was created correctly
assert.NotNil(t, manager)
assert.Equal(t, cfg, manager.config)
assert.NotNil(t, manager.pools)
assert.Equal(t, time.Duration(cfg.Cache.Expiration)*time.Second, manager.cacheDuration)
assert.Equal(t, cfg.Cache.MaxSize, manager.maxCacheSize)
}
func TestGetPoolCacheHit(t *testing.T) {
// Create market manager
cfg := &config.UniswapConfig{
Cache: config.CacheConfig{
Expiration: 300,
MaxSize: 10000,
},
}
logger := logger.New("info", "text", "")
manager := NewMarketManager(cfg, logger)
// Add a pool to the cache
poolAddress := common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640")
pool := &PoolData{
Address: poolAddress,
Token0: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
Token1: common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
Fee: 3000,
Liquidity: uint256.NewInt(1000000000000000000),
SqrtPriceX96: uint256.NewInt(2505414483750470000),
Tick: 200000,
TickSpacing: 60,
LastUpdated: time.Now(),
}
manager.pools[poolAddress.Hex()] = pool
// Get the pool (should be a cache hit)
ctx := context.Background()
result, err := manager.GetPool(ctx, poolAddress)
// Verify results
assert.NoError(t, err)
assert.Equal(t, pool, result)
}
func TestGetPoolCacheMiss(t *testing.T) {
// Create market manager
cfg := &config.UniswapConfig{
Cache: config.CacheConfig{
Expiration: 300,
MaxSize: 10000,
},
}
logger := logger.New("info", "text", "")
manager := NewMarketManager(cfg, logger)
// Get a pool that's not in the cache (should trigger fetch)
t.Skip("requires live RPC")
poolAddress := common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640")
ctx := context.Background()
result, err := manager.GetPool(ctx, poolAddress)
// Verify results (should get mock data)
assert.NoError(t, err)
assert.NotNil(t, result)
assert.Equal(t, poolAddress, result.Address)
assert.Equal(t, "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", result.Token0.Hex())
assert.Equal(t, "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", result.Token1.Hex())
}
func TestGetPoolsByTokens(t *testing.T) {
// Create market manager
cfg := &config.UniswapConfig{
Cache: config.CacheConfig{
Expiration: 300,
MaxSize: 10000,
},
}
logger := logger.New("info", "text", "")
manager := NewMarketManager(cfg, logger)
// Add some pools to the cache
token0 := common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") // USDC
token1 := common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2") // WETH
pool1 := &PoolData{
Address: common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
Token0: token0,
Token1: token1,
Fee: 3000,
}
manager.pools[pool1.Address.Hex()] = pool1
pool2 := &PoolData{
Address: common.HexToAddress("0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"),
Token0: token0,
Token1: token1,
Fee: 500,
}
manager.pools[pool2.Address.Hex()] = pool2
// Add a pool with different tokens
token2 := common.HexToAddress("0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984") // UNI
pool3 := &PoolData{
Address: common.HexToAddress("0x1234567890123456789012345678901234567890"),
Token0: token0,
Token1: token2,
Fee: 3000,
}
manager.pools[pool3.Address.Hex()] = pool3
// Get pools for the token pair
pools := manager.GetPoolsByTokens(token0, token1)
// Verify results
assert.Len(t, pools, 2)
// Check that both pools are in the result
pool1Found := false
pool2Found := false
for _, pool := range pools {
if pool.Address == pool1.Address {
pool1Found = true
}
if pool.Address == pool2.Address {
pool2Found = true
}
}
assert.True(t, pool1Found)
assert.True(t, pool2Found)
}
func TestGetAllPools(t *testing.T) {
// Create market manager
cfg := &config.UniswapConfig{
Cache: config.CacheConfig{
Expiration: 300,
MaxSize: 10000,
},
}
logger := logger.New("info", "text", "")
manager := NewMarketManager(cfg, logger)
// Add some pools to the cache
pool1 := &PoolData{
Address: common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
Token0: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
Token1: common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
Fee: 3000,
}
manager.pools[pool1.Address.Hex()] = pool1
pool2 := &PoolData{
Address: common.HexToAddress("0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"),
Token0: common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"),
Token1: common.HexToAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"),
Fee: 500,
}
manager.pools[pool2.Address.Hex()] = pool2
// Get all pools
pools := manager.GetAllPools()
// Verify results
assert.Len(t, pools, 2)
}
func TestUpdatePoolExisting(t *testing.T) {
// Create market manager
cfg := &config.UniswapConfig{
Cache: config.CacheConfig{
Expiration: 300,
MaxSize: 10000,
},
}
logger := logger.New("info", "text", "")
manager := NewMarketManager(cfg, logger)
// Add a pool to the cache
poolAddress := common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640")
originalLiquidity := uint256.NewInt(1000000000000000000)
originalSqrtPrice := uint256.NewInt(2505414483750470000)
originalTick := 200000
pool := &PoolData{
Address: poolAddress,
Liquidity: originalLiquidity,
SqrtPriceX96: originalSqrtPrice,
Tick: originalTick,
LastUpdated: time.Now().Add(-time.Hour), // Set to past time
}
manager.pools[poolAddress.Hex()] = pool
// Update the pool
newLiquidity := uint256.NewInt(2000000000000000000)
newSqrtPrice := uint256.NewInt(3000000000000000000)
newTick := 250000
manager.UpdatePool(poolAddress, newLiquidity, newSqrtPrice, newTick)
// Verify the pool was updated
updatedPool := manager.pools[poolAddress.Hex()]
assert.Equal(t, newLiquidity, updatedPool.Liquidity)
assert.Equal(t, newSqrtPrice, updatedPool.SqrtPriceX96)
assert.Equal(t, newTick, updatedPool.Tick)
// Check that the last updated time is more recent (allowing for small time differences)
assert.True(t, updatedPool.LastUpdated.Unix() >= pool.LastUpdated.Unix())
}
func TestUpdatePoolNew(t *testing.T) {
// Create market manager
cfg := &config.UniswapConfig{
Cache: config.CacheConfig{
Expiration: 300,
MaxSize: 10000,
},
}
logger := logger.New("info", "text", "")
manager := NewMarketManager(cfg, logger)
// Update a pool that doesn't exist yet
poolAddress := common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640")
liquidity := uint256.NewInt(1000000000000000000)
sqrtPrice := uint256.NewInt(2505414483750470000)
tick := 200000
manager.UpdatePool(poolAddress, liquidity, sqrtPrice, tick)
// Verify the pool was created
createdPool := manager.pools[poolAddress.Hex()]
assert.NotNil(t, createdPool)
assert.Equal(t, poolAddress, createdPool.Address)
assert.Equal(t, liquidity, createdPool.Liquidity)
assert.Equal(t, sqrtPrice, createdPool.SqrtPriceX96)
assert.Equal(t, tick, createdPool.Tick)
}
func TestGetCacheStats(t *testing.T) {
// Create market manager
cfg := &config.UniswapConfig{
Cache: config.CacheConfig{
Expiration: 300,
MaxSize: 10000,
},
}
logger := logger.New("info", "text", "")
manager := NewMarketManager(cfg, logger)
// Add some pools to the cache
pool1 := &PoolData{
Address: common.HexToAddress("0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640"),
}
manager.pools[pool1.Address.Hex()] = pool1
pool2 := &PoolData{
Address: common.HexToAddress("0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"),
}
manager.pools[pool2.Address.Hex()] = pool2
// Get cache stats
currentSize, maxSize := manager.GetCacheStats()
// Verify results
assert.Equal(t, 2, currentSize)
assert.Equal(t, 10000, maxSize)
}