Skip to content

Commit 017026b

Browse files
authored
Merge branch 'main' into workaround
2 parents 6a62341 + 23e2790 commit 017026b

File tree

4 files changed

+985
-898
lines changed

4 files changed

+985
-898
lines changed

tests/model_registry/model_catalog/conftest.py

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
REDHAT_AI_CATALOG_ID,
1919
)
2020
from tests.model_registry.model_catalog.utils import get_models_from_catalog_api
21-
from tests.model_registry.constants import CUSTOM_CATALOG_ID1
21+
from tests.model_registry.constants import CUSTOM_CATALOG_ID1, DEFAULT_CUSTOM_MODEL_CATALOG
2222
from tests.model_registry.utils import (
2323
get_rest_headers,
2424
is_model_catalog_ready,
@@ -225,3 +225,27 @@ def models_from_filter_query(
225225
LOGGER.info(f"Filter query '{filter_query}' returned {len(model_names)} models: {', '.join(model_names)}")
226226

227227
return model_names
228+
229+
230+
@pytest.fixture()
231+
def labels_configmap_patch(admin_client: DynamicClient, model_registry_namespace: str) -> dict[str, Any]:
232+
# Get the editable ConfigMap
233+
sources_cm = ConfigMap(name=DEFAULT_CUSTOM_MODEL_CATALOG, client=admin_client, namespace=model_registry_namespace)
234+
235+
# Parse current data and add test label
236+
current_data = yaml.safe_load(sources_cm.instance.data["sources.yaml"])
237+
238+
new_label = {
239+
"name": "test-dynamic",
240+
"displayName": "Dynamic Test Label",
241+
"description": "A label added during test execution",
242+
}
243+
244+
if "labels" not in current_data:
245+
current_data["labels"] = []
246+
current_data["labels"].append(new_label)
247+
248+
patches = {"data": {"sources.yaml": yaml.dump(current_data, default_flow_style=False)}}
249+
250+
with ResourceEditor(patches={sources_cm: patches}):
251+
yield patches
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import pytest
2+
from typing import Any
3+
from kubernetes.dynamic import DynamicClient
4+
from simple_logger.logger import get_logger
5+
6+
7+
from utilities.infra import get_openshift_token
8+
from timeout_sampler import TimeoutSampler
9+
10+
from tests.model_registry.model_catalog.utils import (
11+
get_labels_from_configmaps,
12+
get_labels_from_api,
13+
verify_labels_match,
14+
)
15+
16+
LOGGER = get_logger(name=__name__)
17+
18+
19+
class TestLabelsEndpoint:
20+
"""Test class for the model catalog labels endpoint."""
21+
22+
@pytest.mark.smoke
23+
def test_labels_endpoint_default_data(
24+
self,
25+
admin_client: DynamicClient,
26+
model_registry_namespace: str,
27+
model_catalog_rest_url: list[str],
28+
):
29+
"""
30+
Smoke test: Validate default labels from ConfigMaps are returned by the endpoint.
31+
"""
32+
LOGGER.info("Testing labels endpoint with default data")
33+
34+
# Get expected labels from ConfigMaps
35+
expected_labels = get_labels_from_configmaps(admin_client=admin_client, namespace=model_registry_namespace)
36+
37+
# Get labels from API
38+
api_labels = get_labels_from_api(
39+
model_catalog_rest_url=model_catalog_rest_url[0], user_token=get_openshift_token()
40+
)
41+
42+
# Verify they match
43+
verify_labels_match(expected_labels=expected_labels, api_labels=api_labels)
44+
45+
@pytest.mark.sanity
46+
def test_labels_endpoint_configmap_updates(
47+
self,
48+
admin_client: DynamicClient,
49+
model_registry_namespace: str,
50+
model_catalog_rest_url: list[str],
51+
labels_configmap_patch: dict[str, Any],
52+
):
53+
"""
54+
Sanity test: Edit the editable ConfigMap and verify changes are reflected in API.
55+
"""
56+
_ = labels_configmap_patch
57+
58+
def _check_updated_labels():
59+
# Get updated expected labels from ConfigMaps
60+
expected_labels = get_labels_from_configmaps(admin_client=admin_client, namespace=model_registry_namespace)
61+
62+
# Get labels from API
63+
api_labels = get_labels_from_api(
64+
model_catalog_rest_url=model_catalog_rest_url[0], user_token=get_openshift_token()
65+
)
66+
67+
# Verify they match (including the new label)
68+
verify_labels_match(expected_labels=expected_labels, api_labels=api_labels)
69+
70+
sampler = TimeoutSampler(wait_timeout=60, sleep=5, func=_check_updated_labels)
71+
next(iter(sampler))

tests/model_registry/model_catalog/utils.py

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Any, Tuple, List
1+
from typing import Any, Tuple, List, Dict
22
import yaml
33

44
from kubernetes.dynamic import DynamicClient
@@ -22,7 +22,9 @@
2222
CATALOG_CONTAINER,
2323
PERFORMANCE_DATA_DIR,
2424
)
25+
from tests.model_registry.constants import DEFAULT_CUSTOM_MODEL_CATALOG, DEFAULT_MODEL_CATALOG_CM
2526
from tests.model_registry.utils import execute_get_command
27+
from tests.model_registry.utils import get_rest_headers
2628

2729
LOGGER = get_logger(name=__name__)
2830

@@ -1121,3 +1123,75 @@ def validate_model_artifacts_match_criteria_or(
11211123

11221124
LOGGER.error(f"Model {model_name} failed all OR validations")
11231125
return False
1126+
1127+
1128+
def get_labels_from_configmaps(admin_client: DynamicClient, namespace: str) -> List[Dict[str, Any]]:
1129+
"""
1130+
Get all labels from both model catalog ConfigMaps.
1131+
1132+
Args:
1133+
admin_client: Kubernetes client
1134+
namespace: Namespace containing the ConfigMaps
1135+
1136+
Returns:
1137+
List of all label dictionaries from both ConfigMaps
1138+
"""
1139+
labels = []
1140+
1141+
# Get labels from default ConfigMap
1142+
default_cm = ConfigMap(name=DEFAULT_MODEL_CATALOG_CM, client=admin_client, namespace=namespace)
1143+
default_data = yaml.safe_load(default_cm.instance.data["sources.yaml"])
1144+
if "labels" in default_data:
1145+
labels.extend(default_data["labels"])
1146+
1147+
# Get labels from sources ConfigMap
1148+
sources_cm = ConfigMap(name=DEFAULT_CUSTOM_MODEL_CATALOG, client=admin_client, namespace=namespace)
1149+
sources_data = yaml.safe_load(sources_cm.instance.data["sources.yaml"])
1150+
if "labels" in sources_data:
1151+
labels.extend(sources_data["labels"])
1152+
1153+
return labels
1154+
1155+
1156+
def get_labels_from_api(model_catalog_rest_url: str, user_token: str) -> List[Dict[str, Any]]:
1157+
"""
1158+
Get labels from the API endpoint.
1159+
1160+
Args:
1161+
model_catalog_rest_url: Base URL for model catalog API
1162+
user_token: Authentication token
1163+
1164+
Returns:
1165+
List of label dictionaries from API response
1166+
"""
1167+
url = f"{model_catalog_rest_url}labels"
1168+
headers = get_rest_headers(token=user_token)
1169+
response = execute_get_command(url=url, headers=headers)
1170+
return response["items"]
1171+
1172+
1173+
def verify_labels_match(expected_labels: List[Dict[str, Any]], api_labels: List[Dict[str, Any]]) -> None:
1174+
"""
1175+
Verify that all expected labels are present in the API response.
1176+
1177+
Args:
1178+
expected_labels: Labels expected from ConfigMaps
1179+
api_labels: Labels returned by API
1180+
1181+
Raises:
1182+
AssertionError: If any expected label is not found in API response
1183+
"""
1184+
LOGGER.info(f"Verifying {len(expected_labels)} expected labels against {len(api_labels)} API labels")
1185+
1186+
for expected_label in expected_labels:
1187+
found = False
1188+
for api_label in api_labels:
1189+
if (
1190+
expected_label.get("name") == api_label.get("name")
1191+
and expected_label.get("displayName") == api_label.get("displayName")
1192+
and expected_label.get("description") == api_label.get("description")
1193+
):
1194+
found = True
1195+
break
1196+
1197+
assert found, f"Expected label not found in API response: {expected_label}"

0 commit comments

Comments
 (0)