diff --git a/tests/model_serving/maas_billing/maas_subscription/conftest.py b/tests/model_serving/maas_billing/maas_subscription/conftest.py index afd9ef51d..00bc0bf6e 100644 --- a/tests/model_serving/maas_billing/maas_subscription/conftest.py +++ b/tests/model_serving/maas_billing/maas_subscription/conftest.py @@ -203,6 +203,64 @@ def maas_headers_for_actor_api_key(maas_api_key_for_actor: str) -> dict[str, str return build_maas_headers(token=maas_api_key_for_actor) +@pytest.fixture(scope="class") +def api_key_bound_to_free_subscription( + request_session_http: requests.Session, + base_url: str, + ocp_token_for_actor: str, + maas_subscription_tinyllama_free: MaaSSubscription, + maas_subscription_controller_enabled_latest: None, + maas_gateway_api: None, + maas_api_gateway_reachable: None, +) -> Generator[str, Any, Any]: + """ + API key bound to the free subscription at mint time. Revoked on teardown. + """ + _, body = create_api_key( + base_url=base_url, + ocp_user_token=ocp_token_for_actor, + request_session_http=request_session_http, + api_key_name=f"e2e-auth-enforce-{generate_random_name()}", + subscription=maas_subscription_tinyllama_free.name, + ) + yield body["key"] + revoke_api_key( + request_session_http=request_session_http, + base_url=base_url, + key_id=body["id"], + ocp_user_token=ocp_token_for_actor, + ) + + +@pytest.fixture(scope="class") +def api_key_bound_to_premium_subscription( + request_session_http: requests.Session, + base_url: str, + ocp_token_for_actor: str, + maas_subscription_tinyllama_premium: MaaSSubscription, + maas_subscription_controller_enabled_latest: None, + maas_gateway_api: None, + maas_api_gateway_reachable: None, +) -> Generator[str, Any, Any]: + """ + API key bound to the premium subscription at mint time. Revoked on teardown. + """ + _, body = create_api_key( + base_url=base_url, + ocp_user_token=ocp_token_for_actor, + request_session_http=request_session_http, + api_key_name=f"e2e-sub-enforce-{generate_random_name()}", + subscription=maas_subscription_tinyllama_premium.name, + ) + yield body["key"] + revoke_api_key( + request_session_http=request_session_http, + base_url=base_url, + key_id=body["id"], + ocp_user_token=ocp_token_for_actor, + ) + + @pytest.fixture(scope="class") def maas_wrong_group_service_account_token( maas_api_server_url: str, diff --git a/tests/model_serving/maas_billing/maas_subscription/test_maas_auth_enforcement.py b/tests/model_serving/maas_billing/maas_subscription/test_maas_auth_enforcement.py index f004951fa..19b2c956a 100644 --- a/tests/model_serving/maas_billing/maas_subscription/test_maas_auth_enforcement.py +++ b/tests/model_serving/maas_billing/maas_subscription/test_maas_auth_enforcement.py @@ -33,18 +33,18 @@ def test_authorized_user_gets_200( self, request_session_http: requests.Session, model_url_tinyllama_free: str, - maas_headers_for_actor_api_key: dict[str, str], + api_key_bound_to_free_subscription: str, ) -> None: - payload = chat_payload_for_url(model_url=model_url_tinyllama_free) - + """ + Verify a free user with a subscription-bound API key can access the free model. + """ resp = poll_expected_status( request_session_http=request_session_http, model_url=model_url_tinyllama_free, - headers=maas_headers_for_actor_api_key, - payload=payload, + headers=build_maas_headers(token=api_key_bound_to_free_subscription), + payload=chat_payload_for_url(model_url=model_url_tinyllama_free), expected_statuses={200}, ) - LOGGER.info(f"test_authorized_user_gets_200 -> POST {model_url_tinyllama_free} returned {resp.status_code}") assert resp.status_code == 200, f"Expected 200, got {resp.status_code}: {resp.text[:200]}" @@ -99,10 +99,12 @@ def test_wrong_group_sa_denied_on_premium_model( model_url=model_url_tinyllama_premium, headers=maas_headers_for_wrong_group_sa, payload=payload, - expected_statuses={403}, + expected_statuses={401}, ) LOGGER.info( "test_wrong_group_sa_denied_on_premium_model -> " f"POST {model_url_tinyllama_premium} returned {resp.status_code}" ) - assert resp.status_code == 403, f"Expected 403, got {resp.status_code}: {resp.text[:200]}" + assert resp.status_code == 401, ( + f"Expected 401 (SA token not authenticated as MaaS user), got {resp.status_code}: {resp.text[:200]}" + ) diff --git a/tests/model_serving/maas_billing/maas_subscription/test_maas_sub_enforcement.py b/tests/model_serving/maas_billing/maas_subscription/test_maas_sub_enforcement.py index f50acadd7..723318cdc 100644 --- a/tests/model_serving/maas_billing/maas_subscription/test_maas_sub_enforcement.py +++ b/tests/model_serving/maas_billing/maas_subscription/test_maas_sub_enforcement.py @@ -4,7 +4,10 @@ import requests import structlog -from tests.model_serving.maas_billing.maas_subscription.utils import chat_payload_for_url +from tests.model_serving.maas_billing.maas_subscription.utils import ( + chat_payload_for_url, +) +from tests.model_serving.maas_billing.utils import build_maas_headers LOGGER = structlog.get_logger(name=__name__) @@ -31,18 +34,20 @@ def test_subscribed_user_gets_200( self, request_session_http: requests.Session, model_url_tinyllama_premium: str, - maas_headers_for_actor_api_key: dict[str, str], + api_key_bound_to_premium_subscription: str, ) -> None: + """ + Verify a premium user with a subscription-bound API key can access the premium model. + """ resp = request_session_http.post( url=model_url_tinyllama_premium, - headers=maas_headers_for_actor_api_key, + headers=build_maas_headers(token=api_key_bound_to_premium_subscription), json=chat_payload_for_url(model_url=model_url_tinyllama_premium), timeout=60, ) LOGGER.info(f"test_subscribed_user_gets_200 -> {resp.status_code}") assert resp.status_code == 200, f"Expected 200, got {resp.status_code}: {resp.text[:200]}" - @pytest.mark.tier1 @pytest.mark.parametrize("ocp_token_for_actor", [{"type": "premium"}], indirect=True) def test_explicit_subscription_header_works( self, diff --git a/tests/model_serving/maas_billing/maas_subscription/utils.py b/tests/model_serving/maas_billing/maas_subscription/utils.py index af118a2bc..80b555960 100644 --- a/tests/model_serving/maas_billing/maas_subscription/utils.py +++ b/tests/model_serving/maas_billing/maas_subscription/utils.py @@ -187,6 +187,7 @@ def create_api_key( request_timeout_seconds: int = 60, expires_in: str | None = None, raise_on_error: bool = True, + subscription: str | None = None, ) -> tuple[Response, dict[str, Any]]: """ Create an API key via MaaS API and return (response, parsed_body). @@ -199,12 +200,17 @@ def create_api_key( When None, no expiresIn field is sent and the key does not expire. raise_on_error: When True (default), raises AssertionError for non-200/201 responses. Set to False when testing error cases (e.g. 400 rejection). + subscription: Optional MaaSSubscription name to bind at mint time. + When provided, the key is bound to this subscription for inference. + When None, the API auto-selects the highest-priority subscription. """ api_keys_url = f"{base_url}/v1/api-keys" payload: dict[str, Any] = {"name": api_key_name} if expires_in is not None: payload["expiresIn"] = expires_in + if subscription is not None: + payload["subscription"] = subscription response = request_session_http.post( url=api_keys_url,