Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions tests/model_registry/mcp_servers/config/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
MCP_CATALOG_INVALID_SOURCE,
MCP_CATALOG_SOURCE,
MCP_CATALOG_SOURCE2,
MCP_CATALOG_SOURCE3,
MCP_CATALOG_SOURCE_ID,
MCP_CATALOG_SOURCE_NAME,
MCP_SERVERS_YAML,
MCP_SERVERS_YAML2,
MCP_SERVERS_YAML3,
MCP_SERVERS_YAML_CATALOG_PATH,
)
from tests.model_registry.utils import (
Expand Down Expand Up @@ -63,6 +65,46 @@ def mcp_multi_source_configmap_patch(
)


@pytest.fixture(scope="class")
def mcp_source_label_configmap_patch(
admin_client: DynamicClient,
model_registry_namespace: str,
mcp_catalog_rest_urls: list[str],
model_registry_rest_headers: dict[str, str],
) -> Generator[None]:
"""
Class-scoped fixture that patches the model-catalog-sources ConfigMap
with three MCP catalog sources: two labeled and one unlabeled.
Used for sourceLabel filtering tests (TC-API-036 to TC-API-039).
"""
catalog_config_map, current_data = get_mcp_catalog_sources(
admin_client=admin_client, model_registry_namespace=model_registry_namespace
)
if "mcp_catalogs" not in current_data:
current_data["mcp_catalogs"] = []
current_data["mcp_catalogs"].extend([MCP_CATALOG_SOURCE, MCP_CATALOG_SOURCE2, MCP_CATALOG_SOURCE3])

patches = {
"data": {
"sources.yaml": yaml.dump(current_data, default_flow_style=False),
"mcp-servers.yaml": MCP_SERVERS_YAML,
"mcp-servers-2.yaml": MCP_SERVERS_YAML2,
"mcp-servers-3.yaml": MCP_SERVERS_YAML3,
}
}

with ResourceEditor(patches={catalog_config_map: patches}):
wait_for_model_catalog_pod_ready_after_deletion(
client=admin_client, model_registry_namespace=model_registry_namespace
)
wait_for_mcp_catalog_api(url=mcp_catalog_rest_urls[0], headers=model_registry_rest_headers)
yield

wait_for_model_catalog_pod_ready_after_deletion(
client=admin_client, model_registry_namespace=model_registry_namespace
)


@pytest.fixture(scope="class")
def mcp_invalid_yaml_configmap_patch(
request: pytest.FixtureRequest,
Expand Down
73 changes: 73 additions & 0 deletions tests/model_registry/mcp_servers/config/test_source_label.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
from typing import Self

import pytest
from simple_logger.logger import get_logger

from tests.model_registry.mcp_servers.constants import (
MCP_CATALOG_SOURCE2_NAME,
MCP_CATALOG_SOURCE_NAME,
)
from tests.model_registry.utils import execute_get_command

LOGGER = get_logger(name=__name__)


@pytest.mark.usefixtures("mcp_source_label_configmap_patch")
class TestMCPServerSourceLabel:
"""Tests for MCP server sourceLabel filtering (TC-API-036 to TC-API-039)."""

@pytest.mark.smoke
def test_mcp_server_source_label(
self: Self,
mcp_catalog_rest_urls: list[str],
model_registry_rest_headers: dict[str, str],
):
"""
Validate MCP server filtering by source label (TC-API-036, TC-API-038, TC-API-039).
"""
source1_size = execute_get_command(
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
headers=model_registry_rest_headers,
params={"sourceLabel": MCP_CATALOG_SOURCE_NAME},
)["size"]
source2_size = execute_get_command(
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
headers=model_registry_rest_headers,
params={"sourceLabel": MCP_CATALOG_SOURCE2_NAME},
)["size"]
null_label_size = execute_get_command(
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
headers=model_registry_rest_headers,
params={"sourceLabel": "null"},
)["size"]
no_filtered_size = execute_get_command(
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
headers=model_registry_rest_headers,
)["size"]
both_labeled_size = execute_get_command(
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
headers=model_registry_rest_headers,
params={"sourceLabel": f"{MCP_CATALOG_SOURCE_NAME},{MCP_CATALOG_SOURCE2_NAME}"},
)["size"]
LOGGER.info(f"no_filtered_size: {no_filtered_size}")
assert no_filtered_size > 0
assert null_label_size >= 0
assert source1_size + source2_size == both_labeled_size
assert no_filtered_size == both_labeled_size + null_label_size

@pytest.mark.tier3
def test_mcp_server_invalid_source_label(
self: Self,
mcp_catalog_rest_urls: list[str],
model_registry_rest_headers: dict[str, str],
):
"""
Validate MCP server filtering by invalid source label (TC-API-037).
"""
invalid_size = execute_get_command(
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
headers=model_registry_rest_headers,
params={"sourceLabel": "invalid"},
)["size"]

assert invalid_size == 0
33 changes: 33 additions & 0 deletions tests/model_registry/mcp_servers/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,36 @@
"calculator": MCP_CATALOG_SOURCE_ID,
"code-reviewer": MCP_CATALOG_SOURCE2_ID,
}

# Source 3: unlabeled source (no labels) for sourceLabel=null testing
MCP_CATALOG_SOURCE3_ID: str = "test_mcp_servers_unlabeled"
MCP_CATALOG_SOURCE3_NAME: str = "Test MCP Servers Unlabeled"
MCP_SERVERS_YAML3_CATALOG_PATH: str = "/data/user-sources/mcp-servers-3.yaml"

MCP_SERVERS_YAML3: str = """\
mcp_servers:
- name: database-connector
description: "Database connection MCP server"
provider: "Data Tools"
version: "1.0.0"
license: "MIT"
tags:
- database
- sql
tools:
- name: execute_query
description: "Execute a database query"
"""

MCP_CATALOG_SOURCE3: dict = {
"name": MCP_CATALOG_SOURCE3_NAME,
"id": MCP_CATALOG_SOURCE3_ID,
"type": "yaml",
"enabled": True,
"properties": {"yamlCatalogPath": MCP_SERVERS_YAML3_CATALOG_PATH},
}

EXPECTED_MCP_SOURCE3_SERVER_NAMES: set[str] = {"database-connector"}
EXPECTED_ALL_MCP_SERVER_NAMES_WITH_UNLABELED: set[str] = (
EXPECTED_ALL_MCP_SERVER_NAMES | EXPECTED_MCP_SOURCE3_SERVER_NAMES
)
27 changes: 11 additions & 16 deletions tests/model_registry/model_catalog/search/test_model_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def test_search_model_catalog_source_label(
Validate search model catalog by source label
"""

redhat_ai_filter_moldels_size = get_models_from_catalog_api(
redhat_ai_filter_models_size = get_models_from_catalog_api(
model_catalog_rest_url=model_catalog_rest_url,
model_registry_rest_headers=model_registry_rest_headers,
source_label=REDHAT_AI_CATALOG_NAME,
Expand All @@ -50,20 +50,24 @@ def test_search_model_catalog_source_label(
model_registry_rest_headers=model_registry_rest_headers,
source_label=REDHAT_AI_VALIDATED_UNESCAPED_CATALOG_NAME,
)["size"]
null_label_models_size = get_models_from_catalog_api(
model_catalog_rest_url=model_catalog_rest_url,
model_registry_rest_headers=model_registry_rest_headers,
source_label="null",
)["size"]
no_filtered_models_size = get_models_from_catalog_api(
model_catalog_rest_url=model_catalog_rest_url, model_registry_rest_headers=model_registry_rest_headers
)["size"]
both_filtered_models_size = get_models_from_catalog_api(
both_labeled_models_size = get_models_from_catalog_api(
model_catalog_rest_url=model_catalog_rest_url,
model_registry_rest_headers=model_registry_rest_headers,
source_label=f"{REDHAT_AI_VALIDATED_UNESCAPED_CATALOG_NAME},{REDHAT_AI_CATALOG_NAME}",
)["size"]
LOGGER.info(f"no_filtered_models_size: {no_filtered_models_size}")
assert no_filtered_models_size > 0
# no_filtered includes models from sources without labels (e.g. Other Models),
# which cannot be queried via sourceLabel, so total >= labeled sum
assert no_filtered_models_size >= both_filtered_models_size
assert redhat_ai_filter_moldels_size + redhat_ai_validated_filter_models_size == both_filtered_models_size
assert null_label_models_size >= 0
assert redhat_ai_filter_models_size + redhat_ai_validated_filter_models_size == both_labeled_models_size
assert no_filtered_models_size == both_labeled_models_size + null_label_models_size

@pytest.mark.tier3
def test_search_model_catalog_invalid_source_label(
Expand All @@ -74,22 +78,13 @@ def test_search_model_catalog_invalid_source_label(
"""
Validate search model catalog by invalid source label
"""

# "null" is a valid source label for sources without explicit labels (e.g. Other Models)
null_size = get_models_from_catalog_api(
model_catalog_rest_url=model_catalog_rest_url,
model_registry_rest_headers=model_registry_rest_headers,
source_label="null",
)["size"]

invalid_size = get_models_from_catalog_api(
model_catalog_rest_url=model_catalog_rest_url,
model_registry_rest_headers=model_registry_rest_headers,
source_label="invalid",
)["size"]

assert null_size >= 0, f"Expected non-negative size for null source label, got {null_size}"
assert invalid_size == 0, f"Expected 0 models for invalid source label, got {invalid_size}"
assert invalid_size == 0

@pytest.mark.parametrize(
"randomly_picked_model_from_catalog_api_by_source,source_filter",
Expand Down
Loading