<?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="0" failures="5" skipped="7" tests="100" time="780.394" timestamp="2026-07-15T14:05:11.924108+00:00" hostname="maas-group-test-97s9l-e2e-maas-openshift-pod"><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyCRUD" name="test_create_api_key" time="0.110" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyCRUD" name="test_list_api_keys" time="0.129" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyCRUD" name="test_revoke_api_key" time="0.085" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyAuthorization" name="test_admin_manage_other_users_keys" time="0.129" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyAuthorization" name="test_non_admin_cannot_access_other_users_keys" time="0.087" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_own_keys" time="0.237" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_other_user_forbidden" time="0.029" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_admin_can_revoke_any_user" time="0.090" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyExpiration" name="test_create_key_within_expiration_limit" time="0.029" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyExpiration" name="test_create_key_at_expiration_limit" time="0.029" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyExpiration" name="test_create_key_exceeds_expiration_limit" time="0.030" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyExpiration" name="test_create_key_without_expiration" time="0.029" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyExpiration" name="test_create_key_with_short_expiration" time="0.031" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyModelInference" name="test_api_key_model_access_success" time="4.900" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyModelInference" name="test_invalid_api_key_rejected" time="0.029" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyModelInference" name="test_no_auth_header_rejected" time="0.026" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyModelInference" name="test_revoked_api_key_rejected" time="2.131" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyModelInference" name="test_api_key_chat_completions" time="0.033" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyRevocationE2E" name="test_double_revoke_returns_404" time="0.085" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyRevocationE2E" name="test_revoke_nonexistent_key_returns_404" time="0.028" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyRevocationE2E" name="test_revoke_then_create_new_key_works" time="0.150" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyRevocationE2E" name="test_individual_revoke_multiple_keys" time="0.182" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeyRevocationE2E" name="test_revoke_keys_rejected_at_gateway" time="0.289" /><testcase classname="test.e2e.tests.test_api_keys.TestEphemeralKeyCleanup" name="test_cronjob_exists_and_configured" time="0.118" /><testcase classname="test.e2e.tests.test_api_keys.TestEphemeralKeyCleanup" name="test_cleanup_networkpolicy_exists" time="0.120" /><testcase classname="test.e2e.tests.test_api_keys.TestEphemeralKeyCleanup" name="test_create_ephemeral_key" time="0.102" /><testcase classname="test.e2e.tests.test_api_keys.TestEphemeralKeyCleanup" name="test_trigger_cleanup_preserves_active_keys" time="0.448" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_create_key_for_active_subscription" time="5.309" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_create_key_for_degraded_subscription" time="15.219" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_create_key_for_failed_subscription" time="15.390" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_create_key_for_pending_subscription" time="15.366" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_reject_key_for_unreconciled_subscription" time="18.801" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeySubscriptionFilter" name="test_search_filters_by_subscription" time="15.510" /><testcase classname="test.e2e.tests.test_api_keys.TestAPIKeySubscriptionFilter" name="test_search_without_subscription_returns_all" time="0.164" /><testcase classname="test.e2e.tests.test_namespace_scoping.TestMaaSAPIWatchNamespace" name="test_subscription_in_subscription_namespace_visible_to_api" time="0.000"><skipped type="pytest.skip" message="test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true">/workspace/source/test/e2e/tests/test_namespace_scoping.py:212: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="test.e2e.tests.test_namespace_scoping.TestMaaSAPIWatchNamespace" name="test_subscription_in_another_namespace_not_visible_to_api" time="0.000"><skipped type="pytest.skip" message="test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true">/workspace/source/test/e2e/tests/test_namespace_scoping.py:245: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="test.e2e.tests.test_namespace_scoping.TestMaaSControllerWatchNamespace" name="test_authpolicy_and_subscription_in_maas_subscription_namespace" time="0.000"><skipped type="pytest.skip" message="test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true">/workspace/source/test/e2e/tests/test_namespace_scoping.py:283: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="test.e2e.tests.test_namespace_scoping.TestMaaSControllerWatchNamespace" name="test_authpolicy_and_subscription_in_another_namespace" time="0.000"><skipped type="pytest.skip" message="test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true">/workspace/source/test/e2e/tests/test_namespace_scoping.py:320: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="test.e2e.tests.test_namespace_scoping.TestModelRef" name="test_auth_policy_model_ref" time="0.000"><skipped type="pytest.skip" message="test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true">/workspace/source/test/e2e/tests/test_namespace_scoping.py:377: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="test.e2e.tests.test_namespace_scoping.TestModelRef" name="test_subscription_model_ref" time="0.000"><skipped type="pytest.skip" message="test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true">/workspace/source/test/e2e/tests/test_namespace_scoping.py:453: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="test.e2e.tests.test_negative_security.TestHeaderSpoofing" name="test_injected_identity_headers_ignored" time="0.079" /><testcase classname="test.e2e.tests.test_negative_security.TestHeaderSpoofing" name="test_duplicate_subscription_headers_ignored" time="0.065" /><testcase classname="test.e2e.tests.test_negative_security.TestExpiredKeyRejection" name="test_expired_key_rejected_at_gateway" time="5.072" /><testcase classname="test.e2e.tests.test_negative_security.TestCrossModelAccess" name="test_key_cannot_access_model_outside_subscription" time="0.058" /><testcase classname="test.e2e.tests.test_negative_security.TestAuthPolicyRemoval" name="test_authpolicy_deletion_revokes_access" time="0.851" /><testcase classname="test.e2e.tests.test_negative_security.TestMissingModelRef" name="test_subscription_with_nonexistent_model_ref" time="1.044" /><testcase classname="test.e2e.tests.test_negative_security.TestMissingModelRef" name="test_authpolicy_with_nonexistent_model_ref" time="0.705" /><testcase classname="test.e2e.tests.test_negative_security.TestHeaderAbuse" name="test_special_characters_in_subscription_header" time="0.173" /><testcase classname="test.e2e.tests.test_negative_security.TestWebhookValidation" name="test_subscription_rejected_in_unlabeled_namespace" time="6.205" /><testcase classname="test.e2e.tests.test_negative_security.TestWebhookValidation" name="test_authpolicy_rejected_in_unlabeled_namespace" time="6.397" /><testcase classname="test.e2e.tests.test_subscription.TestAuthEnforcement" name="test_authorized_user_gets_200" time="0.081" /><testcase classname="test.e2e.tests.test_subscription.TestAuthEnforcement" name="test_no_auth_gets_401" time="0.026" /><testcase classname="test.e2e.tests.test_subscription.TestAuthEnforcement" name="test_invalid_token_gets_403" time="0.050" /><testcase classname="test.e2e.tests.test_subscription.TestAuthEnforcement" name="test_wrong_group_gets_403" time="0.029" /><testcase classname="test.e2e.tests.test_subscription.TestAPIKeySubscriptionBinding" name="test_create_api_key_uses_highest_priority_subscription" time="0.306" /><testcase classname="test.e2e.tests.test_subscription.TestAPIKeySubscriptionBinding" name="test_create_api_key_with_explicit_simulator_subscription" time="0.061" /><testcase classname="test.e2e.tests.test_subscription.TestAPIKeySubscriptionBinding" name="test_create_api_key_nonexistent_subscription_errors" time="0.248" /><testcase classname="test.e2e.tests.test_subscription.TestSubscriptionEnforcement" name="test_subscribed_user_gets_200" time="0.035" /><testcase classname="test.e2e.tests.test_subscription.TestSubscriptionEnforcement" name="test_auth_pass_no_subscription_gets_403" time="8.426" /><testcase classname="test.e2e.tests.test_subscription.TestSubscriptionEnforcement" name="test_rate_limit_exhaustion_gets_429" time="16.723" /><testcase classname="test.e2e.tests.test_subscription.TestSubscriptionEnforcement" name="test_models_endpoint_exempt_from_rate_limiting" time="17.827" /><testcase classname="test.e2e.tests.test_subscription.TestMultipleSubscriptionsPerModel" name="test_user_in_one_of_two_subscriptions_gets_200" time="4.401" /><testcase classname="test.e2e.tests.test_subscription.TestMultipleAuthPoliciesPerModel" name="test_two_auth_policies_or_logic" time="10.871" /><testcase classname="test.e2e.tests.test_subscription.TestMultipleAuthPoliciesPerModel" name="test_delete_one_auth_policy_other_still_works" time="12.548" /><testcase classname="test.e2e.tests.test_subscription.TestCascadeDeletion" name="test_delete_subscription_rebuilds_trlp" time="4.534" /><testcase classname="test.e2e.tests.test_subscription.TestCascadeDeletion" name="test_trlp_persists_during_multi_subscription_deletion" time="17.378" /><testcase classname="test.e2e.tests.test_subscription.TestCascadeDeletion" name="test_delete_last_subscription_denies_access" time="4.626" /><testcase classname="test.e2e.tests.test_subscription.TestCascadeDeletion" name="test_unconfigured_model_denied_by_gateway_auth" time="0.527" /><testcase classname="test.e2e.tests.test_subscription.TestOrderingEdgeCases" name="test_subscription_before_auth_policy" time="17.885" /><testcase classname="test.e2e.tests.test_subscription.TestManagedAnnotation" name="test_authpolicy_managed_false_prevents_update" time="4.271"><skipped type="pytest.skip" message="gateway-only mode: per-model AuthPolicy is not created">/workspace/source/test/e2e/tests/test_subscription.py:1056: gateway-only mode: per-model AuthPolicy is not created</skipped></testcase><testcase classname="test.e2e.tests.test_subscription.TestManagedAnnotation" name="test_trlp_managed_false_prevents_update" time="13.834" /><testcase classname="test.e2e.tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_with_both_access_and_subscription_gets_200" time="5.728" /><testcase classname="test.e2e.tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_with_access_but_no_subscription_gets_403" time="9.240" /><testcase classname="test.e2e.tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_with_subscription_but_no_access_gets_403" time="5.703" /><testcase classname="test.e2e.tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_single_subscription_auto_selects" time="11.587" /><testcase classname="test.e2e.tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_multiple_subscriptions_separate_keys_gets_200" time="9.750" /><testcase classname="test.e2e.tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_mint_api_key_denied_for_inaccessible_subscription" time="9.827" /><testcase classname="test.e2e.tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_group_based_access_gets_200" time="9.134" /><testcase classname="test.e2e.tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_group_based_auth_but_no_subscription_gets_403" time="9.227" /><testcase classname="test.e2e.tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_group_based_subscription_but_no_auth_gets_403" time="5.355" /><testcase classname="test.e2e.tests.test_subscription.TestStatusReporting" name="test_subscription_active_status_with_valid_model" time="5.235" /><testcase classname="test.e2e.tests.test_subscription.TestStatusReporting" name="test_subscription_failed_status_with_missing_model" time="4.803" /><testcase classname="test.e2e.tests.test_subscription.TestStatusReporting" name="test_authpolicy_active_status_with_valid_model" time="4.785" /><testcase classname="test.e2e.tests.test_subscription.TestStatusReporting" name="test_authpolicy_failed_status_with_missing_model" time="4.799" /><testcase classname="test.e2e.tests.test_subscription.TestStatusReporting" name="test_subscription_degraded_status_with_partial_models" time="5.163" /><testcase classname="test.e2e.tests.test_subscription.TestStatusReporting" name="test_subscription_degraded_trlp_blocks_inference" time="91.395" /><testcase classname="test.e2e.tests.test_subscription.TestStatusReporting" name="test_authpolicy_degraded_status_with_partial_models" time="4.790" /><testcase classname="test.e2e.tests.test_subscription.TestStatusReporting" name="test_subscription_status_transitions_on_model_deletion" time="16.451" /><testcase classname="test.e2e.tests.test_subscription.TestDegradedSubscriptionFiltering" name="test_degraded_healthy_model_allows_inference" time="15.271" /><testcase classname="test.e2e.tests.test_subscription.TestDegradedSubscriptionFiltering" name="test_failed_subscription_blocks_inference" time="15.528" /><testcase classname="test.e2e.tests.test_subscription.TestDegradedSubscriptionFiltering" name="test_models_endpoint_with_degraded_subscription_api_key" time="20.136" /><testcase classname="test.e2e.tests.test_subscription.TestDegradedSubscriptionFiltering" name="test_models_endpoint_with_degraded_subscription_kube_token" time="20.104" /><testcase classname="test.e2e.tests.test_models_endpoint.TestModelsEndpoint" name="test_single_subscription_auto_select" time="19.333" /><testcase classname="test.e2e.tests.test_models_endpoint.TestModelsEndpoint" name="test_explicit_subscription_header" time="13.531"><failure message="AssertionError: Expected at least one model in response, got 0. Data was: []&#10;assert 0 &gt; 0&#10; +  where 0 = len([])">self = &lt;test_models_endpoint.TestModelsEndpoint object at 0x7fcd205f0e50&gt;

    def test_explicit_subscription_header(self):
        """
        Test: K8s token with multiple subscriptions can list models by providing
        x-maas-subscription header.
    
        Expected: HTTP 200 with models from only the specified subscription.
    
        Note: Creates SA that has access to both simulator-subscription (via system:authenticated)
        and premium-simulator-subscription (by adding SA to its users list).
        Uses K8s token directly (not API key) since API keys ignore the header.
        """
        sa_name = "e2e-models-explicit-header-sa"
        sa_ns = "default"
        maas_ns = _ns()
        sa_user = None
    
        try:
            # Create service account - will be in system:authenticated group
            # This gives access to simulator-subscription automatically
            sa_token = _create_sa_token(sa_name, namespace=sa_ns)
            sa_user = _sa_to_user(sa_name, namespace=sa_ns)
    
            # Add SA to premium-simulator-subscription to give it access to a second subscription
            log.info(f"Adding {sa_user} to premium-simulator-subscription users")
            subprocess.run([
                "oc", "patch", "maassubscription", PREMIUM_SIMULATOR_SUBSCRIPTION,
                "-n", maas_ns,
                "--type=merge",
                "-p", json.dumps({"spec": {"owner": {"users": [sa_user]}}})
            ], check=True)
    
            _wait_reconcile()
    
            # Test: GET /v1/models WITH x-maas-subscription header using K8s token
            # Expected: Returns models from simulator-subscription only
            log.info("Testing: GET /v1/models with K8s token and explicit subscription header: simulator-subscription")
            url = f"{_maas_api_url()}/v1/models"
            r = _request_with_gateway_retry(
                requests.get,
                url,
                headers={
                    "Authorization": f"Bearer {sa_token}",  # K8s token, not API key
                    "x-maas-subscription": SIMULATOR_SUBSCRIPTION,
                },
            )
    
            assert r.status_code == 200, f"Expected 200 with explicit subscription header, got {r.status_code}: {r.text}"
    
            # Validate response structure
            data = r.json()
            assert data.get("object") == "list", f"Expected object='list', got {data.get('object')}"
            assert "data" in data, "Response missing 'data' field"
            models = data.get("data", []) if data.get("data") is not None else []
    
            # Should have at least one model from simulator-subscription
