mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 18:45:53 +00:00
115 lines
3.9 KiB
Go
115 lines
3.9 KiB
Go
package chartapi
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestBoundedChartPayloadCacheLimitsCardinalityAndBytes(t *testing.T) {
|
|
now := time.Date(2026, time.August, 30, 10, 0, 0, 0, time.UTC)
|
|
cache := newBoundedChartPayloadCache(2, 10)
|
|
|
|
cache.put("first", bytes.Repeat([]byte{'a'}, 6), now.Add(time.Minute), now)
|
|
cache.put("second", bytes.Repeat([]byte{'b'}, 4), now.Add(time.Minute), now)
|
|
cache.put("third", bytes.Repeat([]byte{'c'}, 6), now.Add(time.Minute), now)
|
|
|
|
if _, ok := cache.get("first", now); ok {
|
|
t.Fatal("oldest entry remained after the shared byte budget was exhausted")
|
|
}
|
|
if _, ok := cache.get("second", now); !ok {
|
|
t.Fatal("newer entry was unexpectedly evicted")
|
|
}
|
|
if _, ok := cache.get("third", now); !ok {
|
|
t.Fatal("new entry was not retained")
|
|
}
|
|
if got := len(cache.entries); got != 2 {
|
|
t.Fatalf("retained entries = %d, want 2", got)
|
|
}
|
|
if cache.bytes > cache.maxBytes {
|
|
t.Fatalf("retained bytes = %d, exceeds budget %d", cache.bytes, cache.maxBytes)
|
|
}
|
|
}
|
|
|
|
func TestBoundedChartPayloadCachePurgesExpiredVariantsOnInsert(t *testing.T) {
|
|
now := time.Date(2026, time.August, 30, 10, 0, 0, 0, time.UTC)
|
|
cache := newBoundedChartPayloadCache(4, 32)
|
|
|
|
cache.put("expired-a", []byte("aaaa"), now.Add(time.Second), now)
|
|
cache.put("expired-b", []byte("bbbb"), now.Add(time.Second), now)
|
|
later := now.Add(2 * time.Second)
|
|
cache.put("live", []byte("cccc"), later.Add(time.Second), later)
|
|
|
|
if got := len(cache.entries); got != 1 {
|
|
t.Fatalf("retained entries after expiry sweep = %d, want 1", got)
|
|
}
|
|
if cache.bytes != len("cccc") {
|
|
t.Fatalf("retained bytes after expiry sweep = %d, want %d", cache.bytes, len("cccc"))
|
|
}
|
|
}
|
|
|
|
func TestBoundedChartPayloadCacheDoesNotRetainOversizedResponse(t *testing.T) {
|
|
now := time.Date(2026, time.August, 30, 10, 0, 0, 0, time.UTC)
|
|
cache := newBoundedChartPayloadCache(4, 8)
|
|
cache.put("oversized", bytes.Repeat([]byte{'x'}, 9), now.Add(time.Minute), now)
|
|
|
|
if _, ok := cache.get("oversized", now); ok {
|
|
t.Fatal("response larger than the full cache budget was retained")
|
|
}
|
|
if len(cache.entries) != 0 || cache.bytes != 0 {
|
|
t.Fatalf("oversized response changed cache state: entries=%d bytes=%d", len(cache.entries), cache.bytes)
|
|
}
|
|
}
|
|
|
|
func TestChartRoutesShareOnePayloadRetentionBudget(t *testing.T) {
|
|
now := time.Date(2026, time.August, 30, 10, 0, 0, 0, time.UTC)
|
|
service := NewService(nil)
|
|
service.chartPayloads = newBoundedChartPayloadCache(2, 10)
|
|
|
|
service.cacheInfrastructureChartsPayload("infra", []byte("aaaa"), now)
|
|
service.cacheWorkloadsSummaryChartsPayload("summary", []byte("bbbb"), now)
|
|
service.chartPayloads.put(
|
|
workloadChartsCachePrefix+"workloads",
|
|
[]byte("cccc"),
|
|
now.Add(workloadChartsCacheTTL),
|
|
now,
|
|
)
|
|
|
|
if got := len(service.chartPayloads.entries); got != 2 {
|
|
t.Fatalf("retained entries across chart routes = %d, want shared cap 2", got)
|
|
}
|
|
if service.chartPayloads.bytes > service.chartPayloads.maxBytes {
|
|
t.Fatalf("retained bytes across chart routes = %d, exceeds shared budget %d", service.chartPayloads.bytes, service.chartPayloads.maxBytes)
|
|
}
|
|
if _, ok := service.cachedInfrastructureChartsPayload("infra", now); ok {
|
|
t.Fatal("oldest route entry was not evicted from the shared cache")
|
|
}
|
|
}
|
|
|
|
func TestBoundedChartPayloadCacheStaysWithinBudgetUnderConcurrency(t *testing.T) {
|
|
now := time.Date(2026, time.August, 30, 10, 0, 0, 0, time.UTC)
|
|
cache := newBoundedChartPayloadCache(8, 4<<10)
|
|
payload := bytes.Repeat([]byte{'x'}, 1024)
|
|
|
|
var workers sync.WaitGroup
|
|
for index := range 128 {
|
|
workers.Add(1)
|
|
go func() {
|
|
defer workers.Done()
|
|
key := fmt.Sprintf("variant-%d", index)
|
|
cache.put(key, payload, now.Add(time.Minute), now)
|
|
_, _ = cache.get(key, now)
|
|
}()
|
|
}
|
|
workers.Wait()
|
|
|
|
if got := len(cache.entries); got > cache.maxEntries {
|
|
t.Fatalf("retained entries after concurrent writes = %d, exceeds cap %d", got, cache.maxEntries)
|
|
}
|
|
if cache.bytes > cache.maxBytes {
|
|
t.Fatalf("retained bytes after concurrent writes = %d, exceeds budget %d", cache.bytes, cache.maxBytes)
|
|
}
|
|
}
|