diff --git a/cache/memory/memory.go b/cache/memory/memory.go index c5e36c3..38600ce 100644 --- a/cache/memory/memory.go +++ b/cache/memory/memory.go @@ -158,7 +158,8 @@ func (c *Cache) Set(key string, value []byte, ttl time.Duration) { c.evictToFreeMemory(memoryToFree) } else if atomic.LoadInt64(&c.entryCount) >= c.maxCacheSize { // Fall back to count-based eviction for backward compatibility - c.evictOldest(int(c.maxCacheSize / 10)) // Evict 10% of entries + // 10% of entries, at least one: max/10 is 0 for caps below 10. + c.evictOldest(int(max(1, c.maxCacheSize/10))) } // A non-positive TTL is clamped to defaultTTL so this backend expires diff --git a/cache/memory/small_cap_test.go b/cache/memory/small_cap_test.go new file mode 100644 index 0000000..1a8f210 --- /dev/null +++ b/cache/memory/small_cap_test.go @@ -0,0 +1,23 @@ +package libpack_cache_memory + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +// Pins theorems/Theorems/MemCacheCap.lean: every cap >= 1 bounds the entry count. +func TestSet_SmallEntryCapIsEnforced(t *testing.T) { + for _, maxEntries := range []int64{1, 2, 5, 9, 10, 25} { + t.Run(fmt.Sprintf("cap_%d", maxEntries), func(t *testing.T) { + c := NewWithSize(time.Minute, DefaultMaxMemorySize, maxEntries) + defer c.Shutdown() + for i := 0; i < 100; i++ { + c.Set(fmt.Sprintf("k%d", i), []byte("v"), time.Minute) + assert.LessOrEqual(t, c.CountQueries(), maxEntries) + } + }) + } +}