&gt;           assert len(models) &gt; 0, f"Expected at least one model in response, got {len(models)}. Data was: {data.get('data')}"
E           AssertionError: Expected at least one model in response, got 0. Data was: []
E           assert 0 &gt; 0
E            +  where 0 = len([])

test/e2e/tests/test_models_endpoint.py:407: AssertionError</failure></testcase><testcase classname="test.e2e.tests.test_models_endpoint.TestModelsEndpoint" name="test_empty_subscription_header_value" time="9.211" /><testcase classname="test.e2e.tests.test_models_endpoint.TestModelsEndpoint" name="test_models_filtered_by_subscription" time="14.365" /><testcase classname="test.e2e.tests.test_models_endpoint.TestModelsEndpoint" name="test_deduplication_same_model_multiple_refs" time="14.077"><failure message="AssertionError: Expected only 1 unique model ID (same modelRef listed twice), got 0: set()&#10;assert 0 == 1&#10; +  where 0 = len(set())">self = &lt;test_models_endpoint.TestModelsEndpoint object at 0x7fcd205f0610&gt;

    def test_deduplication_same_model_multiple_refs(self):
        """
        Test 6: Same modelRef listed twice should deduplicate to 1 entry.
    
        Creates a subscription with the SAME modelRef listed TWICE (different rate limits).
        The API deduplicates by (model ID, URL) and returns only 1 entry since both
        references point to the same backend service.
    
        The response includes subscription information showing which subscription(s)
        provide access to the model.
        """
        log.info("Test 6: Same modelRef twice should deduplicate (INTENDED behavior)")
    
        sa_name = "e2e-models-dedup-sa"
        sa_ns = "default"
        maas_ns = _ns()
        subscription_name = "e2e-dedup-subscription"
        auth_policy_name = "e2e-dedup-auth"
        api_key = None
    
        try:
            # Create SA with its own token
            sa_token = _create_sa_token(sa_name, namespace=sa_ns)
            sa_user = _sa_to_user(sa_name, namespace=sa_ns)
    
            # Create auth policy that grants access to the model
            log.info(f"Creating auth policy with access to {MODEL_REF}")
            auth_policy_cr = {
                "apiVersion": "maas.opendatahub.io/v1alpha1",
                "kind": "MaaSAuthPolicy",
                "metadata": {
                    "name": auth_policy_name,
                    "namespace": maas_ns,
                },
                "spec": {
                    "modelRefs": [{"name": MODEL_REF, "namespace": MODEL_NAMESPACE}],
                    "subjects": {
                        "users": [sa_user],
                        "groups": [],
                    },
                },
            }
            subprocess.run(
                ["oc", "apply", "-f", "-"],
                input=json.dumps(auth_policy_cr),
                text=True,
                check=True,
            )
    
            # Create subscription with the SAME model ref TWICE (guaranteed duplicates)
            log.info(f"Creating subscription with {MODEL_REF} listed twice (to test deduplication)")
            subscription_cr = {
                "apiVersion": "maas.opendatahub.io/v1alpha1",
                "kind": "MaaSSubscription",
                "metadata": {
                    "name": subscription_name,
                    "namespace": maas_ns,
                },
                "spec": {
                    "owner": {
                        "users": [sa_user],
                        "groups": [],
                    },
                    "modelRefs": [
                        {
                            "name": MODEL_REF,
                            "namespace": MODEL_NAMESPACE,
                            "tokenRateLimits": [{"limit": 100, "window": "1m"}],
                        },
                        {
                            "name": MODEL_REF,  # Same model ref again - guarantees duplicate
                            "namespace": MODEL_NAMESPACE,
                            "tokenRateLimits": [{"limit": 200, "window": "1m"}],
                        },
                    ],
                },
            }
            subprocess.run(
                ["oc", "apply", "-f", "-"],
                input=json.dumps(subscription_cr),
                text=True,
                check=True,
            )
    
            # Wait for subscription to reconcile before creating API key
            _wait_for_maas_subscription_phase(subscription_name, namespace=maas_ns)
    
            # Create API key bound to our test subscription
            api_key = _create_api_key(sa_token, name="e2e-dedup-test-key", subscription=subscription_name)
    
            # Wait for reconciliation
            _wait_reconcile()
    
            # Query /v1/models with our custom subscription
            log.info(f"Querying /v1/models with subscription: {subscription_name}")
            r = _get_models_with_gateway_retry(
                headers={
                    "Authorization": f"Bearer {api_key}",
                    "x-maas-subscription": subscription_name,
                },
            )
    
            assert r.status_code == 200, f"Expected 200, got {r.status_code}: {r.text}"
            data = r.json()
            models = data.get("data") or []
    
            # Models should be a list
            assert isinstance(models, list), "Models should be a list"
    
            # Get model IDs from response
            model_ids = [m["id"] for m in models]
            unique_ids = set(model_ids)
    
            log.info(f"#x1F4CA API Response: {len(models)} total model(s), {len(unique_ids)} unique ID(s)")
            log.info(f"   Model IDs: {model_ids}")
            log.info(f"   Unique IDs: {unique_ids}")
    
            # Should all be the same model ID (we only referenced one modelRef)
