<?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="0" failures="2" skipped="4" tests="43" time="1071.994" timestamp="2026-10-30T20:31:58.845154+00:00" hostname="ai-gateway-controller-group1b7f012375275290e06ba9ac07a9f273-pod"><testcase classname="tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_create_key_for_pending_subscription" time="36.064" /><testcase classname="tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_reject_key_for_unreconciled_subscription" time="16.403" /><testcase classname="tests.test_negative_security.TestAuthPolicyRemoval" name="test_authpolicy_deletion_revokes_access" time="18.486" /><testcase classname="tests.test_subscription.TestSubscriptionEnforcement" name="test_auth_pass_no_subscription_gets_403" time="3.190" /><testcase classname="tests.test_subscription.TestSubscriptionEnforcement" name="test_rate_limit_exhaustion_gets_429" time="20.412" /><testcase classname="tests.test_subscription.TestSubscriptionEnforcement" name="test_models_endpoint_exempt_from_rate_limiting" time="19.923" /><testcase classname="tests.test_subscription.TestMultipleSubscriptionsPerModel" name="test_user_in_one_of_two_subscriptions_gets_200" time="10.924" /><testcase classname="tests.test_subscription.TestUnlimitedSubscription" name="test_unlimited_and_token_rate_limits_are_mutually_exclusive" time="0.564" /><testcase classname="tests.test_subscription.TestUnlimitedSubscription" name="test_unlimited_key_is_not_rate_limited" time="12.671" /><testcase classname="tests.test_subscription.TestUnlimitedSubscription" name="test_unlimited_subscriptions_share_one_wasm_limit" time="0.145"><skipped type="pytest.skip" message="no Kuadrant WasmPlugin for gateway maas-default-gateway">/workspace/source/test/maas-e2e/test/e2e/tests/test_subscription.py:941: no Kuadrant WasmPlugin for gateway maas-default-gateway</skipped></testcase><testcase classname="tests.test_subscription.TestUnlimitedSubscription" name="test_unlimited_usage_is_metered" time="25.478" /><testcase classname="tests.test_subscription.TestAllUnlimitedModel" name="test_all_unlimited_model_is_not_denied" time="21.014" /><testcase classname="tests.test_subscription.TestMultipleAuthPoliciesPerModel" name="test_two_auth_policies_or_logic" time="21.506" /><testcase classname="tests.test_subscription.TestMultipleAuthPoliciesPerModel" name="test_delete_one_auth_policy_other_still_works" time="1.240" /><testcase classname="tests.test_subscription.TestCascadeDeletion" name="test_delete_subscription_rebuilds_trlp" time="19.696" /><testcase classname="tests.test_subscription.TestCascadeDeletion" name="test_trlp_persists_during_multi_subscription_deletion" time="41.168" /><testcase classname="tests.test_subscription.TestCascadeDeletion" name="test_delete_last_subscription_denies_access" time="22.076" /><testcase classname="tests.test_subscription.TestCascadeDeletion" name="test_unconfigured_model_denied_by_gateway_auth" time="0.700" /><testcase classname="tests.test_subscription.TestRateLimitIdentityGenerations" name="test_legacy_and_short_identities_coexist" time="33.105" /><testcase classname="tests.test_subscription.TestManagedAnnotation" name="test_authpolicy_managed_false_prevents_update" time="0.335"><skipped type="pytest.skip" message="gateway-only mode: per-model AuthPolicy is not created">/workspace/source/test/maas-e2e/test/e2e/tests/test_subscription.py:1728: gateway-only mode: per-model AuthPolicy is not created</skipped></testcase><testcase classname="tests.test_subscription.TestManagedAnnotation" name="test_trlp_managed_false_prevents_update" time="6.245" /><testcase classname="tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_with_access_but_no_subscription_gets_403" time="22.300" /><testcase classname="tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_single_subscription_auto_selects" time="83.641" /><testcase classname="tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_group_based_auth_but_no_subscription_gets_403" time="21.869" /><testcase classname="tests.test_subscription.TestStatusReporting" name="test_subscription_degraded_trlp_blocks_inference" time="99.027" /><testcase classname="tests.test_subscription.TestDegradedSubscriptionFiltering" name="test_failed_subscription_blocks_inference" time="73.758"><failure message="RuntimeError: Failed to get maassubscription/e2e-failed-sub-inf in namespace 'models-as-a-service': E1030 20:42:20.713287  204655 request.go:1360] &quot;Unexpected error when reading response body&quot; err=&quot;http2: client connection lost&quot;&#10;error: unexpected error when reading response body. Please retry. Original error: http2: client connection lost">self = &lt;test_subscription.TestDegradedSubscriptionFiltering object at 0x7f209d3b1190&gt;

    @pytest.mark.serial
    def test_failed_subscription_blocks_inference(self):
        """
        Test: Failed subscription blocks inference via OPA rule.
    
        Setup:
        1. Create subscription with valid model (starts Active)
        2. Create API key
        3. Manually patch subscription to Failed phase
        4. Verify inference is rejected by OPA (403)
    
        Note: We use manual patching because naturally creating a Failed subscription
        requires only invalid models, which don't have routes (404 before OPA runs).
        """
        ns = _ns()
        subscription_name = "e2e-failed-sub-inf"
        auth_name = "e2e-failed-sub-inf-auth"
        sa_name = "e2e-failed-sub-inf-sa"
    
        try:
            oc_token = _create_sa_token(sa_name, namespace=MODEL_NAMESPACE)
            sa_user = _sa_to_user(sa_name, namespace=MODEL_NAMESPACE)
    
            # Create auth policy for valid model
            _create_test_auth_policy(auth_name, MODEL_REF, users=[sa_user])
    
            # Create subscription with valid model (will be Active)
            _create_test_subscription(subscription_name, MODEL_REF, users=[sa_user])
    
            cr = _wait_for_maas_subscription_phase(subscription_name, "Active")
    
            # This test overrides controller-owned status to exercise the gateway
            # rejection path. Let initial TRLP/discovery reconciliation settle first,
            # and run serially so a queued reconcile from parallel test activity does
            # not overwrite the injected Failed phase before the inference request.
