Skip to content

Commit 509f011

Browse files
authored
Merge branch 'main' into standardfix
2 parents 6616f1c + 70a5608 commit 509f011

File tree

15 files changed

+1506
-1128
lines changed

15 files changed

+1506
-1128
lines changed

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ repos:
3636
exclude: .*/__snapshots__/.*|.*-input\.json$|^semgrep\.yaml$
3737

3838
- repo: https://github.com/astral-sh/ruff-pre-commit
39-
rev: v0.15.6
39+
rev: v0.15.7
4040
hooks:
4141
- id: ruff
4242
- id: ruff-format
4343

4444
- repo: https://github.com/gitleaks/gitleaks
45-
rev: v8.30.1
45+
rev: v8.30.0
4646
hooks:
4747
- id: gitleaks
4848

@@ -90,7 +90,7 @@ repos:
9090
- id: actionlint
9191

9292
- repo: https://github.com/DavidAnson/markdownlint-cli2
93-
rev: v0.21.0
93+
rev: v0.22.0
9494
hooks:
9595
- id: markdownlint-cli2
9696
args:

docs/GETTING_STARTED.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,30 @@ To skip RHOAI/ODH-related tests (for example when running in upstream), pass `--
135135

136136
To run tests with admin client only, pass `--tc=use_unprivileged_client:False` to pytest.
137137

138-
### jira integration
139138

140-
To skip running tests which have open bugs, [pytest_jira](https://github.com/rhevm-qe-automation/pytest_jira) plugin is used.
141-
To run tests with jira integration, you need to set `PYTEST_JIRA_URL`, `PYTEST_JIRA_USERNAME` and `PYTEST_JIRA_TOKEN` environment variables.
142-
To make a test with jira marker, add: `@pytest.mark.jira(jira_id="RHOAIENG-0000", run=False)` to the test.
139+
### Skipping tests for known Jira issues
140+
141+
To skip a test that is affected by a known Jira bug, use `pytest.mark.xfail` with `is_jira_issue_open` as the condition:
142+
143+
```python
144+
from utilities.jira import is_jira_issue_open
145+
146+
@pytest.mark.xfail(condition=is_jira_issue_open(jira_id="RHOAIENG-12345"), reason="RHOAIENG-12345", run=False)
147+
def test_example(self):
148+
...
149+
```
150+
151+
- `condition=is_jira_issue_open(...)` checks the Jira issue status at collection time. If the issue is open, the test is marked as xfail.
152+
- `run=False` skips execution entirely while the issue is open. Once the issue is closed/resolved, the test runs normally.
153+
- If Jira is unreachable, the issue is assumed open and the test is skipped.
154+
155+
The following environment variables must be set for Jira connectivity:
156+
157+
```bash
158+
export PYTEST_JIRA_URL=<your_jira_url>
159+
export PYTEST_JIRA_USERNAME=<username>
160+
export PYTEST_JIRA_TOKEN=<token>
161+
```
143162

144163
### Running containerized tests
145164

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ dependencies = [
6161
"grpcio-reflection",
6262
"portforward>=0.7.1",
6363
"pytest-testconfig>=0.2.0",
64-
"pytest-jira>=0.3.21",
64+
6565
"pygithub>=2.5.0",
6666
"timeout-sampler>=1.0.6",
6767
"shortuuid>=1.0.13",

pytest.ini

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ testpaths = tests
55
markers =
66
# General
77
polarion: Store polarion test ID
8-
jira: Store jira bug ID
8+
99
skip_on_disconnected: Mark tests that can only be run in deployments with Internet access i.e. not on disconnected clusters.
1010
parallel: marks tests that can run in parallel along with pytest-xdist
1111

@@ -63,6 +63,3 @@ addopts =
6363
--show-progress
6464
--tc-file=tests/global_config.py
6565
--tc-format=python
66-
--jira
67-
--jira-connection-error-strategy=skip
68-
--jira-disable-docs-search

tests/model_registry/model_catalog/db_constants.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,12 @@
191191
}
192192

193193
# SQL query for accuracy sorting database validation
194-
# Returns an ordered list of model names (context names) that have accuracy metrics.
194+
# Returns an ordered list of model display names that have accuracy metrics.
195+
# Context names are stored as "source_id:display_name", so we strip the prefix
196+
# using SUBSTRING to match the display names returned by the API.
195197
# Models are ordered by their overall_average (accuracy) value from artifact properties.
196-
# Only returns the model name column for easy comparison with API results.
197198
GET_MODELS_BY_ACCURACY_DB_QUERY = """
198-
SELECT c.name
199+
SELECT SUBSTRING(c.name FROM STRPOS(c.name, ':') + 1) AS display_name
199200
FROM "ArtifactProperty" ap
200201
JOIN "Artifact" a ON a.id = ap.artifact_id
201202
JOIN "Attribution" attr ON attr.artifact_id = a.id
@@ -205,12 +206,14 @@
205206
"""
206207

207208
# SQL query for accuracy sorting with task filter database validation
208-
# Returns an ordered list of model names (context names) that have accuracy metrics
209+
# Returns an ordered list of model display names that have accuracy metrics
209210
# and match the specified task filter.
211+
# Context names are stored as "source_id:display_name", so we strip the prefix
212+
# using SUBSTRING to match the display names returned by the API.
210213
# Models are ordered by their overall_average (accuracy) value from artifact properties.
211214
# The tasks field is stored as a JSON array, so we use LIKE pattern matching
212215
GET_MODELS_BY_ACCURACY_WITH_TASK_FILTER_DB_QUERY = """
213-
SELECT c.name
216+
SELECT SUBSTRING(c.name FROM STRPOS(c.name, ':') + 1) AS display_name
214217
FROM "ArtifactProperty" ap
215218
JOIN "Artifact" a ON a.id = ap.artifact_id
216219
JOIN "Attribution" attr ON attr.artifact_id = a.id

tests/model_registry/model_catalog/huggingface/test_huggingface_model_sorting.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ class TestHuggingFaceModelsSorting:
2828
[
2929
("ID", "ASC"),
3030
("ID", "DESC"),
31-
pytest.param("NAME", "ASC", marks=pytest.mark.xfail(reason="RHOAIENG-54579")),
32-
pytest.param("NAME", "DESC", marks=pytest.mark.xfail(reason="RHOAIENG-54579")),
31+
("NAME", "ASC"),
32+
("NAME", "DESC"),
3333
("CREATE_TIME", "ASC"),
3434
("CREATE_TIME", "DESC"),
3535
("LAST_UPDATE_TIME", "ASC"),

tests/model_registry/model_catalog/sorting/test_model_sorting.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ class TestAccuracySorting:
3232
],
3333
)
3434
@pytest.mark.tier1
35-
@pytest.mark.xfail(reason="RHOAIENG-54579")
3635
def test_accuracy_sorting_works_correctly(
3736
self: Self,
3837
admin_client: DynamicClient,

tests/model_serving/maas_billing/maas_subscription/conftest.py

Lines changed: 83 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,25 @@
2424
from tests.model_serving.maas_billing.maas_subscription.utils import (
2525
MAAS_DB_NAMESPACE,
2626
MAAS_SUBSCRIPTION_NAMESPACE,
27+
create_and_yield_api_key_id,
2728
create_api_key,
2829
create_maas_subscription,
2930
get_maas_postgres_resources,
3031
patch_llmisvc_with_maas_router_and_tiers,
32+
resolve_api_key_username,
3133
revoke_api_key,
34+
wait_for_auth_ready,
3235
wait_for_postgres_connection_log,
3336
wait_for_postgres_deployment_ready,
3437
)
3538
from tests.model_serving.maas_billing.utils import build_maas_headers
3639
from utilities.constants import DscComponents
3740
from utilities.general import generate_random_name
38-
from utilities.infra import create_inference_token, create_ns, login_with_user_password
41+
from utilities.infra import create_inference_token, create_ns, get_openshift_token, login_with_user_password
3942
from utilities.llmd_constants import ContainerImages, ModelStorage
4043
from utilities.llmd_utils import create_llmisvc
4144
from utilities.plugins.constant import OpenAIEnpoints
45+
from utilities.resources.auth import Auth
4246

4347
LOGGER = get_logger(name=__name__)
4448

@@ -662,22 +666,91 @@ def active_api_key_id(
662666
"""
663667
Create a single active API key and return its ID for revoke tests.
664668
"""
665-
key_name = f"e2e-fixture-key-{generate_random_name()}"
666-
_, body = create_api_key(
669+
yield from create_and_yield_api_key_id(
670+
request_session_http=request_session_http,
667671
base_url=base_url,
668672
ocp_user_token=ocp_token_for_actor,
669-
request_session_http=request_session_http,
670-
api_key_name=key_name,
673+
key_name_prefix="e2e-fixture-key",
671674
)
672-
LOGGER.info(f"active_api_key_id: created key id={body['id']}")
673-
yield body["id"]
674-
LOGGER.info(f"Fixture teardown: revoking key {body['id']}")
675-
revoke_api_key(
675+
676+
677+
@pytest.fixture(scope="function")
678+
def free_user_username(
679+
request_session_http: requests.Session,
680+
base_url: str,
681+
ocp_token_for_actor: str,
682+
active_api_key_id: str,
683+
) -> str:
684+
"""Resolve and return the free (non-admin) actor's username from their active API key."""
685+
username = resolve_api_key_username(
676686
request_session_http=request_session_http,
677687
base_url=base_url,
678-
key_id=body["id"],
688+
key_id=active_api_key_id,
679689
ocp_user_token=ocp_token_for_actor,
680690
)
691+
LOGGER.info(f"free_user_username: resolved username from key id={active_api_key_id}")
692+
return username
693+
694+
695+
@pytest.fixture(scope="function")
696+
def admin_username(
697+
request_session_http: requests.Session,
698+
base_url: str,
699+
admin_ocp_token: str,
700+
admin_active_api_key_id: str,
701+
) -> str:
702+
"""Resolve and return the admin actor's username from their active API key."""
703+
username = resolve_api_key_username(
704+
request_session_http=request_session_http,
705+
base_url=base_url,
706+
key_id=admin_active_api_key_id,
707+
ocp_user_token=admin_ocp_token,
708+
)
709+
LOGGER.info(f"admin_username: resolved username from key id={admin_active_api_key_id}")
710+
return username
711+
712+
713+
@pytest.fixture(scope="function")
714+
def admin_active_api_key_id(
715+
request_session_http: requests.Session,
716+
base_url: str,
717+
admin_ocp_token: str,
718+
) -> Generator[str, Any, Any]:
719+
"""Create an active API key as the admin user, yield its ID, and revoke on teardown."""
720+
yield from create_and_yield_api_key_id(
721+
request_session_http=request_session_http,
722+
base_url=base_url,
723+
ocp_user_token=admin_ocp_token,
724+
key_name_prefix="e2e-authz-admin",
725+
)
726+
727+
728+
@pytest.fixture(scope="class")
729+
def admin_ocp_token(admin_client: DynamicClient) -> Generator[str, Any, Any]:
730+
"""Temporarily adds dedicated-admins to Auth CR adminGroups so the admin token is recognised by MaaS."""
731+
auth = Auth(client=admin_client, name="auth")
732+
current_groups: list[str] = list(auth.instance.spec.adminGroups or [])
733+
patched_groups = list(set(current_groups + ["dedicated-admins"]))
734+
735+
auth_conditions = (auth.instance.status or {}).get("conditions") or []
736+
ready_before = next(
737+
(condition for condition in auth_conditions if condition.get("type") == "Ready"),
738+
{},
739+
)
740+
baseline_time: str = ready_before.get("lastTransitionTime", "")
741+
742+
LOGGER.info(f"admin_ocp_token: patching Auth CR adminGroups to {patched_groups}")
743+
with ResourceEditor(patches={auth: {"spec": {"adminGroups": patched_groups}}}):
744+
wait_for_auth_ready(auth=auth, baseline_time=baseline_time)
745+
auth_conditions_after = (auth.instance.status or {}).get("conditions") or []
746+
ready_after = next(
747+
(condition for condition in auth_conditions_after if condition.get("type") == "Ready"),
748+
{},
749+
)
750+
cleanup_baseline_time: str = ready_after.get("lastTransitionTime", "")
751+
yield get_openshift_token(client=admin_client)
752+
753+
wait_for_auth_ready(auth=auth, baseline_time=cleanup_baseline_time)
681754

682755

683756
@pytest.fixture(scope="function")

0 commit comments

Comments
 (0)