Skip to content

test: Add deployment tests for registered models#1174

Merged
sheltoncyril merged 6 commits intoopendatahub-io:mainfrom
dbasunag:deployment_test
Mar 6, 2026
Merged

test: Add deployment tests for registered models#1174
sheltoncyril merged 6 commits intoopendatahub-io:mainfrom
dbasunag:deployment_test

Conversation

@dbasunag
Copy link
Copy Markdown
Collaborator

@dbasunag dbasunag commented Mar 5, 2026

Pull Request

Summary

Related Issues

  • Fixes:
  • JIRA:

How it has been tested

  • Locally
  • Jenkins

Additional Requirements

  • If this PR introduces a new test image, did you create a PR to mirror it in disconnected environment?
  • If this PR introduces new marker(s)/adds a new component, was relevant ticket created to update relevant Jenkins job?

Summary by CodeRabbit

  • Tests

    • Added comprehensive end-to-end tests for Model Registry deployments using the OpenVINO runtime, including a deployment validation that performs HTTP requests against the served model to confirm accessibility and responses.
  • Chores

    • Expanded test infrastructure to support full deployment workflows: namespace and secret setup, serving/runtime and inference service orchestration, predictor pod detection, port-forwarding to the model server, and SSL/CA handling for secure connections.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 5, 2026

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

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 5, 2026

📝 Walkthrough

Walkthrough

Adds end-to-end OpenVINO/OpenDataHub Model Registry test infrastructure and an E2E test: new exceptions, helper to fetch the OpenVINO image, fixtures for namespace, secret, ServingRuntime, InferenceService, predictor Pod lookup, and port-forwarding; updates a model artifact URI; adds a test that calls the deployed model endpoint via HTTP.

Changes

Cohort / File(s) Summary
Test Infrastructure & Fixtures
tests/model_registry/model_registry/rest_api/conftest.py
Adds ModelRegistryDeploymentError and PredictorPodNotFoundError; adds get_openvino_image_from_rhoai_csv() helper; introduces fixtures: model_registry_deployment_ns, model_registry_connection_secret, model_registry_serving_runtime, model_registry_inference_service, model_registry_predictor_pod, and model_registry_model_portforward to orchestrate namespace, secrets, ServingRuntime, InferenceService, pod lookup, and port-forwarding for OpenVINO model deployments.
Test Constants
tests/model_registry/model_registry/rest_api/constants.py
Changes MODEL_ARTIFACT URI from an HTTP URL to a Hugging Face custom URI scheme hf://jonburdo/test2.
End-to-End Deployment Test
tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py
Adds TestModelRegistryDeployment with test_registered_model_deployment() that uses the new fixtures to deploy a registered model and issues an HTTP GET to the model endpoint; adds imports for requests, InferenceService, Namespace, and ServingRuntime.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description uses the required template structure but all substantive sections are empty or unchecked, providing no actual details about changes, testing, or related issues. Fill in the Summary with a brief description of changes, populate Related Issues with actual GitHub/JIRA references, and check the appropriate testing checkboxes to indicate how the changes were verified.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'test: Add deployment tests for registered models' clearly and concisely describes the main change: adding new deployment tests for registered models.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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: 2

🧹 Nitpick comments (2)
tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py (1)

369-371: Remove unused fixture arguments from the test signature.

