9eaddbb6e8
Split monitor.go (1131→350 lines) into alerts.go, checks.go, sites.go, and maintenance.go by concern. Split sqlstore.go (833→470 lines) into sqlstore_alerts.go, sqlstore_history.go, and sqlstore_maintenance.go by domain. Tests move with their implementation. Pure reorganization — no behavioral changes.
169 lines
4.7 KiB
Go
169 lines
4.7 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"gitea.lerkolabs.com/lerkolabs/uptop/internal/models"
|
|
)
|
|
|
|
func TestPruneExpiredMaintenanceWindows(t *testing.T) {
|
|
s := newTestStore(t)
|
|
|
|
now := time.Now()
|
|
|
|
// Expired 10 days ago — should be pruned with 7d retention.
|
|
old := models.MaintenanceWindow{
|
|
MonitorID: 0,
|
|
Title: "Old Window",
|
|
Type: "maintenance",
|
|
StartTime: now.Add(-11 * 24 * time.Hour),
|
|
EndTime: now.Add(-10 * 24 * time.Hour),
|
|
}
|
|
if err := s.AddMaintenanceWindow(context.Background(), old); err != nil {
|
|
t.Fatalf("AddMaintenanceWindow (old): %v", err)
|
|
}
|
|
|
|
// Expired 1 day ago — within 7d retention, should survive.
|
|
recent := models.MaintenanceWindow{
|
|
MonitorID: 0,
|
|
Title: "Recent Window",
|
|
Type: "maintenance",
|
|
StartTime: now.Add(-2 * 24 * time.Hour),
|
|
EndTime: now.Add(-1 * 24 * time.Hour),
|
|
}
|
|
if err := s.AddMaintenanceWindow(context.Background(), recent); err != nil {
|
|
t.Fatalf("AddMaintenanceWindow (recent): %v", err)
|
|
}
|
|
|
|
// Ongoing — no end time, should survive.
|
|
ongoing := models.MaintenanceWindow{
|
|
MonitorID: 0,
|
|
Title: "Ongoing Window",
|
|
Type: "maintenance",
|
|
StartTime: now.Add(-1 * time.Hour),
|
|
}
|
|
if err := s.AddMaintenanceWindow(context.Background(), ongoing); err != nil {
|
|
t.Fatalf("AddMaintenanceWindow (ongoing): %v", err)
|
|
}
|
|
|
|
pruned, err := s.PruneExpiredMaintenanceWindows(context.Background(), 7*24*time.Hour)
|
|
if err != nil {
|
|
t.Fatalf("PruneExpiredMaintenanceWindows: %v", err)
|
|
}
|
|
if pruned != 1 {
|
|
t.Errorf("expected 1 pruned, got %d", pruned)
|
|
}
|
|
|
|
all, err := s.GetAllMaintenanceWindows(context.Background(), 100)
|
|
if err != nil {
|
|
t.Fatalf("GetAllMaintenanceWindows: %v", err)
|
|
}
|
|
if len(all) != 2 {
|
|
t.Fatalf("expected 2 remaining windows, got %d", len(all))
|
|
}
|
|
for _, w := range all {
|
|
if w.Title == "Old Window" {
|
|
t.Error("old window should have been pruned")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGetOverlappingMaintenanceWindows(t *testing.T) {
|
|
s := newTestStore(t)
|
|
ctx := context.Background()
|
|
|
|
site := models.SiteConfig{Name: "web", URL: "https://example.com", Interval: 30}
|
|
if err := s.AddSite(ctx, site); err != nil {
|
|
t.Fatalf("AddSite: %v", err)
|
|
}
|
|
|
|
now := time.Now()
|
|
|
|
active := models.MaintenanceWindow{
|
|
MonitorID: 1,
|
|
Title: "Deploy v2",
|
|
Type: "maintenance",
|
|
StartTime: now.Add(-30 * time.Minute),
|
|
EndTime: now.Add(30 * time.Minute),
|
|
}
|
|
if err := s.AddMaintenanceWindow(ctx, active); err != nil {
|
|
t.Fatalf("AddMaintenanceWindow: %v", err)
|
|
}
|
|
|
|
ended := models.MaintenanceWindow{
|
|
MonitorID: 1,
|
|
Title: "Old deploy",
|
|
Type: "maintenance",
|
|
StartTime: now.Add(-3 * time.Hour),
|
|
EndTime: now.Add(-2 * time.Hour),
|
|
}
|
|
if err := s.AddMaintenanceWindow(ctx, ended); err != nil {
|
|
t.Fatalf("AddMaintenanceWindow: %v", err)
|
|
}
|
|
|
|
t.Run("same monitor overlaps", func(t *testing.T) {
|
|
overlaps, err := s.GetOverlappingMaintenanceWindows(ctx, 1, now, now.Add(1*time.Hour))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(overlaps) != 1 {
|
|
t.Fatalf("expected 1 overlap, got %d", len(overlaps))
|
|
}
|
|
if overlaps[0].Title != "Deploy v2" {
|
|
t.Errorf("expected 'Deploy v2', got %q", overlaps[0].Title)
|
|
}
|
|
})
|
|
|
|
t.Run("different monitor no overlap", func(t *testing.T) {
|
|
overlaps, err := s.GetOverlappingMaintenanceWindows(ctx, 99, now, now.Add(1*time.Hour))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(overlaps) != 0 {
|
|
t.Errorf("expected 0 overlaps, got %d", len(overlaps))
|
|
}
|
|
})
|
|
|
|
t.Run("global window overlaps all", func(t *testing.T) {
|
|
global := models.MaintenanceWindow{
|
|
MonitorID: 0,
|
|
Title: "Global freeze",
|
|
Type: "maintenance",
|
|
StartTime: now.Add(-10 * time.Minute),
|
|
EndTime: now.Add(2 * time.Hour),
|
|
}
|
|
if err := s.AddMaintenanceWindow(ctx, global); err != nil {
|
|
t.Fatalf("AddMaintenanceWindow: %v", err)
|
|
}
|
|
overlaps, err := s.GetOverlappingMaintenanceWindows(ctx, 1, now, now.Add(1*time.Hour))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(overlaps) != 2 {
|
|
t.Errorf("expected 2 overlaps (specific + global), got %d", len(overlaps))
|
|
}
|
|
})
|
|
|
|
t.Run("indefinite window overlaps", func(t *testing.T) {
|
|
overlaps, err := s.GetOverlappingMaintenanceWindows(ctx, 1, now, time.Time{})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(overlaps) < 1 {
|
|
t.Error("expected at least 1 overlap for indefinite window")
|
|
}
|
|
})
|
|
|
|
t.Run("ended window excluded", func(t *testing.T) {
|
|
overlaps, err := s.GetOverlappingMaintenanceWindows(ctx, 1, now.Add(-4*time.Hour), now.Add(-3*time.Hour))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(overlaps) != 0 {
|
|
t.Errorf("expected 0 overlaps for past range, got %d", len(overlaps))
|
|
}
|
|
})
|
|
}
|