Backend: new overviewMetrics store — a single-tier (2s x 360 = 12min) in-memory ring fed by ServerService.RefreshStatus for exactly the eight overview series (cpu/mem/swap/diskUsage/netUp/netDown/tcpCount/udpCount). No persistence, no gob snapshot, no system-history page: the only surface is GET /panel/api/server/history/:metric/:bucket restricted to those keys, which seeds the frontend curves. ~46KB bounded by construction; the ring starts empty on every panel start. Frontend: restore useOverviewHistory + the VitalTile/ThroughputCard/ ConnectionsCard sparklines and the IndexPage wiring exactly as they were before P7 removed them along with the history module.
68 lines
2.2 KiB
Go
68 lines
2.2 KiB
Go
package service
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// The overview ring is a fresh store per test (the package singleton is fed
|
|
// by RefreshStatus in production); these tests pin its contract: only the
|
|
// overview keys are written by AppendOverviewSample, samples come back
|
|
// through AggregateOverviewMetric, and the window is hard-bounded by the
|
|
// single tier's capacity no matter how long the panel runs.
|
|
|
|
func overviewTestStatus(cpu float64) *Status {
|
|
st := &Status{Cpu: cpu, TcpCount: 3, UdpCount: 2}
|
|
st.Mem.Current = 1
|
|
st.Mem.Total = 4
|
|
st.Swap.Current = 0
|
|
st.Swap.Total = 0
|
|
st.Disk.Current = 1
|
|
st.Disk.Total = 2
|
|
st.NetIO.Up = 100
|
|
st.NetIO.Down = 200
|
|
return st
|
|
}
|
|
|
|
func TestOverviewRingAppendAndAggregate(t *testing.T) {
|
|
s := &ServerService{}
|
|
base := time.Now().Add(-10 * time.Second).Truncate(2 * time.Second)
|
|
for i := range 5 {
|
|
s.AppendOverviewSample(base.Add(time.Duration(i*2)*time.Second), overviewTestStatus(float64(i)))
|
|
}
|
|
// overviewMetrics is the package singleton; the samples just appended
|
|
// must be visible through the aggregate the controller serves.
|
|
out := s.AggregateOverviewMetric("cpu", 2, 60)
|
|
if len(out) == 0 {
|
|
t.Fatal("AggregateOverviewMetric(cpu) returned no points after appends")
|
|
}
|
|
last := out[len(out)-1]
|
|
if got, _ := last["v"].(float64); got != 4 {
|
|
t.Fatalf("last cpu point = %v, want 4", last["v"])
|
|
}
|
|
for _, key := range OverviewMetricKeys {
|
|
if pts := s.AggregateOverviewMetric(key, 2, 60); len(pts) == 0 {
|
|
t.Fatalf("overview key %q has no points after AppendOverviewSample", key)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestOverviewRingIsBounded(t *testing.T) {
|
|
h := newMetricHistoryWithTiers([]tierSpec{{resolution: 2, capacity: 360}})
|
|
base := time.Unix(1_700_000_000, 0)
|
|
// Feed ten windows' worth of samples; the ring must not grow past its
|
|
// single tier capacity.
|
|
for i := range 3600 {
|
|
h.append("cpu", base.Add(time.Duration(i*2)*time.Second), float64(i))
|
|
}
|
|
// readSamples returns the closed buckets plus the still-open one, so the
|
|
// hard bound is capacity+1.
|
|
raw := h.series["cpu"].tiers[0].readSamples()
|
|
if len(raw) > 361 {
|
|
t.Fatalf("overview tier retained %d samples, want <= 361 (capacity + open bucket)", len(raw))
|
|
}
|
|
if len(raw) == 0 {
|
|
t.Fatal("overview tier retained no samples")
|
|
}
|
|
}
|