Skip to content

Commit 019e815

Browse files
authored
Merge branch 'main' into test/api-key-expiration
2 parents 7c78ded + 9fec2c5 commit 019e815

38 files changed

+473
-107
lines changed

.coderabbit.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@
55
inheritance: true
66

77
reviews:
8+
path_instructions:
9+
- path: "**/*.py"
10+
instructions: |
11+
- This project targets Python 3.14 (requires-python = '==3.14.*').
12+
- Per PEP 758 (https://peps.python.org/pep-0758/), bare 'except ExcA, ExcB:' without parentheses is valid syntax in Python 3.14+ and means catching both exceptions. Do not flag this as a Python 2-style except clause or suggest adding parentheses.
13+
- Focus on security, test structure and coding style adherence in new code introduced
14+
- Code should follow python, pytest best practices
15+
- Ensure we use https://github.com/RedHatQE/openshift-python-wrapper/ instead of direct oc calls when possible
16+
- Code reuse, test parameterization, proper test dependency should be also encouraged
17+
- Check CONSTITUTION and AGENTS files
818
review_status: false
919
changed_files_summary: true
1020
suggested_labels: true

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@ assignees: ''
1414

1515
<!-- Add any steps to reproduce the issue being reported or add any relevant stacktrace -->
1616

17+
## Expected behavior
18+
19+
A clear and concise description of what you expected to happen.
20+
1721
## Proposed Solution
1822

1923
<!-- If you have a suggestion for how to fix this, describe it here -->
2024

2125
## Additional Context
2226

23-
<!-- Any other information that might be helpful -->
27+
### Environment
28+
29+
<!-- Any other information that might be helpful, including environment -->

.github/pull_request_template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
- Fixes: <!-- github issue -->
1111
- JIRA: <!-- Jira information -->
1212

13-
## How it has been tested
13+
## Please review and indicate how it has been tested
1414

1515
- [ ] Locally
1616
- [ ] Jenkins

conftest.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def pytest_addoption(parser: Parser) -> None:
5454
serving_arguments_group = parser.getgroup(name="Serving arguments")
5555
model_validation_automation_group = parser.getgroup(name="Model Validation Automation")
5656
hf_group = parser.getgroup(name="Hugging Face")
57+
logging_group = parser.getgroup(name="Logging")
5758
model_registry_group = parser.getgroup(name="Model Registry options")
5859
# AWS config and credentials options
5960
aws_group.addoption(
@@ -206,6 +207,14 @@ def pytest_addoption(parser: Parser) -> None:
206207
help="Indicates if the model registry tests are to be run against custom namespace",
207208
)
208209

210+
# Logging options
211+
logging_group.addoption(
212+
"--readable-logs",
213+
action="store_true",
214+
default=False,
215+
help="Write logs in human-readable format instead of JSON",
216+
)
217+
209218

210219
def pytest_cmdline_main(config: Any) -> None:
211220
config.option.basetemp = py_config["tmp_base_dir"] = f"{config.option.basetemp}-{shortuuid.uuid()}"
@@ -326,6 +335,7 @@ def pytest_sessionstart(session: Session) -> None:
326335
log_level=log_level,
327336
thread_name=thread_name,
328337
enable_console=enable_console_value,
338+
human_readable=session.config.getoption("--readable-logs"),
329339
)
330340

331341
# Now safe to log after configuration is applied (only to file when console disabled)

docs/GETTING_STARTED.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,3 +168,28 @@ To run tests in containerized environment:
168168
```bash
169169
podman run -v $HOME:/mnt/host:Z -e KUBECONFIG=/mnt/host/kubeconfig quay.io/opendatahub/opendatahub-tests
170170
```
171+
172+
## Debugging test failures
173+
174+
### Human-readable log output
175+
176+
By default, logs are written in JSON format to `pytest-tests.log`. To write logs in human-readable format instead, use the `--readable-logs` flag:
177+
178+
```bash
179+
uv run pytest --readable-logs tests/my_test.py
180+
```
181+
182+
This produces output like:
183+
184+
```text
185+
2026-03-25T10:48:56.864955+00:00 conftest INFO Writing tests log to pytest-tests.log (conftest.py:342)
186+
2026-03-25T10:48:56.881095+00:00 ocp_resources.resource INFO Trying to get client via new_client_from_config (resource.py:272)
187+
```
188+
189+
### Converting existing JSON logs
190+
191+
To convert an existing JSON log file to readable format:
192+
193+
```bash
194+
sed 's/^[^{]*//' pytest-tests.log | jq -r '"\(.timestamp) [\(.level)] \(.logger): \(.event)"'
195+
```

tests/model_explainability/evalhub/test_evalhub_health.py

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import pytest
22
from ocp_resources.route import Route
33

4-
from tests.model_explainability.evalhub.utils import validate_evalhub_health
4+
from tests.model_explainability.evalhub.utils import validate_evalhub_health, validate_evalhub_providers
55

66

77
@pytest.mark.parametrize(
88
"model_namespace",
99
[
1010
pytest.param(
11-
{"name": "test-evalhub-health"},
11+
{"name": "test-evalhub-health-providers"},
1212
),
1313
],
1414
indirect=True,
1515
)
1616
@pytest.mark.smoke
1717
@pytest.mark.model_explainability
18-
class TestEvalHubHealth:
19-
"""Tests for basic EvalHub service health and availability."""
18+
class TestEvalHub:
19+
"""Tests for basic EvalHub service health and providers."""
2020

