Skip to content

Add guardrails healthcheck to each test before sending out requests#1014

Merged
sheltoncyril merged 7 commits intoopendatahub-io:mainfrom
sheltoncyril:guardrails-healthcheck
Jan 14, 2026
Merged

Add guardrails healthcheck to each test before sending out requests#1014
sheltoncyril merged 7 commits intoopendatahub-io:mainfrom
sheltoncyril:guardrails-healthcheck

Conversation

@sheltoncyril
Copy link
Copy Markdown
Contributor

@sheltoncyril sheltoncyril commented Jan 13, 2026

This PR addresses issues found with some tests where DSC updates were not caught after a patch and also adds a healthcheck of the guardrails resource before sending out requests.

Summary by CodeRabbit

  • Tests

    • Refactored health check tests to use fixture-based approach for improved test organization.
    • Enhanced test reliability with retry logic and better service readiness verification.
  • New Features

    • Added health check functionality with timeout handling and authentication support.
  • Bug Fixes

    • Improved validation error handling with explicit null checks.
  • Chores

    • Updated container image tags for inference services.

✏️ Tip: You can customize this high-level summary in your review settings.

@github-actions
Copy link
Copy Markdown

The following are automatically added/executed:

  • PR size label.
  • Run pre-commit
  • Run tox
  • Add PR author as the PR assignee
  • Build image based on the PR

Available user actions:

  • To mark a PR as WIP, add /wip in a comment. To remove it from the PR comment /wip cancel to the PR.
  • To block merging of a PR, add /hold in a comment. To un-block merging of PR comment /hold cancel.
  • To mark a PR as approved, add /lgtm in a comment. To remove, add /lgtm cancel.
    lgtm label removed on each new commit push.
  • To mark PR as verified comment /verified to the PR, to un-verify comment /verified cancel to the PR.
    verified label removed on each new commit push.
  • To Cherry-pick a merged PR /cherry-pick <target_branch_name> to the PR. If <target_branch_name> is valid,
    and the current PR is merged, a cherry-picked PR would be created and linked to the current PR.
  • To build and push image to quay, add /build-push-pr-image in a comment. This would create an image with tag
    pr-<pr_number> to quay repository. This image tag, however would be deleted on PR merge or close action.
Supported labels

{'/cherry-pick', '/verified', '/hold', '/lgtm', '/build-push-pr-image', '/wip'}

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 13, 2026

📝 Walkthrough

Walkthrough

Adds a centralized Guardrails health-check utility with retry and auth headers, a pytest fixture that invokes it, refactors tests to use the fixture instead of explicit health-endpoint tests, updates detector utils error handling, and modifies an inference fixture to conditionally enable DSC "Headed" mode with guarded readiness waiting.

Changes

Cohort / File(s) Summary
Guardrails utility
utilities/guardrails.py
New module: get_auth_headers(token) and check_guardrails_health_endpoint(host, token, ca_bundle_file) with @retry (wait_timeout=10, sleep=2) performing HTTPS GET /health with CA bundle verification.
Fixtures
tests/fixtures/guardrails.py, tests/fixtures/inference.py
Added guardrails_healthcheck fixture calling centralized health check. tests/fixtures/inference.py: added logger and retry helpers; llm_d_inference_sim_isvc now depends on patched_dsc_kserve_headed; patched_dsc_kserve_headed conditionally patches DSC to "Headed" and waits for readiness with guarded retries.
Guardrails tests
tests/model_explainability/guardrails/test_guardrails.py
Removed explicit health-endpoint test methods; many test methods now accept the guardrails_healthcheck fixture instead of calling health helpers directly.
Guardrails test utils
tests/model_explainability/guardrails/utils.py
Removed local get_auth_headers() and check_guardrails_health_endpoint(); tightened verify_builtin_detector_unsuitable_input_response checks for None/empty warnings (raises UnexpectedValueError), and increased retry sleep in send_and_verify_unsuitable_input_detection to 4s.
Exceptions
utilities/exceptions.py
Added new UnexpectedValueError exception class with docstring.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.35% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding guardrails healthcheck before test requests. It directly reflects the PR's primary objective visible in the raw summary.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

🧹 Recent nitpick comments
tests/model_explainability/guardrails/utils.py (1)

130-141: Good fix for None handling, but apply consistently to output response function.

The None handling logic for warnings is correct here. However, verify_builtin_detector_unsuitable_output_response at line 176 still uses .get("warnings", []), which returns None (not []) when the key exists with a None value, causing len(None) to raise TypeError.

Consider applying the same defensive check to the output response function for consistency.

