2025-04-05 03:48:18 +02:00

234 lines
4.6 KiB
Go

package main
import (
"sync"
"testing"
"time"
)
func TestSetAndGet(t *testing.T) {
cache := NewTtlCache()
defer cache.Stop()
cache.Set("key1", "value1", 0)
val, ok := cache.Get("key1")
if !ok {
t.Error("Failed to get value that was just set")
}
if val != "value1" {
t.Errorf("Expected 'value1', got %s", val)
}
val, ok = cache.Get("non-existent")
if ok {
t.Error("Get should return false for non-existent key")
}
if val != "" {
t.Errorf("Expected empty string for non-existent key, got %s", val)
}
cache.Set("key1", "updated", 0)
val, ok = cache.Get("key1")
if !ok {
t.Error("Failed to get updated value")
}
if val != "updated" {
t.Errorf("Expected 'updated', got %s", val)
}
}
func TestDelete(t *testing.T) {
cache := NewTtlCache()
defer cache.Stop()
cache.Set("key1", "value1", 0)
_, ok := cache.Get("key1")
if !ok {
t.Error("Key should exist before deletion")
}
cache.Delete("key1")
_, ok = cache.Get("key1")
if ok {
t.Error("Key should have been deleted")
}
cache.Delete("non-existent")
}
func TestExpiration(t *testing.T) {
cache := NewTtlCache()
defer cache.Stop()
cache.Set("short", "shortvalue", 50*time.Millisecond)
cache.Set("forever", "eternalvalue", 0)
_, ok1 := cache.Get("short")
_, ok2 := cache.Get("forever")
if !ok1 || !ok2 {
t.Error("Both keys should exist initially")
}
time.Sleep(100 * time.Millisecond)
_, ok1 = cache.Get("short")
_, ok2 = cache.Get("forever")
if ok1 {
t.Error("Key with short TTL should have expired")
}
if !ok2 {
t.Error("Key with no TTL should not expire")
}
}
func TestAutomaticCleanup(t *testing.T) {
cache := NewTtlCache()
defer cache.Stop()
cache.Set("expiring", "value", 1*time.Second)
val, ok := cache.Get("expiring")
if !ok || val != "value" {
t.Error("Key should exist initially")
}
time.Sleep(6 * time.Second)
_, ok = cache.Get("expiring")
if ok {
t.Error("Expired key should have been automatically cleaned up")
}
}
func TestConcurrentAccess(t *testing.T) {
cache := NewTtlCache()
defer cache.Stop()
var wg sync.WaitGroup
numOperations := 100
for i := range 5 {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
for j := range numOperations {
key := "key" + string(rune('A'+workerID))
cache.Set(key, "value"+string(rune('0'+j%10)), 500*time.Millisecond)
time.Sleep(5 * time.Millisecond)
}
}(i)
}
for i := range 5 {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
for j := range numOperations {
key := "key" + string(rune('A'+j%5))
cache.Get(key)
time.Sleep(2 * time.Millisecond)
}
}(i)
}
for i := range 2 {
wg.Add(1)
go func(workerID int) {
defer wg.Done()
for j := range numOperations / 5 {
key := "key" + string(rune('A'+j%5))
cache.Delete(key)
time.Sleep(10 * time.Millisecond)
}
}(i)
}
wg.Wait()
}
func TestStopSafety(t *testing.T) {
cache := NewTtlCache()
cache.Stop()
cache.Set("key", "value", 0)
val, ok := cache.Get("key")
if !ok || val != "value" {
t.Error("Cache operations should still work after Stop")
}
defer func() {
if r := recover(); r != nil {
t.Error("Multiple calls to Stop should not panic")
}
}()
cache.Stop()
}
func TestEmptyStrings(t *testing.T) {
cache := NewTtlCache()
defer cache.Stop()
cache.Set("", "empty key", 0)
val, ok := cache.Get("")
if !ok {
t.Error("Failed to get value with empty string key")
}
if val != "empty key" {
t.Errorf("Expected 'empty key', got '%s'", val)
}
cache.Set("empty-value", "", 0)
val, ok = cache.Get("empty-value")
if !ok {
t.Error("Failed to get empty string value")
}
if val != "" {
t.Errorf("Expected empty string, got '%s'", val)
}
cache.Delete("")
_, ok = cache.Get("")
if ok {
t.Error("Empty string key should be deleted")
}
}
func TestTtlUpdates(t *testing.T) {
cache := NewTtlCache()
defer cache.Stop()
cache.Set("key", "value", 100*time.Millisecond)
time.Sleep(50 * time.Millisecond)
cache.Set("key", "value", 500*time.Millisecond)
time.Sleep(100 * time.Millisecond)
val, ok := cache.Get("key")
if !ok {
t.Error("Key should not have expired after TTL update")
}
if val != "value" {
t.Errorf("Expected 'value', got '%s'", val)
}
time.Sleep(400 * time.Millisecond)
_, ok = cache.Get("key")
if ok {
t.Error("Key should have expired after the updated TTL")
}
cache.Set("convert", "value", 100*time.Millisecond)
time.Sleep(50 * time.Millisecond)
cache.Set("convert", "permanent", 0)
time.Sleep(100 * time.Millisecond)
val, ok = cache.Get("convert")
if !ok {
t.Error("Key should not expire after conversion to non-expiring")
}
if val != "permanent" {
t.Errorf("Expected 'permanent', got '%s'", val)
}
}