Repository: envoyproxy/gateway
Pull request #5992 (2025-05-10 06:53:14)

ci: enable conformance test for GatewayNamespaceMode

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/envoyproxy/gateway/pull/5992?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 65.82%. Comparing base [(`6a76f5a`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/6a76f5aedde45679dd8efc3a1777f6167e5b82a2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy) to head [(`6c31ce9`)](https://app.codecov.io/gh/envoyproxy/gateway/commit/6c31ce972489cd82473a3b9453ad382500663b15?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #5992      +/-   ##
==========================================
- Coverage   65.89%   65.82%   -0.07%     
==========================================
  Files         217      217              
  Lines       36063    36063              
==========================================
- Hits        23762    23739      -23     
- Misses      10827    10844      +17     
- Partials     1474     1480       +6     
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/envoyproxy/gateway/pull/5992?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=envoyproxy).

<details><summary> :rocket: New features to boost your workflow: </summary>

- :snowflake: [Test Analytics](https://docs.codecov.com/docs/test-analytics): Detect flaky tests, report on failures, and find test suite problems.
</details>

Patch:
diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go
index 6a6565cd8..75fcd286a 100644
--- a/internal/gatewayapi/backendtrafficpolicy.go
+++ b/internal/gatewayapi/backendtrafficpolicy.go
@@ -165,7 +165,7 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources
 
 				if policy.Spec.MergeType == nil {
 					// Set conditions for translation error if it got any
-					if err := t.translateBackendTrafficPolicyForRoute(policy, route, xdsIR, resources); err != nil {
+					if err := t.translateBackendTrafficPolicyForRoute(policy, route, xdsIR, resources, nil); err != nil {
 						status.SetTranslationErrorForPolicyAncestors(&policy.Status,
 							ancestorRefs,
 							t.GatewayControllerName,
@@ -176,28 +176,34 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources
 				} else {
 					// merge with parent target policy if exists
 					for _, gwNN := range routeParents.UnsortedList() {
+						ancestorRef := getAncestorRefForPolicy(gwNN, nil)
 						// find policy for Gateway
 						gwPolicy := gatewayPolicyMap[gwNN]
 						if gwPolicy == nil {
 							// not found, fall back to the current policy
-							if err := t.translateBackendTrafficPolicyForRoute(policy, route, xdsIR, resources); err != nil {
-								status.SetTranslationErrorForPolicyAncestors(&policy.Status,
-									ancestorRefs,
+							if err := t.translateBackendTrafficPolicyForRoute(policy, route, xdsIR, resources, &gwNN); err != nil {
+								status.SetConditionForPolicyAncestor(&policy.Status,
+									ancestorRef,
 									t.GatewayControllerName,
-									policy.Generation,
+									gwapiv1a2.PolicyConditionAccepted, metav1.ConditionFalse,
+									egv1a1.PolicyReasonInvalid,
 									status.Error2ConditionMsg(err),
+									policy.Generation,
 								)
 							}
+
 							continue
 						}
 
 						// merge with parent policy
 						if err := t.translateBackendTrafficPolicyForRouteWithMerge(policy, gwNN, gwPolicy, route, xdsIR, resources); err != nil {
-							status.SetTranslationErrorForPolicyAncestors(&policy.Status,
-								ancestorRefs,
+							status.SetConditionForPolicyAncestor(&policy.Status,
+								ancestorRef,
 								t.GatewayControllerName,
-								policy.Generation,
+								gwapiv1a2.PolicyConditionAccepted, metav1.ConditionFalse,
+								egv1a1.PolicyReasonInvalid,
 								status.Error2ConditionMsg(err),
+								policy.Generation,
 							)
 							continue
 						}
@@ -207,8 +213,8 @@ func (t *Translator) ProcessBackendTrafficPolicies(resources *resource.Resources
 						}
 						gatewayPolicyMerged[gwNN].Insert(utils.NamespacedName(route).String())
 
-						status.SetConditionForPolicyAncestors(&policy.Status,
-							ancestorRefs,
+						status.SetConditionForPolicyAncestor(&policy.Status,
+							ancestorRef,
 							t.GatewayControllerName,
 							egv1a1.PolicyConditionMerged,
 							metav1.ConditionTrue,
@@ -393,6 +399,7 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(
 	route RouteContext,
 	xdsIR resource.XdsIRMap,
 	resources *resource.Resources,
+	gatewayNN *types.NamespacedName,
 ) error {
 	tf, errs := t.buildTrafficFeatures(policy, resources)
 	if tf == nil {
@@ -401,7 +408,12 @@ func (t *Translator) translateBackendTrafficPolicyForRoute(
 	}
 
 	// Apply IR to all relevant routes
-	for _, x := range xdsIR {
+	for key, x := range xdsIR {
+		// if gatewayNN is not nil, only apply to the specific gateway
+		if gatewayNN != nil && key != t.IRKey(*gatewayNN) {
+			// Skip if not the gateway wanted
+			continue
+		}
 		applyTrafficFeatureToRoute(route, tf, errs, policy, x)
 	}
 
diff --git a/tools/make/golang.mk b/tools/make/golang.mk
index 50a182527..7255afa64 100644
--- a/tools/make/golang.mk
+++ b/tools/make/golang.mk
@@ -69,7 +69,7 @@ go.testdata.complete: ## Override test ouputdata
 go.test.coverage: go.test.cel ## Run go unit and integration tests in GitHub Actions
 	@$(LOG_TARGET)
 	KUBEBUILDER_ASSETS="$(shell go tool setup-envtest use $(ENVTEST_K8S_VERSION) -p path)" \
-		go test ./... --tags=integration -race -coverprofile=coverage.xml -covermode=atomic
+		go test ./... --tags=integration -race -coverprofile=coverage.xml -covermode=atomic -coverpkg=./...
 
 .PHONY: go.test.cel
 go.test.cel: manifests # Run the CEL validation tests


Test patch:
diff --git a/.github/workflows/build_and_test.yaml b/.github/workflows/build_and_test.yaml
index 580d2d741..651841c7b 100644
--- a/.github/workflows/build_and_test.yaml
+++ b/.github/workflows/build_and_test.yaml
@@ -88,13 +88,20 @@ jobs:
         target:
         - version: v1.30.10
           ipFamily: ipv4
+          profile: default
         - version: v1.31.6
           ipFamily: ipv4
+          profile: default
         - version: v1.32.3
           ipFamily: ipv6  # only run ipv6 test on this version to save time
+          profile: default
         # TODO: this's IPv4 first, need a way to test IPv6 first.
         - version: v1.33.0
           ipFamily: dual  # only run dual test on latest version to save time
+          profile: default
+        - version: v1.33.0
+          ipFamily: dual  # only run dual test on latest version to save time
+          profile: gateway-namespace-mode
     steps:
     - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2
     - uses: ./tools/github-actions/setup-deps
@@ -116,6 +123,7 @@ jobs:
         KIND_NODE_TAG: ${{ matrix.target.version }}
         IMAGE_PULL_POLICY: IfNotPresent
         IP_FAMILY: ${{ matrix.target.ipFamily }}
+        KUBE_DEPLOY_PROFILE: ${{ matrix.target.profile }}
       run: make conformance
 
   e2e-test:
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-with-multi-parents.in.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-with-multi-parents.in.yaml
new file mode 100644
index 000000000..e9ac3501c
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-with-multi-parents.in.yaml
@@ -0,0 +1,89 @@
+gateways:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    metadata:
+      namespace: envoy-gateway
+      name: gateway-1
+    spec:
+      gatewayClassName: envoy-gateway-class
+      listeners:
+        - name: http
+          protocol: HTTP
+          port: 80
+          allowedRoutes:
+            namespaces:
+              from: All
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: Gateway
+    metadata:
+      namespace: envoy-gateway
+      name: gateway-2
+    spec:
+      gatewayClassName: envoy-gateway-class
+      listeners:
+        - name: http
+          protocol: HTTP
+          port: 80
+          allowedRoutes:
+            namespaces:
+              from: All
+httpRoutes:
+  - apiVersion: gateway.networking.k8s.io/v1
+    kind: HTTPRoute
+    metadata:
+      namespace: default
+      name: httproute-1
+    spec:
+      hostnames:
+        - gateway.envoyproxy.io
+      parentRefs:
+        - namespace: envoy-gateway
+          name: gateway-1
+          sectionName: http
+        - namespace: envoy-gateway
+          name: gateway-2
+          sectionName: http
+      rules:
+        - matches:
+            - path:
+                value: "/"
+          backendRefs:
+            - name: service-1
+              port: 8080
+backendTrafficPolicies:
+  - apiVersion: gateway.envoyproxy.io/v1alpha1
+    kind: BackendTrafficPolicy
+    metadata:
+      namespace: envoy-gateway
+      name: policy-for-gateway1
+    spec:
+      targetRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: gateway-1
+      timeout:
+        tcp:
+          connectTimeout: 15s
+        http:
+          connectionIdleTimeout: 16s
+          maxConnectionDuration: 17s
+      httpUpgrade:
+        - type: websocket
+  - apiVersion: gateway.envoyproxy.io/v1alpha1
+    kind: BackendTrafficPolicy
+    metadata:
+      namespace: default
+      name: policy-for-route
+    spec:
+      targetRef:
+        group: gateway.networking.k8s.io
+        kind: HTTPRoute
+        name: httproute-1
+      mergeType: StrategicMerge
+      timeout:
+        tcp:
+          connectTimeout: 10s
+      connection:
+        bufferLimit: 100M
+      httpUpgrade:
+        - type: "spdy/3.1"
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-with-multi-parents.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-with-multi-parents.out.yaml
new file mode 100644
index 000000000..70ff8aee3
--- /dev/null
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge-with-multi-parents.out.yaml
@@ -0,0 +1,386 @@
+backendTrafficPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: BackendTrafficPolicy
+  metadata:
+    creationTimestamp: null
+    name: policy-for-route
+    namespace: default
+  spec:
+    connection:
+      bufferLimit: 100M
+    httpUpgrade:
+    - type: spdy/3.1
+    mergeType: StrategicMerge
+    targetRef:
+      group: gateway.networking.k8s.io
+      kind: HTTPRoute
+      name: httproute-1
+    timeout:
+      tcp:
+        connectTimeout: 10s
+  status:
+    ancestors:
+    - ancestorRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+      conditions:
+      - lastTransitionTime: null
+        message: Merged with policy envoy-gateway/policy-for-gateway1
+        reason: Merged
+        status: "True"
+        type: Merged
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+    - ancestorRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+      conditions:
+      - lastTransitionTime: null
+        message: Policy has been accepted.
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+    - ancestorRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: gateway-2
+        namespace: envoy-gateway
+        sectionName: http
+      conditions:
+      - lastTransitionTime: null
+        message: Policy has been accepted.
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+  kind: BackendTrafficPolicy
+  metadata:
+    creationTimestamp: null
+    name: policy-for-gateway1
+    namespace: envoy-gateway
+  spec:
+    httpUpgrade:
+    - type: websocket
+    targetRef:
+      group: gateway.networking.k8s.io
+      kind: Gateway
+      name: gateway-1
+    timeout:
+      http:
+        connectionIdleTimeout: 16s
+        maxConnectionDuration: 17s
+      tcp:
+        connectTimeout: 15s
+  status:
+    ancestors:
+    - ancestorRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+      conditions:
+      - lastTransitionTime: null
+        message: Policy has been accepted.
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: 'This policy is being merged by other backendTrafficPolicies for
+          these routes: [default/httproute-1]'
+        reason: Merged
+        status: "True"
+        type: Merged
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: Gateway
+  metadata:
+    creationTimestamp: null
+    name: gateway-1
+    namespace: envoy-gateway
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - allowedRoutes:
+        namespaces:
+          from: All
+      name: http
+      port: 80
+      protocol: HTTP
+  status:
+    listeners:
+    - attachedRoutes: 1
+      conditions:
+      - lastTransitionTime: null
+        message: Sending translated listener configuration to the data plane
+        reason: Programmed
+        status: "True"
+        type: Programmed
+      - lastTransitionTime: null
+        message: Listener has been successfully translated
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Listener references have been resolved
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      name: http
+      supportedKinds:
+      - group: gateway.networking.k8s.io
+        kind: HTTPRoute
+      - group: gateway.networking.k8s.io
+        kind: GRPCRoute
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: Gateway
+  metadata:
+    creationTimestamp: null
+    name: gateway-2
+    namespace: envoy-gateway
+  spec:
+    gatewayClassName: envoy-gateway-class
+    listeners:
+    - allowedRoutes:
+        namespaces:
+          from: All
+      name: http
+      port: 80
+      protocol: HTTP
+  status:
+    listeners:
+    - attachedRoutes: 1
+      conditions:
+      - lastTransitionTime: null
+        message: Sending translated listener configuration to the data plane
+        reason: Programmed
+        status: "True"
+        type: Programmed
+      - lastTransitionTime: null
+        message: Listener has been successfully translated
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Listener references have been resolved
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      name: http
+      supportedKinds:
+      - group: gateway.networking.k8s.io
+        kind: HTTPRoute
+      - group: gateway.networking.k8s.io
+        kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+  kind: HTTPRoute
+  metadata:
+    creationTimestamp: null
+    name: httproute-1
+    namespace: default
+  spec:
+    hostnames:
+    - gateway.envoyproxy.io
+    parentRefs:
+    - name: gateway-1
+      namespace: envoy-gateway
+      sectionName: http
+    - name: gateway-2
+      namespace: envoy-gateway
+      sectionName: http
+    rules:
+    - backendRefs:
+      - name: service-1
+        port: 8080
+      matches:
+      - path:
+          value: /
+  status:
+    parents:
+    - conditions:
+      - lastTransitionTime: null
+        message: Route is accepted
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Resolved all the Object references for the Route
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+      parentRef:
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+    - conditions:
+      - lastTransitionTime: null
+        message: Route is accepted
+        reason: Accepted
+        status: "True"
+        type: Accepted
+      - lastTransitionTime: null
+        message: Resolved all the Object references for the Route
+        reason: ResolvedRefs
+        status: "True"
+        type: ResolvedRefs
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+      parentRef:
+        name: gateway-2
+        namespace: envoy-gateway
+        sectionName: http
+infraIR:
+  envoy-gateway/gateway-1:
+    proxy:
+      listeners:
+      - address: null
+        name: envoy-gateway/gateway-1/http
+        ports:
+        - containerPort: 10080
+          name: http-80
+          protocol: HTTP
+          servicePort: 80
+      metadata:
+        labels:
+          gateway.envoyproxy.io/owning-gateway-name: gateway-1
+          gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+      name: envoy-gateway/gateway-1
+      namespace: envoy-gateway-system
+  envoy-gateway/gateway-2:
+    proxy:
+      listeners:
+      - address: null
+        name: envoy-gateway/gateway-2/http
+        ports:
+        - containerPort: 10080
+          name: http-80
+          protocol: HTTP
+          servicePort: 80
+      metadata:
+        labels:
+          gateway.envoyproxy.io/owning-gateway-name: gateway-2
+          gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+      name: envoy-gateway/gateway-2
+      namespace: envoy-gateway-system
+xdsIR:
+  envoy-gateway/gateway-1:
+    accessLog:
+      json:
+      - path: /dev/stdout
+    http:
+    - address: 0.0.0.0
+      hostnames:
+      - '*'
+      isHTTP2: false
+      metadata:
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+      name: envoy-gateway/gateway-1/http
+      path:
+        escapedSlashesAction: UnescapeAndRedirect
+        mergeSlashes: true
+      port: 10080
+      routes:
+      - destination:
+          name: httproute/default/httproute-1/rule/0
+          settings:
+          - addressType: IP
+            endpoints:
+            - host: 7.7.7.7
+              port: 8080
+            name: httproute/default/httproute-1/rule/0/backend/0
+            protocol: HTTP
+            weight: 1
+        hostname: gateway.envoyproxy.io
+        isHTTP2: false
+        metadata:
+          kind: HTTPRoute
+          name: httproute-1
+          namespace: default
+        name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
+        pathMatch:
+          distinct: false
+          name: ""
+          prefix: /
+        traffic:
+          backendConnection:
+            bufferLimit: 100000000
+          httpUpgrade:
+          - spdy/3.1
+          - websocket
+          name: default/policy-for-route
+          timeout:
+            http:
+              connectionIdleTimeout: 16s
+              maxConnectionDuration: 17s
+            tcp:
+              connectTimeout: 10s
+    readyListener:
+      address: 0.0.0.0
+      ipFamily: IPv4
+      path: /ready
+      port: 19003
+  envoy-gateway/gateway-2:
+    accessLog:
+      json:
+      - path: /dev/stdout
+    http:
+    - address: 0.0.0.0
+      hostnames:
+      - '*'
+      isHTTP2: false
+      metadata:
+        kind: Gateway
+        name: gateway-2
+        namespace: envoy-gateway
+        sectionName: http
+      name: envoy-gateway/gateway-2/http
+      path:
+        escapedSlashesAction: UnescapeAndRedirect
+        mergeSlashes: true
+      port: 10080
+      routes:
+      - destination:
+          name: httproute/default/httproute-1/rule/0
+          settings:
+          - addressType: IP
+            endpoints:
+            - host: 7.7.7.7
+              port: 8080
+            name: httproute/default/httproute-1/rule/0/backend/0
+            protocol: HTTP
+            weight: 1
+        hostname: gateway.envoyproxy.io
+        isHTTP2: false
+        metadata:
+          kind: HTTPRoute
+          name: httproute-1
+          namespace: default
+        name: httproute/default/httproute-1/rule/0/match/0/gateway_envoyproxy_io
+        pathMatch:
+          distinct: false
+          name: ""
+          prefix: /
+        traffic:
+          backendConnection:
+            bufferLimit: 100000000
+          httpUpgrade:
+          - spdy/3.1
+          name: default/policy-for-route
+          timeout:
+            tcp:
+              connectTimeout: 10s
+    readyListener:
+      address: 0.0.0.0
+      ipFamily: IPv4
+      path: /ready
+      port: 19003
diff --git a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge.out.yaml b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge.out.yaml
index 1d876fb04..8905ab639 100644
--- a/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge.out.yaml
+++ b/internal/gatewayapi/testdata/backendtrafficpolicy-strategic-merge.out.yaml
@@ -25,13 +25,20 @@ backendTrafficPolicies:
         kind: Gateway
         name: gateway-1
         namespace: envoy-gateway
-        sectionName: http
       conditions:
       - lastTransitionTime: null
         message: Merged with policy envoy-gateway/policy-for-gateway
         reason: Merged
         status: "True"
         type: Merged
+      controllerName: gateway.envoyproxy.io/gatewayclass-controller
+    - ancestorRef:
+        group: gateway.networking.k8s.io
+        kind: Gateway
+        name: gateway-1
+        namespace: envoy-gateway
+        sectionName: http
+      conditions:
       - lastTransitionTime: null
         message: Policy has been accepted.
         reason: Accepted
diff --git a/test/e2e/tests/ext_auth_grpc_service.go b/test/e2e/tests/ext_auth_grpc_service.go
index 8f846cb50..7c6ec207a 100644
--- a/test/e2e/tests/ext_auth_grpc_service.go
+++ b/test/e2e/tests/ext_auth_grpc_service.go
@@ -86,15 +86,7 @@ var GRPCExtAuthTest = suite.ConformanceTest{
 				Namespace: ns,
 			}
 
-			req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http")
-			cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req)
-			if err != nil {
-				t.Errorf("failed to get expected response: %v", err)
-			}
-
-			if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil {
-				t.Errorf("failed to compare request and response: %v", err)
-			}
+			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse)
 		})
 
 		t.Run("invalid credential", func(t *testing.T) {
@@ -112,15 +104,7 @@ var GRPCExtAuthTest = suite.ConformanceTest{
 				Namespace: ns,
 			}
 
-			req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http")
-			cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req)
-			if err != nil {
-				t.Errorf("failed to get expected response: %v", err)
-			}
-
-			if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil {
-				t.Errorf("failed to compare request and response: %v", err)
-			}
+			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse)
 		})
 
 		t.Run("http route without ext auth authentication", func(t *testing.T) {
@@ -135,15 +119,7 @@ var GRPCExtAuthTest = suite.ConformanceTest{
 				Namespace: ns,
 			}
 
-			req := http.MakeRequest(t, &expectedResponse, gwAddr, "HTTP", "http")
-			cReq, cResp, err := suite.RoundTripper.CaptureRoundTrip(req)
-			if err != nil {
-				t.Errorf("failed to get expected response: %v", err)
-			}
-
-			if err := http.CompareRequest(t, &req, cReq, cResp, expectedResponse); err != nil {
-				t.Errorf("failed to compare request and response: %v", err)
-			}
+			http.MakeRequestAndExpectEventuallyConsistentResponse(t, suite.RoundTripper, suite.TimeoutConfig, gwAddr, expectedResponse)
 		})
 	},
 }