Files
spark-operator/internal/webhook/scheduledsparkapplication_validator_test.go
Shingo OmuraandGitHub 566d62aa81 Bump k8s to v1.35.4 and controller-runtime to v0.23.3 (#2946)
* Bump k8s api to 0.35.4, and controller runtime to v0.23.3

Signed-off-by: Shingo Omura <[email protected]>

* Migrated defaulter/validator to be Generic ones

Signed-off-by: Shingo Omura <[email protected]>

* Fixed Endpoints deprecation lint errors (replaced with EndpointSlice)

Signed-off-by: Shingo Omura <[email protected]>

* Deleted unused internal/controller/sparkapplication/validator.go

Signed-off-by: Shingo Omura <[email protected]>

* Add SA1019 exclusion to golangci-lint config so to preserve Requeue behavior of controllers

Signed-off-by: Shingo Omura <[email protected]>

* Fixed util.WriteObjectToFile test. Null creationTimestamp is not marshalled anymore.

Signed-off-by: Shingo Omura <[email protected]>

* Updated codegen and API definitions

Signed-off-by: Shingo Omura <[email protected]>

* Fixed mgr.GetEventRecorderFor() deprecation (replace with mgr.GetEventRecorder())

Signed-off-by: Shingo Omura <[email protected]>

---------

Signed-off-by: Shingo Omura <[email protected]>
2026-06-18 15:36:34 +00:00

143 lines
4.3 KiB
Go

/*
Copyright 2025 The Kubeflow authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package webhook
import (
"context"
"strings"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/kubeflow/spark-operator/v2/api/v1beta2"
)
func TestScheduledSparkApplicationValidatorValidateCreate(t *testing.T) {
validator := NewScheduledSparkApplicationValidator()
t.Run("accepts ScheduledSparkApplication instances", func(t *testing.T) {
app := &v1beta2.ScheduledSparkApplication{
ObjectMeta: metav1.ObjectMeta{
Name: "test-app",
Namespace: "default",
},
}
warnings, err := validator.ValidateCreate(context.Background(), app)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if len(warnings) != 0 {
t.Fatalf("expected no warnings, got %v", warnings)
}
})
}
func TestScheduledSparkApplicationValidatorValidateUpdate(t *testing.T) {
validator := NewScheduledSparkApplicationValidator()
t.Run("accepts ScheduledSparkApplication instances", func(t *testing.T) {
oldApp := &v1beta2.ScheduledSparkApplication{
ObjectMeta: metav1.ObjectMeta{
Name: "test-app",
Namespace: "default",
},
}
newApp := &v1beta2.ScheduledSparkApplication{
ObjectMeta: metav1.ObjectMeta{
Name: "test-app",
Namespace: "default",
},
}
warnings, err := validator.ValidateUpdate(context.Background(), oldApp, newApp)
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if len(warnings) != 0 {
t.Fatalf("expected no warnings, got %v", warnings)
}
})
}
func TestScheduledSparkApplicationValidatorValidateDelete(t *testing.T) {
validator := NewScheduledSparkApplicationValidator()
t.Run("accepts ScheduledSparkApplication instances", func(t *testing.T) {
warnings, err := validator.ValidateDelete(context.Background(), &v1beta2.ScheduledSparkApplication{})
if err != nil {
t.Fatalf("expected no error, got %v", err)
}
if len(warnings) != 0 {
t.Fatalf("expected no warnings, got %v", warnings)
}
})
}
func TestScheduledSparkApplicationValidatorValidateName(t *testing.T) {
validator := NewScheduledSparkApplicationValidator()
tests := []struct {
name string
appName string
wantError bool
}{
// Valid names
{"valid simple name", "test-app", false},
{"valid name with numbers", "test-app-123", false},
{"valid single letter", "a", false},
{"valid name ending with number", "my-app-1", false},
{"valid name with multiple hyphens", "my-test-app-123", false},
{"valid 63 char name", strings.Repeat("a", 63), false},
{"valid name with hyphens in middle", "a-b-c-d-e", false},
// Invalid names
{"name starting with number", "123test-app", true},
{"name with uppercase", "Test-App", true},
{"name with uppercase at start", "TestApp", true},
{"name with uppercase in middle", "test-App", true},
{"name starting with hyphen", "-test-app", true},
{"name ending with hyphen", "test-app-", true},
{"name with consecutive hyphens", "test--app", false}, // Kubernetes validation allows consecutive hyphens
{"empty name", "", true},
{"name too long", strings.Repeat("a", 64), true},
{"name with special characters", "test@app", true},
{"name with underscore", "test_app", true},
{"name with spaces", "test app", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
app := &v1beta2.ScheduledSparkApplication{
ObjectMeta: metav1.ObjectMeta{
Name: tt.appName,
Namespace: "default",
},
}
_, err := validator.ValidateCreate(context.Background(), app)
hasError := err != nil
if hasError != tt.wantError {
t.Errorf("validateName(%q) = error %v, wantError %v, got error: %v", tt.appName, hasError, tt.wantError, err)
}
if hasError && err.Error() == "" {
t.Errorf("validateName(%q) should return a non-empty error message, got: %v", tt.appName, err)
}
})
}
}