Skip to content

Commit d3c35b1

Browse files
committed
Add node latency tracker tests
1 parent 2818650 commit d3c35b1

File tree

1 file changed

+117
-6
lines changed

1 file changed

+117
-6
lines changed

cluster-autoscaler/core/scaledown/latencytracker/node_latency_tracker_test.go

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,126 @@ package latencytracker
1919
import (
2020
"testing"
2121
"time"
22+
23+
apiv1 "k8s.io/api/core/v1"
24+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2225
)
2326

24-
func TestObserveDeletion_NoOpIfNodeNotTracked(t *testing.T) {
25-
tracker := NewNodeLatencyTracker()
26-
now := time.Now()
27+
func TestNodeLatencyTracker(t *testing.T) {
28+
baseTime := time.Now()
29+
30+
tests := []struct {
31+
name string
32+
setupNodes map[string]NodeInfo
33+
unneededList []string
34+
currentlyInDeletion map[string]bool
35+
updateThresholds map[string]time.Duration
36+
observeDeletion []string
37+
expectedTrackedNodes []string
38+
expectedDeletionTimes map[string]time.Duration
39+
}{
40+
{
41+
name: "add new unneeded nodes",
42+
setupNodes: map[string]NodeInfo{},
43+
unneededList: []string{"node1", "node2"},
44+
currentlyInDeletion: map[string]bool{},
45+
updateThresholds: map[string]time.Duration{},
46+
observeDeletion: []string{},
47+
expectedTrackedNodes: []string{"node1", "node2"},
48+
},
49+
{
50+
name: "observe deletion with threshold",
51+
setupNodes: map[string]NodeInfo{
52+
"node1": {UnneededSince: baseTime, Threshold: 2 * time.Second},
53+
},
54+
unneededList: []string{},
55+
currentlyInDeletion: map[string]bool{},
56+
updateThresholds: map[string]time.Duration{},
57+
observeDeletion: []string{"node1"},
58+
expectedTrackedNodes: []string{},
59+
expectedDeletionTimes: map[string]time.Duration{
60+
"node1": 3 * time.Second, // simulate observation 5s after UnneededSince, threshold 2s
61+
},
62+
},
63+
{
64+
name: "remove unneeded node not in deletion",
65+
setupNodes: map[string]NodeInfo{
66+
"node1": {UnneededSince: baseTime, Threshold: 1 * time.Second},
67+
"node2": {UnneededSince: baseTime, Threshold: 0},
68+
},
69+
unneededList: []string{"node2"}, // node1 is removed from unneeded
70+
currentlyInDeletion: map[string]bool{},
71+
updateThresholds: map[string]time.Duration{},
72+
observeDeletion: []string{},
73+
expectedTrackedNodes: []string{"node2"},
74+
expectedDeletionTimes: map[string]time.Duration{
75+
"node1": 5*time.Second - 1*time.Second, // assume current timestamp baseTime+5s
76+
},
77+
},
78+
{
79+
name: "update threshold",
80+
setupNodes: map[string]NodeInfo{
81+
"node1": {UnneededSince: baseTime, Threshold: 1 * time.Second},
82+
},
83+
unneededList: []string{"node1"},
84+
currentlyInDeletion: map[string]bool{},
85+
updateThresholds: map[string]time.Duration{
86+
"node1": 4 * time.Second,
87+
},
88+
observeDeletion: []string{},
89+
expectedTrackedNodes: []string{"node1"},
90+
},
91+
}
92+
93+
for _, tt := range tests {
94+
t.Run(tt.name, func(t *testing.T) {
95+
tracker := NewNodeLatencyTracker()
96+
for name, info := range tt.setupNodes {
97+
tracker.nodes[name] = info
98+
}
99+
100+
for node, threshold := range tt.updateThresholds {
101+
tracker.UpdateThreshold(node, threshold)
102+
}
103+
unneededNodes := make([]*apiv1.Node, len(tt.unneededList))
104+
for i, name := range tt.unneededList {
105+
unneededNodes[i] = &apiv1.Node{ObjectMeta: metav1.ObjectMeta{Name: name}}
106+
}
107+
// simulate current timestamp as baseTime + 5s
108+
currentTime := baseTime.Add(5 * time.Second)
109+
tracker.UpdateStateWithUnneededList(unneededNodes, tt.currentlyInDeletion, currentTime)
110+
111+
// Observe deletions
112+
for _, node := range tt.observeDeletion {
113+
tracker.ObserveDeletion(node, currentTime)
114+
}
27115

28-
tracker.ObserveDeletion("node1", now)
116+
// Check tracked nodes
117+
gotTracked := tracker.GetTrackedNodes()
118+
expectedMap := make(map[string]struct{})
119+
for _, n := range tt.expectedTrackedNodes {
120+
expectedMap[n] = struct{}{}
121+
}
122+
for _, n := range gotTracked {
123+
if _, ok := expectedMap[n]; !ok {
124+
t.Errorf("unexpected tracked node %q", n)
125+
}
126+
delete(expectedMap, n)
127+
}
128+
for n := range expectedMap {
129+
t.Errorf("expected node %q to be tracked, but was not", n)
130+
}
29131

30-
if len(tracker.nodes) != 0 {
31-
t.Errorf("expected no nodes tracked, got %d", len(tracker.nodes))
132+
for node, expectedDuration := range tt.expectedDeletionTimes {
133+
info, ok := tt.setupNodes[node]
134+
if !ok {
135+
continue
136+
}
137+
duration := currentTime.Sub(info.UnneededSince) - info.Threshold
138+
if duration != expectedDuration {
139+
t.Errorf("node %q expected deletion duration %v, got %v", node, expectedDuration, duration)
140+
}
141+
}
142+
})
32143
}
33144
}

0 commit comments

Comments
 (0)