2121
def test_evalhub_health_endpoint(
2222
self,
@@ -30,3 +30,19 @@ def test_evalhub_health_endpoint(
3030
token=current_client_token,
3131
ca_bundle_file=evalhub_ca_bundle_file,
3232
)
33+
34+
def test_evalhub_providers_list(
35+
self,
36+
current_client_token: str,
37+
evalhub_ca_bundle_file: str,
38+
evalhub_route: Route,
39+
model_namespace,
40+
) -> None:
41+
"""Test that the evaluations providers endpoint returns a non-empty list."""
42+
43+
validate_evalhub_providers(
44+
host=evalhub_route.host,
45+
token=current_client_token,
46+
ca_bundle_file=evalhub_ca_bundle_file,
47+
tenant=model_namespace.name,
48+
)

tests/model_explainability/evalhub/utils.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,31 @@
33
from tests.model_explainability.evalhub.constants import (
44
EVALHUB_HEALTH_PATH,
55
EVALHUB_HEALTH_STATUS_HEALTHY,
6+
EVALHUB_PROVIDERS_PATH,
67
)
78
from utilities.guardrails import get_auth_headers
89
from utilities.opendatahub_logger import get_logger
910

1011
LOGGER = get_logger(name=__name__)
1112

13+
TENANT_HEADER: str = "X-Tenant"
14+
15+
16+
def _build_headers(token: str, tenant: str | None = None) -> dict[str, str]:
17+
"""Build request headers with auth and optional tenant.
18+
19+
Args:
20+
token: Bearer token for authentication.
21+
tenant: Namespace for the X-Tenant header. Omitted if None.
22+
23+
Returns:
24+
Headers dict.
25+
"""
26+
headers = get_auth_headers(token=token)
27+
if tenant is not None:
28+
headers[TENANT_HEADER] = tenant
29+
return headers
30+
1231

1332
def validate_evalhub_health(
1433
host: str,
@@ -45,3 +64,26 @@ def validate_evalhub_health(
4564
f"Expected status '{EVALHUB_HEALTH_STATUS_HEALTHY}', got '{data['status']}'"
4665
)
4766
assert "timestamp" in data, "Health response missing 'timestamp' field"
67+
68+
69+
def validate_evalhub_providers(
70+
host: str,
71+
token: str,
72+
ca_bundle_file: str,
73+
tenant: str | None = None,
74+
) -> dict:
75+
"""Smoke test for the EvalHub providers endpoint."""
76+
url = f"https://{host}{EVALHUB_PROVIDERS_PATH}"
77+
78+
response = requests.get(
79+
url=url,
80+
headers=_build_headers(token=token, tenant=tenant),
81+
verify=ca_bundle_file,
82+
timeout=10,
83+
)
84+
response.raise_for_status()
85+
86+
data = response.json()
87+
assert data.get("items"), f"Smoke test failed: Providers list is empty for tenant {tenant}"
88+
89+
return data

tests/model_registry/conftest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from tests.model_registry.constants import (
2626
DB_BASE_RESOURCES_NAME,
2727
DB_RESOURCE_NAME,
28-
DEFAULT_CUSTOM_MODEL_CATALOG,
28+
DEFAULT_MCP_CATALOG_CM,
2929
KUBERBACPROXY_STR,
3030
MR_INSTANCE_BASE_NAME,
3131
MR_INSTANCE_NAME,
@@ -493,15 +493,15 @@ def mcp_servers_configmap_patch(
493493
model_registry_rest_headers: dict[str, str],
494494
) -> Generator[None]:
495495
"""
496-
Class-scoped fixture that patches the model-catalog-sources ConfigMap.
496+
Class-scoped fixture that patches the mcp-catalog-sources ConfigMap.
497497
498498
Sets two keys in the ConfigMap data:
499499
- sources.yaml: catalog source definition pointing to the MCP servers YAML,
500500
plus named queries for filtering by custom properties
501501
- mcp-servers.yaml: the actual MCP server definitions
502502
"""
503503
catalog_config_map = ConfigMap(
504-
name=DEFAULT_CUSTOM_MODEL_CATALOG,
504+
name=DEFAULT_MCP_CATALOG_CM,
505505
client=admin_client,
506506
namespace=model_registry_namespace,
507507
)

tests/model_registry/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class ModelRegistryEndpoints:
6767
}
6868
MODEL_REGISTRY_POD_FILTER: str = "component=model-registry"
6969
DEFAULT_CUSTOM_MODEL_CATALOG: str = "model-catalog-sources"
70+
DEFAULT_MCP_CATALOG_CM: str = "mcp-catalog-sources"
7071
SAMPLE_MODEL_NAME1 = "mistralai/Mistral-7B-Instruct-v0.3"
7172
CUSTOM_CATALOG_ID1: str = "sample_custom_catalog1"
7273
DEFAULT_MODEL_CATALOG_CM: str = "model-catalog-default-sources"

tests/model_registry/mcp_servers/config/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ def mcp_multi_source_configmap_patch(
3535
model_registry_rest_headers: dict[str, str],
3636
) -> Generator[None]:
3737
"""
38-
Class-scoped fixture that patches the model-catalog-sources ConfigMap
38+
Class-scoped fixture that patches the mcp-catalog-sources ConfigMap
3939
with two MCP catalog sources pointing to two different YAML files.
4040
"""
4141
catalog_config_map, current_data = get_mcp_catalog_sources(

0 commit comments

Comments
 (0)