&gt;           assert len(unique_ids) == 1, \
                f"Expected only 1 unique model ID (same modelRef listed twice), got {len(unique_ids)}: {unique_ids}"
E               AssertionError: Expected only 1 unique model ID (same modelRef listed twice), got 0: set()
E               assert 0 == 1
E                +  where 0 = len(set())

test/e2e/tests/test_models_endpoint.py:690: AssertionError</failure></testcase><testcase classname="test.e2e.tests.test_models_endpoint.TestModelsEndpoint" name="test_different_modelrefs_same_model_id" time="137.115"><failure message="AssertionError: Expected only 1 unique model ID (both modelRefs serve facebook/opt-125m), got 0: set()&#10;assert 0 == 1&#10; +  where 0 = len(set())">self = &lt;test_models_endpoint.TestModelsEndpoint object at 0x7fcd205f0730&gt;

    def test_different_modelrefs_same_model_id(self):
        """
        Test 7: Different modelRefs serving same model ID return separate entries.
    
        Uses two DIFFERENT MaaSModelRefs (each listed ONCE) that both serve the
        SAME model ID:
        - MODEL_REF (facebook-opt-125m-simulated) → serves "facebook/opt-125m"
        - PREMIUM_MODEL_REF (premium-simulated-simulated-premium) → serves "facebook/opt-125m"
    
        The API deduplicates by (model ID, URL). Since these are different backend
        services with different URLs, they return as 2 separate entries even though
        they serve the same model ID.
    
        Each entry shows the same model ID but different URL and subscription.
        """
        log.info("Test 7: Different modelRefs same ID should deduplicate (INTENDED behavior)")
    
        sa_name = "e2e-models-diff-refs-sa"
        sa_ns = "default"
        maas_ns = _ns()
        subscription_name = "e2e-diff-refs-subscription"
        auth_policy_name = "e2e-diff-refs-auth"
        api_key = None
    
        try:
            # Create SA
            sa_token = _create_sa_token(sa_name, namespace=sa_ns)
            sa_user = _sa_to_user(sa_name, namespace=sa_ns)
    
            # Create auth policy with both modelRefs
            log.info(f"Creating auth policy with {MODEL_REF} and {PREMIUM_MODEL_REF}")
            auth_policy_cr = {
                "apiVersion": "maas.opendatahub.io/v1alpha1",
                "kind": "MaaSAuthPolicy",
                "metadata": {
                    "name": auth_policy_name,
                    "namespace": maas_ns,
                },
                "spec": {
                    "modelRefs": [
                        {"name": MODEL_REF, "namespace": MODEL_NAMESPACE},
                        {"name": PREMIUM_MODEL_REF, "namespace": MODEL_NAMESPACE},
                    ],
                    "subjects": {
                        "users": [sa_user],
                        "groups": [],
                    },
                },
            }
            subprocess.run(
                ["oc", "apply", "-f", "-"],
                input=json.dumps(auth_policy_cr),
                text=True,
                check=True,
            )
    
            # Create subscription with both modelRefs (each listed ONCE)
            log.info(f"Creating subscription with {MODEL_REF} and {PREMIUM_MODEL_REF}")
            subscription_cr = {
                "apiVersion": "maas.opendatahub.io/v1alpha1",
                "kind": "MaaSSubscription",
                "metadata": {
                    "name": subscription_name,
                    "namespace": maas_ns,
                },
                "spec": {
                    "owner": {
                        "users": [sa_user],
                        "groups": [],
                    },
                    "modelRefs": [
                        {
                            "name": MODEL_REF,
                            "namespace": MODEL_NAMESPACE,
                            "tokenRateLimits": [{"limit": 100, "window": "1m"}],
                        },
                        {
                            "name": PREMIUM_MODEL_REF,
                            "namespace": MODEL_NAMESPACE,
                            "tokenRateLimits": [{"limit": 200, "window": "1m"}],
                        },
                    ],
                },
            }
            subprocess.run(
                ["oc", "apply", "-f", "-"],
                input=json.dumps(subscription_cr),
                text=True,
                check=True,
            )
    
            # Wait for auth and subscription reconciliation before creating API key.
            # Both modelRefs must be governed before /v1/models probes each backend.
            _wait_for_maas_auth_policy_phase(
                auth_policy_name,
                namespace=maas_ns,
                timeout=120,
            )
            _wait_for_maas_subscription_phase(
                subscription_name,
                namespace=maas_ns,
                timeout=120,
                require_model_statuses=True,
            )
            _wait_for_model_ready(MODEL_REF, namespace=MODEL_NAMESPACE)
            _wait_for_model_ready(PREMIUM_MODEL_REF, namespace=MODEL_NAMESPACE)
    
            # Create API key bound to our test subscription
            api_key = _create_api_key(sa_token, name="e2e-diff-refs-test-key", subscription=subscription_name)
    
            _wait_reconcile()
    
            # Query /v1/models. Gateway policy propagation and backend model probes can
            # lag behind CR readiness, so wait until both modelRefs appear.
            log.info(f"Querying /v1/models with subscription: {subscription_name}")
            request_headers = {
                "Authorization": f"Bearer {api_key}",
                "x-maas-subscription": subscription_name,
            }
            deadline = time.time() + 120
            r = None
            models = []
            model_ids = []
            while time.time() &lt; deadline:
                r = _get_models_with_gateway_retry(headers=request_headers)
                if r.status_code == 200:
                    models = r.json().get("data") or []
                    assert isinstance(models, list), "Models should be a list"
                    model_ids = [m["id"] for m in models]
                    urls = [m.get("url") for m in models if m.get("id") == MODEL_NAME and m.get("url")]
                    if len(models) == 2 and len(set(urls)) == 2:
                        break
                    log.info(
                        "Models response not fully propagated yet; "
                        f"got {len(models)} entries: {model_ids}"
                    )
                else:
                    log.info(f"Waiting for /v1/models HTTP 200; got {r.status_code}: {r.text[:200]}")
                time.sleep(5)
    
            assert r is not None, "Expected /v1/models response, got none"
            assert r.status_code == 200, f"Expected 200, got {r.status_code}: {r.text}"
    
            assert isinstance(models, list), "Models should be a list"
    
            # Get model IDs from response
            unique_ids = set(model_ids)
    
            log.info(f"#x1F4CA API Response: {len(models)} total model(s), {len(unique_ids)} unique ID(s)")
            log.info(f"   Model IDs: {model_ids}")
            log.info(f"   Unique IDs: {unique_ids}")
            log.info("   Subscription had: 2 different modelRefs both serving 'facebook/opt-125m'")
    
            # Both modelRefs serve the same model ID
