mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-30 22:26:38 +08:00
b928928958
Node and dashboard 24h capacity, network, and disk IO charts only used the latest limited raw snapshots (120/500 rows), so historical hour buckets stayed empty. Prefer ClickHouse hourly aggregates with counter deltas, and fall back to raw snapshots when aggregation is unavailable.
216 lines
6.5 KiB
Go
216 lines
6.5 KiB
Go
// Copyright 2026 Arctel.net
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package observability
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/Rain-kl/Wavelet/internal/model"
|
|
)
|
|
|
|
func TestBuildTrafficTrendPointsFromHourlyBucketsByHour(t *testing.T) {
|
|
now := time.Date(2026, 7, 2, 15, 30, 0, 0, time.UTC)
|
|
hourly := []*model.OpenFlareTrafficHourly{
|
|
{
|
|
NodeID: "node-a",
|
|
Hour: now.Add(-2 * time.Hour).Truncate(time.Hour),
|
|
RequestCount: 12,
|
|
ErrorCount: 1,
|
|
UniqueVisitorCount: 4,
|
|
},
|
|
}
|
|
points := BuildTrafficTrendPointsFromHourly(now, hourly)
|
|
if len(points) != observabilityTrendBuckets {
|
|
t.Fatalf("BuildTrafficTrendPointsFromHourly() len = %d, want %d", len(points), observabilityTrendBuckets)
|
|
}
|
|
}
|
|
|
|
func TestBuildTrafficTrendPointsBucketsByHour(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := time.Date(2026, 6, 19, 17, 30, 0, 0, time.UTC)
|
|
reports := []*model.OpenFlareRequestReport{
|
|
{
|
|
NodeID: "node-a",
|
|
WindowStartedAt: now.Add(-3 * time.Hour),
|
|
WindowEndedAt: now.Add(-3*time.Hour + time.Minute),
|
|
RequestCount: 10,
|
|
ErrorCount: 1,
|
|
},
|
|
{
|
|
NodeID: "node-a",
|
|
WindowStartedAt: now.Add(-30 * time.Minute),
|
|
WindowEndedAt: now.Add(-29 * time.Minute),
|
|
RequestCount: 6,
|
|
ErrorCount: 0,
|
|
},
|
|
}
|
|
|
|
points := BuildTrafficTrendPoints(now, reports)
|
|
if len(points) != observabilityTrendBuckets {
|
|
t.Fatalf("BuildTrafficTrendPoints() len = %d, want %d", len(points), observabilityTrendBuckets)
|
|
}
|
|
|
|
var totalRequests int64
|
|
for _, point := range points {
|
|
totalRequests += point.RequestCount
|
|
}
|
|
if totalRequests != 16 {
|
|
t.Fatalf("total request_count = %d, want 16", totalRequests)
|
|
}
|
|
|
|
currentHour := points[len(points)-1]
|
|
if currentHour.RequestCount != 6 {
|
|
t.Fatalf("current hour request_count = %d, want 6", currentHour.RequestCount)
|
|
}
|
|
if currentHour.ErrorCount != 0 {
|
|
t.Fatalf("current hour error_count = %d, want 0", currentHour.ErrorCount)
|
|
}
|
|
}
|
|
|
|
func TestBuildMetricSnapshotViewsMergesOpenrestyObservation(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
capturedAt := time.Date(2026, 6, 19, 12, 0, 0, 0, time.UTC)
|
|
snapshots := []*model.OpenFlareMetricSnapshot{
|
|
{
|
|
ID: 1,
|
|
NodeID: "node-a",
|
|
CapturedAt: capturedAt,
|
|
CPUUsagePercent: 12.5,
|
|
},
|
|
}
|
|
openrestyObs := []*model.OpenFlareNodeObservationOpenresty{
|
|
{
|
|
NodeID: "node-a",
|
|
CapturedAt: capturedAt.Add(5 * time.Second),
|
|
OpenrestyRxBytes: 4096,
|
|
OpenrestyTxBytes: 8192,
|
|
OpenrestyConnections: 7,
|
|
},
|
|
}
|
|
|
|
views := BuildMetricSnapshotViews(snapshots, openrestyObs)
|
|
if len(views) != 1 {
|
|
t.Fatalf("BuildMetricSnapshotViews() len = %d, want 1", len(views))
|
|
}
|
|
if views[0].OpenrestyRxBytes != 4096 {
|
|
t.Fatalf("OpenrestyRxBytes = %d, want 4096", views[0].OpenrestyRxBytes)
|
|
}
|
|
if views[0].OpenrestyTxBytes != 8192 {
|
|
t.Fatalf("OpenrestyTxBytes = %d, want 8192", views[0].OpenrestyTxBytes)
|
|
}
|
|
if views[0].OpenrestyConnections != 7 {
|
|
t.Fatalf("OpenrestyConnections = %d, want 7", views[0].OpenrestyConnections)
|
|
}
|
|
}
|
|
|
|
func TestLatestTrafficReportUsesLatestWindowEndedAt(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
older := &model.OpenFlareRequestReport{
|
|
WindowEndedAt: time.Date(2026, 6, 19, 10, 0, 0, 0, time.UTC),
|
|
RequestCount: 3,
|
|
}
|
|
newer := &model.OpenFlareRequestReport{
|
|
WindowEndedAt: time.Date(2026, 6, 19, 11, 0, 0, 0, time.UTC),
|
|
RequestCount: 9,
|
|
}
|
|
|
|
latest := latestTrafficReport([]*model.OpenFlareRequestReport{older, newer})
|
|
if latest == nil || latest.RequestCount != 9 {
|
|
t.Fatalf("latestTrafficReport() = %#v, want newer report with request_count 9", latest)
|
|
}
|
|
}
|
|
|
|
func TestBuildTrafficWindowSummaryNilWithoutReport(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
if summary := buildTrafficWindowSummary(nil); summary != nil {
|
|
t.Fatalf("buildTrafficWindowSummary(nil) = %#v, want nil", summary)
|
|
}
|
|
}
|
|
|
|
func TestBuildCapacityTrendPointsFromHourlyFillsBuckets(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := time.Date(2026, 7, 10, 9, 30, 0, 0, time.UTC)
|
|
hourly := []*model.OpenFlareMetricHourly{
|
|
{
|
|
Hour: now.Add(-3 * time.Hour).Truncate(time.Hour),
|
|
AverageCPUUsagePercent: 42.5,
|
|
AverageMemoryUsagePercent: 61.2,
|
|
ReportedNodes: 1,
|
|
},
|
|
{
|
|
Hour: now.Truncate(time.Hour),
|
|
AverageCPUUsagePercent: 12.0,
|
|
AverageMemoryUsagePercent: 50.0,
|
|
ReportedNodes: 2,
|
|
},
|
|
}
|
|
|
|
points := BuildCapacityTrendPointsFromHourly(now, hourly)
|
|
if len(points) != observabilityTrendBuckets {
|
|
t.Fatalf("len = %d, want %d", len(points), observabilityTrendBuckets)
|
|
}
|
|
if points[len(points)-4].AverageCPUUsagePercent != 42.5 {
|
|
t.Fatalf("hour-3 cpu = %v, want 42.5", points[len(points)-4].AverageCPUUsagePercent)
|
|
}
|
|
if points[len(points)-1].ReportedNodes != 2 {
|
|
t.Fatalf("current hour reported_nodes = %d, want 2", points[len(points)-1].ReportedNodes)
|
|
}
|
|
}
|
|
|
|
func TestBuildNetworkTrendPointsUsesCounterDeltas(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := time.Date(2026, 7, 10, 9, 30, 0, 0, time.UTC)
|
|
base := now.Truncate(time.Hour)
|
|
snapshots := []*model.OpenFlareMetricSnapshot{
|
|
{NodeID: "n1", CapturedAt: base.Add(10 * time.Minute), NetworkRxBytes: 1000, NetworkTxBytes: 2000},
|
|
{NodeID: "n1", CapturedAt: base.Add(20 * time.Minute), NetworkRxBytes: 1500, NetworkTxBytes: 2600},
|
|
}
|
|
openrestyObs := []*model.OpenFlareNodeObservationOpenresty{
|
|
{NodeID: "n1", CapturedAt: base.Add(10 * time.Minute), OpenrestyRxBytes: 100, OpenrestyTxBytes: 200},
|
|
{NodeID: "n1", CapturedAt: base.Add(20 * time.Minute), OpenrestyRxBytes: 180, OpenrestyTxBytes: 250},
|
|
}
|
|
|
|
points := BuildNetworkTrendPoints(now, snapshots, openrestyObs)
|
|
current := points[len(points)-1]
|
|
if current.NetworkRxBytes != 500 {
|
|
t.Fatalf("network_rx_bytes = %d, want 500", current.NetworkRxBytes)
|
|
}
|
|
if current.NetworkTxBytes != 600 {
|
|
t.Fatalf("network_tx_bytes = %d, want 600", current.NetworkTxBytes)
|
|
}
|
|
if current.OpenrestyRxBytes != 80 {
|
|
t.Fatalf("openresty_rx_bytes = %d, want 80", current.OpenrestyRxBytes)
|
|
}
|
|
if current.OpenrestyTxBytes != 50 {
|
|
t.Fatalf("openresty_tx_bytes = %d, want 50", current.OpenrestyTxBytes)
|
|
}
|
|
}
|
|
|
|
func TestBuildDiskIOTrendPointsFromHourlyFillsBuckets(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
now := time.Date(2026, 7, 10, 9, 30, 0, 0, time.UTC)
|
|
hourly := []*model.OpenFlareMetricHourly{
|
|
{
|
|
Hour: now.Add(-1 * time.Hour).Truncate(time.Hour),
|
|
DiskReadBytes: 1024,
|
|
DiskWriteBytes: 2048,
|
|
ReportedNodes: 1,
|
|
},
|
|
}
|
|
|
|
points := BuildDiskIOTrendPointsFromHourly(now, hourly)
|
|
prev := points[len(points)-2]
|
|
if prev.DiskReadBytes != 1024 || prev.DiskWriteBytes != 2048 {
|
|
t.Fatalf("previous hour disk io = %#v, want read=1024 write=2048", prev)
|
|
}
|
|
}
|