Files
tyto/backend/internal/collectors/cpu_test.go
vikingowl f4dbc55851 feat: add dashboard customization, alerts, PWA, and mobile support
Dashboard Editor & Layout:
- Full-screen visual editor for reorganizing cards
- Drag-and-drop cards between sections
- Toggle card visibility with persistence to localStorage
- Reset to default layout option

Alerts System:
- Threshold-based alerts for CPU, memory, temperature, disk, GPU
- Alert manager with duration requirements
- AlertsCard component with settings UI
- API endpoints for alerts CRUD

New Collectors:
- Docker container monitoring with parallel stats fetching
- Systemd service status via D-Bus
- Historical metrics storage (1 hour at 1s intervals)

PWA Support:
- Service worker with offline caching
- Web app manifest with SVG icons
- iOS PWA meta tags

Mobile Responsive:
- Collapsible hamburger menu on mobile
- Adaptive grid layouts for all screen sizes
- Touch-friendly hover states
- Safe area insets for notched devices

UI Enhancements:
- Light/dark theme toggle with persistence
- Keyboard shortcuts (T=theme, R=refresh, ?=help)
- Per-process expandable details in ProcessesCard
- Sparkline charts for historical data

Performance Fixes:
- Buffered SSE channels to prevent blocking
- Parallel Docker stats collection with timeout
- D-Bus timeout for systemd collector

Tests:
- Unit tests for CPU, memory, network collectors
- Alert manager tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 05:35:28 +01:00

100 lines
2.7 KiB
Go

package collectors
import (
"os"
"path/filepath"
"testing"
)
func TestCPUCollector(t *testing.T) {
// Create temp directory with mock /proc files
tmpDir := t.TempDir()
procPath := filepath.Join(tmpDir, "proc")
sysPath := filepath.Join(tmpDir, "sys")
// Create necessary directories
if err := os.MkdirAll(procPath, 0755); err != nil {
t.Fatal(err)
}
if err := os.MkdirAll(filepath.Join(sysPath, "devices/system/cpu/cpufreq/policy0"), 0755); err != nil {
t.Fatal(err)
}
// Create mock /proc/stat
statContent := `cpu 10132153 290696 3084719 46828483 16683 0 25195 0 0 0
cpu0 1393280 32966 572056 13343292 6130 0 17875 0 0 0
cpu1 1264380 32862 535089 13315801 3580 0 7275 0 0 0
`
if err := os.WriteFile(filepath.Join(procPath, "stat"), []byte(statContent), 0644); err != nil {
t.Fatal(err)
}
// Create mock /proc/loadavg
loadavgContent := "1.23 2.34 3.45 1/123 12345\n"
if err := os.WriteFile(filepath.Join(procPath, "loadavg"), []byte(loadavgContent), 0644); err != nil {
t.Fatal(err)
}
// Create mock scaling_cur_freq
if err := os.WriteFile(filepath.Join(sysPath, "devices/system/cpu/cpufreq/policy0/scaling_cur_freq"), []byte("3500000\n"), 0644); err != nil {
t.Fatal(err)
}
collector := NewCPUCollector(procPath, sysPath)
// First collection to initialize
_, err := collector.Collect()
if err != nil {
t.Fatalf("First collect failed: %v", err)
}
// Update stat with new values to calculate usage
statContent2 := `cpu 10142153 290696 3084719 46838483 16683 0 25195 0 0 0
cpu0 1403280 32966 572056 13353292 6130 0 17875 0 0 0
cpu1 1274380 32862 535089 13325801 3580 0 7275 0 0 0
`
if err := os.WriteFile(filepath.Join(procPath, "stat"), []byte(statContent2), 0644); err != nil {
t.Fatal(err)
}
// Second collection
stats, err := collector.Collect()
if err != nil {
t.Fatalf("Second collect failed: %v", err)
}
// Verify results
if len(stats.Cores) == 0 {
t.Error("Expected at least one core")
}
if stats.LoadAverage.Load1 != 1.23 {
t.Errorf("Expected load1=1.23, got %f", stats.LoadAverage.Load1)
}
if stats.LoadAverage.Load5 != 2.34 {
t.Errorf("Expected load5=2.34, got %f", stats.LoadAverage.Load5)
}
if stats.LoadAverage.Load15 != 3.45 {
t.Errorf("Expected load15=3.45, got %f", stats.LoadAverage.Load15)
}
}
func TestCPUCollector_MissingFiles(t *testing.T) {
tmpDir := t.TempDir()
collector := NewCPUCollector(tmpDir, tmpDir)
// Should handle gracefully - either error or empty stats is acceptable
stats, err := collector.Collect()
if err != nil {
// Error is acceptable for missing files
return
}
// If no error, should have empty/default values
if len(stats.Cores) != 0 {
t.Errorf("Expected no cores with missing files, got %d", len(stats.Cores))
}
}