&gt;           assert len(unique_ids) == 1, \
                f"Expected only 1 unique model ID (both modelRefs serve {MODEL_NAME}), got {len(unique_ids)}: {unique_ids}"
E               AssertionError: Expected only 1 unique model ID (both modelRefs serve facebook/opt-125m), got 0: set()
E               assert 0 == 1
E                +  where 0 = len(set())

test/e2e/tests/test_models_endpoint.py:876: AssertionError</failure></testcase><testcase classname="test.e2e.tests.test_models_endpoint.TestModelsEndpoint" name="test_multiple_distinct_models_in_subscription" time="20.711"><failure message="AssertionError: Expected to find both distinct models {'test/e2e-distinct-model', 'test/e2e-distinct-model-2'}, but got set()&#10;assert set() == {'test/e2e-di...inct-model-2'}&#10;  &#10;  Extra items in the right set:&#10;  'test/e2e-distinct-model'&#10;  'test/e2e-distinct-model-2'&#10;  &#10;  Full diff:&#10;  + set()...&#10;  &#10;  ...Full output truncated (4 lines hidden), use '-vv' to show">self = &lt;test_models_endpoint.TestModelsEndpoint object at 0x7fcd204b03a0&gt;

    def test_multiple_distinct_models_in_subscription(self):
        """
        Test 8: Multiple distinct models should return exactly 2 entries (1 per unique ID).
    
        Uses pre-deployed models (both known to not have backend duplication issues):
        - DISTINCT_MODEL_REF (simulated-distinct) serving "test/e2e-distinct-model"
        - DISTINCT_MODEL_2_REF (simulated-distinct-2) serving "test/e2e-distinct-model-2"
    
        Creates a subscription with both models. The API should return exactly 2 entries
        (one for each distinct model ID), with no duplicates.
    
        This test validates that when backend models don't have duplication bugs, the
        API correctly returns one entry per distinct model ID.
        """
        log.info("Test 8: Multiple distinct models should return 2 entries")
    
        sa_name = "e2e-models-distinct-sa"
        sa_ns = "default"
        maas_ns = _ns()
        subscription_name = "e2e-distinct-models-subscription"
        auth_policy_name = "e2e-distinct-models-auth"
        api_key = None
    
        try:
            # Create SA
            sa_token = _create_sa_token(sa_name, namespace=sa_ns)
            sa_user = _sa_to_user(sa_name, namespace=sa_ns)
    
            # Create auth policy with both distinct models
            log.info(f"Creating auth policy with {DISTINCT_MODEL_REF} and {DISTINCT_MODEL_2_REF}")
            auth_policy_cr = {
                "apiVersion": "maas.opendatahub.io/v1alpha1",
                "kind": "MaaSAuthPolicy",
                "metadata": {
                    "name": auth_policy_name,
                    "namespace": maas_ns,
                },
                "spec": {
                    "modelRefs": [
                        {"name": DISTINCT_MODEL_REF, "namespace": MODEL_NAMESPACE},
                        {"name": DISTINCT_MODEL_2_REF, "namespace": MODEL_NAMESPACE},
                    ],
                    "subjects": {
                        "users": [sa_user],
                        "groups": [],
                    },
                },
            }
            subprocess.run(
                ["oc", "apply", "-f", "-"],
                input=json.dumps(auth_policy_cr),
                text=True,
                check=True,
            )
    
            # Create subscription with both distinct models
            log.info(f"Creating subscription with {DISTINCT_MODEL_REF} and {DISTINCT_MODEL_2_REF}")
            subscription_cr = {
                "apiVersion": "maas.opendatahub.io/v1alpha1",
                "kind": "MaaSSubscription",
                "metadata": {
                    "name": subscription_name,
                    "namespace": maas_ns,
                },
                "spec": {
                    "owner": {
                        "users": [sa_user],
                        "groups": [],
                    },
                    "modelRefs": [
                        {
                            "name": DISTINCT_MODEL_REF,
                            "namespace": MODEL_NAMESPACE,
                            "tokenRateLimits": [{"limit": 100, "window": "1m"}],
                        },
                        {
                            "name": DISTINCT_MODEL_2_REF,
                            "namespace": MODEL_NAMESPACE,
                            "tokenRateLimits": [{"limit": 100, "window": "1m"}],
                        },
                    ],
                },
            }
            subprocess.run(
                ["oc", "apply", "-f", "-"],
                input=json.dumps(subscription_cr),
                text=True,
                check=True,
            )
    
            # Wait for subscription to reconcile before creating API key
            _wait_for_maas_subscription_phase(subscription_name, namespace=maas_ns)
    
            # Wait for models to become Ready after governance pairing is created
            log.info("Waiting for models to reconcile and become Ready...")
            _wait_for_model_ready(DISTINCT_MODEL_REF, namespace=MODEL_NAMESPACE)
            _wait_for_model_ready(DISTINCT_MODEL_2_REF, namespace=MODEL_NAMESPACE)
    
            # Create API key bound to our test subscription
            api_key = _create_api_key(sa_token, name="e2e-distinct-models-test-key", subscription=subscription_name)
    
            _wait_reconcile()
    
            # Query /v1/models (use retry helper for gateway/Authorino propagation)
            log.info(f"Querying /v1/models with subscription: {subscription_name}")
            r = _request_with_gateway_retry(
                requests.get,
                f"{_maas_api_url()}/v1/models",
                headers={
                    "Authorization": f"Bearer {api_key}",
                    "x-maas-subscription": subscription_name,
                },
            )
    
            assert r.status_code == 200, f"Expected 200, got {r.status_code}: {r.text}"
            data = r.json()
            models = data.get("data") or []
    
            assert isinstance(models, list), "Models should be a list"
    
            # Get model IDs from response
            model_ids = [m["id"] for m in models]
            unique_ids = set(model_ids)
    
            log.info(f"#x1F4CA API Response: {len(models)} total model(s), {len(unique_ids)} unique ID(s)")
            log.info(f"   Model IDs: {model_ids}")
            log.info(f"   Unique IDs: {unique_ids}")
            log.info(f"   Subscription had: 2 modelRefs ({DISTINCT_MODEL_REF}, {DISTINCT_MODEL_2_REF})")
    
            # Verify we got BOTH expected model IDs
            expected_ids = {DISTINCT_MODEL_ID, DISTINCT_MODEL_2_ID}