&gt;           _wait_for_subscription_inference_ready(
                subscription_name,
                MODEL_REF,
                namespace=ns,
                model_namespace=MODEL_NAMESPACE,
            )

test/maas-e2e/test/e2e/tests/test_subscription.py:3083: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
test/maas-e2e/test/e2e/tests/test_helper.py:1840: in _wait_for_subscription_inference_ready
    return _wait_for_subscription_trlp_status(
test/maas-e2e/test/e2e/tests/test_helper.py:1763: in _wait_for_subscription_trlp_status
    cr = _get_cr("maassubscription", name, namespace)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kind = 'maassubscription', name = 'e2e-failed-sub-inf'
namespace = 'models-as-a-service', oc_timeout = None, oc_retry_budget = None

    def _get_cr(kind, name, namespace=None, *, oc_timeout=None, oc_retry_budget=None):
        """Get a CR as dict, or None if not found. Retries on transient errors.
    
        Returns None only when the resource genuinely does not exist (server NotFound).
        Raises RuntimeError for other failures (RBAC, missing CRD, transport errors
        that persist after retries) so callers can distinguish infrastructure issues
        from true absence.
        """
        namespace = namespace or _ns()
        result = _run_oc(
            ["oc", "get", kind, name, "-n", namespace, "-o", "json"],
            timeout=oc_timeout,
            retry_budget=oc_retry_budget,
        )
    
        if result.returncode == 0:
            return json.loads(result.stdout)
    
        if _is_not_found_error(result.stderr):
            return None
    
        log.error(f"Failed to get {kind}/{name} in namespace '{namespace}': {result.stderr.strip()}")
&gt;       raise RuntimeError(
            f"Failed to get {kind}/{name} in namespace '{namespace}': {result.stderr.strip()}"
        )
E       RuntimeError: Failed to get maassubscription/e2e-failed-sub-inf in namespace 'models-as-a-service': E1030 20:42:20.713287  204655 request.go:1360] "Unexpected error when reading response body" err="http2: client connection lost"
E       error: unexpected error when reading response body. Please retry. Original error: http2: client connection lost

