|
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +import pytest |
| 4 | +import requests |
| 5 | +from kubernetes.dynamic import DynamicClient |
| 6 | +from simple_logger.logger import get_logger |
| 7 | + |
| 8 | +from tests.model_serving.maas_billing.maas_subscription.utils import ( |
| 9 | + chat_payload_for_url, |
| 10 | + create_maas_subscription, |
| 11 | + poll_expected_status, |
| 12 | +) |
| 13 | + |
| 14 | +LOGGER = get_logger(name=__name__) |
| 15 | + |
| 16 | + |
| 17 | +@pytest.mark.usefixtures( |
| 18 | + "maas_unprivileged_model_namespace", |
| 19 | + "maas_subscription_controller_enabled_latest", |
| 20 | + "maas_gateway_api", |
| 21 | + "maas_api_gateway_reachable", |
| 22 | + "maas_inference_service_tinyllama_free", |
| 23 | + "maas_model_tinyllama_free", |
| 24 | + "maas_auth_policy_tinyllama_free", |
| 25 | + "maas_subscription_tinyllama_free", |
| 26 | +) |
| 27 | +class TestMultipleSubscriptionsNoHeader: |
| 28 | + """ |
| 29 | + Validates that a token qualifying for multiple subscriptions on the same model |
| 30 | + is denied when no x-maas-subscription header is provided to disambiguate. |
| 31 | + """ |
| 32 | + |
| 33 | + @pytest.mark.smoke |
| 34 | + @pytest.mark.parametrize("ocp_token_for_actor", [{"type": "free"}], indirect=True) |
| 35 | + def test_multiple_matching_subscriptions_no_header_gets_403( |
| 36 | + self, |
| 37 | + request_session_http: requests.Session, |
| 38 | + admin_client: DynamicClient, |
| 39 | + maas_free_group: str, |
| 40 | + maas_model_tinyllama_free, |
| 41 | + model_url_tinyllama_free: str, |
| 42 | + maas_subscription_tinyllama_free, |
| 43 | + maas_headers_for_actor_api_key: dict[str, str], |
| 44 | + maas_subscription_namespace, |
| 45 | + ) -> None: |
| 46 | + """ |
| 47 | + Verify that a token qualifying for multiple subscriptions receives 403 |
| 48 | + when no x-maas-subscription header is provided. |
| 49 | +
|
| 50 | + Given two subscriptions for the same model that the free actor qualifies for, |
| 51 | + when the actor sends a request without the x-maas-subscription header, |
| 52 | + then the request should be denied with 403 because the subscription |
| 53 | + selection is ambiguous. |
| 54 | + """ |
| 55 | + _ = maas_subscription_tinyllama_free |
| 56 | + |
| 57 | + with create_maas_subscription( |
| 58 | + admin_client=admin_client, |
| 59 | + subscription_namespace=maas_subscription_namespace.name, |
| 60 | + subscription_name="e2e-second-free-subscription", |
| 61 | + owner_group_name=maas_free_group, |
| 62 | + model_name=maas_model_tinyllama_free.name, |
| 63 | + model_namespace=maas_model_tinyllama_free.namespace, |
| 64 | + tokens_per_minute=500, |
| 65 | + window="1m", |
| 66 | + priority=5, |
| 67 | + teardown=True, |
| 68 | + wait_for_resource=True, |
| 69 | + ) as second_subscription: |
| 70 | + second_subscription.wait_for_condition(condition="Ready", status="True", timeout=300) |
| 71 | + |
| 72 | + payload = chat_payload_for_url(model_url=model_url_tinyllama_free) |
| 73 | + |
| 74 | + LOGGER.info( |
| 75 | + f"Testing: free actor has two subscriptions " |
| 76 | + f"('{maas_subscription_tinyllama_free.name}' and '{second_subscription.name}') " |
| 77 | + f"with no x-maas-subscription header — expecting 403" |
| 78 | + ) |
| 79 | + |
| 80 | + response = poll_expected_status( |
| 81 | + request_session_http=request_session_http, |
| 82 | + model_url=model_url_tinyllama_free, |
| 83 | + headers=maas_headers_for_actor_api_key, |
| 84 | + payload=payload, |
| 85 | + expected_statuses={403}, |
| 86 | + ) |
| 87 | + |
| 88 | + assert response.status_code == 403, ( |
| 89 | + f"Expected 403 when multiple subscriptions exist and no header is provided, " |
| 90 | + f"got {response.status_code}: {(response.text or '')[:200]}" |
| 91 | + ) |
0 commit comments