The arguments admin_client, model_registry_deployment_ns, and model_registry_serving_runtime are not referenced in the method body. The class-level @pytest.mark.usefixtures(...) already ensures setup for the latter two, allowing them to be safely removed from the signature.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py`
around lines 369 - 371, Remove the unused fixture arguments from the test
function signature: drop admin_client, model_registry_deployment_ns, and
model_registry_serving_runtime from the function parameter list so the test no
longer declares unused fixtures; the class-level `@pytest.mark.usefixtures`
already provides setup for model_registry_deployment_ns and
model_registry_serving_runtime, so keep any other required fixtures but ensure
the test function only lists fixtures it actually references.
tests/model_registry/model_registry/rest_api/conftest.py (1)

739-741: Use LOGGER.exception() to preserve traceback context when logging caught exceptions.

In exception handlers where the exception is caught, logged, and re-raised, use LOGGER.exception() instead of LOGGER.error() to automatically include the full traceback. This improves diagnostics without requiring manual exception formatting.

Suggested fix
-    except Exception as expt:
-        LOGGER.error(f"Failed to set up port forwarding for pod {model_registry_predictor_pod.name}: {expt}")
+    except Exception:
+        LOGGER.exception(
+            f"Failed to set up port forwarding for pod {model_registry_predictor_pod.name}"
+        )
         raise
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/model_registry/model_registry/rest_api/conftest.py` around lines 739 -
741, Replace the LOGGER.error call in the exception handler that catches setup
errors for port forwarding (the except Exception as expt: block referencing
model_registry_predictor_pod.name) with LOGGER.exception so the traceback is
preserved; keep the existing message text ("Failed to set up port forwarding for
pod {model_registry_predictor_pod.name}: {expt}" or similar) but call
LOGGER.exception(...) and then re-raise the exception as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/model_registry/model_registry/rest_api/conftest.py`:
- Around line 486-487: The test currently silently defaults model_uri to
"hf://jonburdo/test2" when register_model_data lacks "external_id"—change this
to fail fast instead: fetch register_model_data =
registered_model_rest_api.get("register_model", {}) and require model_uri =
register_model_data.get("external_id") (no hardcoded default), then raise an
explicit error or assertion (e.g., ValueError or assert) if model_uri is None or
empty so the test fails when upstream registration didn't provide an
external_id; apply the same change to the other occurrences referenced by the
comment (the model_uri lookups around the later block at the noted lines).
- Around line 724-727: Replace the hard-coded local_port=9998 with an ephemeral
free port allocation: open a temporary socket, bind to ('', 0) to obtain a free
port (use sock.getsockname()[1]), close the socket, assign that value to
local_port and rebuild local_url = f"http://localhost:{local_port}/v1/models";
ensure any port-forward logic (where remote_port=8888 and local_port is used)
uses this dynamically acquired local_port so parallel test runs won't collide.

---

Nitpick comments:
In `@tests/model_registry/model_registry/rest_api/conftest.py`:
- Around line 739-741: Replace the LOGGER.error call in the exception handler
that catches setup errors for port forwarding (the except Exception as expt:
block referencing model_registry_predictor_pod.name) with LOGGER.exception so
the traceback is preserved; keep the existing message text ("Failed to set up
port forwarding for pod {model_registry_predictor_pod.name}: {expt}" or similar)
but call LOGGER.exception(...) and then re-raise the exception as before.

In
`@tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py`:
- Around line 369-371: Remove the unused fixture arguments from the test
function signature: drop admin_client, model_registry_deployment_ns, and
model_registry_serving_runtime from the function parameter list so the test no
longer declares unused fixtures; the class-level `@pytest.mark.usefixtures`
already provides setup for model_registry_deployment_ns and
model_registry_serving_runtime, so keep any other required fixtures but ensure
the test function only lists fixtures it actually references.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1237ea60-9849-4c3c-88cb-63a1209002db

📥 Commits

Reviewing files that changed from the base of the PR and between 44389d6 and b8b4b63.

📒 Files selected for processing (3)
  • tests/model_registry/model_registry/rest_api/conftest.py
  • tests/model_registry/model_registry/rest_api/constants.py
  • tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py

Comment thread tests/model_registry/model_registry/rest_api/conftest.py
Comment thread tests/model_registry/model_registry/rest_api/conftest.py
Comment thread tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py Outdated
Comment thread tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py Outdated
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.

🧹 Nitpick comments (2)
tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py (2)

390-395: Make endpoint assertion resilient to transient startup timing.

A single GET can intermittently fail in e2e environments even when the deployment becomes healthy seconds later. Use a bounded poll before final assertion.

🛠️ Suggested reliability improvement
+import time
 import requests
@@
-        model_response = requests.get(model_endpoint, timeout=10)
-        LOGGER.info(f"Model endpoint status: {model_response.status_code}")
-
-        assert model_response.status_code == 200, (
-            f"Model endpoint returned status code:{model_response.status_code}: response text{model_response.text}"
-        )
+        deadline = time.monotonic() + 60
+        last_error: str | None = None
+        model_response = None
+
+        while time.monotonic() < deadline:
+            try:
+                model_response = requests.get(model_endpoint, timeout=10)
+                LOGGER.info(f"Model endpoint status: {model_response.status_code}")
+                if model_response.status_code == 200:
+                    break
+                last_error = f"status={model_response.status_code}, body={model_response.text}"
+            except requests.RequestException as exc:
+                last_error = str(exc)
+            time.sleep(2)
+
+        assert model_response is not None and model_response.status_code == 200, (
+            f"Model endpoint did not become ready within timeout. Last error: {last_error}"
+        )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py`
around lines 390 - 395, The test does a single requests.get to model_endpoint
(model_response) which can transiently fail; replace the single GET with a
bounded polling loop in test_model_registry_rest_api.py that retries
requests.get(model_endpoint, timeout=10) until status_code == 200 or a total
wait (e.g., 30–60s) is exceeded, sleeping a short interval between attempts; log
each attempt via LOGGER and after the loop assert the final
model_response.status_code == 200 (including model_response.text in the
assertion message) so the test is resilient to startup timing.

369-371: Remove unused test parameters to clear ARG002 noise.

These parameters are not referenced in the test body, so they add lint noise without improving coverage.

♻️ Proposed cleanup
     def test_registered_model_deployment(
         self,
-        admin_client: DynamicClient,
-        model_registry_deployment_ns: Namespace,
-        model_registry_serving_runtime: ServingRuntime,
         model_registry_inference_service: InferenceService,
         model_registry_model_portforward: str,
         registered_model_rest_api: dict[str, Any],
     ) -> None:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py`
around lines 369 - 371, This test function is declaring unused parameters
(admin_client, model_registry_deployment_ns, model_registry_serving_runtime)
which trigger ARG002; remove these unused fixture parameters from the test
function signature in
tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py so
the test body no longer declares them, leaving only the fixtures actually
referenced in the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py`:
- Around line 390-395: The test does a single requests.get to model_endpoint
(model_response) which can transiently fail; replace the single GET with a
bounded polling loop in test_model_registry_rest_api.py that retries
requests.get(model_endpoint, timeout=10) until status_code == 200 or a total
wait (e.g., 30–60s) is exceeded, sleeping a short interval between attempts; log
each attempt via LOGGER and after the loop assert the final
model_response.status_code == 200 (including model_response.text in the
assertion message) so the test is resilient to startup timing.
- Around line 369-371: This test function is declaring unused parameters
(admin_client, model_registry_deployment_ns, model_registry_serving_runtime)
which trigger ARG002; remove these unused fixture parameters from the test
function signature in
tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py so
the test body no longer declares them, leaving only the fixtures actually
referenced in the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1d850fe3-3fa8-4df8-b072-6a7a30d127aa

📥 Commits

Reviewing files that changed from the base of the PR and between b8b4b63 and 343d906.

📒 Files selected for processing (1)
  • tests/model_registry/model_registry/rest_api/test_model_registry_rest_api.py

@dbasunag dbasunag requested a review from fege March 5, 2026 19:42
Copy link
Copy Markdown
Contributor

@sheltoncyril sheltoncyril left a comment

Choose a reason for hiding this comment

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

/lgtm

@sheltoncyril sheltoncyril enabled auto-merge (squash) March 6, 2026 16:47
Copy link
Copy Markdown
Contributor

@fege fege left a comment

Choose a reason for hiding this comment

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

/lgtm

@sheltoncyril sheltoncyril merged commit d476993 into opendatahub-io:main Mar 6, 2026
9 checks passed
@dbasunag dbasunag deleted the deployment_test branch March 6, 2026 16:48
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 6, 2026

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

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