Skip to content

Commit 6d1cce3

Browse files
authored
test: add session fixture to enable all catalogs for tests (#903)
1 parent 175b58b commit 6d1cce3

File tree

5 files changed

+98
-9
lines changed

5 files changed

+98
-9
lines changed

tests/model_registry/model_catalog/conftest.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@
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, DEFAULT_CUSTOM_MODEL_CATALOG
21+
from tests.model_registry.constants import (
22+
CUSTOM_CATALOG_ID1,
23+
DEFAULT_MODEL_CATALOG_CM,
24+
DEFAULT_CUSTOM_MODEL_CATALOG,
25+
)
2226
from tests.model_registry.utils import (
2327
get_rest_headers,
2428
is_model_catalog_ready,
@@ -34,6 +38,46 @@
3438
LOGGER = get_logger(name=__name__)
3539

3640

41+
@pytest.fixture(scope="session")
42+
def enabled_model_catalog_config_map(
43+
admin_client: DynamicClient,
44+
model_registry_namespace: str,
45+
) -> ConfigMap:
46+
"""
47+
Enable all catalogs in the default model catalog configmap
48+
"""
49+
# Get operator-managed default sources ConfigMap
50+
default_sources_cm = ConfigMap(
51+
name=DEFAULT_MODEL_CATALOG_CM, client=admin_client, namespace=model_registry_namespace, ensure_exists=True
52+
)
53+
54+
# Get the sources.yaml content from default sources
55+
default_sources_yaml = default_sources_cm.instance.data.get("sources.yaml", "")
56+
57+
# Parse the YAML and extract only catalogs, enabling each one
58+
parsed_yaml = yaml.safe_load(default_sources_yaml)
59+
if not parsed_yaml or "catalogs" not in parsed_yaml:
60+
raise RuntimeError("No catalogs found in default sources ConfigMap")
61+
62+
for catalog in parsed_yaml["catalogs"]:
63+
catalog["enabled"] = True
64+
enabled_yaml_dict = {"catalogs": parsed_yaml["catalogs"]}
65+
enabled_sources_yaml = yaml.dump(enabled_yaml_dict, default_flow_style=False, sort_keys=False)
66+
67+
LOGGER.info("Adding enabled catalogs to model-catalog-sources ConfigMap")
68+
69+
# Get user-managed sources ConfigMap
70+
user_sources_cm = ConfigMap(
71+
name=DEFAULT_CUSTOM_MODEL_CATALOG, client=admin_client, namespace=model_registry_namespace, ensure_exists=True
72+
)
73+
74+
patches = {"data": {"sources.yaml": enabled_sources_yaml}}
75+
76+
with ResourceEditor(patches={user_sources_cm: patches}):
77+
is_model_catalog_ready(client=admin_client, model_registry_namespace=model_registry_namespace)
78+
yield user_sources_cm
79+
80+
3781
@pytest.fixture(scope="class")
3882
def model_catalog_config_map(
3983
request: pytest.FixtureRequest, admin_client: DynamicClient, model_registry_namespace: str

tests/model_registry/model_catalog/test_default_model_catalog.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,7 @@ class TestModelCatalogDefault:
164164
def test_model_catalog_default_catalog_sources(
165165
self,
166166
pytestconfig: pytest.Config,
167+
enabled_model_catalog_config_map: ConfigMap,
167168
test_idp_user: UserTestSession,
168169
model_catalog_rest_url: list[str],
169170
user_token_for_api_calls: str,
@@ -199,6 +200,7 @@ def test_model_catalog_default_catalog_sources(
199200

200201
def test_model_default_catalog_get_models_by_source(
201202
self: Self,
203+
enabled_model_catalog_config_map: ConfigMap,
202204
model_catalog_rest_url: list[str],
203205
randomly_picked_model_from_catalog_api_by_source: tuple[dict[Any, Any], str, str],
204206
):
@@ -211,6 +213,7 @@ def test_model_default_catalog_get_models_by_source(
211213

212214
def test_model_default_catalog_get_model_by_name(
213215
self: Self,
216+
enabled_model_catalog_config_map: ConfigMap,
214217
model_catalog_rest_url: list[str],
215218
user_token_for_api_calls: str,
216219
randomly_picked_model_from_catalog_api_by_source: tuple[dict[Any, Any], str, str],
@@ -228,6 +231,7 @@ def test_model_default_catalog_get_model_by_name(
228231

229232
def test_model_default_catalog_get_model_artifact(
230233
self: Self,
234+
enabled_model_catalog_config_map: ConfigMap,
231235
model_catalog_rest_url: list[str],
232236
user_token_for_api_calls: str,
233237
randomly_picked_model_from_catalog_api_by_source: tuple[dict[Any, Any], str, str],
@@ -253,6 +257,7 @@ class TestModelCatalogDefaultData:
253257

254258
def test_model_default_catalog_number_of_models(
255259
self: Self,
260+
enabled_model_catalog_config_map: ConfigMap,
256261
default_catalog_api_response: dict[Any, Any],
257262
default_model_catalog_yaml_content: dict[Any, Any],
258263
):
@@ -269,6 +274,7 @@ def test_model_default_catalog_number_of_models(
269274

270275
def test_model_default_catalog_correspondence_of_model_name(
271276
self: Self,
277+
enabled_model_catalog_config_map: ConfigMap,
272278
default_catalog_api_response: dict[Any, Any],
273279
default_model_catalog_yaml_content: dict[Any, Any],
274280
catalog_openapi_schema: dict[Any, Any],
@@ -322,6 +328,7 @@ def test_model_default_catalog_correspondence_of_model_name(
322328

323329
def test_model_default_catalog_random_artifact(
324330
self: Self,
331+
enabled_model_catalog_config_map: ConfigMap,
325332
default_model_catalog_yaml_content: dict[Any, Any],
326333
model_catalog_rest_url: list[str],
327334
model_registry_rest_headers: dict[str, str],

tests/model_registry/model_catalog/test_filter_options_endpoint.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import pytest
22
from typing import Self
33
from simple_logger.logger import get_logger
4-
4+
from ocp_resources.config_map import ConfigMap
55
from tests.model_registry.model_catalog.utils import (
66
validate_filter_options_structure,
77
execute_database_query,
@@ -45,6 +45,7 @@ class TestFilterOptionsEndpoint:
4545
)
4646
def test_filter_options_endpoint_validation(
4747
self: Self,
48+
enabled_model_catalog_config_map: ConfigMap,
4849
model_catalog_rest_url: list[str],
4950
user_token_for_api_calls: str,
5051
test_idp_user: UserTestSession,
@@ -97,6 +98,7 @@ def test_filter_options_endpoint_validation(
9798
)
9899
def test_comprehensive_coverage_against_database(
99100
self: Self,
101+
enabled_model_catalog_config_map: ConfigMap,
100102
model_catalog_rest_url: list[str],
101103
user_token_for_api_calls: str,
102104
model_registry_namespace: str,

tests/model_registry/model_catalog/test_model_search.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22
from dictdiffer import diff
3-
3+
from ocp_resources.config_map import ConfigMap
44
from simple_logger.logger import get_logger
55
from typing import Self, Any
66
from tests.model_registry.model_catalog.constants import (
@@ -32,7 +32,10 @@
3232
class TestSearchModelCatalog:
3333
@pytest.mark.smoke
3434
def test_search_model_catalog_source_label(
35-
self: Self, model_catalog_rest_url: list[str], model_registry_rest_headers: dict[str, str]
35+
self: Self,
36+
enabled_model_catalog_config_map: ConfigMap,
37+
model_catalog_rest_url: list[str],
38+
model_registry_rest_headers: dict[str, str],
3639
):
3740
"""
3841
RHOAIENG-33656: Validate search model catalog by source label
@@ -61,7 +64,10 @@ def test_search_model_catalog_source_label(
6164
assert redhat_ai_filter_moldels_size + redhat_ai_validated_filter_models_size == both_filtered_models_size
6265

6366
def test_search_model_catalog_invalid_source_label(
64-
self: Self, model_catalog_rest_url: list[str], model_registry_rest_headers: dict[str, str]
67+
self: Self,
68+
enabled_model_catalog_config_map: ConfigMap,
69+
model_catalog_rest_url: list[str],
70+
model_registry_rest_headers: dict[str, str],
6571
):
6672
"""
6773
RHOAIENG-33656:
@@ -102,6 +108,7 @@ def test_search_model_catalog_invalid_source_label(
102108
)
103109
def test_search_model_catalog_match(
104110
self: Self,
111+
enabled_model_catalog_config_map: ConfigMap,
105112
model_catalog_rest_url: list[str],
106113
model_registry_rest_headers: dict[str, str],
107114
randomly_picked_model_from_catalog_api_by_source: tuple[dict[Any, Any], str, str],
@@ -145,6 +152,7 @@ class TestSearchModelArtifact:
145152
)
146153
def test_validate_model_artifacts_by_artifact_type(
147154
self: Self,
155+
enabled_model_catalog_config_map: ConfigMap,
148156
model_catalog_rest_url: list[str],
149157
model_registry_rest_headers: dict[str, str],
150158
randomly_picked_model_from_catalog_api_by_source: tuple[dict[Any, Any], str, str],
@@ -210,6 +218,7 @@ def test_validate_model_artifacts_by_artifact_type(
210218
)
211219
def test_error_handled_for_invalid_artifact_type(
212220
self: Self,
221+
enabled_model_catalog_config_map: ConfigMap,
213222
model_catalog_rest_url: list[str],
214223
model_registry_rest_headers: dict[str, str],
215224
randomly_picked_model_from_catalog_api_by_source: tuple[dict[Any, Any], str, str],
@@ -248,6 +257,7 @@ def test_error_handled_for_invalid_artifact_type(
248257
)
249258
def test_multiple_artifact_type_filtering(
250259
self: Self,
260+
enabled_model_catalog_config_map: ConfigMap,
251261
model_catalog_rest_url: list[str],
252262
model_registry_rest_headers: dict[str, str],
253263
randomly_picked_model_from_catalog_api_by_source: tuple[dict[Any, Any], str, str],
@@ -299,6 +309,7 @@ class TestSearchModelCatalogQParameter:
299309
)
300310
def test_q_parameter_basic_search(
301311
self: Self,
312+
enabled_model_catalog_config_map: ConfigMap,
302313
search_term: str,
303314
model_catalog_rest_url: list[str],
304315
model_registry_rest_headers: dict[str, str],
@@ -338,6 +349,7 @@ def test_q_parameter_basic_search(
338349
)
339350
def test_q_parameter_case_insensitive(
340351
self: Self,
352+
enabled_model_catalog_config_map: ConfigMap,
341353
search_term: str,
342354
case_variant: str,
343355
model_catalog_rest_url: list[str],
@@ -388,6 +400,7 @@ def test_q_parameter_case_insensitive(
388400

389401
def test_q_parameter_no_results(
390402
self: Self,
403+
enabled_model_catalog_config_map: ConfigMap,
391404
model_catalog_rest_url: list[str],
392405
model_registry_rest_headers: dict[str, str],
393406
model_registry_namespace: str,
@@ -417,6 +430,7 @@ def test_q_parameter_no_results(
417430
def test_q_parameter_empty_query(
418431
self: Self,
419432
search_term,
433+
enabled_model_catalog_config_map: ConfigMap,
420434
model_catalog_rest_url: list[str],
421435
model_registry_rest_headers: dict[str, str],
422436
):
@@ -434,6 +448,7 @@ def test_q_parameter_empty_query(
434448

435449
def test_q_parameter_with_source_label_filter(
436450
self: Self,
451+
enabled_model_catalog_config_map: ConfigMap,
437452
model_catalog_rest_url: list[str],
438453
model_registry_rest_headers: dict[str, str],
439454
):
@@ -479,6 +494,7 @@ def test_q_parameter_with_source_label_filter(
479494
class TestSearchModelsByFilterQuery:
480495
def test_search_models_by_filter_query(
481496
self: Self,
497+
enabled_model_catalog_config_map: ConfigMap,
482498
model_catalog_rest_url: list[str],
483499
model_registry_rest_headers: dict[str, str],
484500
model_registry_namespace: str,
@@ -524,6 +540,7 @@ def test_search_models_by_filter_query(
524540

525541
def test_search_models_by_invalid_filter_query(
526542
self: Self,
543+
enabled_model_catalog_config_map: ConfigMap,
527544
model_catalog_rest_url: list[str],
528545
model_registry_rest_headers: dict[str, str],
529546
model_registry_namespace: str,
@@ -563,6 +580,7 @@ def test_search_models_by_invalid_filter_query(
563580
@pytest.mark.downstream_only
564581
def test_presence_performance_data_on_pod(
565582
self: Self,
583+
enabled_model_catalog_config_map: ConfigMap,
566584
admin_client: DynamicClient,
567585
model_registry_namespace: str,
568586
):

tests/model_registry/utils.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,12 @@
4343
LOGGER = get_logger(name=__name__)
4444

4545

46+
class TransientUnauthorizedError(Exception):
47+
"""Exception for transient 401 Unauthorized errors that should be retried."""
48+
49+
pass
50+
51+
4652
def get_mr_service_by_label(client: DynamicClient, namespace_name: str, mr_instance: ModelRegistry) -> Service:
4753
"""
4854
Args:
@@ -702,13 +708,25 @@ def execute_get_call(
702708
resp = requests.get(url=url, headers=headers, verify=verify, timeout=60, params=params)
703709
LOGGER.info(f"Encoded url from requests library: {resp.url}")
704710
if resp.status_code not in [200, 201]:
711+
# Raise custom exception for 401 errors that can be retried (OAuth/kube-rbac-proxy initialization)
712+
if resp.status_code == 401:
713+
raise TransientUnauthorizedError(f"Get call failed for resource: {url}, 401: {resp.text}")
714+
# Raise regular exception for other errors (400, 403, 404, etc.) that should fail immediately
705715
raise ResourceNotFoundError(f"Get call failed for resource: {url}, {resp.status_code}: {resp.text}")
706716
return resp
707717

708718

709-
@retry(wait_timeout=60, sleep=5, exceptions_dict={ResourceNotFoundError: []})
719+
@retry(wait_timeout=60, sleep=5, exceptions_dict={ResourceNotFoundError: [], TransientUnauthorizedError: []})
710720
def wait_for_model_catalog_api(url: str, headers: dict[str, str], verify: bool | str = False) -> requests.Response:
711-
return execute_get_call(url=f"{url}sources", headers=headers, verify=verify)
721+
"""
722+
Wait for model catalog API to be ready and fully initialized checks both /sources and /models endpoints
723+
to ensure OAuth/kube-rbac-proxy is fully initialized.
724+
"""
725+
LOGGER.info(f"Waiting for model catalog API at {url}sources")
726+
execute_get_call(url=f"{url}sources", headers=headers, verify=verify)
727+
LOGGER.info(f"Verifying model catalog API readiness at {url}models")
728+
729+
return execute_get_call(url=f"{url}models", headers=headers, verify=verify)
712730

713731

714732
def execute_get_command(
@@ -741,9 +759,9 @@ def validate_model_catalog_sources(
741759
url=model_catalog_sources_url,
742760
headers=rest_headers,
743761
)["items"]
744-
LOGGER.info(results)
762+
LOGGER.info(f"Model catalog sources: {results}")
745763
# this is for the default catalog:
746-
assert len(results) == len(expected_catalog_values) + 2
764+
assert len(results) == len(expected_catalog_values)
747765
ids_from_query = [result_entry["id"] for result_entry in results]
748766
ids_expected = [expected_entry["id"] for expected_entry in expected_catalog_values]
749767
assert set(ids_expected).issubset(set(ids_from_query)), f"Expected: {expected_catalog_values}. Actual: {results}"

0 commit comments

Comments
 (0)