Skip to content
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions pkg/autoscaler/controller/autoscale_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,75 @@ func TestTwoBackendsHighLoad_then_DoOptimize_expect_DistributionA5B4(t *testing.
}
}

func TestDefaultPanicThreshold_DoOptimize_NoPanic(t *testing.T) {
ns := "ns"
msA := &workload.ModelServing{ObjectMeta: metav1.ObjectMeta{Name: "ms-nil-a", Namespace: ns}, Spec: workload.ModelServingSpec{Replicas: ptrInt32(1)}}
msB := &workload.ModelServing{ObjectMeta: metav1.ObjectMeta{Name: "ms-nil-b", Namespace: ns}, Spec: workload.ModelServingSpec{Replicas: ptrInt32(2)}}
client := clientfake.NewSimpleClientset(msA, msB)
msLister := workloadLister.NewModelServingLister(newModelServingIndexer(msA, msB))

srv := httptest.NewServer(httpHandlerWithBody("# TYPE load gauge\nload 10\n"))
defer srv.Close()
u, _ := url.Parse(srv.URL)
host, portStr, _ := net.SplitHostPort(u.Host)
port := toInt32(portStr)

paramA := workload.HeterogeneousTargetParam{Target: workload.Target{TargetRef: corev1.ObjectReference{Kind: workload.ModelServingKind.Kind, Namespace: ns, Name: "ms-nil-a"}, MetricEndpoint: workload.MetricEndpoint{Uri: u.Path, Port: port}}, MinReplicas: 1, MaxReplicas: 5, Cost: 10}
paramB := workload.HeterogeneousTargetParam{Target: workload.Target{TargetRef: corev1.ObjectReference{Kind: workload.ModelServingKind.Kind, Namespace: ns, Name: "ms-nil-b"}, MetricEndpoint: workload.MetricEndpoint{Uri: u.Path, Port: port}}, MinReplicas: 2, MaxReplicas: 4, Cost: 20}
// PanicThresholdPercent set to CRD default of 200 — per API guarantee this is never nil
var threshold int32 = 200
policy := &workload.AutoscalingPolicy{Spec: workload.AutoscalingPolicySpec{TolerancePercent: 0, Metrics: []workload.AutoscalingPolicyMetric{{MetricName: "load", TargetValue: resource.MustParse("1")}}, Behavior: workload.AutoscalingPolicyBehavior{ScaleUp: workload.AutoscalingPolicyScaleUpPolicy{PanicPolicy: workload.AutoscalingPolicyPanicPolicy{Period: metav1.Duration{Duration: 1 * time.Second}, PanicThresholdPercent: &threshold}}}}}
binding := &workload.AutoscalingPolicyBinding{ObjectMeta: metav1.ObjectMeta{Name: "binding-nil", Namespace: ns}, Spec: workload.AutoscalingPolicyBindingSpec{PolicyRef: corev1.LocalObjectReference{Name: "ap"}, HeterogeneousTarget: &workload.HeterogeneousTarget{Params: []workload.HeterogeneousTargetParam{paramA, paramB}, CostExpansionRatePercent: 100}}}

lbsA := map[string]string{}
lbsB := map[string]string{}
pods := []*corev1.Pod{readyPod(ns, "pod-nil-a", host, lbsA), readyPod(ns, "pod-nil-b", host, lbsB)}
ac := &AutoscaleController{client: client, namespace: ns, modelServingLister: msLister, podsLister: fakePodLister{podsByNs: map[string][]*corev1.Pod{ns: pods}}, scalerMap: map[string]*autoscalerAutoscaler{}, optimizerMap: map[string]*autoscalerOptimizer{}}

if err := ac.doOptimize(context.Background(), binding, policy); err != nil {
t.Fatalf("doOptimize should not error with default PanicThresholdPercent: %v", err)
}
}
Comment on lines +226 to +254
Copy link

Copilot AI Feb 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test name "TestDefaultPanicThreshold_DoOptimize_NoPanic" is ambiguous. It's unclear whether "NoPanic" means "no crash/error" or "panic mode not activated". The test only verifies that doOptimize doesn't return an error, but doesn't verify whether panic mode was activated or not. Consider either: (1) Renaming to something like "TestDefaultPanicThreshold_DoOptimize_NoError" to clarify it's testing for absence of errors, or (2) Adding assertions to verify whether panic mode was actually triggered and renaming accordingly.

Copilot uses AI. Check for mistakes.

