From 43aff62d0bc07552e9a53b255688343db232fba5 Mon Sep 17 00:00:00 2001 From: Lior Lieberman Date: Sat, 29 Nov 2025 00:41:18 +0000 Subject: [PATCH] feat: Refactor conformance suite to generate mesh tests from a shared test file --- .../httproute-request-header-modifier.go | 44 +++- .../tests/httproute-simple-same-namespace.go | 47 ++-- .../mesh/httproute-request-header-modifier.go | 213 ------------------ .../mesh/httproute-simple-same-namespace.go | 52 ----- conformance/utils/suite/conformance.go | 16 +- conformance/utils/suite/suite.go | 24 ++ 6 files changed, 103 insertions(+), 293 deletions(-) delete mode 100644 conformance/tests/mesh/httproute-request-header-modifier.go delete mode 100644 conformance/tests/mesh/httproute-simple-same-namespace.go diff --git a/conformance/tests/httproute-request-header-modifier.go b/conformance/tests/httproute-request-header-modifier.go index 228c132ca0..c6788c0d58 100644 --- a/conformance/tests/httproute-request-header-modifier.go +++ b/conformance/tests/httproute-request-header-modifier.go @@ -21,6 +21,7 @@ import ( "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/gateway-api/conformance/utils/echo" "sigs.k8s.io/gateway-api/conformance/utils/http" "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" "sigs.k8s.io/gateway-api/conformance/utils/suite" @@ -43,7 +44,13 @@ var HTTPRouteBackendRequestHeaderModifier = suite.ConformanceTest{ features.SupportHTTPRouteBackendRequestHeaderModification, }, Manifests: []string{"tests/httproute-request-header-modifier-backend.yaml"}, - Test: HTTPRouteRequestHeaderModifier.Test, + MeshFeatures: []features.FeatureName{ + features.SupportMesh, + features.SupportHTTPRoute, + features.SupportMeshHTTPRouteBackendRequestHeaderModification, + }, + MeshManifests: []string{"tests/mesh/httproute-request-header-modifier-backend.yaml"}, + Test: HTTPRouteRequestHeaderModifier.Test, } var HTTPRouteRequestHeaderModifier = suite.ConformanceTest{ @@ -54,12 +61,26 @@ var HTTPRouteRequestHeaderModifier = suite.ConformanceTest{ features.SupportHTTPRoute, }, Manifests: []string{"tests/httproute-request-header-modifier.yaml"}, - Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { - ns := "gateway-conformance-infra" - routeNN := types.NamespacedName{Name: "request-header-modifier", Namespace: ns} - gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} - gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) - kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) + MeshFeatures: []features.FeatureName{ + features.SupportMesh, + features.SupportHTTPRoute, + }, + MeshManifests: []string{"tests/mesh/httproute-request-header-modifier.yaml"}, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + var ns string + var gwAddr string + var client echo.MeshPod + + if !s.CurrentTest.IsMesh { + ns = "gateway-conformance-infra" + routeNN := types.NamespacedName{Name: "request-header-modifier", Namespace: ns} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: ns} + gwAddr = kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, s.Client, s.TimeoutConfig, s.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, s.Client, s.TimeoutConfig, routeNN, gwNN) + } else { + ns = "gateway-conformance-mesh" + client = echo.ConnectToApp(t, s, echo.MeshAppEchoV1) + } testCases := []http.ExpectedResponse{{ Request: http.Request{ @@ -207,9 +228,16 @@ var HTTPRouteRequestHeaderModifier = suite.ConformanceTest{ // Declare tc here to avoid loop variable // reuse issues across parallel tests. tc := testCases[i] + if s.CurrentTest.IsMesh { + tc.Backend = "echo-v1" + } t.Run(tc.GetTestCaseName(i), func(t *testing.T) { t.Parallel() - http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, tc) + if !s.CurrentTest.IsMesh { + http.MakeRequestAndExpectEventuallyConsistentResponse(t, s.RoundTripper, s.TimeoutConfig, gwAddr, tc) + } else { + client.MakeRequestAndExpectEventuallyConsistentResponse(t, tc, s.TimeoutConfig) + } }) } }, diff --git a/conformance/tests/httproute-simple-same-namespace.go b/conformance/tests/httproute-simple-same-namespace.go index 57ee680d91..88553edc16 100644 --- a/conformance/tests/httproute-simple-same-namespace.go +++ b/conformance/tests/httproute-simple-same-namespace.go @@ -22,6 +22,7 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/gateway-api/apis/v1beta1" + "sigs.k8s.io/gateway-api/conformance/utils/echo" "sigs.k8s.io/gateway-api/conformance/utils/http" "sigs.k8s.io/gateway-api/conformance/utils/kubernetes" "sigs.k8s.io/gateway-api/conformance/utils/suite" @@ -40,20 +41,38 @@ var HTTPRouteSimpleSameNamespace = suite.ConformanceTest{ features.SupportHTTPRoute, }, Manifests: []string{"tests/httproute-simple-same-namespace.yaml"}, - Test: func(t *testing.T, suite *suite.ConformanceTestSuite) { - ns := v1beta1.Namespace("gateway-conformance-infra") - routeNN := types.NamespacedName{Name: "gateway-conformance-infra-test", Namespace: string(ns)} - gwNN := types.NamespacedName{Name: "same-namespace", Namespace: string(ns)} - gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, suite.Client, suite.TimeoutConfig, suite.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) - kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, suite.Client, suite.TimeoutConfig, routeNN, gwNN) - - t.Run("Simple HTTP request should reach infra-backend", func(t *testing.T) { - http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, http.ExpectedResponse{ - Request: http.Request{Path: "/"}, - Response: http.Response{StatusCode: 200}, - Backend: "infra-backend-v1", - Namespace: "gateway-conformance-infra", + MeshFeatures: []features.FeatureName{ + features.SupportMesh, + features.SupportHTTPRoute, + }, + MeshManifests: []string{"tests/mesh/httproute-simple-same-namespace.yaml"}, + Test: func(t *testing.T, s *suite.ConformanceTestSuite) { + if !s.CurrentTest.IsMesh { + ns := v1beta1.Namespace("gateway-conformance-infra") + routeNN := types.NamespacedName{Name: "gateway-conformance-infra-test", Namespace: string(ns)} + gwNN := types.NamespacedName{Name: "same-namespace", Namespace: string(ns)} + gwAddr := kubernetes.GatewayAndHTTPRoutesMustBeAccepted(t, s.Client, s.TimeoutConfig, s.ControllerName, kubernetes.NewGatewayRef(gwNN), routeNN) + kubernetes.HTTPRouteMustHaveResolvedRefsConditionsTrue(t, s.Client, s.TimeoutConfig, routeNN, gwNN) + + t.Run("Simple HTTP request should reach infra-backend", func(t *testing.T) { + http.MakeRequestAndExpectEventuallyConsistentResponse(t, s.RoundTripper, s.TimeoutConfig, gwAddr, http.ExpectedResponse{ + Request: http.Request{Path: "/"}, + Response: http.Response{StatusCode: 200}, + Backend: "infra-backend-v1", + Namespace: "gateway-conformance-infra", + }) + }) + } else { + ns := "gateway-conformance-mesh" + client := echo.ConnectToApp(t, s, echo.MeshAppEchoV1) + t.Run("Simple HTTP request should reach infra-backend", func(t *testing.T) { + client.MakeRequestAndExpectEventuallyConsistentResponse(t, http.ExpectedResponse{ + Request: http.Request{Path: "/", Host: "echo"}, + Response: http.Response{StatusCode: 200}, + Backend: "echo-v1", + Namespace: ns, + }, s.TimeoutConfig) }) - }) + } }, } diff --git a/conformance/tests/mesh/httproute-request-header-modifier.go b/conformance/tests/mesh/httproute-request-header-modifier.go deleted file mode 100644 index 8c7abb83fb..0000000000 --- a/conformance/tests/mesh/httproute-request-header-modifier.go +++ /dev/null @@ -1,213 +0,0 @@ -/* -Copyright 2025 The Kubernetes 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 meshtests - -import ( - "testing" - - "sigs.k8s.io/gateway-api/conformance/utils/echo" - "sigs.k8s.io/gateway-api/conformance/utils/http" - "sigs.k8s.io/gateway-api/conformance/utils/suite" - "sigs.k8s.io/gateway-api/pkg/features" -) - -func init() { - MeshConformanceTests = append(MeshConformanceTests, - MeshHTTPRouteRequestHeaderModifier, - MeshHTTPRouteBackendRequestHeaderModifier, - ) -} - -var MeshHTTPRouteBackendRequestHeaderModifier = suite.ConformanceTest{ - ShortName: "MeshHTTPRouteBackendRequestHeaderModifier", - Description: "An HTTPRoute backend has request header modifier filters applied correctly", - Features: []features.FeatureName{ - features.SupportMesh, - features.SupportHTTPRoute, - features.SupportMeshHTTPRouteBackendRequestHeaderModification, - }, - Manifests: []string{"tests/mesh/httproute-request-header-modifier-backend.yaml"}, - Test: MeshHTTPRouteRequestHeaderModifier.Test, -} - -var MeshHTTPRouteRequestHeaderModifier = suite.ConformanceTest{ - ShortName: "MeshHTTPRouteRequestHeaderModifier", - Description: "An HTTPRoute has request header modifier filters applied correctly", - Features: []features.FeatureName{ - features.SupportMesh, - features.SupportHTTPRoute, - }, - Manifests: []string{"tests/mesh/httproute-request-header-modifier.yaml"}, - Test: func(t *testing.T, s *suite.ConformanceTestSuite) { - ns := "gateway-conformance-mesh" - client := echo.ConnectToApp(t, s, echo.MeshAppEchoV1) - - testCases := []http.ExpectedResponse{ - { - Request: http.Request{ - Path: "/set", - Headers: map[string]string{ - "Some-Other-Header": "val", - }, - }, - ExpectedRequest: &http.ExpectedRequest{ - Request: http.Request{ - Path: "/set", - Headers: map[string]string{ - "Some-Other-Header": "val", - "X-Header-Set": "set-overwrites-values", - }, - }, - }, - Backend: "echo-v1", - Namespace: ns, - }, { - Request: http.Request{ - Path: "/set", - Headers: map[string]string{ - "Some-Other-Header": "val", - "X-Header-Set": "some-other-value", - }, - }, - ExpectedRequest: &http.ExpectedRequest{ - Request: http.Request{ - Path: "/set", - Headers: map[string]string{ - "Some-Other-Header": "val", - "X-Header-Set": "set-overwrites-values", - }, - }, - }, - Backend: "echo-v1", - Namespace: ns, - }, { - Request: http.Request{ - Path: "/add", - Headers: map[string]string{ - "Some-Other-Header": "val", - }, - }, - ExpectedRequest: &http.ExpectedRequest{ - Request: http.Request{ - Path: "/add", - Headers: map[string]string{ - "Some-Other-Header": "val", - "X-Header-Add": "add-appends-values", - }, - }, - }, - Backend: "echo-v1", - Namespace: ns, - }, { - Request: http.Request{ - Path: "/add", - Headers: map[string]string{ - "Some-Other-Header": "val", - "X-Header-Add": "some-other-value", - }, - }, - ExpectedRequest: &http.ExpectedRequest{ - Request: http.Request{ - Path: "/add", - Headers: map[string]string{ - "Some-Other-Header": "val", - "X-Header-Add": "some-other-value,add-appends-values", - }, - }, - }, - Backend: "echo-v1", - Namespace: ns, - }, { - Request: http.Request{ - Path: "/remove", - Headers: map[string]string{ - "X-Header-Remove": "val", - }, - }, - ExpectedRequest: &http.ExpectedRequest{ - Request: http.Request{ - Path: "/remove", - }, - AbsentHeaders: []string{"X-Header-Remove"}, - }, - Backend: "echo-v1", - Namespace: ns, - }, { - Request: http.Request{ - Path: "/multiple", - Headers: map[string]string{ - "X-Header-Set-2": "set-val-2", - "X-Header-Add-2": "add-val-2", - "X-Header-Remove-2": "remove-val-2", - "Another-Header": "another-header-val", - }, - }, - ExpectedRequest: &http.ExpectedRequest{ - Request: http.Request{ - Path: "/multiple", - Headers: map[string]string{ - "X-Header-Set-1": "header-set-1", - "X-Header-Set-2": "header-set-2", - "X-Header-Add-1": "header-add-1", - "X-Header-Add-2": "add-val-2,header-add-2", - "X-Header-Add-3": "header-add-3", - "Another-Header": "another-header-val", - }, - }, - AbsentHeaders: []string{"X-Header-Remove-1", "X-Header-Remove-2"}, - }, - Backend: "echo-v1", - Namespace: ns, - }, { - Request: http.Request{ - Path: "/case-insensitivity", - // The filter uses canonicalized header names, - // the request uses lowercase names. - Headers: map[string]string{ - "x-header-set": "original-val-set", - "x-header-add": "original-val-add", - "x-header-remove": "original-val-remove", - "Another-Header": "another-header-val", - }, - }, - ExpectedRequest: &http.ExpectedRequest{ - Request: http.Request{ - Path: "/case-insensitivity", - Headers: map[string]string{ - "X-Header-Set": "header-set", - "X-Header-Add": "original-val-add,header-add", - "Another-Header": "another-header-val", - }, - }, - AbsentHeaders: []string{"x-header-remove", "X-Header-Remove"}, - }, - Backend: "echo-v1", - Namespace: ns, - }, - } - - for i := range testCases { - // Declare tc here to avoid loop variable - // reuse issues across parallel tests. - tc := testCases[i] - t.Run(tc.GetTestCaseName(i), func(t *testing.T) { - t.Parallel() - client.MakeRequestAndExpectEventuallyConsistentResponse(t, tc, s.TimeoutConfig) - }) - } - }, -} diff --git a/conformance/tests/mesh/httproute-simple-same-namespace.go b/conformance/tests/mesh/httproute-simple-same-namespace.go deleted file mode 100644 index 77813f29af..0000000000 --- a/conformance/tests/mesh/httproute-simple-same-namespace.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2025 The Kubernetes 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 meshtests - -import ( - "testing" - - "sigs.k8s.io/gateway-api/conformance/utils/echo" - "sigs.k8s.io/gateway-api/conformance/utils/http" - "sigs.k8s.io/gateway-api/conformance/utils/suite" - "sigs.k8s.io/gateway-api/pkg/features" -) - -func init() { - MeshConformanceTests = append(MeshConformanceTests, MeshHTTPRouteSimpleSameNamespace) -} - -var MeshHTTPRouteSimpleSameNamespace = suite.ConformanceTest{ - ShortName: "MeshHTTPRouteSimpleSameNamespace", - Description: "A single HTTPRoute in the gateway-conformance-mesh namespace attaches to a Service in the same namespace", - Features: []features.FeatureName{ - features.SupportMesh, - features.SupportHTTPRoute, - }, - Manifests: []string{"tests/mesh/httproute-simple-same-namespace.yaml"}, - Test: func(t *testing.T, s *suite.ConformanceTestSuite) { - ns := "gateway-conformance-mesh" - client := echo.ConnectToApp(t, s, echo.MeshAppEchoV1) - t.Run("Simple HTTP request should reach infra-backend", func(t *testing.T) { - client.MakeRequestAndExpectEventuallyConsistentResponse(t, http.ExpectedResponse{ - Request: http.Request{Path: "/", Host: "echo"}, - Response: http.Response{StatusCode: 200}, - Backend: "echo-v1", - Namespace: ns, - }, s.TimeoutConfig) - }) - }, -} diff --git a/conformance/utils/suite/conformance.go b/conformance/utils/suite/conformance.go index 12db14d6df..6db7cb3290 100644 --- a/conformance/utils/suite/conformance.go +++ b/conformance/utils/suite/conformance.go @@ -29,12 +29,15 @@ import ( type ConformanceTest struct { ShortName string Description string - Features []features.FeatureName - Manifests []string - Slow bool - Parallel bool - Test func(*testing.T, *ConformanceTestSuite) - Provisional bool + Features []features.FeatureName + Manifests []string + MeshFeatures []features.FeatureName + MeshManifests []string + IsMesh bool + Slow bool + Parallel bool + Test func(*testing.T, *ConformanceTestSuite) + Provisional bool } // Run runs an individual tests, applying and cleaning up the required manifests @@ -43,6 +46,7 @@ func (test *ConformanceTest) Run(t *testing.T, suite *ConformanceTestSuite) { if test.Parallel { t.Parallel() } + suite.CurrentTest = test var featuresInfo string // Test against features if the user hasn't focused on a single test diff --git a/conformance/utils/suite/suite.go b/conformance/utils/suite/suite.go index a17131ccde..195641027a 100644 --- a/conformance/utils/suite/suite.go +++ b/conformance/utils/suite/suite.go @@ -65,6 +65,7 @@ type ConformanceTestSuite struct { RestConfig *rest.Config RoundTripper roundtripper.RoundTripper GRPCClient grpc.Client + CurrentTest *ConformanceTest GatewayClassName string MeshName string ControllerName string @@ -459,7 +460,30 @@ func (suite *ConformanceTestSuite) Run(t *testing.T, tests []ConformanceTest) er // run all tests and collect the test results for conformance reporting results := make(map[string]testResult) sleepForTestIsolation := false + + testsToRun := []ConformanceTest{} for _, test := range tests { + if len(test.MeshFeatures) > 0 { + // Create Gateway version + gwTest := test + gwTest.MeshFeatures = nil + gwTest.MeshManifests = nil + gwTest.IsMesh = false + testsToRun = append(testsToRun, gwTest) + + // Create Mesh version + meshTest := test + meshTest.ShortName = "Mesh" + meshTest.ShortName + meshTest.Features = test.MeshFeatures + meshTest.Manifests = test.MeshManifests + meshTest.IsMesh = true + testsToRun = append(testsToRun, meshTest) + } else { + testsToRun = append(testsToRun, test) + } + } + + for _, test := range testsToRun { res := testSucceeded if suite.RunTest != "" && test.ShortName != suite.RunTest { res = testSkipped