package tokens import ( "testing" "time" "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/assert" "github.com/fraktal/mev-beta/internal/logger" ) func TestNewMetadataCache(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) assert.NotNil(t, cache) assert.NotNil(t, cache.cache) assert.Equal(t, log, cache.logger) assert.Equal(t, "data/tokens.json", cache.cacheFile) } func TestTokenMetadataCreation(t *testing.T) { metadata := &TokenMetadata{ Address: common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), Symbol: "USDC", Name: "USD Coin", Decimals: 6, Verified: true, FirstSeen: time.Now(), LastSeen: time.Now(), SeenCount: 1, } assert.Equal(t, "USDC", metadata.Symbol) assert.Equal(t, "USD Coin", metadata.Name) assert.Equal(t, uint8(6), metadata.Decimals) assert.True(t, metadata.Verified) assert.Equal(t, uint64(1), metadata.SeenCount) } func TestCacheGetMissing(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) address := common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") metadata, exists := cache.Get(address) assert.False(t, exists) assert.Nil(t, metadata) } func TestCacheSetAndGet(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) metadata := &TokenMetadata{ Address: common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), Symbol: "USDC", Name: "USD Coin", Decimals: 6, Verified: true, SeenCount: 1, } cache.Set(metadata) retrieved, exists := cache.Get(metadata.Address) assert.True(t, exists) assert.NotNil(t, retrieved) assert.Equal(t, "USDC", retrieved.Symbol) assert.Equal(t, uint8(6), retrieved.Decimals) } func TestCacheSetFirstSeen(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) metadata := &TokenMetadata{ Address: common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), Symbol: "USDC", Name: "USD Coin", Decimals: 6, } before := time.Now() cache.Set(metadata) after := time.Now() retrieved, exists := cache.Get(metadata.Address) assert.True(t, exists) assert.NotNil(t, retrieved.FirstSeen) assert.True(t, retrieved.FirstSeen.After(before.Add(-1*time.Second)) || retrieved.FirstSeen.Equal(before)) assert.True(t, retrieved.FirstSeen.Before(after.Add(1*time.Second)) || retrieved.FirstSeen.Equal(after)) } func TestCacheMultipleTokens(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) tokens := []struct { address string symbol string decimals uint8 }{ {"0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "USDC", 6}, {"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "WETH", 18}, {"0xdAC17F958D2ee523a2206206994597C13D831ec7", "USDT", 6}, } for _, token := range tokens { metadata := &TokenMetadata{ Address: common.HexToAddress(token.address), Symbol: token.symbol, Decimals: token.decimals, Verified: true, SeenCount: 1, } cache.Set(metadata) } assert.Equal(t, 3, len(cache.cache)) for _, token := range tokens { retrieved, exists := cache.Get(common.HexToAddress(token.address)) assert.True(t, exists) assert.Equal(t, token.symbol, retrieved.Symbol) assert.Equal(t, token.decimals, retrieved.Decimals) } } func TestGetOrCreateMissing(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) address := common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") metadata := cache.GetOrCreate(address) assert.NotNil(t, metadata) assert.Equal(t, "UNKNOWN", metadata.Symbol) assert.Equal(t, "Unknown Token", metadata.Name) assert.Equal(t, uint8(18), metadata.Decimals) // Default assumption assert.False(t, metadata.Verified) assert.Equal(t, address, metadata.Address) } func TestGetOrCreateExisting(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) address := common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") metadata := &TokenMetadata{ Address: address, Symbol: "USDC", Decimals: 6, Verified: true, SeenCount: 1, } cache.Set(metadata) retrieved := cache.GetOrCreate(address) assert.Equal(t, "USDC", retrieved.Symbol) assert.Equal(t, uint8(6), retrieved.Decimals) assert.True(t, retrieved.Verified) } func TestSeenCountIncrement(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) address := common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") metadata := &TokenMetadata{ Address: address, Symbol: "USDC", Decimals: 6, SeenCount: 1, } // First set cache.Set(metadata) retrieved, _ := cache.Get(address) assert.Equal(t, uint64(1), retrieved.SeenCount) // Second set - should increment metadata2 := &TokenMetadata{ Address: address, Symbol: "USDC", Decimals: 6, SeenCount: 1, } cache.Set(metadata2) retrieved2, _ := cache.Get(address) assert.Equal(t, uint64(2), retrieved2.SeenCount) } func TestLastSeenUpdate(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) address := common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") metadata := &TokenMetadata{ Address: address, Symbol: "USDC", Decimals: 6, } cache.Set(metadata) firstLastSeen := cache.cache[address].LastSeen time.Sleep(10 * time.Millisecond) metadata2 := &TokenMetadata{ Address: address, Symbol: "USDC", Decimals: 6, } cache.Set(metadata2) secondLastSeen := cache.cache[address].LastSeen assert.True(t, secondLastSeen.After(firstLastSeen)) } func TestFirstSeenPreserved(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) address := common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") metadata := &TokenMetadata{ Address: address, Symbol: "USDC", Decimals: 6, } cache.Set(metadata) firstFirstSeen := cache.cache[address].FirstSeen time.Sleep(10 * time.Millisecond) metadata2 := &TokenMetadata{ Address: address, Symbol: "USDC", Decimals: 6, } cache.Set(metadata2) secondFirstSeen := cache.cache[address].FirstSeen assert.Equal(t, firstFirstSeen, secondFirstSeen) } func TestTokenMetadataVerified(t *testing.T) { tests := []struct { name string verified bool symbol string }{ {"Verified USDC", true, "USDC"}, {"Verified WETH", true, "WETH"}, {"Unverified token", false, "UNKNOWN"}, {"Unverified custom", false, "CUSTOM"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { metadata := &TokenMetadata{ Address: common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), Symbol: tt.symbol, Verified: tt.verified, SeenCount: 1, } assert.Equal(t, tt.verified, metadata.Verified) }) } } func TestTotalSupplyMetadata(t *testing.T) { metadata := &TokenMetadata{ Address: common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), Symbol: "USDC", TotalSupply: "30000000000000000", // 30M USDC Decimals: 6, Verified: true, SeenCount: 1, } assert.NotEmpty(t, metadata.TotalSupply) assert.Equal(t, "30000000000000000", metadata.TotalSupply) } func TestCacheConcurrency(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) done := make(chan bool, 10) errors := make(chan error, 10) // Test concurrent writes for i := 0; i < 5; i++ { go func(index int) { addr := common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") metadata := &TokenMetadata{ Address: addr, Symbol: "USDC", Decimals: 6, SeenCount: uint64(index), } cache.Set(metadata) done <- true }(i) } // Test concurrent reads for i := 0; i < 5; i++ { go func() { addr := common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") _, _ = cache.Get(addr) done <- true }() } // Wait for all goroutines for i := 0; i < 10; i++ { select { case <-done: // Success case <-errors: t.Fatal("Concurrent operation failed") } } } func TestCacheSize(t *testing.T) { log := logger.New("info", "text", "") cache := NewMetadataCache(log) addresses := []string{ "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2", "0xdAC17F958D2ee523a2206206994597C13D831ec7", "0x6B175474E89094C44Da98b954EedeAC495271d0F", "0x2260FAC5E5542a773Aa44fBCfeDd66150d0310be", } for _, addr := range addresses { metadata := &TokenMetadata{ Address: common.HexToAddress(addr), Symbol: "TEST", Decimals: 18, SeenCount: 1, } cache.Set(metadata) } assert.Equal(t, len(addresses), len(cache.cache)) } func TestMetadataDecimalVariations(t *testing.T) { tests := []struct { symbol string decimals uint8 expected uint8 }{ {"USDC", 6, 6}, {"USDT", 6, 6}, {"WETH", 18, 18}, {"DAI", 18, 18}, {"WBTC", 8, 8}, {"LINK", 18, 18}, {"AAVE", 18, 18}, } for _, tt := range tests { t.Run(tt.symbol, func(t *testing.T) { metadata := &TokenMetadata{ Address: common.HexToAddress("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), Symbol: tt.symbol, Decimals: tt.decimals, SeenCount: 1, } assert.Equal(t, tt.expected, metadata.Decimals) }) } }