func TestSetPanicThreshold_DoOptimize_PanicModeWorks(t *testing.T) {
ns := "ns"
msA := &workload.ModelServing{ObjectMeta: metav1.ObjectMeta{Name: "ms-panic-a", Namespace: ns}, Spec: workload.ModelServingSpec{Replicas: ptrInt32(1)}}
msB := &workload.ModelServing{ObjectMeta: metav1.ObjectMeta{Name: "ms-panic-b", Namespace: ns}, Spec: workload.ModelServingSpec{Replicas: ptrInt32(2)}}
client := clientfake.NewSimpleClientset(msA, msB)
msLister := workloadLister.NewModelServingLister(newModelServingIndexer(msA, msB))

srv := httptest.NewServer(httpHandlerWithBody("# TYPE load gauge\nload 100\n"))
defer srv.Close()
u, _ := url.Parse(srv.URL)
host, portStr, _ := net.SplitHostPort(u.Host)
port := toInt32(portStr)

paramA := workload.HeterogeneousTargetParam{Target: workload.Target{TargetRef: corev1.ObjectReference{Kind: workload.ModelServingKind.Kind, Namespace: ns, Name: "ms-panic-a"}, MetricEndpoint: workload.MetricEndpoint{Uri: u.Path, Port: port}}, MinReplicas: 1, MaxReplicas: 5, Cost: 10}
paramB := workload.HeterogeneousTargetParam{Target: workload.Target{TargetRef: corev1.ObjectReference{Kind: workload.ModelServingKind.Kind, Namespace: ns, Name: "ms-panic-b"}, MetricEndpoint: workload.MetricEndpoint{Uri: u.Path, Port: port}}, MinReplicas: 2, MaxReplicas: 4, Cost: 20}
// PanicThresholdPercent set to 200 — with load=100, recommended will far exceed threshold
var threshold int32 = 200
policy := &workload.AutoscalingPolicy{Spec: workload.AutoscalingPolicySpec{TolerancePercent: 0, Metrics: []workload.AutoscalingPolicyMetric{{MetricName: "load", TargetValue: resource.MustParse("1")}}, Behavior: workload.AutoscalingPolicyBehavior{ScaleUp: workload.AutoscalingPolicyScaleUpPolicy{PanicPolicy: workload.AutoscalingPolicyPanicPolicy{Period: metav1.Duration{Duration: 1 * time.Second}, PanicThresholdPercent: &threshold}}}}}
binding := &workload.AutoscalingPolicyBinding{ObjectMeta: metav1.ObjectMeta{Name: "binding-panic", Namespace: ns}, Spec: workload.AutoscalingPolicyBindingSpec{PolicyRef: corev1.LocalObjectReference{Name: "ap"}, HeterogeneousTarget: &workload.HeterogeneousTarget{Params: []workload.HeterogeneousTargetParam{paramA, paramB}, CostExpansionRatePercent: 100}}}
Comment on lines +271 to +274
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TestSetPanicThreshold_DoOptimize_PanicModeWorks doesn’t actually validate panic mode: PanicModeHold is left nil, so NewStatus() sets PanicModeHoldMilliseconds to 0 and RefreshPanicMode() will never make IsPanicMode() true. Either set PanicModeHold in the policy and assert the optimizer entered panic mode (e.g., via ac.optimizerMap[formatAutoscalerMapKey(...)]), or rename the test/assertions to reflect that it only checks update actions.

Copilot uses AI. Check for mistakes.

lbsA := map[string]string{}
lbsB := map[string]string{}
pods := []*corev1.Pod{readyPod(ns, "pod-panic-a", host, lbsA), readyPod(ns, "pod-panic-b", host, lbsB)}
ac := &AutoscaleController{client: client, namespace: ns, modelServingLister: msLister, podsLister: fakePodLister{podsByNs: map[string][]*corev1.Pod{ns: pods}}, scalerMap: map[string]*autoscalerAutoscaler{}, optimizerMap: map[string]*autoscalerOptimizer{}}

if err := ac.doOptimize(context.Background(), binding, policy); err != nil {
t.Fatalf("doOptimize error: %v", err)
}
updates := 0
for _, a := range client.Fake.Actions() {
if a.GetVerb() == "update" && a.GetResource().Resource == "modelservings" {
updates++
}
}
if updates == 0 {
t.Fatalf("expected update actions when PanicThresholdPercent is set, got 0")
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The assertion in this test only checks that at least one update action occurred (updates > 0), which is quite broad. For a more robust test, you should assert the specific final replica counts for each ModelServing instance, similar to TestTwoBackendsHighLoad_then_DoOptimize_expect_DistributionA5B4. This would verify that the panic mode scaling logic is working precisely as intended. Based on the test's configuration, the expected distribution should be 5 replicas for ms-panic-a and 4 for ms-panic-b.

}

func httpHandlerWithBody(body string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(body)) })
}
Expand Down
Loading