Skip to content

Commit ab3aeb9

Browse files
committed
change: Split async job test and remove older test
1 parent a489187 commit ab3aeb9

5 files changed

Lines changed: 42 additions & 150 deletions

File tree

tests/model_registry/async_job/conftest.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def to_dict(self) -> None:
5151
self.res["spec"]["template"]["spec"]["volumes"] = self.volumes
5252

5353

54-
@pytest.fixture(scope="function")
54+
@pytest.fixture(scope="class")
5555
def s3_secret_for_async_job(
5656
admin_client: DynamicClient,
5757
service_account: ServiceAccount,
@@ -79,7 +79,7 @@ def s3_secret_for_async_job(
7979
yield secret
8080

8181

82-
@pytest.fixture(scope="function")
82+
@pytest.fixture(scope="class")
8383
def oci_secret_for_async_job(
8484
admin_client: DynamicClient,
8585
service_account: ServiceAccount,
@@ -112,7 +112,7 @@ def oci_secret_for_async_job(
112112
yield secret
113113

114114

115-
@pytest.fixture(scope="function")
115+
@pytest.fixture(scope="class")
116116
def model_sync_async_job(
117117
admin_client: DynamicClient,
118118
sa_token: str,
@@ -357,7 +357,7 @@ def oci_registry_host(oci_registry_route: Route) -> str:
357357
return oci_registry_route.instance.spec.host
358358

359359

360-
@pytest.fixture(scope="function")
360+
@pytest.fixture(scope="class")
361361
def create_test_data_in_minio_from_image(
362362
minio_service: Service,
363363
admin_client: DynamicClient,

tests/model_registry/async_job/test_async_upload_e2e.py

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import pytest
55
from kubernetes.dynamic import DynamicClient
66
from ocp_resources.job import Job
7-
from ocp_resources.route import Route
87
from model_registry.types import ArtifactState, RegisteredModelState
98
from tests.model_registry.async_job.constants import (
109
ASYNC_UPLOAD_JOB_NAME,
@@ -60,32 +59,36 @@
6059
indirect=True,
6160
)
6261
class TestAsyncUploadE2E:
63-
"""Test for async upload job with real MinIO, OCI registry, and Model Registry"""
62+
"""RHOAIENG-29344: Test for async upload job with real MinIO, OCI registry, and Model Registry"""
6463

65-
def test_async_upload_job(
64+
@pytest.mark.dependency(name="job_creation_and_pod_spawning")
65+
def test_job_creation_and_pod_spawning(
6666
self: Self,
6767
admin_client: DynamicClient,
6868
model_sync_async_job: Job,
69-
model_registry_client: list[ModelRegistryClient],
70-
oci_registry_route: Route,
7169
) -> None:
7270
"""
73-
RHOAIENG-29344: Test for async upload job execution using model from KSERVE_MINIO_IMAGE
74-
Steps:
75-
Create a model from KSERVE_MINIO_IMAGE
76-
Create a job to upload the model to OCI registry
77-
Verify the job completed successfully
78-
Verify the model is uploaded to OCI registry
79-
Verify the model is registered in model registry
71+
Verify job creation and pod spawning
8072
"""
81-
LOGGER.info("Starting async upload job test with KSERVE_MINIO_IMAGE")
73+
LOGGER.info("Verifying job creation and pod spawning")
74+
8275
# Wait for job to create a pod
8376
job_pod = get_latest_job_pod(admin_client=admin_client, job=model_sync_async_job)
8477
assert job_pod.name.startswith(ASYNC_UPLOAD_JOB_NAME)
8578

86-
# Verify OCI registry contains the uploaded artifact
87-
registry_host = oci_registry_route.instance.spec.host
88-
registry_url = f"http://{registry_host}"
79+
@pytest.mark.dependency(name="oci_registry_verification", depends=["job_creation_and_pod_spawning"])
80+
def test_oci_registry_verification(
81+
self: Self,
82+
oci_registry_host: str,
83+
) -> None:
84+
"""
85+
Verify OCI registry upload
86+
- Model manifest exists in OCI registry
87+
- Manifest has correct structure and layers
88+
"""
89+
LOGGER.info("Verifying OCI registry upload")
90+
91+
registry_url = f"http://{oci_registry_host}"
8992

9093
LOGGER.info(f"Verifying artifact in OCI registry: {registry_url}/v2/{REPO_NAME}/manifests/{TAG}")
9194

@@ -101,8 +104,20 @@ def test_async_upload_job(
101104
assert len(manifest["manifests"]) > 0, "Manifest should have at least one manifest"
102105
LOGGER.info(f"Manifest contains {len(manifest['manifests'])} layer(s)")
103106

104-
# Verify model registry metadata was updated
107+
@pytest.mark.dependency(name="model_registry_verification", depends=["oci_registry_verification"])
108+
def test_model_registry_verification(
109+
self: Self,
110+
model_registry_client: list[ModelRegistryClient],
111+
oci_registry_host: str,
112+
) -> None:
113+
"""
114+
Verify model registration
115+
- Model is registered in model registry
116+
- Model artifact has correct attributes
117+
"""
105118
LOGGER.info("Verifying model registry model and artifact")
119+
120+
# Verify model registry metadata was updated
106121
client = model_registry_client[0]
107122
model = client.get_registered_model(name=MODEL_NAME)
108123
assert model.state == RegisteredModelState.LIVE
@@ -112,7 +127,7 @@ def test_async_upload_job(
112127
# Validate model artifact attributes
113128
assert model_artifact.name == MODEL_NAME
114129
assert model_artifact.state == ArtifactState.LIVE
115-
assert model_artifact.uri == f"oci://{registry_host}/{REPO_NAME}"
130+
assert model_artifact.uri == f"oci://{oci_registry_host}/{REPO_NAME}"
116131
assert model_artifact.storage_key == MODEL_DATA["model_storage_key"]
117132
assert model_artifact.storage_path == MODEL_DATA["model_storage_path"]
118133

tests/model_registry/async_job/test_basic_oci_registry.py

Lines changed: 0 additions & 67 deletions
This file was deleted.

tests/model_registry/async_job/utils.py

Lines changed: 0 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import hashlib
21
import requests
3-
import json
42
from kubernetes.dynamic import DynamicClient
53
from ocp_resources.job import Job
64
from ocp_resources.pod import Pod
@@ -33,60 +31,6 @@ def get_latest_job_pod(admin_client: DynamicClient, job: Job) -> Pod:
3331
return latest_pod
3432

3533

36-
def push_blob_to_oci_registry(registry_url: str, data: bytes, repo: str = "test/simple-artifact") -> str:
37-
"""
38-
Push a blob to an OCI registry.
39-
https://specs.opencontainers.org/distribution-spec/?v=v1.0.0#pushing-blobs
40-
POST to /v2/<repo>/blobs/uploads/ in order to initiate the upload
41-
The response will contain a Location header that contains the upload URL
42-
PUT to the Location URL with the data to be uploaded
43-
"""
44-
45-
blob_digest = f"sha256:{hashlib.sha256(data).hexdigest()}"
46-
47-
LOGGER.info(f"Pushing blob with digest: {blob_digest}")
48-
49-
upload_response = requests.post(f"{registry_url}/v2/{repo}/blobs/uploads/", timeout=10)
50-
LOGGER.info(f"Blob upload initiation: {upload_response.status_code}")
51-
assert upload_response.status_code == 202, f"Failed to initiate blob upload: {upload_response.status_code}"
52-
53-
upload_location = upload_response.headers.get("Location")
54-
LOGGER.info(f"Upload location: {upload_location}")
55-
base_url = f"{registry_url}{upload_location}"
56-
upload_url = f"{base_url}?digest={blob_digest}"
57-
response = requests.put(url=upload_url, data=data, headers={"Content-Type": "application/octet-stream"}, timeout=10)
58-
assert response.status_code == 201, f"Failed to upload blob: {response.status_code}"
59-
return blob_digest
60-
61-
62-
def create_manifest(blob_digest: str, config_json: str, config_digest: str, data: bytes) -> bytes:
63-
"""Create a manifest for an OCI registry."""
64-
65-
manifest = {
66-
"schemaVersion": 2,
67-
"mediaType": "application/vnd.oci.image.manifest.v1+json",
68-
"config": {
69-
"mediaType": "application/vnd.oci.image.config.v1+json",
70-
"size": len(config_json),
71-
"digest": config_digest,
72-
},
73-
"layers": [{"mediaType": "application/vnd.oci.image.layer.v1.tar", "size": len(data), "digest": blob_digest}],
74-
}
75-
76-
return json.dumps(manifest, separators=(",", ":")).encode("utf-8")
77-
78-
79-
def push_manifest_to_oci_registry(registry_url: str, manifest: bytes, repo: str, tag: str) -> None:
80-
"""Push a manifest to an OCI registry."""
81-
response = requests.put(
82-
f"{registry_url}/v2/{repo}/manifests/{tag}",
83-
data=manifest,
84-
headers={"Content-Type": "application/vnd.oci.image.manifest.v1+json"},
85-
timeout=10,
86-
)
87-
assert response.status_code == 201, f"Failed to push manifest: {response.status_code}"
88-
89-
9034
def pull_manifest_from_oci_registry(registry_url: str, repo: str, tag: str) -> dict:
9135
"""Pull a manifest from an OCI registry."""
9236
response = requests.get(

tests/model_registry/conftest.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,7 @@ def model_registry_pod(admin_client: DynamicClient, model_registry_namespace: st
308308
return mr_pod[0]
309309

310310

311-
@pytest.fixture(scope="function")
311+
@pytest.fixture(scope="module")
312312
def sa_namespace(request: pytest.FixtureRequest, admin_client: DynamicClient) -> Generator[Namespace, None, None]:
313313
"""
314314
Creates a namespace
@@ -321,7 +321,7 @@ def sa_namespace(request: pytest.FixtureRequest, admin_client: DynamicClient) ->
321321
yield ns
322322

323323

324-
@pytest.fixture(scope="function")
324+
@pytest.fixture(scope="class")
325325
def service_account(admin_client: DynamicClient, sa_namespace: Namespace) -> Generator[ServiceAccount, None, None]:
326326
"""
327327
Creates a ServiceAccount.
@@ -332,7 +332,7 @@ def service_account(admin_client: DynamicClient, sa_namespace: Namespace) -> Gen
332332
yield sa
333333

334334

335-
@pytest.fixture(scope="function")
335+
@pytest.fixture(scope="class")
336336
def sa_token(service_account: ServiceAccount) -> str:
337337
"""
338338
Retrieves a short-lived token for the ServiceAccount using 'oc create token'.
@@ -376,7 +376,7 @@ def sa_token(service_account: ServiceAccount) -> str:
376376
raise
377377

378378

379-
@pytest.fixture(scope="function")
379+
@pytest.fixture(scope="class")
380380
def mr_access_role(
381381
admin_client: DynamicClient,
382382
model_registry_namespace: str,
@@ -412,7 +412,7 @@ def mr_access_role(
412412
yield role
413413

414414

415-
@pytest.fixture(scope="function")
415+
@pytest.fixture(scope="class")
416416
def mr_access_role_binding(
417417
admin_client: DynamicClient,
418418
model_registry_namespace: str,

0 commit comments

Comments
 (0)