♻️ Suggested fix for verify_builtin_detector_unsuitable_output_response
     unsuitable_output_warning = "UNSUITABLE_OUTPUT"
-    warnings = response_data.get("warnings", [])
+    warnings = response_data.get("warnings")
+    if warnings is None:
+        raise UnexpectedValueError("Expected warnings in response, got None")
     if len(warnings) != 1:
tests/fixtures/inference.py (1)

222-234: Consider using != for clearer intent.

The condition not ... == "Headed" is less readable than != "Headed".

♻️ Suggested change
-    if not dsc.instance.spec.components.kserve.rawDeploymentServiceConfig == "Headed":
+    if dsc.instance.spec.components.kserve.rawDeploymentServiceConfig != "Headed":

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f782a92 and 0eda77f.

📒 Files selected for processing (3)
  • tests/fixtures/inference.py
  • tests/model_explainability/guardrails/utils.py
  • utilities/exceptions.py
🧰 Additional context used
🧬 Code graph analysis (2)
tests/fixtures/inference.py (2)
utilities/infra.py (2)
  • get_data_science_cluster (883-884)
  • wait_for_dsc_status_ready (1063-1069)
tests/conftest.py (2)
  • dsc_resource (491-492)
  • admin_client (79-80)
tests/model_explainability/guardrails/utils.py (2)
utilities/exceptions.py (1)
  • UnexpectedValueError (141-142)
utilities/guardrails.py (1)
  • get_auth_headers (8-9)
🪛 Ruff (0.14.11)
tests/fixtures/inference.py

173-173: Unused function argument: patched_dsc_kserve_headed

(ARG001)

tests/model_explainability/guardrails/utils.py

131-131: Avoid specifying long messages outside the exception class

(TRY003)


137-137: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (6)
utilities/exceptions.py (1)

139-142: LGTM!

The new UnexpectedValueError exception follows the established pattern in this module and provides a clear semantic for validation failures.

tests/model_explainability/guardrails/utils.py (2)

11-12: LGTM!

Imports correctly reference the refactored utilities from utilities.guardrails and utilities.exceptions.


293-293: LGTM!

Increased retry sleep to 4 seconds. This gives the service more time to stabilize between retries for input detection.

tests/fixtures/inference.py (3)

15-30: LGTM!

Clean setup of module-level logger and necessary imports for the retry and DSC utilities.


138-139: LGTM!

Container image updated to new version with built-in dataset.


168-174: Fixture dependency is intentional.

The patched_dsc_kserve_headed parameter is a pytest fixture dependency that ensures the DSC is configured in Headed mode before this inference service is created. The ARG001 static analysis warning is a false positive in this context.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In @tests/fixtures/guardrails.py:
- Around line 164-172: The fixture guardrails_healthcheck ignores the boolean
returned by check_guardrails_health_endpoint so failures are silent; change the
fixture to assert the return value (e.g., assert
check_guardrails_health_endpoint(...) , "Guardrails healthcheck failed") or
raise a RuntimeError when it returns False, and also make the fixture scope
consistent by adding scope="class" to the @pytest.fixture decorator (unless the
per-test execution is intentional—if so, add a comment explaining why). Ensure
you reference guardrails_healthcheck and check_guardrails_health_endpoint when
making these changes.