&gt;           assert unique_ids == expected_ids, \
                f"Expected to find both distinct models {expected_ids}, but got {unique_ids}"
E               AssertionError: Expected to find both distinct models {'test/e2e-distinct-model', 'test/e2e-distinct-model-2'}, but got set()
E               assert set() == {'test/e2e-di...inct-model-2'}
E                 
E                 Extra items in the right set:
E                 'test/e2e-distinct-model'
E                 'test/e2e-distinct-model-2'
E                 
E                 Full diff:
E                 + set()...
E                 
E                 ...Full output truncated (4 lines hidden), use '-vv' to show

test/e2e/tests/test_models_endpoint.py:1043: AssertionError</failure></testcase><testcase classname="test.e2e.tests.test_models_endpoint.TestModelsEndpoint" name="test_user_token_returns_all_models" time="15.589"><failure message="AssertionError: Expected at least 2 models (from 2 subscriptions), got 0&#10;assert 0 &gt;= 2&#10; +  where 0 = len([])">self = &lt;test_models_endpoint.TestModelsEndpoint object at 0x7fcd1fe1c190&gt;

    def test_user_token_returns_all_models(self):
        """
        Test: User token automatically returns models from all subscriptions.
    
        Creates a user with access to TWO subscriptions containing different models.
        Queries without X-MaaS-Subscription header and validates:
        - Returns models from ALL accessible subscriptions
        - Each model includes subscriptions array showing which subscription(s) provide access
        - Models appearing in multiple subscriptions have aggregated subscription list
        """
        log.info("Test: User token returns models from all subscriptions")
    
        sa_name = "e2e-return-all-sa"
        sa_ns = "default"
        maas_ns = _ns()
        sub1_name = "e2e-return-all-sub1"
        sub2_name = "e2e-return-all-sub2"
        auth1_name = "e2e-return-all-auth1"
        auth2_name = "e2e-return-all-auth2"
    
        try:
            # Create SA
            sa_token = _create_sa_token(sa_name, namespace=sa_ns)
            sa_user = _sa_to_user(sa_name, namespace=sa_ns)
    
            # Create subscription 1 with DISTINCT_MODEL_REF
            log.info(f"Creating subscription 1 with {DISTINCT_MODEL_REF}")
            _create_test_auth_policy(auth1_name, DISTINCT_MODEL_REF, users=[sa_user])
            _create_test_subscription(sub1_name, DISTINCT_MODEL_REF, users=[sa_user])
    
            # Create subscription 2 with DISTINCT_MODEL_2_REF
            log.info(f"Creating subscription 2 with {DISTINCT_MODEL_2_REF}")
            _create_test_auth_policy(auth2_name, DISTINCT_MODEL_2_REF, users=[sa_user])
            _create_test_subscription(sub2_name, DISTINCT_MODEL_2_REF, users=[sa_user])
    
            _wait_for_maas_auth_policy_phase(auth1_name)
            _wait_for_maas_auth_policy_phase(auth2_name)
            _wait_for_maas_subscription_phase(sub1_name)
            _wait_for_maas_subscription_phase(sub2_name)
    
            # Wait for models to become Ready after governance pairing is created
            log.info("Waiting for models to reconcile and become Ready...")
            _wait_for_model_ready(DISTINCT_MODEL_REF, namespace=MODEL_NAMESPACE)
            _wait_for_model_ready(DISTINCT_MODEL_2_REF, namespace=MODEL_NAMESPACE)
    
            # Query with user token (no X-MaaS-Subscription header)
            log.info("Querying /v1/models with user token (no header)")
            r = _request_with_gateway_retry(
                requests.get,
                f"{_maas_api_url()}/v1/models",
                headers={
                    "Authorization": f"Bearer {sa_token}",
                },
            )
    
            assert r.status_code == 200, f"Expected 200, got {r.status_code}: {r.text}"
            data = r.json()
            models = data.get("data") or []
    
            # Should get models from BOTH subscriptions
            model_ids = [m["id"] for m in models]
            log.info(f"Got {len(models)} models: {model_ids}")
    
            # Validate we got models from both subscriptions
            # (At minimum we should see the 2 distinct models)
&gt;           assert len(models) &gt;= 2, \
                f"Expected at least 2 models (from 2 subscriptions), got {len(models)}"
E               AssertionError: Expected at least 2 models (from 2 subscriptions), got 0
E               assert 0 &gt;= 2
E                +  where 0 = len([])

test/e2e/tests/test_models_endpoint.py:1137: AssertionError</failure></testcase></testsuite></testsuites>