Skip to content

Commit 13889f4

Browse files
committed
test: add MaaS subscription tests
Signed-off-by: Swati Mukund Bagal <sbagal@redhat.com>
1 parent 6237646 commit 13889f4

File tree

2 files changed

+183
-0
lines changed

2 files changed

+183
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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+
)
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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+
MAAS_SUBSCRIPTION_HEADER = "x-maas-subscription"
17+
18+
19+
@pytest.mark.usefixtures(
20+
"maas_unprivileged_model_namespace",
21+
"maas_subscription_controller_enabled_latest",
22+
"maas_gateway_api",
23+
"maas_api_gateway_reachable",
24+
"maas_inference_service_tinyllama_premium",
25+
"maas_model_tinyllama_premium",
26+
"maas_auth_policy_tinyllama_premium",
27+
"maas_subscription_tinyllama_premium",
28+
)
29+
class TestSubscriptionWithoutAuthPolicy:
30+
"""
31+
Validates that holding a subscription is not sufficient for model access;
32+
the token must also be listed in a MaaSAuthPolicy for the model.
33+
"""
34+
35+
@pytest.mark.smoke
36+
@pytest.mark.parametrize("ocp_token_for_actor", [{"type": "free"}], indirect=True)
37+
def test_subscription_without_auth_policy_gets_403(
38+
self,
39+
request_session_http: requests.Session,
40+
admin_client: DynamicClient,
41+
model_url_tinyllama_premium: str,
42+
maas_headers_for_actor_api_key: dict[str, str],
43+
maas_model_tinyllama_premium,
44+
maas_subscription_tinyllama_premium,
45+
) -> None:
46+
"""
47+
Verify that a token with a valid subscription but NOT listed in any MaaSAuthPolicy
48+
for that model is denied with 403.
49+
50+
Given a premium model whose MaaSAuthPolicy only permits the premium group,
51+
When a free actor (system:authenticated but NOT premium group) is given a
52+
subscription for that model,
53+
Then the request should be denied with 403 because the AuthPolicy check
54+
fails regardless of subscription ownership.
55+
"""
56+
with create_maas_subscription(
57+
admin_client=admin_client,
58+
subscription_namespace=maas_subscription_tinyllama_premium.namespace,
59+
subscription_name="e2e-free-actor-premium-sub",
60+
owner_group_name="system:authenticated",
61+
model_name=maas_model_tinyllama_premium.name,
62+
model_namespace=maas_model_tinyllama_premium.namespace,
63+
tokens_per_minute=100,
64+
window="1m",
65+
priority=0,
66+
teardown=True,
67+
wait_for_resource=True,
68+
) as sub_for_free_actor:
69+
sub_for_free_actor.wait_for_condition(condition="Ready", status="True", timeout=300)
70+
71+
headers = dict(maas_headers_for_actor_api_key)
72+
headers[MAAS_SUBSCRIPTION_HEADER] = sub_for_free_actor.name
73+
74+
payload = chat_payload_for_url(model_url=model_url_tinyllama_premium)
75+
76+
LOGGER.info(
77+
f"Testing: free actor has subscription '{sub_for_free_actor.name}' "
78+
f"but is NOT in premium MaaSAuthPolicy — expecting 403"
79+
)
80+
81+
response = poll_expected_status(
82+
request_session_http=request_session_http,
83+
model_url=model_url_tinyllama_premium,
84+
headers=headers,
85+
payload=payload,
86+
expected_statuses={403},
87+
)
88+
89+
assert response.status_code == 403, (
90+
f"Expected 403 for token with subscription but not in MaaSAuthPolicy, "
91+
f"got {response.status_code}: {(response.text or '')[:200]}"
92+
)

0 commit comments

Comments
 (0)