mirror of
https://github.com/alibaba/higress.git
synced 2026-03-03 08:00:49 +08:00
1071 lines
23 KiB
Go
1071 lines
23 KiB
Go
// Copyright (c) 2022 Alibaba Group Holding Ltd.
|
|
//
|
|
// 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 common
|
|
|
|
import (
|
|
"testing"
|
|
|
|
networking "istio.io/api/networking/v1alpha3"
|
|
"istio.io/istio/pilot/pkg/model"
|
|
"istio.io/istio/pkg/config"
|
|
v1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"github.com/alibaba/higress/pkg/ingress/kube/annotations"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestConstructRouteName(t *testing.T) {
|
|
testCases := []struct {
|
|
input *WrapperHTTPRoute
|
|
expect string
|
|
}{
|
|
{
|
|
input: &WrapperHTTPRoute{
|
|
Host: "test.com",
|
|
OriginPathType: Exact,
|
|
OriginPath: "/test",
|
|
HTTPRoute: &networking.HTTPRoute{},
|
|
},
|
|
expect: "test.com-exact-/test",
|
|
},
|
|
{
|
|
input: &WrapperHTTPRoute{
|
|
Host: "*.test.com",
|
|
OriginPathType: PrefixRegex,
|
|
OriginPath: "/test/(.*)/?[0-9]",
|
|
HTTPRoute: &networking.HTTPRoute{},
|
|
},
|
|
expect: "*.test.com-prefixRegex-/test/(.*)/?[0-9]",
|
|
},
|
|
{
|
|
input: &WrapperHTTPRoute{
|
|
Host: "test.com",
|
|
OriginPathType: Exact,
|
|
OriginPath: "/test",
|
|
HTTPRoute: &networking.HTTPRoute{
|
|
Match: []*networking.HTTPMatchRequest{
|
|
{
|
|
Headers: map[string]*networking.StringMatch{
|
|
"b": {
|
|
MatchType: &networking.StringMatch_Regex{
|
|
Regex: "a?c.*",
|
|
},
|
|
},
|
|
"a": {
|
|
MatchType: &networking.StringMatch_Exact{
|
|
Exact: "hello",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expect: "test.com-exact-/test-exact-a-hello-regex-b-a?c.*",
|
|
},
|
|
{
|
|
input: &WrapperHTTPRoute{
|
|
Host: "test.com",
|
|
OriginPathType: Prefix,
|
|
OriginPath: "/test",
|
|
HTTPRoute: &networking.HTTPRoute{
|
|
Match: []*networking.HTTPMatchRequest{
|
|
{
|
|
QueryParams: map[string]*networking.StringMatch{
|
|
"b": {
|
|
MatchType: &networking.StringMatch_Regex{
|
|
Regex: "a?c.*",
|
|
},
|
|
},
|
|
"a": {
|
|
MatchType: &networking.StringMatch_Exact{
|
|
Exact: "hello",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expect: "test.com-prefix-/test-exact:a:hello-regex:b:a?c.*",
|
|
},
|
|
{
|
|
input: &WrapperHTTPRoute{
|
|
Host: "test.com",
|
|
OriginPathType: Prefix,
|
|
OriginPath: "/test",
|
|
HTTPRoute: &networking.HTTPRoute{
|
|
Match: []*networking.HTTPMatchRequest{
|
|
{
|
|
Headers: map[string]*networking.StringMatch{
|
|
"f": {
|
|
MatchType: &networking.StringMatch_Regex{
|
|
Regex: "abc?",
|
|
},
|
|
},
|
|
"e": {
|
|
MatchType: &networking.StringMatch_Exact{
|
|
Exact: "bye",
|
|
},
|
|
},
|
|
},
|
|
QueryParams: map[string]*networking.StringMatch{
|
|
"b": {
|
|
MatchType: &networking.StringMatch_Regex{
|
|
Regex: "a?c.*",
|
|
},
|
|
},
|
|
"a": {
|
|
MatchType: &networking.StringMatch_Exact{
|
|
Exact: "hello",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
expect: "test.com-prefix-/test-exact-e-bye-regex-f-abc?-exact:a:hello-regex:b:a?c.*",
|
|
},
|
|
}
|
|
|
|
for _, c := range testCases {
|
|
t.Run("", func(t *testing.T) {
|
|
out := constructRouteName(c.input)
|
|
if out != c.expect {
|
|
t.Fatalf("Expect %s, but is %s", c.expect, out)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGenerateUniqueRouteName(t *testing.T) {
|
|
input := &WrapperHTTPRoute{
|
|
WrapperConfig: &WrapperConfig{
|
|
Config: &config.Config{
|
|
Meta: config.Meta{
|
|
Name: "foo",
|
|
Namespace: "bar",
|
|
},
|
|
},
|
|
AnnotationsConfig: &annotations.Ingress{},
|
|
},
|
|
Host: "test.com",
|
|
OriginPathType: Prefix,
|
|
OriginPath: "/test",
|
|
ClusterId: "cluster1",
|
|
HTTPRoute: &networking.HTTPRoute{
|
|
Match: []*networking.HTTPMatchRequest{
|
|
{
|
|
Headers: map[string]*networking.StringMatch{
|
|
"f": {
|
|
MatchType: &networking.StringMatch_Regex{
|
|
Regex: "abc?",
|
|
},
|
|
},
|
|
"e": {
|
|
MatchType: &networking.StringMatch_Exact{
|
|
Exact: "bye",
|
|
},
|
|
},
|
|
},
|
|
QueryParams: map[string]*networking.StringMatch{
|
|
"b": {
|
|
MatchType: &networking.StringMatch_Regex{
|
|
Regex: "a?c.*",
|
|
},
|
|
},
|
|
"a": {
|
|
MatchType: &networking.StringMatch_Exact{
|
|
Exact: "hello",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
assert.Equal(t, "bar/foo", GenerateUniqueRouteName("xxx", input))
|
|
assert.Equal(t, "foo", GenerateUniqueRouteName("bar", input))
|
|
|
|
}
|
|
|
|
func TestGetLbStatusList(t *testing.T) {
|
|
clusterPrefix = "gw-123-"
|
|
svcName := clusterPrefix
|
|
svcList := []*v1.Service{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: svcName,
|
|
},
|
|
Spec: v1.ServiceSpec{
|
|
Type: v1.ServiceTypeLoadBalancer,
|
|
},
|
|
Status: v1.ServiceStatus{
|
|
LoadBalancer: v1.LoadBalancerStatus{
|
|
Ingress: []v1.LoadBalancerIngress{
|
|
{
|
|
IP: "2.2.2.2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: svcName,
|
|
},
|
|
Spec: v1.ServiceSpec{
|
|
Type: v1.ServiceTypeLoadBalancer,
|
|
},
|
|
Status: v1.ServiceStatus{
|
|
LoadBalancer: v1.LoadBalancerStatus{
|
|
Ingress: []v1.LoadBalancerIngress{
|
|
{
|
|
Hostname: "1.1.1.1" + SvcHostNameSuffix,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: svcName,
|
|
},
|
|
Spec: v1.ServiceSpec{
|
|
Type: v1.ServiceTypeLoadBalancer,
|
|
},
|
|
Status: v1.ServiceStatus{
|
|
LoadBalancer: v1.LoadBalancerStatus{
|
|
Ingress: []v1.LoadBalancerIngress{
|
|
{
|
|
Hostname: "4.4.4.4" + SvcHostNameSuffix,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: svcName,
|
|
},
|
|
Spec: v1.ServiceSpec{
|
|
Type: v1.ServiceTypeLoadBalancer,
|
|
},
|
|
Status: v1.ServiceStatus{
|
|
LoadBalancer: v1.LoadBalancerStatus{
|
|
Ingress: []v1.LoadBalancerIngress{
|
|
{
|
|
IP: "3.3.3.3",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: svcName,
|
|
},
|
|
Spec: v1.ServiceSpec{
|
|
Type: v1.ServiceTypeClusterIP,
|
|
},
|
|
Status: v1.ServiceStatus{
|
|
LoadBalancer: v1.LoadBalancerStatus{
|
|
Ingress: []v1.LoadBalancerIngress{
|
|
{
|
|
IP: "5.5.5.5",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
lbiList := GetLbStatusList(svcList)
|
|
if len(lbiList) != 4 {
|
|
t.Fatal("len should be 4")
|
|
}
|
|
|
|
if lbiList[0].IP != "1.1.1.1" {
|
|
t.Fatal("should be 1.1.1.1")
|
|
}
|
|
|
|
if lbiList[3].IP != "4.4.4.4" {
|
|
t.Fatal("should be 4.4.4.4")
|
|
}
|
|
}
|
|
|
|
func TestSortRoutes(t *testing.T) {
|
|
input := []*WrapperHTTPRoute{
|
|
{
|
|
WrapperConfig: &WrapperConfig{
|
|
Config: &config.Config{
|
|
Meta: config.Meta{
|
|
Name: "foo",
|
|
Namespace: "bar",
|
|
},
|
|
},
|
|
AnnotationsConfig: &annotations.Ingress{},
|
|
},
|
|
Host: "test.com",
|
|
OriginPathType: Prefix,
|
|
OriginPath: "/",
|
|
ClusterId: "cluster1",
|
|
HTTPRoute: &networking.HTTPRoute{
|
|
Name: "test-1",
|
|
},
|
|
},
|
|
{
|
|
WrapperConfig: &WrapperConfig{
|
|
Config: &config.Config{
|
|
Meta: config.Meta{
|
|
Name: "foo",
|
|
Namespace: "bar",
|
|
},
|
|
},
|
|
AnnotationsConfig: &annotations.Ingress{},
|
|
},
|
|
Host: "test.com",
|
|
OriginPathType: Prefix,
|
|
OriginPath: "/a",
|
|
ClusterId: "cluster1",
|
|
HTTPRoute: &networking.HTTPRoute{
|
|
Name: "test-2",
|
|
},
|
|
},
|
|
{
|
|
WrapperConfig: &WrapperConfig{
|
|
Config: &config.Config{
|
|
Meta: config.Meta{
|
|
Name: "foo",
|
|
Namespace: "bar",
|
|
},
|
|
},
|
|
AnnotationsConfig: &annotations.Ingress{},
|
|
},
|
|
HTTPRoute: &networking.HTTPRoute{
|
|
Name: "test-3",
|
|
},
|
|
IsDefaultBackend: true,
|
|
},
|
|
{
|
|
WrapperConfig: &WrapperConfig{
|
|
Config: &config.Config{
|
|
Meta: config.Meta{
|
|
Name: "foo",
|
|
Namespace: "bar",
|
|
},
|
|
},
|
|
AnnotationsConfig: &annotations.Ingress{},
|
|
},
|
|
Host: "test.com",
|
|
OriginPathType: Exact,
|
|
OriginPath: "/b",
|
|
ClusterId: "cluster1",
|
|
HTTPRoute: &networking.HTTPRoute{
|
|
Name: "test-4",
|
|
},
|
|
},
|
|
{
|
|
WrapperConfig: &WrapperConfig{
|
|
Config: &config.Config{
|
|
Meta: config.Meta{
|
|
Name: "foo",
|
|
Namespace: "bar",
|
|
},
|
|
},
|
|
AnnotationsConfig: &annotations.Ingress{},
|
|
},
|
|
Host: "test.com",
|
|
OriginPathType: PrefixRegex,
|
|
OriginPath: "/d(.*)",
|
|
ClusterId: "cluster1",
|
|
HTTPRoute: &networking.HTTPRoute{
|
|
Name: "test-5",
|
|
},
|
|
},
|
|
}
|
|
|
|
SortHTTPRoutes(input)
|
|
if (input[0].HTTPRoute.Name) != "test-4" {
|
|
t.Fatal("should be test-4")
|
|
}
|
|
if (input[1].HTTPRoute.Name) != "test-2" {
|
|
t.Fatal("should be test-2")
|
|
}
|
|
if (input[2].HTTPRoute.Name) != "test-5" {
|
|
t.Fatal("should be test-5")
|
|
}
|
|
if (input[3].HTTPRoute.Name) != "test-1" {
|
|
t.Fatal("should be test-1")
|
|
}
|
|
if (input[4].HTTPRoute.Name) != "test-3" {
|
|
t.Fatal("should be test-3")
|
|
}
|
|
}
|
|
|
|
// TestSortHTTPRoutesWithMoreRules include headers, query params, methods
|
|
func TestSortHTTPRoutesWithMoreRules(t *testing.T) {
|
|
input := []struct {
|
|
order string
|
|
pathType PathType
|
|
path string
|
|
method *networking.StringMatch
|
|
header map[string]*networking.StringMatch
|
|
queryParam map[string]*networking.StringMatch
|
|
}{
|
|
{
|
|
order: "1",
|
|
pathType: Exact,
|
|
path: "/bar",
|
|
},
|
|
{
|
|
order: "2",
|
|
pathType: Prefix,
|
|
path: "/bar",
|
|
},
|
|
{
|
|
order: "3",
|
|
pathType: Prefix,
|
|
path: "/bar",
|
|
method: &networking.StringMatch{
|
|
MatchType: &networking.StringMatch_Regex{Regex: "GET|PUT"},
|
|
},
|
|
},
|
|
{
|
|
order: "4",
|
|
pathType: Prefix,
|
|
path: "/bar",
|
|
method: &networking.StringMatch{
|
|
MatchType: &networking.StringMatch_Regex{Regex: "GET"},
|
|
},
|
|
},
|
|
{
|
|
order: "5",
|
|
pathType: Prefix,
|
|
path: "/bar",
|
|
header: map[string]*networking.StringMatch{
|
|
"foo": {
|
|
MatchType: &networking.StringMatch_Exact{Exact: "bar"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
order: "6",
|
|
pathType: Prefix,
|
|
path: "/bar",
|
|
header: map[string]*networking.StringMatch{
|
|
"foo": {
|
|
MatchType: &networking.StringMatch_Exact{Exact: "bar"},
|
|
},
|
|
"bar": {
|
|
MatchType: &networking.StringMatch_Exact{Exact: "foo"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
order: "7",
|
|
pathType: Prefix,
|
|
path: "/bar",
|
|
queryParam: map[string]*networking.StringMatch{
|
|
"foo": {
|
|
MatchType: &networking.StringMatch_Exact{Exact: "bar"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
order: "8",
|
|
pathType: Prefix,
|
|
path: "/bar",
|
|
queryParam: map[string]*networking.StringMatch{
|
|
"foo": {
|
|
MatchType: &networking.StringMatch_Exact{Exact: "bar"},
|
|
},
|
|
"bar": {
|
|
MatchType: &networking.StringMatch_Exact{Exact: "foo"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
order: "9",
|
|
pathType: Prefix,
|
|
path: "/bar",
|
|
method: &networking.StringMatch{
|
|
MatchType: &networking.StringMatch_Regex{Regex: "GET"},
|
|
},
|
|
queryParam: map[string]*networking.StringMatch{
|
|
"foo": {
|
|
MatchType: &networking.StringMatch_Exact{Exact: "bar"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
order: "10",
|
|
pathType: Prefix,
|
|
path: "/bar",
|
|
method: &networking.StringMatch{
|
|
MatchType: &networking.StringMatch_Regex{Regex: "GET"},
|
|
},
|
|
queryParam: map[string]*networking.StringMatch{
|
|
"bar": {
|
|
MatchType: &networking.StringMatch_Exact{Exact: "foo"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
origin := []string{"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"}
|
|
expect := []string{"1", "9", "10", "4", "3", "6", "5", "8", "7", "2"}
|
|
|
|
var list []*WrapperHTTPRoute
|
|
for idx, val := range input {
|
|
list = append(list, &WrapperHTTPRoute{
|
|
OriginPath: val.path,
|
|
OriginPathType: val.pathType,
|
|
HTTPRoute: &networking.HTTPRoute{
|
|
Name: origin[idx],
|
|
Match: []*networking.HTTPMatchRequest{
|
|
{
|
|
Method: val.method,
|
|
Headers: val.header,
|
|
QueryParams: val.queryParam,
|
|
},
|
|
},
|
|
},
|
|
})
|
|
}
|
|
|
|
SortHTTPRoutes(list)
|
|
|
|
for idx, val := range list {
|
|
if val.HTTPRoute.Name != expect[idx] {
|
|
t.Fatalf("should be %s, but got %s", expect[idx], val.HTTPRoute.Name)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateBackendResource(t *testing.T) {
|
|
groupStr := "networking.higress.io"
|
|
testCases := []struct {
|
|
name string
|
|
resource *v1.TypedLocalObjectReference
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "nil resource",
|
|
resource: nil,
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "nil APIGroup",
|
|
resource: &v1.TypedLocalObjectReference{
|
|
APIGroup: nil,
|
|
Kind: "McpBridge",
|
|
Name: "default",
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "wrong APIGroup",
|
|
resource: &v1.TypedLocalObjectReference{
|
|
APIGroup: &groupStr,
|
|
Kind: "McpBridge",
|
|
Name: "wrong-name",
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "wrong Kind",
|
|
resource: &v1.TypedLocalObjectReference{
|
|
APIGroup: &groupStr,
|
|
Kind: "WrongKind",
|
|
Name: "default",
|
|
},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "valid resource",
|
|
resource: &v1.TypedLocalObjectReference{
|
|
APIGroup: &groupStr,
|
|
Kind: "McpBridge",
|
|
Name: "default",
|
|
},
|
|
expected: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := ValidateBackendResource(tc.resource)
|
|
assert.Equal(t, tc.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateOrUpdateAnnotations(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
annotations map[string]string
|
|
options Options
|
|
expected map[string]string
|
|
}{
|
|
{
|
|
name: "empty annotations",
|
|
annotations: map[string]string{},
|
|
options: Options{
|
|
ClusterId: "test-cluster",
|
|
RawClusterId: "raw-test-cluster",
|
|
},
|
|
expected: map[string]string{
|
|
ClusterIdAnnotation: "test-cluster",
|
|
RawClusterIdAnnotation: "raw-test-cluster",
|
|
},
|
|
},
|
|
{
|
|
name: "existing annotations",
|
|
annotations: map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
},
|
|
options: Options{
|
|
ClusterId: "test-cluster",
|
|
RawClusterId: "raw-test-cluster",
|
|
},
|
|
expected: map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
ClusterIdAnnotation: "test-cluster",
|
|
RawClusterIdAnnotation: "raw-test-cluster",
|
|
},
|
|
},
|
|
{
|
|
name: "overwrite existing cluster annotations",
|
|
annotations: map[string]string{
|
|
ClusterIdAnnotation: "old-cluster",
|
|
RawClusterIdAnnotation: "old-raw-cluster",
|
|
"key1": "value1",
|
|
},
|
|
options: Options{
|
|
ClusterId: "new-cluster",
|
|
RawClusterId: "new-raw-cluster",
|
|
},
|
|
expected: map[string]string{
|
|
ClusterIdAnnotation: "new-cluster",
|
|
RawClusterIdAnnotation: "new-raw-cluster",
|
|
"key1": "value1",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := CreateOrUpdateAnnotations(tc.annotations, tc.options)
|
|
assert.Equal(t, tc.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetClusterId(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
annotations map[string]string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "nil annotations",
|
|
annotations: nil,
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "empty annotations",
|
|
annotations: map[string]string{},
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "with cluster id",
|
|
annotations: map[string]string{
|
|
ClusterIdAnnotation: "test-cluster",
|
|
},
|
|
expected: "test-cluster",
|
|
},
|
|
{
|
|
name: "with other annotations",
|
|
annotations: map[string]string{
|
|
"key1": "value1",
|
|
"key2": "value2",
|
|
},
|
|
expected: "",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := GetClusterId(tc.annotations)
|
|
assert.Equal(t, tc.expected, string(result))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConvertToDNSLabelValidAndCleanHost(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
input string
|
|
}{
|
|
{
|
|
name: "simple host",
|
|
input: "example.com",
|
|
},
|
|
{
|
|
name: "wildcard host",
|
|
input: "*.example.com",
|
|
},
|
|
{
|
|
name: "long host",
|
|
input: "very-long-subdomain.example-service.my-namespace.svc.cluster.local",
|
|
},
|
|
{
|
|
name: "empty host",
|
|
input: "",
|
|
},
|
|
{
|
|
name: "ip address",
|
|
input: "192.168.1.1",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// Test internal convertToDNSLabelValid function (through CleanHost)
|
|
result := CleanHost(tc.input)
|
|
|
|
// Validate result
|
|
assert.NotEmpty(t, result)
|
|
assert.Equal(t, 16, len(result)) // MD5 hash format is fixed length of 16 bytes
|
|
|
|
// Consistency check - same input should produce same output
|
|
result2 := CleanHost(tc.input)
|
|
assert.Equal(t, result, result2)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSplitServiceFQDN(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
fqdn string
|
|
expectedSvc string
|
|
expectedNs string
|
|
expectedValid bool
|
|
}{
|
|
{
|
|
name: "simple fqdn",
|
|
fqdn: "service.namespace",
|
|
expectedSvc: "service",
|
|
expectedNs: "namespace",
|
|
expectedValid: true,
|
|
},
|
|
{
|
|
name: "full k8s fqdn",
|
|
fqdn: "service.namespace.svc.cluster.local",
|
|
expectedSvc: "service",
|
|
expectedNs: "namespace",
|
|
expectedValid: true,
|
|
},
|
|
{
|
|
name: "just service name",
|
|
fqdn: "service",
|
|
expectedSvc: "",
|
|
expectedNs: "",
|
|
expectedValid: false,
|
|
},
|
|
{
|
|
name: "empty string",
|
|
fqdn: "",
|
|
expectedSvc: "",
|
|
expectedNs: "",
|
|
expectedValid: false,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
svc, ns, valid := SplitServiceFQDN(tc.fqdn)
|
|
assert.Equal(t, tc.expectedSvc, svc)
|
|
assert.Equal(t, tc.expectedNs, ns)
|
|
assert.Equal(t, tc.expectedValid, valid)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestConvertBackendService(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
dest *networking.HTTPRouteDestination
|
|
expected model.BackendService
|
|
}{
|
|
{
|
|
name: "simple service",
|
|
dest: &networking.HTTPRouteDestination{
|
|
Destination: &networking.Destination{
|
|
Host: "service.namespace",
|
|
Port: &networking.PortSelector{
|
|
Number: 80,
|
|
},
|
|
},
|
|
Weight: 100,
|
|
},
|
|
expected: model.BackendService{
|
|
Name: "service",
|
|
Namespace: "namespace",
|
|
Port: 80,
|
|
Weight: 100,
|
|
},
|
|
},
|
|
{
|
|
name: "full k8s FQDN",
|
|
dest: &networking.HTTPRouteDestination{
|
|
Destination: &networking.Destination{
|
|
Host: "service.namespace.svc.cluster.local",
|
|
Port: &networking.PortSelector{
|
|
Number: 8080,
|
|
},
|
|
},
|
|
Weight: 50,
|
|
},
|
|
expected: model.BackendService{
|
|
Name: "service",
|
|
Namespace: "namespace",
|
|
Port: 8080,
|
|
Weight: 50,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := ConvertBackendService(tc.dest)
|
|
assert.Equal(t, tc.expected.Name, result.Name)
|
|
assert.Equal(t, tc.expected.Namespace, result.Namespace)
|
|
assert.Equal(t, tc.expected.Port, result.Port)
|
|
assert.Equal(t, tc.expected.Weight, result.Weight)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCreateConvertedName(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
items []string
|
|
expected string
|
|
}{
|
|
{
|
|
name: "empty slice",
|
|
items: []string{},
|
|
expected: "",
|
|
},
|
|
{
|
|
name: "single item",
|
|
items: []string{"example"},
|
|
expected: "example",
|
|
},
|
|
{
|
|
name: "multiple items",
|
|
items: []string{"part1", "part2", "part3"},
|
|
expected: "part1-part2-part3",
|
|
},
|
|
{
|
|
name: "with empty strings",
|
|
items: []string{"part1", "", "part3"},
|
|
expected: "part1-part3",
|
|
},
|
|
{
|
|
name: "all empty strings",
|
|
items: []string{"", "", ""},
|
|
expected: "",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := CreateConvertedName(tc.items...)
|
|
assert.Equal(t, tc.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSortIngressByCreationTime(t *testing.T) {
|
|
configs := []config.Config{
|
|
{
|
|
Meta: config.Meta{
|
|
Name: "c-ingress",
|
|
Namespace: "ns1",
|
|
},
|
|
},
|
|
{
|
|
Meta: config.Meta{
|
|
Name: "a-ingress",
|
|
Namespace: "ns1",
|
|
},
|
|
},
|
|
{
|
|
Meta: config.Meta{
|
|
Name: "b-ingress",
|
|
Namespace: "ns1",
|
|
},
|
|
},
|
|
}
|
|
|
|
expected := []string{"a-ingress", "b-ingress", "c-ingress"}
|
|
|
|
SortIngressByCreationTime(configs)
|
|
|
|
var actual []string
|
|
for _, cfg := range configs {
|
|
actual = append(actual, cfg.Name)
|
|
}
|
|
|
|
assert.Equal(t, expected, actual, "When the timestamps are the same, the configuration should be sorted by name")
|
|
|
|
sameNamespaceConfigs := []config.Config{
|
|
{
|
|
Meta: config.Meta{
|
|
Name: "same-name",
|
|
Namespace: "c-ns",
|
|
},
|
|
},
|
|
{
|
|
Meta: config.Meta{
|
|
Name: "same-name",
|
|
Namespace: "a-ns",
|
|
},
|
|
},
|
|
{
|
|
Meta: config.Meta{
|
|
Name: "same-name",
|
|
Namespace: "b-ns",
|
|
},
|
|
},
|
|
}
|
|
|
|
expectedNamespace := []string{"a-ns", "b-ns", "c-ns"}
|
|
|
|
SortIngressByCreationTime(sameNamespaceConfigs)
|
|
|
|
var actualNamespace []string
|
|
for _, cfg := range sameNamespaceConfigs {
|
|
actualNamespace = append(actualNamespace, cfg.Namespace)
|
|
}
|
|
|
|
assert.Equal(t, expectedNamespace, actualNamespace, "When the names are the same, the configuration should be sorted by namespace")
|
|
}
|
|
|
|
func TestPartMd5(t *testing.T) {
|
|
testCases := []struct {
|
|
name string
|
|
input string
|
|
length int
|
|
}{
|
|
{
|
|
name: "empty string",
|
|
input: "",
|
|
length: 8,
|
|
},
|
|
{
|
|
name: "simple string",
|
|
input: "test",
|
|
length: 8,
|
|
},
|
|
{
|
|
name: "complex string",
|
|
input: "this-is-a-long-string-with-special-chars-!@#$%^&*()",
|
|
length: 8,
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
result := partMd5(tc.input)
|
|
|
|
// Check result format
|
|
assert.Equal(t, tc.length, len(result), "MD5 hash excerpt should be 8 characters")
|
|
|
|
// Run twice to ensure deterministic output
|
|
result2 := partMd5(tc.input)
|
|
assert.Equal(t, result, result2, "partMd5 function should be deterministic")
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetLbStatusListV1AndV1Beta1(t *testing.T) {
|
|
clusterPrefix = "gw-123-"
|
|
svcName := clusterPrefix
|
|
svcList := []*v1.Service{
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: svcName,
|
|
},
|
|
Spec: v1.ServiceSpec{
|
|
Type: v1.ServiceTypeLoadBalancer,
|
|
},
|
|
Status: v1.ServiceStatus{
|
|
LoadBalancer: v1.LoadBalancerStatus{
|
|
Ingress: []v1.LoadBalancerIngress{
|
|
{
|
|
IP: "2.2.2.2",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: svcName,
|
|
},
|
|
Spec: v1.ServiceSpec{
|
|
Type: v1.ServiceTypeLoadBalancer,
|
|
},
|
|
Status: v1.ServiceStatus{
|
|
LoadBalancer: v1.LoadBalancerStatus{
|
|
Ingress: []v1.LoadBalancerIngress{
|
|
{
|
|
Hostname: "1.1.1.1" + SvcHostNameSuffix,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Test the V1 version
|
|
t.Run("GetLbStatusListV1", func(t *testing.T) {
|
|
lbiList := GetLbStatusListV1(svcList)
|
|
|
|
assert.Equal(t, 2, len(lbiList), "There should be 2 entry points")
|
|
assert.Equal(t, "1.1.1.1", lbiList[0].IP, "The first IP should be 1.1.1.1")
|
|
assert.Equal(t, "2.2.2.2", lbiList[1].IP, "The second IP should be 2.2.2.2")
|
|
})
|
|
|
|
// Test the V1Beta1 version
|
|
t.Run("GetLbStatusListV1Beta1", func(t *testing.T) {
|
|
lbiList := GetLbStatusListV1Beta1(svcList)
|
|
|
|
assert.Equal(t, 2, len(lbiList), "There should be 2 entry points")
|
|
assert.Equal(t, "1.1.1.1", lbiList[0].IP, "The first IP should be 1.1.1.1")
|
|
assert.Equal(t, "2.2.2.2", lbiList[1].IP, "The second IP should be 2.2.2.2")
|
|
})
|
|
}
|