In @tests/fixtures/inference.py:
- Line 212: The line using time.sleep(20) contains an invalid noqa directive "#
noqa: FCN001"; remove this invalid directive and either leave no noqa or replace
it with the correct linter code if you intentionally want to silence a
security/time-sleep warning (e.g., use Bandit's S113) — update the statement in
tests/fixtures/inference.py where time.sleep is called (the time.sleep(20)
invocation) accordingly so the file no longer references the unknown FCN001
code.

In @utilities/guardrails.py:
- Around line 12-19: Add a timeout to the requests.get call in
check_guardrails_health_endpoint and make non-200 responses raise a TimeoutError
so the @retry decorator can trigger; specifically update
check_guardrails_health_endpoint to call requests.get(..., timeout=some_seconds)
and if response.status_code is not http.HTTPStatus.OK raise a TimeoutError
(including response details) instead of returning False so transient failures
and hangs are retried.
🧹 Nitpick comments (1)
tests/model_explainability/guardrails/utils.py (1)

287-287: Consider documenting or aligning the sleep values across similar retry functions.

The sleep parameter for send_and_verify_unsuitable_input_detection was increased to 4 seconds, while similar functions (send_and_verify_unsuitable_output_detection, send_and_verify_negative_detection, send_and_verify_standalone_detection) retain sleep=1. If this difference is intentional due to different endpoint characteristics, consider adding a brief comment explaining the rationale.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c4fef85 and 64ff51b.

📒 Files selected for processing (5)
  • tests/fixtures/guardrails.py
  • tests/fixtures/inference.py
  • tests/model_explainability/guardrails/test_guardrails.py
  • tests/model_explainability/guardrails/utils.py
  • utilities/guardrails.py
🧰 Additional context used
🧬 Code graph analysis (3)
tests/model_explainability/guardrails/utils.py (1)
utilities/guardrails.py (1)
  • get_auth_headers (8-9)
tests/fixtures/inference.py (2)
utilities/infra.py (2)
  • get_data_science_cluster (883-884)
  • wait_for_dsc_status_ready (1063-1069)
tests/conftest.py (1)
  • dsc_resource (491-492)
tests/fixtures/guardrails.py (3)
utilities/guardrails.py (1)
  • check_guardrails_health_endpoint (13-19)
tests/conftest.py (1)
  • current_client_token (93-94)
tests/model_explainability/guardrails/conftest.py (1)
  • openshift_ca_bundle_file (105-108)
🪛 Ruff (0.14.10)
utilities/guardrails.py

18-18: Probable use of requests call without timeout

(S113)

tests/fixtures/inference.py

172-172: Unused function argument: patched_dsc_kserve_headed

(ARG001)


212-212: Unused noqa directive (unknown: FCN001)

Remove unused noqa directive

(RUF100)

tests/model_explainability/guardrails/test_guardrails.py

146-146: Unused method argument: guardrails_healthcheck

(ARG002)


161-161: Unused method argument: guardrails_healthcheck

(ARG002)


178-178: Unused method argument: guardrails_healthcheck

(ARG002)


208-208: Unused method argument: guardrails_healthcheck

(ARG002)


322-322: Unused method argument: guardrails_healthcheck

(ARG002)


345-345: Unused method argument: guardrails_healthcheck

(ARG002)


366-366: Unused method argument: guardrails_healthcheck

(ARG002)


387-387: Unused method argument: guardrails_healthcheck

(ARG002)


438-438: Unused method argument: guardrails_healthcheck

(ARG002)


452-452: Unused method argument: guardrails_healthcheck

(ARG002)


470-470: Unused method argument: guardrails_healthcheck

(ARG002)


512-512: Unused method argument: hap_detector_isvc

(ARG002)


513-513: Unused method argument: prompt_injection_detector_isvc

(ARG002)


515-515: Unused method argument: guardrails_healthcheck

(ARG002)


528-528: Unused method argument: prompt_injection_detector_isvc

(ARG002)


529-529: Unused method argument: hap_detector_isvc

(ARG002)


531-531: Unused method argument: guardrails_healthcheck

(ARG002)


558-558: Unused method argument: prompt_injection_detector_isvc

(ARG002)


559-559: Unused method argument: hap_detector_isvc

(ARG002)


564-564: Unused method argument: guardrails_healthcheck

(ARG002)

🔇 Additional comments (7)
utilities/guardrails.py (1)

8-9: LGTM!

Simple and reusable helper for constructing authorization headers.

tests/model_explainability/guardrails/utils.py (2)

10-10: LGTM!

Good refactoring to centralize the get_auth_headers utility in utilities/guardrails.py.


130-135: LGTM!

Good defensive check handling the case where warnings is None instead of an empty list, preventing a TypeError on the subsequent len() call.

tests/fixtures/inference.py (2)

172-172: This unused parameter warning is a false positive.

The patched_dsc_kserve_headed parameter is a pytest fixture dependency that ensures the DSC is configured for headed mode before the InferenceService is created. This is the idiomatic pytest pattern for declaring fixture dependencies.


203-218: No action required. The fixture is correctly scoped at the class level (@pytest.fixture(scope="class")), which means the ResourceEditor patch is applied once per test class and reverted after all tests in that class complete. This is the correct behavior for test isolation and is not a problem.

Likely an incorrect or invalid review comment.

tests/model_explainability/guardrails/test_guardrails.py (2)

139-152: LGTM!

The guardrails_healthcheck fixture is correctly added as a dependency to ensure health verification runs before each test. The fixture parameter being "unused" in the test body is the standard pytest pattern for declaring fixture dependencies.


507-521: LGTM!

The hap_detector_isvc and prompt_injection_detector_isvc fixtures are correctly added as dependencies to ensure the HuggingFace detector services are deployed before the gateway tests run. This is the standard pytest fixture dependency pattern.

kpunwatk
kpunwatk previously approved these changes Jan 13, 2026
Copy link
Copy Markdown
Contributor

@kpunwatk kpunwatk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64ff51b and f782a92.

📒 Files selected for processing (1)
  • tests/fixtures/inference.py
🧰 Additional context used
🧬 Code graph analysis (1)
tests/fixtures/inference.py (2)
utilities/infra.py (2)
  • get_data_science_cluster (883-884)
  • wait_for_dsc_status_ready (1063-1069)
tests/conftest.py (2)
  • dsc_resource (491-492)
  • admin_client (79-80)
🪛 Ruff (0.14.11)
tests/fixtures/inference.py

173-173: Unused function argument: patched_dsc_kserve_headed

(ARG001)

🔇 Additional comments (4)
tests/fixtures/inference.py (4)

15-29: LGTM!

New imports and module-level logger are appropriately added and used within the fixture changes.


138-139: LGTM!

Image pinned by SHA256 digest ensures reproducibility.


168-174: Intentional fixture dependency pattern.

The static analysis warning (ARG001) about patched_dsc_kserve_headed being unused is a false positive. This is a standard pytest pattern where the parameter declares a fixture dependency to ensure DSC is configured in Headed mode before this ISVC is created, even though the return value isn't directly used.


222-233: Good optimization to skip redundant patching.

The guarded flow correctly avoids re-applying the patch when already in Headed mode. The transition time capture before entering ResourceEditor context is the right approach to detect when the patch takes effect.

@sheltoncyril sheltoncyril enabled auto-merge (squash) January 13, 2026 23:46
@sheltoncyril sheltoncyril changed the title feat: add guardrails healthcheck to each endpoint test Add guardrails healthcheck to each test before sending out requests Jan 14, 2026
@sheltoncyril sheltoncyril removed the request for review from adolfo-ab January 14, 2026 10:44
@sheltoncyril
Copy link
Copy Markdown
Contributor Author

/verified

@rhods-ci-bot rhods-ci-bot added the Verified Verified pr in Jenkins label Jan 14, 2026
@sheltoncyril sheltoncyril merged commit 51d01a0 into opendatahub-io:main Jan 14, 2026
8 checks passed
@github-actions
Copy link
Copy Markdown

Status of building tag latest: success.
Status of pushing tag latest to image registry: success.

@sheltoncyril
Copy link
Copy Markdown
Contributor Author

/cherry-pick 3.2

rhods-ci-bot pushed a commit that referenced this pull request Jan 15, 2026
* feat: add guardrails healthcheck to each endpoint test

* feat: remove time.sleep call
@rhods-ci-bot
Copy link
Copy Markdown
Contributor

Cherry pick action created PR #1022 successfully 🎉!
See: https://github.com/opendatahub-io/opendatahub-tests/actions/runs/21025457359

sheltoncyril added a commit that referenced this pull request Jan 15, 2026
* feat: add guardrails healthcheck to each endpoint test

* feat: remove time.sleep call

Co-authored-by: Shelton Cyril <sheltoncyril@gmail.com>
@sheltoncyril
Copy link
Copy Markdown
Contributor Author

/cherry-pick 2.25

@rhods-ci-bot
Copy link
Copy Markdown
Contributor

Error cherry-picking.

Auto-merging tests/fixtures/guardrails.py
Auto-merging tests/fixtures/inference.py
CONFLICT (content): Merge conflict in tests/fixtures/inference.py
Auto-merging tests/model_explainability/guardrails/test_guardrails.py
CONFLICT (content): Merge conflict in tests/model_explainability/guardrails/test_guardrails.py
Auto-merging tests/model_explainability/guardrails/utils.py
CONFLICT (content): Merge conflict in tests/model_explainability/guardrails/utils.py
Auto-merging utilities/exceptions.py
CONFLICT (content): Merge conflict in utilities/exceptions.py
error: could not apply 51d01a0... feat: add guardrails healthcheck to each endpoint test (#1014)
hint: After resolving the conflicts, mark them with
hint: "git add/rm ", then run
hint: "git cherry-pick --continue".
hint: You can instead skip this commit with "git cherry-pick --skip".
hint: To abort and get back to the state before "git cherry-pick",
hint: run "git cherry-pick --abort".
hint: Disable this message with "git config set advice.mergeConflict false"

@rhods-ci-bot
Copy link
Copy Markdown
Contributor

‼️ cherry pick action failed.
See: https://github.com/opendatahub-io/opendatahub-tests/actions/runs/21217691068

mwaykole pushed a commit to mwaykole/opendatahub-tests that referenced this pull request Jan 23, 2026
…o#1014)

* feat: add guardrails healthcheck to each endpoint test

* feat: remove time.sleep call
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants