mirror of
https://github.com/Noooste/garage-ui.git
synced 2026-07-27 16:18:56 +00:00
047a653446
Signed-off-by: Noooste <83548733+Noooste@users.noreply.github.com>
130 lines
3.0 KiB
Go
130 lines
3.0 KiB
Go
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCache_GetMissReturnsNil(t *testing.T) {
|
|
c := NewCache()
|
|
if v := c.Get("nope"); v != nil {
|
|
t.Errorf("expected nil for missing key, got %v", v)
|
|
}
|
|
}
|
|
|
|
func TestCache_SetThenGetReturnsValue(t *testing.T) {
|
|
c := NewCache()
|
|
c.Set("k", "v", time.Minute)
|
|
got := c.Get("k")
|
|
if got != "v" {
|
|
t.Errorf("Get(k) = %v, want v", got)
|
|
}
|
|
}
|
|
|
|
func TestCache_SetWithDifferentTypes(t *testing.T) {
|
|
c := NewCache()
|
|
c.Set("str", "hello", time.Minute)
|
|
c.Set("int", 42, time.Minute)
|
|
c.Set("slice", []int{1, 2, 3}, time.Minute)
|
|
|
|
if got := c.Get("str"); got != "hello" {
|
|
t.Errorf("str: got %v", got)
|
|
}
|
|
if got := c.Get("int"); got != 42 {
|
|
t.Errorf("int: got %v", got)
|
|
}
|
|
if got, ok := c.Get("slice").([]int); !ok || len(got) != 3 {
|
|
t.Errorf("slice: got %v", c.Get("slice"))
|
|
}
|
|
}
|
|
|
|
func TestCache_GetExpiredReturnsNil(t *testing.T) {
|
|
c := NewCache()
|
|
c.Set("k", "v", 10*time.Millisecond)
|
|
time.Sleep(25 * time.Millisecond)
|
|
if got := c.Get("k"); got != nil {
|
|
t.Errorf("expected nil after TTL, got %v", got)
|
|
}
|
|
}
|
|
|
|
func TestCache_DeleteRemovesItem(t *testing.T) {
|
|
c := NewCache()
|
|
c.Set("k", "v", time.Minute)
|
|
c.Delete("k")
|
|
if got := c.Get("k"); got != nil {
|
|
t.Errorf("expected nil after Delete, got %v", got)
|
|
}
|
|
}
|
|
|
|
func TestCache_DeleteMissingKeyIsNoOp(t *testing.T) {
|
|
c := NewCache()
|
|
// Should not panic or error.
|
|
c.Delete("never-set")
|
|
}
|
|
|
|
func TestCache_ClearRemovesAllItems(t *testing.T) {
|
|
c := NewCache()
|
|
c.Set("a", 1, time.Minute)
|
|
c.Set("b", 2, time.Minute)
|
|
c.Set("c", 3, time.Minute)
|
|
|
|
c.Clear()
|
|
|
|
if c.Get("a") != nil || c.Get("b") != nil || c.Get("c") != nil {
|
|
t.Errorf("expected all items cleared")
|
|
}
|
|
}
|
|
|
|
func TestCache_SetOverwrites(t *testing.T) {
|
|
c := NewCache()
|
|
c.Set("k", "v1", time.Minute)
|
|
c.Set("k", "v2", time.Minute)
|
|
if got := c.Get("k"); got != "v2" {
|
|
t.Errorf("expected v2 after overwrite, got %v", got)
|
|
}
|
|
}
|
|
|
|
// TestCache_ConcurrentAccess exercises the RWMutex under load. Run with
|
|
// `go test -race` to catch data races. Uses bounded concurrency so the test
|
|
// stays deterministic.
|
|
func TestCache_ConcurrentAccess(t *testing.T) {
|
|
c := NewCache()
|
|
const goroutines = 50
|
|
const opsPerGoroutine = 100
|
|
|
|
var wg sync.WaitGroup
|
|
wg.Add(goroutines)
|
|
|
|
for g := range goroutines {
|
|
go func(id int) {
|
|
defer wg.Done()
|
|
for i := range opsPerGoroutine {
|
|
key := fmt.Sprintf("k%d", (id+i)%10)
|
|
c.Set(key, i, time.Minute)
|
|
_ = c.Get(key)
|
|
if i%10 == 0 {
|
|
c.Delete(key)
|
|
}
|
|
}
|
|
}(g)
|
|
}
|
|
|
|
wg.Wait()
|
|
// If we got here without a panic and `-race` is clean, the RWMutex is
|
|
// protecting the map correctly.
|
|
}
|
|
|
|
// TestGlobalCache_IsUsable is a smoke test for the package-level var.
|
|
// It doesn't Clear() afterwards because the global is shared state that
|
|
// other packages may depend on at test time.
|
|
func TestGlobalCache_IsUsable(t *testing.T) {
|
|
key := "stage2-smoke-key"
|
|
GlobalCache.Set(key, "x", time.Minute)
|
|
t.Cleanup(func() { GlobalCache.Delete(key) })
|
|
if got := GlobalCache.Get(key); got != "x" {
|
|
t.Errorf("GlobalCache.Get = %v, want x", got)
|
|
}
|
|
}
|