Files
mrlee 8f6fac802f feat(web): 仪表盘补系统资源/中国 IP 段/通知通道与公网穿透卡,修复 frpc 版本读取
仪表盘:修「服务」卡写死的「正常」——改为按 Fonu/Nginx/DDNS/证书四项实际状态聚合,异常时点名并降级徽标

仪表盘:新增「性能」面板(今日异常/平均响应,/api/status 早已返回但从未展示)与「流量 Top 5」(复用已有逐规则流量轮询);/api/status 每 15 秒刷新并在页面隐藏时暂停(连同流量轮询)

仪表盘:新增「公网穿透」统计卡;运行状态面板增加公网反代行,数据来自新增的 GET /api/frpmulti/routes/summary(逐台服务端聚合,单台失败不中断)

仪表盘:新增一行三卡「系统资源 / 中国 IP 段 / 通知通道」;系统资源为新增跨平台采集 internal/sysinfo(Linux /proc、Windows kernel32,无新依赖)

FRP:修复 frpc 版本读不出来——RuntimeStatus.Version 带 omitempty,--version 探测失败时字段被省略,前端只能显示未检测;现回落按 <FrpDir>/bin/<版本>/ 目录名推导(与「FRP 应用配置」弹窗的 active_version 同源),并逐台服务端探测版本

清理:删除 DashboardView 死样式(.dashboard/.stat-highlight/.proxy-metric*)
2026-09-23 19:10:43 +08:00

62 lines
2.1 KiB
Go

package sysinfo
import "testing"
func TestCPUPercent(t *testing.T) {
prev := CPUSample{Total: 1000, Idle: 400}
if got := CPUPercent(prev, CPUSample{Total: 1200, Idle: 500}); got != 50 {
t.Fatalf("expected 50%%, got %v", got)
}
// 首次采样(prev 为零值)不给出占用率
if got := CPUPercent(CPUSample{}, CPUSample{Total: 1200, Idle: 500}); got != 0 {
t.Fatalf("expected 0 on first sample, got %v", got)
}
// 计数器未前进
if got := CPUPercent(prev, prev); got != 0 {
t.Fatalf("expected 0 when counters stalled, got %v", got)
}
}
func TestParseProcStat(t *testing.T) {
raw := "cpu 100 20 80 400 10 0 5 0 0 0\ncpu0 1 2 3 4 5 6 7 8 9 10\n"
sample := parseProcStat(raw)
// 总时间 100+20+80+400+10+0+5 = 615,空闲 400(idle)+10(iowait) = 410
if sample.Total != 615 || sample.Idle != 410 {
t.Fatalf("unexpected sample: %+v", sample)
}
}
func TestParseMemInfo(t *testing.T) {
raw := "MemTotal: 16384000 kB\nMemFree: 1000000 kB\nMemAvailable: 8192000 kB\n"
total, used := parseMemInfo(raw)
if total != 16384000*1024 {
t.Fatalf("unexpected total: %d", total)
}
if used != (16384000-8192000)*1024 {
t.Fatalf("unexpected used: %d", used)
}
// 缺少 MemAvailable 时回落到 MemFree + Buffers + Cached
fallback := "MemTotal: 1000 kB\nMemFree: 200 kB\nBuffers: 100 kB\nCached: 300 kB\n"
total, used = parseMemInfo(fallback)
if total != 1000*1024 || used != 400*1024 {
t.Fatalf("unexpected fallback: total=%d used=%d", total, used)
}
}
func TestReadReportsHostResources(t *testing.T) {
stats, sample := Read(CPUSample{})
if stats.MemTotalBytes == 0 && stats.DiskTotalBytes == 0 {
t.Skip("当前平台未实现资源采集")
}
if stats.MemTotalBytes == 0 || stats.MemUsedBytes > stats.MemTotalBytes {
t.Fatalf("unexpected memory: used=%d total=%d", stats.MemUsedBytes, stats.MemTotalBytes)
}
if stats.DiskTotalBytes == 0 || stats.DiskUsedBytes > stats.DiskTotalBytes {
t.Fatalf("unexpected disk: used=%d total=%d", stats.DiskUsedBytes, stats.DiskTotalBytes)
}
if sample.Total == 0 {
t.Fatalf("expected non-zero cpu counters")
}
}