Skip to content

Commit de2f59c

Browse files
authored
test: add verification for filtering mcp server by source label (#1285)
Signed-off-by: fege <fmosca@redhat.com>
1 parent b28f0ed commit de2f59c

File tree

4 files changed

+159
-16
lines changed

4 files changed

+159
-16
lines changed

tests/model_registry/mcp_servers/config/conftest.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,12 @@
1111
MCP_CATALOG_INVALID_SOURCE,
1212
MCP_CATALOG_SOURCE,
1313
MCP_CATALOG_SOURCE2,
14+
MCP_CATALOG_SOURCE3,
1415
MCP_CATALOG_SOURCE_ID,
1516
MCP_CATALOG_SOURCE_NAME,
1617
MCP_SERVERS_YAML,
1718
MCP_SERVERS_YAML2,
19+
MCP_SERVERS_YAML3,
1820
MCP_SERVERS_YAML_CATALOG_PATH,
1921
)
2022
from tests.model_registry.utils import (
@@ -63,6 +65,46 @@ def mcp_multi_source_configmap_patch(
6365
)
6466

6567

68+
@pytest.fixture(scope="class")
69+
def mcp_source_label_configmap_patch(
70+
admin_client: DynamicClient,
71+
model_registry_namespace: str,
72+
mcp_catalog_rest_urls: list[str],
73+
model_registry_rest_headers: dict[str, str],
74+
) -> Generator[None]:
75+
"""
76+
Class-scoped fixture that patches the model-catalog-sources ConfigMap
77+
with three MCP catalog sources: two labeled and one unlabeled.
78+
Used for sourceLabel filtering tests (TC-API-036 to TC-API-039).
79+
"""
80+
catalog_config_map, current_data = get_mcp_catalog_sources(
81+
admin_client=admin_client, model_registry_namespace=model_registry_namespace
82+
)
83+
if "mcp_catalogs" not in current_data:
84+
current_data["mcp_catalogs"] = []
85+
current_data["mcp_catalogs"].extend([MCP_CATALOG_SOURCE, MCP_CATALOG_SOURCE2, MCP_CATALOG_SOURCE3])
86+
87+
patches = {
88+
"data": {
89+
"sources.yaml": yaml.dump(current_data, default_flow_style=False),
90+
"mcp-servers.yaml": MCP_SERVERS_YAML,
91+
"mcp-servers-2.yaml": MCP_SERVERS_YAML2,
92+
"mcp-servers-3.yaml": MCP_SERVERS_YAML3,
93+
}
94+
}
95+
96+
with ResourceEditor(patches={catalog_config_map: patches}):
97+
wait_for_model_catalog_pod_ready_after_deletion(
98+
client=admin_client, model_registry_namespace=model_registry_namespace
99+
)
100+
wait_for_mcp_catalog_api(url=mcp_catalog_rest_urls[0], headers=model_registry_rest_headers)
101+
yield
102+
103+
wait_for_model_catalog_pod_ready_after_deletion(
104+
client=admin_client, model_registry_namespace=model_registry_namespace
105+
)
106+
107+
66108
@pytest.fixture(scope="class")
67109
def mcp_invalid_yaml_configmap_patch(
68110
request: pytest.FixtureRequest,
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
from typing import Self
2+
3+
import pytest
4+
from simple_logger.logger import get_logger
5+
6+
from tests.model_registry.mcp_servers.constants import (
7+
MCP_CATALOG_SOURCE2_NAME,
8+
MCP_CATALOG_SOURCE_NAME,
9+
)
10+
from tests.model_registry.utils import execute_get_command
11+
12+
LOGGER = get_logger(name=__name__)
13+
14+
15+
@pytest.mark.usefixtures("mcp_source_label_configmap_patch")
16+
class TestMCPServerSourceLabel:
17+
"""Tests for MCP server sourceLabel filtering (TC-API-036 to TC-API-039)."""
18+
19+
@pytest.mark.smoke
20+
def test_mcp_server_source_label(
21+
self: Self,
22+
mcp_catalog_rest_urls: list[str],
23+
model_registry_rest_headers: dict[str, str],
24+
):
25+
"""
26+
Validate MCP server filtering by source label (TC-API-036, TC-API-038, TC-API-039).
27+
"""
28+
source1_size = execute_get_command(
29+
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
30+
headers=model_registry_rest_headers,
31+
params={"sourceLabel": MCP_CATALOG_SOURCE_NAME},
32+
)["size"]
33+
source2_size = execute_get_command(
34+
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
35+
headers=model_registry_rest_headers,
36+
params={"sourceLabel": MCP_CATALOG_SOURCE2_NAME},
37+
)["size"]
38+
null_label_size = execute_get_command(
39+
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
40+
headers=model_registry_rest_headers,
41+
params={"sourceLabel": "null"},
42+
)["size"]
43+
no_filtered_size = execute_get_command(
44+
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
45+
headers=model_registry_rest_headers,
46+
)["size"]
47+
both_labeled_size = execute_get_command(
48+
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
49+
headers=model_registry_rest_headers,
50+
params={"sourceLabel": f"{MCP_CATALOG_SOURCE_NAME},{MCP_CATALOG_SOURCE2_NAME}"},
51+
)["size"]
52+
LOGGER.info(f"no_filtered_size: {no_filtered_size}")
53+
assert no_filtered_size > 0
54+
assert null_label_size >= 0
55+
assert source1_size + source2_size == both_labeled_size
56+
assert no_filtered_size == both_labeled_size + null_label_size
57+
58+
@pytest.mark.tier3
59+
def test_mcp_server_invalid_source_label(
60+
self: Self,
61+
mcp_catalog_rest_urls: list[str],
62+
model_registry_rest_headers: dict[str, str],
63+
):
64+
"""
65+
Validate MCP server filtering by invalid source label (TC-API-037).
66+
"""
67+
invalid_size = execute_get_command(
68+
url=f"{mcp_catalog_rest_urls[0]}mcp_servers",
69+
headers=model_registry_rest_headers,
70+
params={"sourceLabel": "invalid"},
71+
)["size"]
72+
73+
assert invalid_size == 0

tests/model_registry/mcp_servers/constants.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,36 @@
184184
"calculator": MCP_CATALOG_SOURCE_ID,
185185
"code-reviewer": MCP_CATALOG_SOURCE2_ID,
186186
}
187+
188+
# Source 3: unlabeled source (no labels) for sourceLabel=null testing
189+
MCP_CATALOG_SOURCE3_ID: str = "test_mcp_servers_unlabeled"
190+
MCP_CATALOG_SOURCE3_NAME: str = "Test MCP Servers Unlabeled"
191+
MCP_SERVERS_YAML3_CATALOG_PATH: str = "/data/user-sources/mcp-servers-3.yaml"
192+
193+
MCP_SERVERS_YAML3: str = """\
194+
mcp_servers:
195+
- name: database-connector
196+
description: "Database connection MCP server"
197+
provider: "Data Tools"
198+
version: "1.0.0"
199+
license: "MIT"
200+
tags:
201+
- database
202+
- sql
203+
tools:
204+
- name: execute_query
205+
description: "Execute a database query"
206+
"""
207+
208+
MCP_CATALOG_SOURCE3: dict = {
209+
"name": MCP_CATALOG_SOURCE3_NAME,
210+
"id": MCP_CATALOG_SOURCE3_ID,
211+
"type": "yaml",
212+
"enabled": True,
213+
"properties": {"yamlCatalogPath": MCP_SERVERS_YAML3_CATALOG_PATH},
214+
}
215+
216+
EXPECTED_MCP_SOURCE3_SERVER_NAMES: set[str] = {"database-connector"}
217+
EXPECTED_ALL_MCP_SERVER_NAMES_WITH_UNLABELED: set[str] = (
218+
EXPECTED_ALL_MCP_SERVER_NAMES | EXPECTED_MCP_SOURCE3_SERVER_NAMES
219+
)

tests/model_registry/model_catalog/search/test_model_search.py

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def test_search_model_catalog_source_label(
4040
Validate search model catalog by source label
4141
"""
4242

43-
redhat_ai_filter_moldels_size = get_models_from_catalog_api(
43+
redhat_ai_filter_models_size = get_models_from_catalog_api(
4444
model_catalog_rest_url=model_catalog_rest_url,
4545
model_registry_rest_headers=model_registry_rest_headers,
4646
source_label=REDHAT_AI_CATALOG_NAME,
@@ -50,20 +50,24 @@ def test_search_model_catalog_source_label(
5050
model_registry_rest_headers=model_registry_rest_headers,
5151
source_label=REDHAT_AI_VALIDATED_UNESCAPED_CATALOG_NAME,
5252
)["size"]
53+
null_label_models_size = get_models_from_catalog_api(
54+
model_catalog_rest_url=model_catalog_rest_url,
55+
model_registry_rest_headers=model_registry_rest_headers,
56+
source_label="null",
57+
)["size"]
5358
no_filtered_models_size = get_models_from_catalog_api(
5459
model_catalog_rest_url=model_catalog_rest_url, model_registry_rest_headers=model_registry_rest_headers
5560
)["size"]
56-
both_filtered_models_size = get_models_from_catalog_api(
61+
both_labeled_models_size = get_models_from_catalog_api(
5762
model_catalog_rest_url=model_catalog_rest_url,
5863
model_registry_rest_headers=model_registry_rest_headers,
5964
source_label=f"{REDHAT_AI_VALIDATED_UNESCAPED_CATALOG_NAME},{REDHAT_AI_CATALOG_NAME}",
6065
)["size"]
6166
LOGGER.info(f"no_filtered_models_size: {no_filtered_models_size}")
6267
assert no_filtered_models_size > 0
63-
# no_filtered includes models from sources without labels (e.g. Other Models),
64-
# which cannot be queried via sourceLabel, so total >= labeled sum
65-
assert no_filtered_models_size >= both_filtered_models_size
66-
assert redhat_ai_filter_moldels_size + redhat_ai_validated_filter_models_size == both_filtered_models_size
68+
assert null_label_models_size >= 0
69+
assert redhat_ai_filter_models_size + redhat_ai_validated_filter_models_size == both_labeled_models_size
70+
assert no_filtered_models_size == both_labeled_models_size + null_label_models_size
6771

6872
@pytest.mark.tier3
6973
def test_search_model_catalog_invalid_source_label(
@@ -74,22 +78,13 @@ def test_search_model_catalog_invalid_source_label(
7478
"""
7579
Validate search model catalog by invalid source label
7680
"""
77-
78-
# "null" is a valid source label for sources without explicit labels (e.g. Other Models)
79-
null_size = get_models_from_catalog_api(
80-
model_catalog_rest_url=model_catalog_rest_url,
81-
model_registry_rest_headers=model_registry_rest_headers,
82-
source_label="null",
83-
)["size"]
84-
8581
invalid_size = get_models_from_catalog_api(
8682
model_catalog_rest_url=model_catalog_rest_url,
8783
model_registry_rest_headers=model_registry_rest_headers,
8884
source_label="invalid",
8985
)["size"]
9086

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

9489
@pytest.mark.parametrize(
9590
"randomly_picked_model_from_catalog_api_by_source,source_filter",

0 commit comments

Comments
 (0)