From 52e61a202f7ed3f55f40691512bfd4a2bd80fa28 Mon Sep 17 00:00:00 2001 From: Raghav Maheshwari Date: Fri, 10 Apr 2026 01:54:00 +0530 Subject: [PATCH 1/3] KNOX-3296: Expand CI integration tests: health metrics, HSTS, and KnoxLDAP auth paths --- .github/workflows/tests/test_health.py | 24 +++++++++++++++++++ .../tests/test_knoxauth_preauth_and_paths.py | 21 +++++++++++++++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests/test_health.py b/.github/workflows/tests/test_health.py index 18acc309b1..0ab67bcf60 100644 --- a/.github/workflows/tests/test_health.py +++ b/.github/workflows/tests/test_health.py @@ -53,6 +53,30 @@ def test_health_metrics_returns_json(self): payload = json.loads(response.text) self.assertIsInstance(payload, dict) + def test_health_metrics_contains_core_fields(self): + """Metrics JSON should expose the same top-level keys as the Java GatewayHealthFuncTest.""" + url = self.base_url + "gateway/health/v1/metrics?pretty=true" + response = knox_get(url) + self.assertEqual(response.status_code, 200) + payload = json.loads(response.text) + expected = {"timers", "histograms", "counters", "gauges", "version", "meters"} + self.assertTrue(expected.issubset(payload.keys()), msg=f"Missing keys: {expected - set(payload.keys())}") + + def test_health_metrics_without_pretty_returns_json(self): + url = self.base_url + "gateway/health/v1/metrics" + response = knox_get(url) + self.assertEqual(response.status_code, 200) + self.assertIn("application/json", response.headers.get("Content-Type", "")) + payload = json.loads(response.text) + self.assertIsInstance(payload, dict) + + def test_health_ping_content_type_is_plain_text(self): + url = self.base_url + "gateway/health/v1/ping" + response = knox_get(url) + self.assertEqual(response.status_code, 200) + content_type = response.headers.get("Content-Type", "") + self.assertIn("text/plain", content_type) + if __name__ == '__main__': unittest.main() diff --git a/.github/workflows/tests/test_knoxauth_preauth_and_paths.py b/.github/workflows/tests/test_knoxauth_preauth_and_paths.py index ce9b2f9e75..76b4d83f1b 100644 --- a/.github/workflows/tests/test_knoxauth_preauth_and_paths.py +++ b/.github/workflows/tests/test_knoxauth_preauth_and_paths.py @@ -16,7 +16,7 @@ from requests.auth import HTTPBasicAuth -from common_utils import gateway_base_url, knox_get, knox_post +from common_utils import collect_actor_group_values, gateway_base_url, knox_get, knox_post class TestKnoxAuthServicePreAuthAndPaths(unittest.TestCase): @@ -47,6 +47,25 @@ def test_preauth_post_supported(self): self.assertIn(actor_id_header, response.headers) self.assertEqual(response.headers[actor_id_header], "guest") + def test_preauth_get_with_guest_credentials(self): + response = knox_get( + self.preauth_url, + auth=HTTPBasicAuth("guest", "guest-password"), + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.headers.get("x-knox-actor-username"), "guest") + + def test_preauth_get_with_admin_includes_mapped_groups(self): + response = knox_get( + self.preauth_url, + auth=HTTPBasicAuth("admin", "admin-password"), + ) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.headers.get("x-knox-actor-username"), "admin") + groups = collect_actor_group_values(response, prefix="x-knox-actor-groups") + for name in ("longGroupName1", "longGroupName2"): + self.assertIn(name, groups) + def test_extauthz_additional_path_not_ignored_in_knoxldap(self): response = knox_get( self.extauthz_url + "/does-not-exist", From 2b2fda9a767d9f1fedf96920035146cf4df488b1 Mon Sep 17 00:00:00 2001 From: Raghav Maheshwari Date: Fri, 10 Apr 2026 01:58:04 +0530 Subject: [PATCH 2/3] KNOX-3296: Added test descriptions --- .github/workflows/tests/test_health.py | 9 ++++++--- .../workflows/tests/test_knoxauth_preauth_and_paths.py | 8 ++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests/test_health.py b/.github/workflows/tests/test_health.py index 0ab67bcf60..236fc70db3 100644 --- a/.github/workflows/tests/test_health.py +++ b/.github/workflows/tests/test_health.py @@ -21,13 +21,13 @@ class TestKnoxHealth(unittest.TestCase): + """Integration checks for the gateway health REST API (ping and metrics).""" + def setUp(self): self.base_url = gateway_base_url() def test_health_ping_ok_and_hsts(self): - """ - Basic health check to ensure Knox is up and running. - """ + """Ping returns 200, body OK, and the gateway sends the expected HSTS header.""" url = self.base_url + "gateway/health/v1/ping" print(f"Checking connectivity to {url}...") try: @@ -43,6 +43,7 @@ def test_health_ping_ok_and_hsts(self): self.fail(f"Health check failed with unexpected error: {e}") def test_health_metrics_returns_json(self): + """Metrics with pretty=true returns 200 and a JSON object with application/json content type.""" url = self.base_url + "gateway/health/v1/metrics?pretty=true" response = knox_get(url) self.assertEqual(response.status_code, 200) @@ -63,6 +64,7 @@ def test_health_metrics_contains_core_fields(self): self.assertTrue(expected.issubset(payload.keys()), msg=f"Missing keys: {expected - set(payload.keys())}") def test_health_metrics_without_pretty_returns_json(self): + """Metrics without pretty still returns 200 and parseable JSON.""" url = self.base_url + "gateway/health/v1/metrics" response = knox_get(url) self.assertEqual(response.status_code, 200) @@ -71,6 +73,7 @@ def test_health_metrics_without_pretty_returns_json(self): self.assertIsInstance(payload, dict) def test_health_ping_content_type_is_plain_text(self): + """Ping response declares text/plain Content-Type.""" url = self.base_url + "gateway/health/v1/ping" response = knox_get(url) self.assertEqual(response.status_code, 200) diff --git a/.github/workflows/tests/test_knoxauth_preauth_and_paths.py b/.github/workflows/tests/test_knoxauth_preauth_and_paths.py index 76b4d83f1b..45d51a5c82 100644 --- a/.github/workflows/tests/test_knoxauth_preauth_and_paths.py +++ b/.github/workflows/tests/test_knoxauth_preauth_and_paths.py @@ -20,16 +20,20 @@ class TestKnoxAuthServicePreAuthAndPaths(unittest.TestCase): + """KnoxLDAP auth service: preauth behavior and extauthz path handling.""" + def setUp(self): self.base_url = gateway_base_url() self.preauth_url = self.base_url + "gateway/knoxldap/auth/api/v1/pre" self.extauthz_url = self.base_url + "gateway/knoxldap/auth/api/v1/extauthz" def test_preauth_requires_auth(self): + """Preauth without credentials must return 401.""" response = knox_get(self.preauth_url) self.assertEqual(response.status_code, 401) def test_preauth_bad_credentials_unauthorized(self): + """Invalid Basic auth must return 401.""" response = knox_get( self.preauth_url, auth=HTTPBasicAuth("baduser", "badpass"), @@ -37,6 +41,7 @@ def test_preauth_bad_credentials_unauthorized(self): self.assertEqual(response.status_code, 401) def test_preauth_post_supported(self): + """POST preauth with guest credentials returns 200 and x-knox-actor-username.""" response = knox_post( self.preauth_url, auth=HTTPBasicAuth("guest", "guest-password"), @@ -48,6 +53,7 @@ def test_preauth_post_supported(self): self.assertEqual(response.headers[actor_id_header], "guest") def test_preauth_get_with_guest_credentials(self): + """GET preauth with guest returns 200 and actor username guest.""" response = knox_get( self.preauth_url, auth=HTTPBasicAuth("guest", "guest-password"), @@ -56,6 +62,7 @@ def test_preauth_get_with_guest_credentials(self): self.assertEqual(response.headers.get("x-knox-actor-username"), "guest") def test_preauth_get_with_admin_includes_mapped_groups(self): + """GET preauth with admin returns mapped LDAP groups in x-knox-actor-groups* headers.""" response = knox_get( self.preauth_url, auth=HTTPBasicAuth("admin", "admin-password"), @@ -67,6 +74,7 @@ def test_preauth_get_with_admin_includes_mapped_groups(self): self.assertIn(name, groups) def test_extauthz_additional_path_not_ignored_in_knoxldap(self): + """Unknown path under extauthz returns 404; extra segments are not ignored as success.""" response = knox_get( self.extauthz_url + "/does-not-exist", auth=HTTPBasicAuth("guest", "guest-password"), From 2d7ee8e4f32a5a8ad9034a9436c1569f007f01bf Mon Sep 17 00:00:00 2001 From: Raghav Maheshwari Date: Fri, 10 Apr 2026 20:07:54 +0530 Subject: [PATCH 3/3] Address review feedback on workflow integration tests --- .github/workflows/tests/test_health.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.github/workflows/tests/test_health.py b/.github/workflows/tests/test_health.py index 236fc70db3..a62e2f2bd5 100644 --- a/.github/workflows/tests/test_health.py +++ b/.github/workflows/tests/test_health.py @@ -23,6 +23,11 @@ class TestKnoxHealth(unittest.TestCase): """Integration checks for the gateway health REST API (ping and metrics).""" + # Top-level keys expected in /metrics JSON (aligned with Java GatewayHealthFuncTest). + _METRICS_TOP_LEVEL_KEYS = frozenset( + {"timers", "histograms", "counters", "gauges", "version", "meters"} + ) + def setUp(self): self.base_url = gateway_base_url() @@ -60,23 +65,30 @@ def test_health_metrics_contains_core_fields(self): response = knox_get(url) self.assertEqual(response.status_code, 200) payload = json.loads(response.text) - expected = {"timers", "histograms", "counters", "gauges", "version", "meters"} - self.assertTrue(expected.issubset(payload.keys()), msg=f"Missing keys: {expected - set(payload.keys())}") + self.assertTrue( + self._METRICS_TOP_LEVEL_KEYS.issubset(payload.keys()), + msg=f"Missing keys: {self._METRICS_TOP_LEVEL_KEYS - set(payload.keys())}", + ) def test_health_metrics_without_pretty_returns_json(self): - """Metrics without pretty still returns 200 and parseable JSON.""" + """Metrics without pretty still returns 200, parseable JSON, and the same top-level keys as pretty.""" url = self.base_url + "gateway/health/v1/metrics" response = knox_get(url) self.assertEqual(response.status_code, 200) self.assertIn("application/json", response.headers.get("Content-Type", "")) payload = json.loads(response.text) self.assertIsInstance(payload, dict) + self.assertTrue( + self._METRICS_TOP_LEVEL_KEYS.issubset(payload.keys()), + msg=f"Missing keys: {self._METRICS_TOP_LEVEL_KEYS - set(payload.keys())}", + ) def test_health_ping_content_type_is_plain_text(self): - """Ping response declares text/plain Content-Type.""" + """Ping response declares text/plain Content-Type and body OK.""" url = self.base_url + "gateway/health/v1/ping" response = knox_get(url) self.assertEqual(response.status_code, 200) + self.assertEqual(response.text.strip(), "OK") content_type = response.headers.get("Content-Type", "") self.assertIn("text/plain", content_type)