test/maas-e2e/test/e2e/tests/test_helper.py:874: RuntimeError</failure></testcase><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_single_subscription_auto_select" time="122.877" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_explicit_subscription_header" time="1.369" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_models_filtered_by_subscription" time="1.455" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_different_modelrefs_same_model_id" time="52.005" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_empty_model_list" time="13.904" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_central_models_endpoint_exempt_from_rate_limiting" time="21.007" /><testcase classname="tests.test_tenant.TestTenantLifecycle" name="test_payload_processing_deployed_with_active_tenant" time="7.437" /><testcase classname="tests.test_tenant.TestTenantLifecycle" name="test_payload_processing_and_pre_processing_resources" time="0.303"><skipped type="pytest.skip" message="default payload-processing is praxis-extproc; MaasTenantConfig.spec.payloadProcessing.resources is MaaS IPP-only">/workspace/source/test/maas-e2e/test/e2e/tests/test_tenant.py:401: default payload-processing is praxis-extproc; MaasTenantConfig.spec.payloadProcessing.resources is MaaS IPP-only</skipped></testcase><testcase classname="tests.test_aitenant_lifecycle.TestAITenantRevocationOperandsSerial" name="test_aitenant_delete_completes_after_cleanup_serviceaccount_removed" time="36.200" /><testcase classname="tests.test_tenant_namespace_discovery.TestTenantDiscoveryDormantMode" name="test_dormant_mode_ignores_labeled_namespace" time="0.510"><skipped type="pytest.skip" message="Dormant-mode test mutates controller flags; set ENABLE_TENANT_DISCOVERY_DORMANT_E2E=true">/workspace/source/test/maas-e2e/test/e2e/tests/test_tenant_namespace_discovery.py:359: Dormant-mode test mutates controller flags; set ENABLE_TENANT_DISCOVERY_DORMANT_E2E=true</skipped></testcase><testcase classname="tests.test_multi_tenant_maas_api.TestPerTenantMaaSAPI" name="test_dual_listeners_keep_one_authconfig_per_route_rule" time="39.233"><failure message="AssertionError: Expected exactly one Ready and Available non-deleting Authorino AuthConfig per rule for odh-ai-gateway-infra/maas-api-route-e2e-authcfg-http-72e6e5; got {'rule-5': [('021d04473ab381151d247cb90c6ca266b9eb5abdd9024211b3b9108630f98eb4', 'e5b2bf55-c836-47bd-9086-7add766863c3', True), ('8eca78d6f1eb41b5e93d6cb108cda6e85b0e0bffd802a6f926354de3062cc6cc', 'c3c34e31-1956-4375-9d57-c101f69b62b5', True)], 'rule-1': [('3daec984bf845a6a6c2372be3e44ffb8882594439bd6247ca32b06504d536d2e', 'd7f1a...&#10;[+2 more lines; full report in element text]">self = &lt;test_multi_tenant_maas_api.TestPerTenantMaaSAPI object at 0x7f209d2505d0&gt;
dual_listener_tenant_case = {'suffix': '72e6e5', 'tenant_ns': 'ai-tenant-e2e-authcfg-http-72e6e5', 'tenant_label_name': 'e2e-authcfg-http-72e6e5', 'gateway_name': 'e2e-authcfg-http-72e6e5', ...}

    @pytest.mark.nightly
    @pytest.mark.serial
    def test_dual_listeners_keep_one_authconfig_per_route_rule(self, dual_listener_tenant_case):
        """One stable Authorino AuthConfig per rule when both listeners attach the route."""
        case = dual_listener_tenant_case
        gateway = wait_for_json(
            "gateway",
            case["gateway_name"],
            GATEWAY_NAMESPACE,
            predicate=lambda obj: {"http", "https"}
            &lt;= _gateway_listener_names_with_routes(obj),
            timeout=180,
        )
        listeners = {
            listener.get("name"): listener
            for listener in (gateway.get("spec") or {}).get("listeners") or []
        }
        assert {"http", "https"} &lt;= set(listeners), (
            f"{case['gateway_name']} must have both HTTP and HTTPS listeners, got {listeners!r}"
        )
        assert listeners["http"].get("protocol") == "HTTP" and listeners["http"].get("port") == 80
        assert listeners["https"].get("protocol") == "HTTPS" and listeners["https"].get("port") == 443
        assert listeners["http"].get("hostname") == listeners["https"].get("hostname"), (
            f"Test setup must use a shared hostname across listeners: {listeners!r}"
        )
    
        route_name = _tenant_api_names(case)["httproute"]
        route = wait_for_json(
            "httproute",
            route_name,
            INFRA_NAMESPACE,
            predicate=lambda obj: _route_accepted_by_gateway(obj, case["gateway_name"]),
            timeout=180,
        )
        authconfigs_by_rule = _wait_for_stable_route_authconfigs(route)
        rules = (route.get("spec") or {}).get("rules") or []
        expected_rules = {f"rule-{index}" for index in range(1, len(rules) + 1)}
        assert set(authconfigs_by_rule) == expected_rules
