/* Copyright 2021 The Karmada 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 http://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 configuration import ( "fmt" "strings" "testing" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" "k8s.io/apimachinery/pkg/util/validation/field" configv1alpha1 "github.com/karmada-io/karmada/pkg/apis/config/v1alpha1" ) func strPtr(s string) *string { return &s } func int32Ptr(i int32) *int32 { return &i } func TestHasWildcardOperation(t *testing.T) { tests := []struct { name string operations []configv1alpha1.InterpreterOperation expected bool }{ { name: "no operations", operations: nil, expected: false, }, { name: "has wildcard operation", operations: []configv1alpha1.InterpreterOperation{ configv1alpha1.InterpreterOperationAll, }, expected: true, }, { name: "no have wildcard operation", operations: []configv1alpha1.InterpreterOperation{ configv1alpha1.InterpreterOperationInterpretReplica, }, expected: false, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result := hasWildcardOperation(test.operations) if result != test.expected { t.Errorf("Case: %s failed: expected get %v, but got %v", test.name, test.expected, result) } }) } } func TestIsAcceptedExploreReviewVersions(t *testing.T) { tests := []struct { name string version string expected bool }{ { name: "is accepted interpreter context versions", version: acceptedInterpreterContextVersions[0], expected: true, }, { name: "is not accepted interpreter context versions", version: "", expected: false, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { result := isAcceptedExploreReviewVersions(test.version) if result != test.expected { t.Errorf("Case: %s failed: expected get %v, but got %v", test.name, test.expected, result) } }) } } func TestValidateRuleWithOperations(t *testing.T) { fldPath := field.NewPath("webhooks").Child("rules") notSupportedOperation := []configv1alpha1.InterpreterOperation{(configv1alpha1.InterpreterOperation)("notsupported")} tests := []struct { name string ruleWithOperations *configv1alpha1.RuleWithOperations expectedError string }{ { name: "no operations", ruleWithOperations: &configv1alpha1.RuleWithOperations{}, expectedError: "operations: Required value", }, { name: "2 operations with wildcard", ruleWithOperations: &configv1alpha1.RuleWithOperations{ Operations: []configv1alpha1.InterpreterOperation{ configv1alpha1.InterpreterOperationAll, configv1alpha1.InterpreterOperationInterpretReplica, }, }, expectedError: "if '*' is present, must not specify other operations", }, { name: "not supported operations", ruleWithOperations: &configv1alpha1.RuleWithOperations{Operations: notSupportedOperation}, expectedError: "operations[0]: Unsupported value", }, { name: "not validated rule", ruleWithOperations: &configv1alpha1.RuleWithOperations{ Operations: []configv1alpha1.InterpreterOperation{ configv1alpha1.InterpreterOperationAll, }, Rule: configv1alpha1.Rule{ APIGroups: []string{""}, }}, expectedError: "apiVersions: Required value", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { errs := validateRuleWithOperations(test.ruleWithOperations, fldPath) err := errs.ToAggregate() if err != nil { if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" { t.Errorf("Case: %s failed: expected to contain %s, got %s", test.name, e, a) } } else { if test.expectedError != "" { t.Errorf("Case: %s failed: unexpected no error, expected to contain %s", test.name, test.expectedError) } } }) } } func TestValidateExploreReviewVersions(t *testing.T) { fldPath := field.NewPath("webhooks").Child("interpreterContextVersions") tests := []struct { name string versions []string expectedError string }{ { name: "no versions", versions: nil, expectedError: fmt.Sprintf("must specify one of %v", strings.Join(acceptedInterpreterContextVersions, ", ")), }, { name: "duplicate versions", versions: []string{"v1", "v1"}, expectedError: "duplicate version", }, { name: "invalid versions", versions: []string{"test", "test"}, expectedError: fmt.Sprintf("must include at least one of %v", strings.Join(acceptedInterpreterContextVersions, ", ")), }, { name: "invalid DNS (RFC 1035) label", versions: []string{"a b"}, expectedError: "a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { errs := validateInterpreterContextVersions(test.versions, fldPath) err := errs.ToAggregate() if err != nil { if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" { t.Errorf("Case: %s failed: expected to contain %s, got %s", test.name, e, a) } } else { if test.expectedError != "" { t.Errorf("Case: %s failed: unexpected no error, expected to contain %s", test.name, test.expectedError) } } }) } } func TestValidateWebhook(t *testing.T) { fldPath := field.NewPath("webhooks") tests := []struct { name string hook *configv1alpha1.ResourceInterpreterWebhook expectedError string }{ { name: "not qualified domain name", hook: &configv1alpha1.ResourceInterpreterWebhook{ Name: "", }, expectedError: "webhooks.name: Required value", }, { name: "invalid rules", hook: &configv1alpha1.ResourceInterpreterWebhook{ Rules: []configv1alpha1.RuleWithOperations{ { Operations: []configv1alpha1.InterpreterOperation{}, }, }, }, expectedError: "operations: Required value", }, { name: "invalid timeout", hook: &configv1alpha1.ResourceInterpreterWebhook{ TimeoutSeconds: int32Ptr(60), }, expectedError: "the timeout value must be between 1 and 30 seconds", }, { name: "ClientConfig: exactly one of url or service is required", hook: &configv1alpha1.ResourceInterpreterWebhook{ ClientConfig: admissionregistrationv1.WebhookClientConfig{}, }, expectedError: "exactly one of url or service is required", }, { name: "ClientConfig: invalid URL", hook: &configv1alpha1.ResourceInterpreterWebhook{ ClientConfig: admissionregistrationv1.WebhookClientConfig{ URL: strPtr(""), }, }, expectedError: "host must be specified", }, { name: "ClientConfig: invalid service", hook: &configv1alpha1.ResourceInterpreterWebhook{ ClientConfig: admissionregistrationv1.WebhookClientConfig{ Service: &admissionregistrationv1.ServiceReference{ Name: "", Port: int32Ptr(8080), Path: strPtr(""), }, }, }, expectedError: "service name is required", }, { name: "invalid interpreter context versions", hook: &configv1alpha1.ResourceInterpreterWebhook{ InterpreterContextVersions: []string{""}, }, expectedError: fmt.Sprintf("must include at least one of %v", strings.Join(acceptedInterpreterContextVersions, ", ")), }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { errs := validateWebhook(test.hook, fldPath) err := errs.ToAggregate() if err != nil { if e, a := test.expectedError, err.Error(); !strings.Contains(a, e) || e == "" { t.Errorf("Case: %s failed: expected to contain %s, got %s", test.name, e, a) } } else { if test.expectedError != "" { t.Errorf("Case: %s failed: unexpected no error, expected to contain %s", test.name, test.expectedError) } } }) } }