From 9098d56975236fbeea08466fca73731b4f90401f Mon Sep 17 00:00:00 2001 From: Tiago Vier Preto Date: Mon, 20 Apr 2026 14:07:12 -0300 Subject: [PATCH 1/4] feat: Add tests for named HTTPRouteRule sectionName targeting Signed-off-by: Tiago Vier Preto Signed-off-by: Tiago Vier Preto <83984051+Tiago-Vier-Preto@users.noreply.github.com> --- testsuite/gateway/gateway_api/route.py | 15 +++-- .../test_authpolicy_named_http_route_rule.py | 57 +++++++++++++++++++ .../section/test_named_route_rule.py | 34 +++++++++++ 3 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 testsuite/tests/singlecluster/gateway/authpolicy/test_authpolicy_named_http_route_rule.py create mode 100644 testsuite/tests/singlecluster/limitador/section/test_named_route_rule.py diff --git a/testsuite/gateway/gateway_api/route.py b/testsuite/gateway/gateway_api/route.py index ddb024346..66e827de1 100644 --- a/testsuite/gateway/gateway_api/route.py +++ b/testsuite/gateway/gateway_api/route.py @@ -90,7 +90,9 @@ def remove_all_hostnames(self): self.model.spec.hostnames = [] @modify - def add_rule(self, backend: "Backend", *route_matches: RouteMatch, filters: list[URLRewriteFilter] = None): + def add_rule( + self, backend: "Backend", *route_matches: RouteMatch, filters: list[URLRewriteFilter] = None, name: str = None + ): """Adds rule to the Route""" rules: dict[str, typing.Any] = {"backendRefs": [backend.reference]} matches = list(route_matches) @@ -100,6 +102,8 @@ def add_rule(self, backend: "Backend", *route_matches: RouteMatch, filters: list rules["matches"] = [asdict(match) for match in matches] if filters: rules["filters"] = [asdict(f) for f in filters] + if name: + rules["name"] = name self.model.spec.rules.append(rules) @modify @@ -108,10 +112,11 @@ def remove_all_rules(self): self.model.spec.rules = [] @modify - def add_backend(self, backend: "Backend", prefix="/"): - self.model.spec.rules.append( - {"backendRefs": [backend.reference], "matches": [{"path": {"value": prefix, "type": "PathPrefix"}}]} - ) + def add_backend(self, backend: "Backend", prefix="/", name: str = None): + rule = {"backendRefs": [backend.reference], "matches": [{"path": {"value": prefix, "type": "PathPrefix"}}]} + if name: + rule["name"] = name + self.model.spec.rules.append(rule) @modify def remove_all_backend(self): diff --git a/testsuite/tests/singlecluster/gateway/authpolicy/test_authpolicy_named_http_route_rule.py b/testsuite/tests/singlecluster/gateway/authpolicy/test_authpolicy_named_http_route_rule.py new file mode 100644 index 000000000..f839a59d7 --- /dev/null +++ b/testsuite/tests/singlecluster/gateway/authpolicy/test_authpolicy_named_http_route_rule.py @@ -0,0 +1,57 @@ +""" +Tests that an AuthPolicy is correctly applied to a specific named rule section of +an HTTPRoute, protecting only the traffic handled by that named rule. +""" + +import pytest +from testsuite.kuadrant.policy.authorization.auth_policy import AuthPolicy + +pytestmark = [pytest.mark.authorino, pytest.mark.kuadrant_only] + + +@pytest.fixture(scope="module") +def route(route, backend): + """ + Overrides the default route to have two paths: one for a protected + service (/get) and one for a public one (/anything). + Rules are explicitly named. + """ + route.remove_all_rules() + route.add_backend(backend, "/get", name="get-rule") # This becomes the target "get-rule" + route.add_backend(backend, "/anything", name="anything-rule") # This is the public rule + return route + + +@pytest.fixture(scope="module") +def authorization(cluster, blame, module_label, oidc_provider, route): + """ + Creates an AuthPolicy that targets a specific named rule ('get-rule') within the + HTTPRoute. + """ + policy = AuthPolicy.create_instance( + cluster, + blame("authz"), + route, # Target is the HTTPRoute + section_name="get-rule", # Target the specific named rule + labels={"testRun": module_label}, + ) + policy.identity.add_oidc("basic", oidc_provider.well_known["issuer"]) + return policy + + +def test_authpolicy_section_name_targeting_named_http_route_rule(client, auth): + """ + Tests that an AuthPolicy attached to a specific explicitly named HTTPRoute rule protects + only the requests handled by that rule. + """ + # The '/anything' path is handled by a different, untargeted rule and should be public. + response = client.get("/anything") + assert response.status_code == 200 + + # The '/get' path is handled by the targeted 'get-rule' and should require authentication. + response = client.get("/get") + assert response.status_code == 401 + + # The '/get' path with a valid token should be allowed. + response = client.get("/get", auth=auth) + assert response.status_code == 200 diff --git a/testsuite/tests/singlecluster/limitador/section/test_named_route_rule.py b/testsuite/tests/singlecluster/limitador/section/test_named_route_rule.py new file mode 100644 index 000000000..fe95c0ed8 --- /dev/null +++ b/testsuite/tests/singlecluster/limitador/section/test_named_route_rule.py @@ -0,0 +1,34 @@ +"""Tests that the RLP is correctly applied to the specific named route rule""" + +import pytest + +from testsuite.kuadrant.policy.rate_limit import Limit, RateLimitPolicy + +pytestmark = [pytest.mark.limitador] + + +@pytest.fixture(scope="module") +def route(route, backend): + """Add two named backend rules for different paths to the route""" + route.remove_all_rules() + route.add_backend(backend, "/get", name="get-rule") + route.add_backend(backend, "/anything", name="anything-rule") + return route + + +@pytest.fixture(scope="module") +def rate_limit(cluster, blame, module_label, route): + """Add a RateLimitPolicy targeting the get-rule HTTPRoute Rule.""" + rlp = RateLimitPolicy.create_instance(cluster, blame("limit"), route, "get-rule", labels={"testRun": module_label}) + rlp.add_limit("basic", [Limit(2, "10s")]) + return rlp + + +def test_limit_match_named_route_rule(client): + """Tests that RLP correctly applies to the specific named HTTPRoute Rule""" + responses = client.get_many("/get", 2) + responses.assert_all(status_code=200) + assert client.get("/get").status_code == 429 + + response = client.get("/anything") + assert response.status_code == 200 \ No newline at end of file From d102af9f5552a2fa50289ab2cf72f00971aeff8f Mon Sep 17 00:00:00 2001 From: Tiago Vier Preto <83984051+Tiago-Vier-Preto@users.noreply.github.com> Date: Fri, 24 Apr 2026 14:16:37 +0000 Subject: [PATCH 2/4] fix format issues and add global variable Signed-off-by: Tiago Vier Preto <83984051+Tiago-Vier-Preto@users.noreply.github.com> --- .../limitador/section/test_named_route_rule.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/testsuite/tests/singlecluster/limitador/section/test_named_route_rule.py b/testsuite/tests/singlecluster/limitador/section/test_named_route_rule.py index fe95c0ed8..024ace2f1 100644 --- a/testsuite/tests/singlecluster/limitador/section/test_named_route_rule.py +++ b/testsuite/tests/singlecluster/limitador/section/test_named_route_rule.py @@ -6,6 +6,8 @@ pytestmark = [pytest.mark.limitador] +LIMIT = Limit(2, "10s") + @pytest.fixture(scope="module") def route(route, backend): @@ -20,15 +22,15 @@ def route(route, backend): def rate_limit(cluster, blame, module_label, route): """Add a RateLimitPolicy targeting the get-rule HTTPRoute Rule.""" rlp = RateLimitPolicy.create_instance(cluster, blame("limit"), route, "get-rule", labels={"testRun": module_label}) - rlp.add_limit("basic", [Limit(2, "10s")]) + rlp.add_limit("basic", [LIMIT]) return rlp def test_limit_match_named_route_rule(client): """Tests that RLP correctly applies to the specific named HTTPRoute Rule""" - responses = client.get_many("/get", 2) + responses = client.get_many("/get", LIMIT.limit) responses.assert_all(status_code=200) assert client.get("/get").status_code == 429 response = client.get("/anything") - assert response.status_code == 200 \ No newline at end of file + assert response.status_code == 200 From 7b0c23b520dd61fe6d6ed379d269443039945258 Mon Sep 17 00:00:00 2001 From: Tiago Vier Preto <83984051+Tiago-Vier-Preto@users.noreply.github.com> Date: Tue, 19 May 2026 19:12:00 -0300 Subject: [PATCH 3/4] Update testsuite/gateway/gateway_api/route.py Co-authored-by: Emma Roche Signed-off-by: Tiago Vier Preto <83984051+Tiago-Vier-Preto@users.noreply.github.com> --- testsuite/gateway/gateway_api/route.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testsuite/gateway/gateway_api/route.py b/testsuite/gateway/gateway_api/route.py index 66e827de1..f820048e4 100644 --- a/testsuite/gateway/gateway_api/route.py +++ b/testsuite/gateway/gateway_api/route.py @@ -113,7 +113,7 @@ def remove_all_rules(self): @modify def add_backend(self, backend: "Backend", prefix="/", name: str = None): - rule = {"backendRefs": [backend.reference], "matches": [{"path": {"value": prefix, "type": "PathPrefix"}}]} + rule: dict[str, typing.Any] = {"backendRefs": [backend.reference], "matches": [{"path": {"value": prefix, "type": "PathPrefix"}}]} if name: rule["name"] = name self.model.spec.rules.append(rule) From 3bda78a5e9e52d7687168cea43e9525148c619c3 Mon Sep 17 00:00:00 2001 From: Tiago Vier Preto <83984051+Tiago-Vier-Preto@users.noreply.github.com> Date: Tue, 19 May 2026 22:22:00 +0000 Subject: [PATCH 4/4] fixed lint errors Signed-off-by: Tiago Vier Preto <83984051+Tiago-Vier-Preto@users.noreply.github.com> --- testsuite/gateway/gateway_api/route.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testsuite/gateway/gateway_api/route.py b/testsuite/gateway/gateway_api/route.py index f820048e4..fddfa12fa 100644 --- a/testsuite/gateway/gateway_api/route.py +++ b/testsuite/gateway/gateway_api/route.py @@ -113,7 +113,10 @@ def remove_all_rules(self): @modify def add_backend(self, backend: "Backend", prefix="/", name: str = None): - rule: dict[str, typing.Any] = {"backendRefs": [backend.reference], "matches": [{"path": {"value": prefix, "type": "PathPrefix"}}]} + rule: dict[str, typing.Any] = { + "backendRefs": [backend.reference], + "matches": [{"path": {"value": prefix, "type": "PathPrefix"}}], + } if name: rule["name"] = name self.model.spec.rules.append(rule)