mirror of
https://github.com/status-im/eth-rpc-proxy.git
synced 2026-08-31 03:31:14 +00:00
495 lines
12 KiB
Go
495 lines
12 KiB
Go
package cache_rules
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"go.uber.org/mock/gomock"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zaptest"
|
|
|
|
"github.com/status-im/proxy-common/models"
|
|
|
|
"go-proxy-cache/internal/interfaces"
|
|
"go-proxy-cache/internal/interfaces/mock"
|
|
localModels "go-proxy-cache/internal/models"
|
|
)
|
|
|
|
func TestNewClassifier(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
if classifier == nil {
|
|
t.Fatal("NewClassifier returned nil")
|
|
}
|
|
if classifier.logger != logger {
|
|
t.Error("Logger not set correctly")
|
|
}
|
|
if classifier.configTTL != mockConfig {
|
|
t.Error("ConfigTTL not set correctly")
|
|
}
|
|
}
|
|
|
|
func TestNewClassifier_NilLogger(t *testing.T) {
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
|
|
// Should not panic with nil logger
|
|
classifier := NewClassifier(nil, mockConfig)
|
|
if classifier == nil {
|
|
t.Fatal("NewClassifier returned nil with nil logger")
|
|
}
|
|
if classifier.logger != nil {
|
|
t.Error("Expected nil logger to be preserved")
|
|
}
|
|
}
|
|
|
|
func TestNewClassifier_NilConfig(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
|
|
// Should not panic with nil config
|
|
classifier := NewClassifier(logger, nil)
|
|
if classifier == nil {
|
|
t.Fatal("NewClassifier returned nil with nil config")
|
|
}
|
|
if classifier.configTTL != nil {
|
|
t.Error("Expected nil config to be preserved")
|
|
}
|
|
}
|
|
|
|
func TestGetTtl_NilRequest(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
result := classifier.GetTtl("ethereum", "mainnet", nil)
|
|
|
|
expected := models.CacheInfo{TTL: 0, CacheType: "none"}
|
|
if result != expected {
|
|
t.Errorf("Expected %+v, got %+v", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestGetTtl_EmptyMethod(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
request := &localModels.JSONRPCRequest{
|
|
Method: "",
|
|
ID: 1,
|
|
}
|
|
|
|
result := classifier.GetTtl("ethereum", "mainnet", request)
|
|
|
|
expected := models.CacheInfo{TTL: 0, CacheType: "none"}
|
|
if result != expected {
|
|
t.Errorf("Expected %+v, got %+v", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestGetTtl_CacheTypeNone(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
request := &localModels.JSONRPCRequest{
|
|
Method: "eth_sendTransaction",
|
|
ID: 1,
|
|
}
|
|
|
|
// Mock the config to return CacheTypeNone for the method
|
|
mockConfig.EXPECT().
|
|
GetCacheTypeForMethod("eth_sendTransaction").
|
|
Return(models.CacheTypeNone)
|
|
|
|
result := classifier.GetTtl("ethereum", "mainnet", request)
|
|
|
|
expected := models.CacheInfo{TTL: 0, CacheType: models.CacheTypeNone}
|
|
if result != expected {
|
|
t.Errorf("Expected %+v, got %+v", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestGetTtl_ZeroTTL(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
request := &localModels.JSONRPCRequest{
|
|
Method: "eth_getBalance",
|
|
ID: 1,
|
|
}
|
|
|
|
// Mock the config to return a cache type but zero TTL
|
|
mockConfig.EXPECT().
|
|
GetCacheTypeForMethod("eth_getBalance").
|
|
Return(models.CacheTypeShort)
|
|
|
|
mockConfig.EXPECT().
|
|
GetTtlForCacheType("ethereum", "mainnet", models.CacheTypeShort).
|
|
Return(time.Duration(0))
|
|
|
|
result := classifier.GetTtl("ethereum", "mainnet", request)
|
|
|
|
expected := models.CacheInfo{TTL: 0, CacheType: models.CacheTypeNone}
|
|
if result != expected {
|
|
t.Errorf("Expected %+v, got %+v", expected, result)
|
|
}
|
|
}
|
|
|
|
func TestGetTtl_ValidCaching(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
tests := []struct {
|
|
name string
|
|
request *localModels.JSONRPCRequest
|
|
chain string
|
|
network string
|
|
cacheType models.CacheType
|
|
ttl time.Duration
|
|
expected models.CacheInfo
|
|
description string
|
|
}{
|
|
{
|
|
name: "permanent cache type",
|
|
request: &localModels.JSONRPCRequest{
|
|
Method: "eth_getBlockByHash",
|
|
ID: 1,
|
|
},
|
|
chain: "ethereum",
|
|
network: "mainnet",
|
|
cacheType: models.CacheTypePermanent,
|
|
ttl: 2 * time.Hour,
|
|
expected: models.CacheInfo{
|
|
TTL: 2 * time.Hour,
|
|
CacheType: models.CacheTypePermanent,
|
|
},
|
|
description: "should return permanent cache info for permanent cache type",
|
|
},
|
|
{
|
|
name: "short cache type",
|
|
request: &localModels.JSONRPCRequest{
|
|
Method: "eth_getBalance",
|
|
ID: 2,
|
|
},
|
|
chain: "polygon",
|
|
network: "mainnet",
|
|
cacheType: models.CacheTypeShort,
|
|
ttl: 30 * time.Second,
|
|
expected: models.CacheInfo{
|
|
TTL: 30 * time.Second,
|
|
CacheType: models.CacheTypeShort,
|
|
},
|
|
description: "should return short cache info for short cache type",
|
|
},
|
|
{
|
|
name: "minimal cache type",
|
|
request: &localModels.JSONRPCRequest{
|
|
Method: "eth_call",
|
|
ID: 3,
|
|
},
|
|
chain: "arbitrum",
|
|
network: "one",
|
|
cacheType: models.CacheTypeMinimal,
|
|
ttl: 5 * time.Second,
|
|
expected: models.CacheInfo{
|
|
TTL: 5 * time.Second,
|
|
CacheType: models.CacheTypeMinimal,
|
|
},
|
|
description: "should return minimal cache info for minimal cache type",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Setup mocks
|
|
mockConfig.EXPECT().
|
|
GetCacheTypeForMethod(tt.request.Method).
|
|
Return(tt.cacheType)
|
|
|
|
mockConfig.EXPECT().
|
|
GetTtlForCacheType(tt.chain, tt.network, tt.cacheType).
|
|
Return(tt.ttl)
|
|
|
|
result := classifier.GetTtl(tt.chain, tt.network, tt.request)
|
|
|
|
if result != tt.expected {
|
|
t.Errorf("%s: expected %+v, got %+v", tt.description, tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetTtl_DifferentChainNetworkCombinations(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
tests := []struct {
|
|
name string
|
|
chain string
|
|
network string
|
|
description string
|
|
}{
|
|
{
|
|
name: "ethereum mainnet",
|
|
chain: "ethereum",
|
|
network: "mainnet",
|
|
description: "should handle ethereum mainnet",
|
|
},
|
|
{
|
|
name: "polygon mainnet",
|
|
chain: "polygon",
|
|
network: "mainnet",
|
|
description: "should handle polygon mainnet",
|
|
},
|
|
{
|
|
name: "arbitrum one",
|
|
chain: "arbitrum",
|
|
network: "one",
|
|
description: "should handle arbitrum one",
|
|
},
|
|
{
|
|
name: "empty chain and network",
|
|
chain: "",
|
|
network: "",
|
|
description: "should handle empty chain and network",
|
|
},
|
|
{
|
|
name: "unknown chain and network",
|
|
chain: "unknown",
|
|
network: "unknown",
|
|
description: "should handle unknown chain and network",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
request := &localModels.JSONRPCRequest{
|
|
Method: "eth_getBalance",
|
|
ID: 1,
|
|
}
|
|
|
|
// Setup mocks
|
|
mockConfig.EXPECT().
|
|
GetCacheTypeForMethod("eth_getBalance").
|
|
Return(models.CacheTypeShort)
|
|
|
|
mockConfig.EXPECT().
|
|
GetTtlForCacheType(tt.chain, tt.network, models.CacheTypeShort).
|
|
Return(30 * time.Second)
|
|
|
|
result := classifier.GetTtl(tt.chain, tt.network, request)
|
|
|
|
expected := models.CacheInfo{
|
|
TTL: 30 * time.Second,
|
|
CacheType: models.CacheTypeShort,
|
|
}
|
|
|
|
if result != expected {
|
|
t.Errorf("%s: expected %+v, got %+v", tt.description, expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetTtl_RequestWithDifferentFields(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
tests := []struct {
|
|
name string
|
|
request *localModels.JSONRPCRequest
|
|
description string
|
|
}{
|
|
{
|
|
name: "request with string ID",
|
|
request: &localModels.JSONRPCRequest{
|
|
Method: "eth_getBalance",
|
|
ID: "test-id",
|
|
Jsonrpc: "2.0",
|
|
},
|
|
description: "should handle request with string ID",
|
|
},
|
|
{
|
|
name: "request with int ID",
|
|
request: &localModels.JSONRPCRequest{
|
|
Method: "eth_getBalance",
|
|
ID: 123,
|
|
Jsonrpc: "2.0",
|
|
},
|
|
description: "should handle request with int ID",
|
|
},
|
|
{
|
|
name: "request with params",
|
|
request: &localModels.JSONRPCRequest{
|
|
Method: "eth_getBalance",
|
|
ID: 1,
|
|
Params: []interface{}{"0x123", "latest"},
|
|
Jsonrpc: "2.0",
|
|
},
|
|
description: "should handle request with params",
|
|
},
|
|
{
|
|
name: "request without jsonrpc field",
|
|
request: &localModels.JSONRPCRequest{
|
|
Method: "eth_getBalance",
|
|
ID: 1,
|
|
},
|
|
description: "should handle request without jsonrpc field",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Setup mocks
|
|
mockConfig.EXPECT().
|
|
GetCacheTypeForMethod("eth_getBalance").
|
|
Return(models.CacheTypeShort)
|
|
|
|
mockConfig.EXPECT().
|
|
GetTtlForCacheType("ethereum", "mainnet", models.CacheTypeShort).
|
|
Return(30 * time.Second)
|
|
|
|
result := classifier.GetTtl("ethereum", "mainnet", tt.request)
|
|
|
|
expected := models.CacheInfo{
|
|
TTL: 30 * time.Second,
|
|
CacheType: models.CacheTypeShort,
|
|
}
|
|
|
|
if result != expected {
|
|
t.Errorf("%s: expected %+v, got %+v", tt.description, expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetTtl_NilConfig(t *testing.T) {
|
|
logger := zaptest.NewLogger(t)
|
|
classifier := NewClassifier(logger, nil)
|
|
|
|
request := &localModels.JSONRPCRequest{
|
|
Method: "eth_getBalance",
|
|
ID: 1,
|
|
}
|
|
|
|
// This should panic when trying to call methods on nil config
|
|
defer func() {
|
|
if r := recover(); r == nil {
|
|
t.Error("Expected panic when config is nil")
|
|
}
|
|
}()
|
|
|
|
classifier.GetTtl("ethereum", "mainnet", request)
|
|
}
|
|
|
|
func TestClassifierInterfaceCompliance(t *testing.T) {
|
|
// This test ensures that Classifier implements the CacheRulesClassifier interface
|
|
logger := zaptest.NewLogger(t)
|
|
ctrl := gomock.NewController(t)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
// This should compile without issues if the interface is properly implemented
|
|
var _ interfaces.CacheRulesClassifier = classifier
|
|
}
|
|
|
|
// Benchmark tests
|
|
func BenchmarkGetTtl(b *testing.B) {
|
|
logger := zap.NewNop()
|
|
ctrl := gomock.NewController(b)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
request := &localModels.JSONRPCRequest{
|
|
Method: "eth_getBalance",
|
|
ID: 1,
|
|
}
|
|
|
|
// Setup mocks for benchmark
|
|
mockConfig.EXPECT().
|
|
GetCacheTypeForMethod("eth_getBalance").
|
|
Return(models.CacheTypeShort).
|
|
AnyTimes()
|
|
|
|
mockConfig.EXPECT().
|
|
GetTtlForCacheType("ethereum", "mainnet", models.CacheTypeShort).
|
|
Return(30 * time.Second).
|
|
AnyTimes()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
classifier.GetTtl("ethereum", "mainnet", request)
|
|
}
|
|
}
|
|
|
|
func BenchmarkGetTtl_NilRequest(b *testing.B) {
|
|
logger := zap.NewNop()
|
|
ctrl := gomock.NewController(b)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
classifier.GetTtl("ethereum", "mainnet", nil)
|
|
}
|
|
}
|
|
|
|
func BenchmarkGetTtl_EmptyMethod(b *testing.B) {
|
|
logger := zap.NewNop()
|
|
ctrl := gomock.NewController(b)
|
|
defer ctrl.Finish()
|
|
|
|
mockConfig := mock.NewMockCacheRulesConfig(ctrl)
|
|
classifier := NewClassifier(logger, mockConfig)
|
|
|
|
request := &localModels.JSONRPCRequest{
|
|
Method: "",
|
|
ID: 1,
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
classifier.GetTtl("ethereum", "mainnet", request)
|
|
}
|
|
}
|