&gt;       assert all(
            len(observations) == 1 and observations[0][2]
            for observations in authconfigs_by_rule.values()
        ), (
            f"Expected exactly one Ready and Available non-deleting Authorino "
            f"AuthConfig per rule for "
            f"{INFRA_NAMESPACE}/{route_name}; got {authconfigs_by_rule!r}"
        )
E       AssertionError: Expected exactly one Ready and Available non-deleting Authorino AuthConfig per rule for odh-ai-gateway-infra/maas-api-route-e2e-authcfg-http-72e6e5; got {'rule-5': [('021d04473ab381151d247cb90c6ca266b9eb5abdd9024211b3b9108630f98eb4', 'e5b2bf55-c836-47bd-9086-7add766863c3', True), ('8eca78d6f1eb41b5e93d6cb108cda6e85b0e0bffd802a6f926354de3062cc6cc', 'c3c34e31-1956-4375-9d57-c101f69b62b5', True)], 'rule-1': [('3daec984bf845a6a6c2372be3e44ffb8882594439bd6247ca32b06504d536d2e', 'd7f1ae65-d00a-4517-9daf-6ea3903a1157', True), ('e5210f88a37fcac91bc21b62d2c9934fbb9b7e201c94a075b3c4a6e0aac9e27d', '3b8ca4e9-4385-4132-b3a8-522e1ad8c682', True)], 'rule-3': [('6364099657eefd5e1927d5aa97abd00d37282b16dd722365713757b16e38d33e', '3b0e141b-c06b-4c3b-9859-591e28065b6e', True), ('8b1be58a6c7d7eb93a305e419f72def1a658cd62e8dcc952c555aed9dfc53fa1', '04e82ce0-4867-45ff-ac2b-e4c25d4b9b3e', True)], 'rule-4': [('675a5f7687d450f7369fd0578898d865366ea227d5e0769e04bfe8cc28e1ed76', 'e7b6eb0f-ec5a-4628-93f8-ec594932fc45', True), ('e73e918a4fb223f33f01c75484b7a0dacdae459c8e036b7a2d0591f650bb6a29', 'd15bdaa5-cb30-43ad-b8c1-a56c194bd6c1', True)], 'rule-2': [('6af13ca9a0d9abc07fd55bbea5fc36eca0f2d5630e824e7241a8233bc9d3a02a', 'cef76299-a711-4d97-88d5-e596094095e7', True), ('86ec7513b212a6e38d38e82302f08ca84e8ed55fb585395d5abd488e0cbbd274', '35609e76-055c-422e-a80b-3b315a5f3157', True)], 'rule-6': [('9f2ba35be1050ed43e63534556c25ac092d8dda6436d6fdef1705df6f8a33fce', '8fcdd95c-051c-4e1e-8acf-3bfc6c81e987', True), ('e6845af9731c4757d97edb98489a2456ebccd84801638ff70dac091b55cc9534', 'cbda6ffa-787c-46bd-a51c-98ae8d6f023d', True)]}
E       assert False
E        +  where False = all(&lt;generator object TestPerTenantMaaSAPI.test_dual_listeners_keep_one_authconfig_per_route_rule.&lt;locals&gt;.&lt;genexpr&gt; at 0x7f209d47eb20&gt;)

test/maas-e2e/test/e2e/tests/test_multi_tenant_maas_api.py:381: AssertionError</failure></testcase><testcase classname="tests.test_multi_tenant_maas_api.TestPerTenantMaaSAPI" name="test_dual_listener_wasm_envoyfilter_generation_stays_stable" time="86.775" /><testcase classname="tests.test_embedding_inference.TestEmbeddingDefaultDeny" name="test_embedding_default_deny_403" time="0.274" /><testcase classname="tests.test_embedding_inference.TestEmbeddingPathRouting" name="test_embedding_path_based_200" time="7.945" /><testcase classname="tests.test_embedding_inference.TestEmbeddingPathRouting" name="test_embedding_bbr_llmisvc_200" time="0.345" /><testcase classname="tests.test_embedding_inference.TestEmbeddingGovernance" name="test_embedding_trlp_429" time="19.295" /><testcase classname="tests.test_embedding_inference.TestEmbeddingGovernance" name="test_embedding_with_governance_200" time="28.071" /></testsuite></testsuites>