mirror of
https://github.com/lukaszraczylo/go-simple-graphql.git
synced 2026-10-12 00:45:31 +00:00
320 lines
7.8 KiB
Go
320 lines
7.8 KiB
Go
package libpack_cache
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type CacheTestSuite struct {
|
|
suite.Suite
|
|
}
|
|
|
|
func (suite *CacheTestSuite) SetupTest() {
|
|
}
|
|
|
|
func TestCachingTestSuite(t *testing.T) {
|
|
suite.Run(t, new(CacheTestSuite))
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_New() {
|
|
suite.T().Run("should return a new cache", func(t *testing.T) {
|
|
cache := New(2 * time.Second)
|
|
defer cache.Stop()
|
|
suite.NotNil(cache)
|
|
})
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_CacheUse() {
|
|
cache := New(5 * time.Second)
|
|
defer cache.Stop()
|
|
|
|
tests := []struct {
|
|
name string
|
|
cache_value string
|
|
}{
|
|
{
|
|
name: "test1",
|
|
cache_value: "test1-123",
|
|
},
|
|
{
|
|
name: "test2",
|
|
cache_value: "test2-123",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
suite.T().Run(tt.name, func(t *testing.T) {
|
|
cache.Set(tt.name, []byte(tt.name), 2*time.Second)
|
|
c, ok := cache.Get(tt.name)
|
|
suite.Equal(true, ok)
|
|
suite.Equal(tt.name, string(c))
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_CacheDelete() {
|
|
cache := New(5 * time.Second)
|
|
defer cache.Stop()
|
|
|
|
tests := []struct {
|
|
name string
|
|
cache_value string
|
|
}{
|
|
{
|
|
name: "test1",
|
|
cache_value: "test1-123",
|
|
},
|
|
{
|
|
name: "test2",
|
|
cache_value: "test2-123",
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
suite.T().Run(tt.name, func(t *testing.T) {
|
|
cache.Set(tt.name, []byte(tt.name), 2*time.Second)
|
|
c, ok := cache.Get(tt.name)
|
|
suite.Equal(true, ok)
|
|
suite.Equal(tt.name, string(c))
|
|
cache.Delete(tt.name)
|
|
c, ok = cache.Get(tt.name)
|
|
suite.Equal(false, ok)
|
|
suite.Equal("", string(c))
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_CacheExpire() {
|
|
cache := New(5 * time.Second)
|
|
defer cache.Stop()
|
|
|
|
tests := []struct {
|
|
name string
|
|
cache_value string
|
|
ttl time.Duration
|
|
}{
|
|
{
|
|
name: "test1",
|
|
cache_value: "test1-123",
|
|
ttl: 100 * time.Millisecond,
|
|
},
|
|
{
|
|
name: "test2",
|
|
cache_value: "test2-123",
|
|
ttl: 200 * time.Millisecond,
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
suite.T().Run(tt.name, func(t *testing.T) {
|
|
cache.Set(tt.name, []byte(tt.name), tt.ttl)
|
|
c, ok := cache.Get(tt.name)
|
|
suite.Equal(true, ok)
|
|
suite.Equal(tt.name, string(c))
|
|
time.Sleep(tt.ttl + 50*time.Millisecond) // Add small buffer
|
|
c, ok = cache.Get(tt.name)
|
|
suite.Equal(false, ok)
|
|
suite.Equal("", string(c))
|
|
})
|
|
}
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_CacheCompression() {
|
|
cache := New(5 * time.Second)
|
|
defer cache.Stop()
|
|
|
|
suite.T().Run("should compress large data", func(t *testing.T) {
|
|
// Create data larger than compression threshold (1024 bytes)
|
|
largeData := make([]byte, 2048)
|
|
for i := range largeData {
|
|
largeData[i] = byte(i % 256)
|
|
}
|
|
|
|
cache.Set("large_key", largeData, 2*time.Second)
|
|
retrieved, ok := cache.Get("large_key")
|
|
suite.True(ok)
|
|
suite.Equal(largeData, retrieved)
|
|
})
|
|
|
|
suite.T().Run("should handle compression errors gracefully", func(t *testing.T) {
|
|
// Test with data that might cause compression issues
|
|
largeData := make([]byte, 1500)
|
|
for i := range largeData {
|
|
largeData[i] = byte(i % 256)
|
|
}
|
|
|
|
cache.Set("compression_test", largeData, 2*time.Second)
|
|
retrieved, ok := cache.Get("compression_test")
|
|
suite.True(ok)
|
|
suite.Equal(largeData, retrieved)
|
|
})
|
|
|
|
suite.T().Run("should not compress small data", func(t *testing.T) {
|
|
smallData := []byte("small data")
|
|
cache.Set("small_key", smallData, 2*time.Second)
|
|
retrieved, ok := cache.Get("small_key")
|
|
suite.True(ok)
|
|
suite.Equal(smallData, retrieved)
|
|
})
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_CacheCompressionEdgeCases() {
|
|
cache := New(5 * time.Second)
|
|
defer cache.Stop()
|
|
|
|
suite.T().Run("should handle highly compressible data", func(t *testing.T) {
|
|
// Create highly compressible data (repeated pattern)
|
|
compressibleData := make([]byte, 2048)
|
|
for i := range compressibleData {
|
|
compressibleData[i] = 'A' // All same character
|
|
}
|
|
|
|
cache.Set("compressible", compressibleData, 2*time.Second)
|
|
retrieved, ok := cache.Get("compressible")
|
|
suite.True(ok)
|
|
suite.Equal(compressibleData, retrieved)
|
|
})
|
|
|
|
suite.T().Run("should handle incompressible data", func(t *testing.T) {
|
|
// Create random-like data that doesn't compress well
|
|
incompressibleData := make([]byte, 1500)
|
|
for i := range incompressibleData {
|
|
incompressibleData[i] = byte((i*7 + i*i) % 256) // Pseudo-random pattern
|
|
}
|
|
|
|
cache.Set("incompressible", incompressibleData, 2*time.Second)
|
|
retrieved, ok := cache.Get("incompressible")
|
|
suite.True(ok)
|
|
suite.Equal(incompressibleData, retrieved)
|
|
})
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_CacheCleanup() {
|
|
cache := New(100 * time.Millisecond) // Short TTL for faster testing
|
|
defer cache.Stop()
|
|
|
|
suite.T().Run("should clean expired entries", func(t *testing.T) {
|
|
cache.Set("expire1", []byte("value1"), 50*time.Millisecond)
|
|
cache.Set("expire2", []byte("value2"), 50*time.Millisecond)
|
|
cache.Set("persist", []byte("persist"), 5*time.Second)
|
|
|
|
// Verify entries exist
|
|
_, ok := cache.Get("expire1")
|
|
suite.True(ok)
|
|
_, ok = cache.Get("expire2")
|
|
suite.True(ok)
|
|
_, ok = cache.Get("persist")
|
|
suite.True(ok)
|
|
|
|
// Wait for expiration
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// Trigger cleanup
|
|
cache.CleanExpiredEntries()
|
|
|
|
// Check expired entries are gone
|
|
_, ok = cache.Get("expire1")
|
|
suite.False(ok)
|
|
_, ok = cache.Get("expire2")
|
|
suite.False(ok)
|
|
|
|
// Check persistent entry still exists
|
|
_, ok = cache.Get("persist")
|
|
suite.True(ok)
|
|
})
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_CacheLazyCleanup() {
|
|
cache := New(5 * time.Second)
|
|
defer cache.Stop()
|
|
|
|
suite.T().Run("should trigger lazy cleanup on expired access", func(t *testing.T) {
|
|
cache.Set("lazy_expire", []byte("value"), 50*time.Millisecond)
|
|
|
|
// Verify entry exists
|
|
_, ok := cache.Get("lazy_expire")
|
|
suite.True(ok)
|
|
|
|
// Wait for expiration
|
|
time.Sleep(100 * time.Millisecond)
|
|
|
|
// Access expired entry should trigger lazy cleanup
|
|
_, ok = cache.Get("lazy_expire")
|
|
suite.False(ok)
|
|
})
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_CacheStop() {
|
|
suite.T().Run("should stop cache gracefully", func(t *testing.T) {
|
|
cache := New(5 * time.Second)
|
|
|
|
// Add some data
|
|
cache.Set("test", []byte("value"), 2*time.Second)
|
|
_, ok := cache.Get("test")
|
|
suite.True(ok)
|
|
|
|
// Stop cache
|
|
cache.Stop()
|
|
|
|
// Should be able to stop multiple times without panic
|
|
cache.Stop()
|
|
})
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_CacheSharding() {
|
|
cache := New(5 * time.Second)
|
|
defer cache.Stop()
|
|
|
|
suite.T().Run("should distribute keys across shards", func(t *testing.T) {
|
|
// Test multiple keys to ensure sharding works
|
|
keys := []string{"key1", "key2", "key3", "key4", "key5"}
|
|
|
|
for _, key := range keys {
|
|
cache.Set(key, []byte("value_"+key), 2*time.Second)
|
|
}
|
|
|
|
for _, key := range keys {
|
|
value, ok := cache.Get(key)
|
|
suite.True(ok)
|
|
suite.Equal("value_"+key, string(value))
|
|
}
|
|
})
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_CachePeriodicCleanup() {
|
|
suite.T().Run("should handle short TTL for periodic cleanup", func(t *testing.T) {
|
|
// Test with very short TTL to trigger the minimum cleanup interval logic
|
|
cache := New(500 * time.Millisecond)
|
|
defer cache.Stop()
|
|
|
|
cache.Set("short_ttl", []byte("value"), 100*time.Millisecond)
|
|
|
|
// Wait for periodic cleanup to potentially run
|
|
time.Sleep(200 * time.Millisecond)
|
|
|
|
_, ok := cache.Get("short_ttl")
|
|
suite.False(ok)
|
|
})
|
|
}
|
|
|
|
func (suite *CacheTestSuite) Test_CacheDecompressionErrors() {
|
|
cache := New(5 * time.Second)
|
|
defer cache.Stop()
|
|
|
|
suite.T().Run("should handle decompression pool edge cases", func(t *testing.T) {
|
|
// This test ensures the decompression pool handles nil readers correctly
|
|
largeData := make([]byte, 2048)
|
|
for i := range largeData {
|
|
largeData[i] = byte(i % 256)
|
|
}
|
|
|
|
// Set and get multiple times to exercise the pool
|
|
for i := 0; i < 5; i++ {
|
|
key := "pool_test_" + string(rune('0'+i))
|
|
cache.Set(key, largeData, 2*time.Second)
|
|
retrieved, ok := cache.Get(key)
|
|
suite.True(ok)
|
|
suite.Equal(largeData, retrieved)
|
|
}
|
|
})
|
|
}
|