mirror of
https://github.com/henrygd/beszel.git
synced 2025-10-30 01:57:04 +00:00
- add one minute charts - update disk io to use bytes - update hub and agent connection interfaces / handlers to be more flexible - change agent cache to use cache time instead of session id - refactor collection of metrics which require deltas to track separately per cache time
247 lines
5.7 KiB
Go
247 lines
5.7 KiB
Go
//go:build testing
|
|
// +build testing
|
|
|
|
package agent
|
|
|
|
import (
|
|
"testing"
|
|
"testing/synctest"
|
|
"time"
|
|
|
|
"github.com/henrygd/beszel/internal/entities/container"
|
|
"github.com/henrygd/beszel/internal/entities/system"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func createTestCacheData() *system.CombinedData {
|
|
return &system.CombinedData{
|
|
Stats: system.Stats{
|
|
Cpu: 50.5,
|
|
Mem: 8192,
|
|
DiskTotal: 100000,
|
|
},
|
|
Info: system.Info{
|
|
Hostname: "test-host",
|
|
},
|
|
Containers: []*container.Stats{
|
|
{
|
|
Name: "test-container",
|
|
Cpu: 25.0,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestNewSystemDataCache(t *testing.T) {
|
|
cache := NewSystemDataCache()
|
|
require.NotNil(t, cache)
|
|
assert.NotNil(t, cache.cache)
|
|
assert.Empty(t, cache.cache)
|
|
}
|
|
|
|
func TestCacheGetSet(t *testing.T) {
|
|
cache := NewSystemDataCache()
|
|
data := createTestCacheData()
|
|
|
|
// Test setting data
|
|
cache.Set(data, 1000) // 1 second cache
|
|
|
|
// Test getting fresh data
|
|
retrieved, isCached := cache.Get(1000)
|
|
assert.True(t, isCached)
|
|
assert.Equal(t, data, retrieved)
|
|
|
|
// Test getting non-existent cache key
|
|
_, isCached = cache.Get(2000)
|
|
assert.False(t, isCached)
|
|
}
|
|
|
|
func TestCacheFreshness(t *testing.T) {
|
|
cache := NewSystemDataCache()
|
|
data := createTestCacheData()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
cacheTimeMs uint16
|
|
sleepMs time.Duration
|
|
expectFresh bool
|
|
}{
|
|
{
|
|
name: "fresh data - well within cache time",
|
|
cacheTimeMs: 1000, // 1 second
|
|
sleepMs: 100, // 100ms
|
|
expectFresh: true,
|
|
},
|
|
{
|
|
name: "fresh data - at 50% of cache time boundary",
|
|
cacheTimeMs: 1000, // 1 second, 50% = 500ms
|
|
sleepMs: 499, // just under 500ms
|
|
expectFresh: true,
|
|
},
|
|
{
|
|
name: "stale data - exactly at 50% cache time",
|
|
cacheTimeMs: 1000, // 1 second, 50% = 500ms
|
|
sleepMs: 500, // exactly 500ms
|
|
expectFresh: false,
|
|
},
|
|
{
|
|
name: "stale data - well beyond cache time",
|
|
cacheTimeMs: 1000, // 1 second
|
|
sleepMs: 800, // 800ms
|
|
expectFresh: false,
|
|
},
|
|
{
|
|
name: "short cache time",
|
|
cacheTimeMs: 200, // 200ms, 50% = 100ms
|
|
sleepMs: 150, // 150ms > 100ms
|
|
expectFresh: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
synctest.Test(t, func(t *testing.T) {
|
|
// Set data
|
|
cache.Set(data, tc.cacheTimeMs)
|
|
|
|
// Wait for the specified duration
|
|
if tc.sleepMs > 0 {
|
|
time.Sleep(tc.sleepMs * time.Millisecond)
|
|
}
|
|
|
|
// Check freshness
|
|
_, isCached := cache.Get(tc.cacheTimeMs)
|
|
assert.Equal(t, tc.expectFresh, isCached)
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCacheMultipleIntervals(t *testing.T) {
|
|
synctest.Test(t, func(t *testing.T) {
|
|
cache := NewSystemDataCache()
|
|
data1 := createTestCacheData()
|
|
data2 := &system.CombinedData{
|
|
Stats: system.Stats{
|
|
Cpu: 75.0,
|
|
Mem: 16384,
|
|
},
|
|
Info: system.Info{
|
|
Hostname: "test-host-2",
|
|
},
|
|
Containers: []*container.Stats{},
|
|
}
|
|
|
|
// Set data for different intervals
|
|
cache.Set(data1, 500) // 500ms cache
|
|
cache.Set(data2, 1000) // 1000ms cache
|
|
|
|
// Both should be fresh immediately
|
|
retrieved1, isCached1 := cache.Get(500)
|
|
assert.True(t, isCached1)
|
|
assert.Equal(t, data1, retrieved1)
|
|
|
|
retrieved2, isCached2 := cache.Get(1000)
|
|
assert.True(t, isCached2)
|
|
assert.Equal(t, data2, retrieved2)
|
|
|
|
// Wait 300ms - 500ms cache should be stale (250ms threshold), 1000ms should still be fresh (500ms threshold)
|
|
time.Sleep(300 * time.Millisecond)
|
|
|
|
_, isCached1 = cache.Get(500)
|
|
assert.False(t, isCached1)
|
|
|
|
_, isCached2 = cache.Get(1000)
|
|
assert.True(t, isCached2)
|
|
|
|
// Wait another 300ms (total 600ms) - now 1000ms cache should also be stale
|
|
time.Sleep(300 * time.Millisecond)
|
|
_, isCached2 = cache.Get(1000)
|
|
assert.False(t, isCached2)
|
|
})
|
|
}
|
|
|
|
func TestCacheOverwrite(t *testing.T) {
|
|
cache := NewSystemDataCache()
|
|
data1 := createTestCacheData()
|
|
data2 := &system.CombinedData{
|
|
Stats: system.Stats{
|
|
Cpu: 90.0,
|
|
Mem: 32768,
|
|
},
|
|
Info: system.Info{
|
|
Hostname: "updated-host",
|
|
},
|
|
Containers: []*container.Stats{},
|
|
}
|
|
|
|
// Set initial data
|
|
cache.Set(data1, 1000)
|
|
retrieved, isCached := cache.Get(1000)
|
|
assert.True(t, isCached)
|
|
assert.Equal(t, data1, retrieved)
|
|
|
|
// Overwrite with new data
|
|
cache.Set(data2, 1000)
|
|
retrieved, isCached = cache.Get(1000)
|
|
assert.True(t, isCached)
|
|
assert.Equal(t, data2, retrieved)
|
|
assert.NotEqual(t, data1, retrieved)
|
|
}
|
|
|
|
func TestCacheMiss(t *testing.T) {
|
|
synctest.Test(t, func(t *testing.T) {
|
|
cache := NewSystemDataCache()
|
|
|
|
// Test getting from empty cache
|
|
_, isCached := cache.Get(1000)
|
|
assert.False(t, isCached)
|
|
|
|
// Set data for one interval
|
|
data := createTestCacheData()
|
|
cache.Set(data, 1000)
|
|
|
|
// Test getting different interval
|
|
_, isCached = cache.Get(2000)
|
|
assert.False(t, isCached)
|
|
|
|
// Test getting after data has expired
|
|
time.Sleep(600 * time.Millisecond) // 600ms > 500ms (50% of 1000ms)
|
|
_, isCached = cache.Get(1000)
|
|
assert.False(t, isCached)
|
|
})
|
|
}
|
|
|
|
func TestCacheZeroInterval(t *testing.T) {
|
|
cache := NewSystemDataCache()
|
|
data := createTestCacheData()
|
|
|
|
// Set with zero interval - should allow immediate cache
|
|
cache.Set(data, 0)
|
|
|
|
// With 0 interval, 50% is 0, so it should never be considered fresh
|
|
// (time.Since(lastUpdate) >= 0, which is not < 0)
|
|
_, isCached := cache.Get(0)
|
|
assert.False(t, isCached)
|
|
}
|
|
|
|
func TestCacheLargeInterval(t *testing.T) {
|
|
synctest.Test(t, func(t *testing.T) {
|
|
cache := NewSystemDataCache()
|
|
data := createTestCacheData()
|
|
|
|
// Test with maximum uint16 value
|
|
cache.Set(data, 65535) // ~65 seconds
|
|
|
|
// Should be fresh immediately
|
|
_, isCached := cache.Get(65535)
|
|
assert.True(t, isCached)
|
|
|
|
// Should still be fresh after a short time
|
|
time.Sleep(100 * time.Millisecond)
|
|
_, isCached = cache.Get(65535)
|
|
assert.True(t, isCached)
|
|
})
|
|
}
|