Pulse/internal/monitoring/task_queue_test.go
2025-11-29 19:38:54 +00:00

233 lines
5.7 KiB
Go

package monitoring
import (
"testing"
"time"
)
func TestTaskQueue_Snapshot(t *testing.T) {
tests := []struct {
name string
tasks []ScheduledTask
wantDepth int
wantDueWithinSeconds int
wantPerType map[string]int
}{
{
name: "empty queue",
tasks: []ScheduledTask{},
wantDepth: 0,
wantDueWithinSeconds: 0,
wantPerType: map[string]int{},
},
{
name: "single task due soon (within 12 seconds)",
tasks: []ScheduledTask{
{
InstanceName: "pve-1",
InstanceType: InstanceTypePVE,
NextRun: time.Now().Add(5 * time.Second),
Interval: 30 * time.Second,
Priority: 1.0,
},
},
wantDepth: 1,
wantDueWithinSeconds: 1,
wantPerType: map[string]int{
"pve": 1,
},
},
{
name: "single task NOT due soon (>12 seconds away)",
tasks: []ScheduledTask{
{
InstanceName: "pbs-1",
InstanceType: InstanceTypePBS,
NextRun: time.Now().Add(20 * time.Second),
Interval: 60 * time.Second,
Priority: 1.0,
},
},
wantDepth: 1,
wantDueWithinSeconds: 0,
wantPerType: map[string]int{
"pbs": 1,
},
},
{
name: "multiple tasks of same type",
tasks: []ScheduledTask{
{
InstanceName: "pve-1",
InstanceType: InstanceTypePVE,
NextRun: time.Now().Add(5 * time.Second),
Interval: 30 * time.Second,
Priority: 1.0,
},
{
InstanceName: "pve-2",
InstanceType: InstanceTypePVE,
NextRun: time.Now().Add(15 * time.Second),
Interval: 30 * time.Second,
Priority: 1.0,
},
{
InstanceName: "pve-3",
InstanceType: InstanceTypePVE,
NextRun: time.Now().Add(10 * time.Second),
Interval: 30 * time.Second,
Priority: 1.0,
},
},
wantDepth: 3,
wantDueWithinSeconds: 2, // pve-1 and pve-3 are within 12 seconds
wantPerType: map[string]int{
"pve": 3,
},
},
{
name: "multiple tasks of different types",
tasks: []ScheduledTask{
{
InstanceName: "pve-1",
InstanceType: InstanceTypePVE,
NextRun: time.Now().Add(5 * time.Second),
Interval: 30 * time.Second,
Priority: 1.0,
},
{
InstanceName: "pbs-1",
InstanceType: InstanceTypePBS,
NextRun: time.Now().Add(8 * time.Second),
Interval: 60 * time.Second,
Priority: 1.0,
},
{
InstanceName: "pmg-1",
InstanceType: InstanceTypePMG,
NextRun: time.Now().Add(25 * time.Second),
Interval: 45 * time.Second,
Priority: 1.0,
},
},
wantDepth: 3,
wantDueWithinSeconds: 2, // pve-1 and pbs-1 are within 12 seconds
wantPerType: map[string]int{
"pve": 1,
"pbs": 1,
"pmg": 1,
},
},
{
name: "boundary case: task exactly 12 seconds away",
tasks: []ScheduledTask{
{
InstanceName: "pve-1",
InstanceType: InstanceTypePVE,
NextRun: time.Now().Add(12 * time.Second),
Interval: 30 * time.Second,
Priority: 1.0,
},
},
wantDepth: 1,
wantDueWithinSeconds: 1, // <= 12 seconds should be included
wantPerType: map[string]int{
"pve": 1,
},
},
{
name: "mix of due-soon and not-due-soon tasks",
tasks: []ScheduledTask{
{
InstanceName: "pve-1",
InstanceType: InstanceTypePVE,
NextRun: time.Now().Add(1 * time.Second),
Interval: 30 * time.Second,
Priority: 1.0,
},
{
InstanceName: "pve-2",
InstanceType: InstanceTypePVE,
NextRun: time.Now().Add(50 * time.Second),
Interval: 30 * time.Second,
Priority: 1.0,
},
{
InstanceName: "pbs-1",
InstanceType: InstanceTypePBS,
NextRun: time.Now().Add(11 * time.Second),
Interval: 60 * time.Second,
Priority: 1.0,
},
{
InstanceName: "pbs-2",
InstanceType: InstanceTypePBS,
NextRun: time.Now().Add(100 * time.Second),
Interval: 60 * time.Second,
Priority: 1.0,
},
{
InstanceName: "pmg-1",
InstanceType: InstanceTypePMG,
NextRun: time.Now().Add(30 * time.Second),
Interval: 45 * time.Second,
Priority: 1.0,
},
},
wantDepth: 5,
wantDueWithinSeconds: 2, // pve-1 and pbs-1 are within 12 seconds
wantPerType: map[string]int{
"pve": 2,
"pbs": 2,
"pmg": 1,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
queue := NewTaskQueue()
// Add all tasks to the queue
for _, task := range tt.tasks {
queue.Upsert(task)
}
// Get snapshot
snapshot := queue.Snapshot()
// Verify depth
if snapshot.Depth != tt.wantDepth {
t.Errorf("Depth = %d, want %d", snapshot.Depth, tt.wantDepth)
}
// Verify DueWithinSeconds
if snapshot.DueWithinSeconds != tt.wantDueWithinSeconds {
t.Errorf("DueWithinSeconds = %d, want %d", snapshot.DueWithinSeconds, tt.wantDueWithinSeconds)
}
// Verify PerType map
if len(snapshot.PerType) != len(tt.wantPerType) {
t.Errorf("PerType has %d entries, want %d", len(snapshot.PerType), len(tt.wantPerType))
}
for typeStr, wantCount := range tt.wantPerType {
gotCount, ok := snapshot.PerType[typeStr]
if !ok {
t.Errorf("PerType missing entry for %s", typeStr)
continue
}
if gotCount != wantCount {
t.Errorf("PerType[%s] = %d, want %d", typeStr, gotCount, wantCount)
}
}
// Verify no extra keys in PerType
for typeStr := range snapshot.PerType {
if _, ok := tt.wantPerType[typeStr]; !ok {
t.Errorf("PerType has unexpected entry for %s", typeStr)
}
}
})
}
}