<?xml version="1.0" encoding="utf-8"?><testsuites name="pytest tests"><testsuite name="pytest" errors="1" failures="7" skipped="14" tests="165" time="1868.273" timestamp="2026-10-30T21:24:01.388495+00:00" hostname="ai-gateway-controller-group0e45c3b912b596bc4f0692e59afe8c08-pod"><testcase classname="tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_own_keys" time="204.994" /><testcase classname="tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_combined_user_and_subscription" time="28.738" /><testcase classname="tests.test_api_keys.TestAPIKeyCRUD" name="test_create_api_key" time="223.296" /><testcase classname="tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_admin_can_revoke_any_user" time="1.591" /><testcase classname="tests.test_api_keys.TestAPIKeyExpiration" name="test_expiration_policy_boundaries[at-limit]" time="0.302" /><testcase classname="tests.test_api_keys.TestAPIKeyExpiration" name="test_expiration_policy_boundaries[exceeds-limit]" time="0.303" /><testcase classname="tests.test_api_keys.TestAPIKeyExpiration" name="test_expiration_policy_boundaries[without-expiration]" time="0.302" /><testcase classname="tests.test_api_keys.TestAPIKeyExpiration" name="test_expiration_policy_boundaries[short-expiration]" time="0.300" /><testcase classname="tests.test_api_keys.TestAPIKeyModelInference" name="test_api_key_model_access_success" time="0.614" /><testcase classname="tests.test_api_keys.TestAPIKeyModelInference" name="test_invalid_api_key_rejected" time="0.248" /><testcase classname="tests.test_api_keys.TestAPIKeyModelInference" name="test_no_auth_header_rejected" time="0.211" /><testcase classname="tests.test_api_keys.TestAPIKeyModelInference" name="test_revoked_api_key_rejected" time="3.114" /><testcase classname="tests.test_api_keys.TestAPIKeyModelInference" name="test_api_key_chat_completions" time="0.266" /><testcase classname="tests.test_api_keys.TestAPIKeyRevocationE2E" name="test_double_revoke_returns_404" time="0.912" /><testcase classname="tests.test_api_keys.TestAPIKeyRevocationE2E" name="test_revoke_nonexistent_key_returns_404" time="0.304" /><testcase classname="tests.test_api_keys.TestAPIKeyRevocationE2E" name="test_revoke_then_create_new_key_works" time="1.414" /><testcase classname="tests.test_api_keys.TestAPIKeyRevocationE2E" name="test_individual_revoke_multiple_keys" time="1.997" /><testcase classname="tests.test_api_keys.TestAPIKeyExpiration" name="test_expiration_policy_boundaries[within-limit]" time="0.340" /><testcase classname="tests.test_api_keys.TestEphemeralKeyCleanup" name="test_cronjob_exists_and_configured" time="10.137" /><testcase classname="tests.test_api_keys.TestAPIKeyRevocationE2E" name="test_revoke_keys_rejected_at_gateway" time="3.020" /><testcase classname="tests.test_api_keys.TestEphemeralKeyCleanup" name="test_create_ephemeral_key" time="0.990" /><testcase classname="tests.test_api_keys.TestEphemeralKeyCleanup" name="test_trigger_cleanup_preserves_active_keys" time="0.483"><skipped type="pytest.skip" message="Cannot find maas-api pod in odh-ai-gateway-infra: error: error executing jsonpath &quot;{.items[0].metadata.name}&quot;: Error executing template: array index out of bounds: index 0, length 0. Printing more information for debugging the template:&#10;&#09;template was:&#10;&#09;&#09;{.items[0].metadata.name}&#10;&#09;object given to jsonpath engine was:&#10;&#09;&#09;map[string]interface {}{&quot;apiVersion&quot;:&quot;v1&quot;, &quot;items&quot;:[]interface {}{}, &quot;kind&quot;:&quot;List&quot;, &quot;metadata&quot;:map[string]interface {}{&quot;resourceVersion&quot;:&quot;&quot;}}">/workspace/source/test/maas-e2e/test/e2e/tests/test_api_keys.py:1429: Cannot find maas-api pod in odh-ai-gateway-infra: error: error executing jsonpath "{.items[0].metadata.name}": Error executing template: array index out of bounds: index 0, length 0. Printing more information for debugging the template:
	template was:
		{.items[0].metadata.name}
	object given to jsonpath engine was:
		map[string]interface {}{"apiVersion":"v1", "items":[]interface {}{}, "kind":"List", "metadata":map[string]interface {}{"resourceVersion":""}}</skipped></testcase><testcase classname="tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_create_key_for_active_subscription" time="23.131" /><testcase classname="tests.test_api_keys.TestEphemeralKeyCleanup" name="test_cleanup_networkpolicy_exists" time="0.153" /><testcase classname="tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_create_key_for_failed_subscription" time="15.497" /><testcase classname="tests.test_api_keys.TestAPIKeyCRUD" name="test_revoke_api_key" time="253.731" /><testcase classname="tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_by_subscription_forbidden_for_non_admin" time="0.351" /><testcase classname="tests.test_api_keys.TestAPIKeySubscriptionFilter" name="test_search_without_subscription_returns_all" time="11.804" /><testcase classname="tests.test_api_keys.TestAPIKeySubscriptionFilter" name="test_search_filters_by_subscription" time="31.674" /><testcase classname="tests.test_api_keys.TestAPIKeyCRUD" name="test_list_api_keys" time="263.532" /><testcase classname="tests.test_api_keys.TestAPIKeySubscriptionPhases" name="test_create_key_for_degraded_subscription" time="25.497" /><testcase classname="tests.test_api_keys.TestAPIKeyLabels" name="test_create_api_key_with_labels" time="0.609" /><testcase classname="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/maas-e2e/test/e2e/tests/test_namespace_scoping.py:214: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="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/maas-e2e/test/e2e/tests/test_namespace_scoping.py:247: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="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/maas-e2e/test/e2e/tests/test_namespace_scoping.py:287: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="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/maas-e2e/test/e2e/tests/test_namespace_scoping.py:324: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="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/maas-e2e/test/e2e/tests/test_namespace_scoping.py:382: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="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/maas-e2e/test/e2e/tests/test_namespace_scoping.py:456: test_namespace_scoping validates single-tenant dormant mode; skipped when ENABLE_TENANT_NAMESPACE_DISCOVERY=true</skipped></testcase><testcase classname="tests.test_negative_security.TestAPIKeyManagementIsolation" name="test_api_key_cannot_mint_another_api_key" time="1.069" /><testcase classname="tests.test_negative_security.TestHeaderSpoofing" name="test_forged_identity_headers_rejected_on_key_mint[username-only]" time="1.745" /><testcase classname="tests.test_negative_security.TestHeaderSpoofing" name="test_forged_identity_headers_rejected_on_key_mint[group-only]" time="1.780" /><testcase classname="tests.test_negative_security.TestHeaderSpoofing" name="test_forged_identity_headers_rejected_on_key_mint[keyname-only]" time="1.939" /><testcase classname="tests.test_negative_security.TestHeaderSpoofing" name="test_forged_identity_headers_rejected_on_key_mint[identity-envelope]" time="1.739" /><testcase classname="tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_by_subscription" time="28.683" /><testcase classname="tests.test_negative_security.TestHeaderSpoofing" name="test_forged_identity_headers_rejected_on_key_mint[admin-scope]" time="1.685" /><testcase classname="tests.test_negative_security.TestHeaderSpoofing" name="test_injected_identity_headers_rejected_on_inference" time="0.698" /><testcase classname="tests.test_negative_security.TestHeaderSpoofing" name="test_duplicate_subscription_headers_ignored" time="9.014" /><testcase classname="tests.test_negative_security.TestExpiredKeyRejection" name="test_expired_key_rejected_at_gateway" time="5.560" /><testcase classname="tests.test_api_keys.TestAPIKeyLabels" name="test_labels_validation_errors" time="1.225" /><testcase classname="tests.test_negative_security.TestMissingModelRef" name="test_subscription_with_nonexistent_model_ref" time="14.487" /><testcase classname="tests.test_api_keys.TestAPIKeyLabels" name="test_search_api_keys_by_labels" time="11.943" /><testcase classname="tests.test_negative_security.TestCrossModelAccess" name="test_key_cannot_access_model_outside_subscription" time="0.709" /><testcase classname="tests.test_api_keys.TestAPIKeyLabels" name="test_backward_compatibility_no_labels" time="0.929" /><testcase classname="tests.test_negative_security.TestInternalEndpointIsolation" name="test_internal_endpoint_not_routable[subscriptions-select]" time="0.358" /><testcase classname="tests.test_negative_security.TestInternalEndpointIsolation" name="test_internal_endpoint_not_routable[api-keys-validate]" time="10.371" /><testcase classname="tests.test_negative_security.TestInternalEndpointIsolation" name="test_internal_endpoint_not_routable[api-keys-cleanup]" time="0.367" /><testcase classname="tests.test_negative_security.TestInternalEndpointIsolation" name="test_v1_models_via_maas_api_prefix" time="0.469" /><testcase classname="tests.test_subscription.TestAuthEnforcement" name="test_authorized_user_gets_200" time="0.591" /><testcase classname="tests.test_subscription.TestAuthEnforcement" name="test_no_auth_gets_401" time="0.213" /><testcase classname="tests.test_subscription.TestAuthEnforcement" name="test_invalid_token_gets_403" time="0.367" /><testcase classname="tests.test_subscription.TestAuthEnforcement" name="test_wrong_group_gets_403" time="0.228" /><testcase classname="tests.test_subscription.TestAPIKeySubscriptionBinding" name="test_create_api_key_uses_highest_priority_subscription" time="3.151" /><testcase classname="tests.test_subscription.TestAPIKeySubscriptionBinding" name="test_create_api_key_with_explicit_simulator_subscription" time="0.693" /><testcase classname="tests.test_subscription.TestAPIKeySubscriptionBinding" name="test_create_api_key_nonexistent_subscription_errors" time="11.125" /><testcase classname="tests.test_api_keys.TestAPIKeyAuthorization" name="test_non_admin_cannot_access_other_users_keys" time="302.124" /><testcase classname="tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_dry_run_by_subscription" time="27.425" /><testcase classname="tests.test_negative_security.TestInternalEndpointIsolation" name="test_health_endpoint_accessible" time="0.207" /><testcase classname="tests.test_negative_security.TestHeaderAbuse" name="test_special_characters_in_subscription_header" time="1.891" /><testcase classname="tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_with_both_access_and_subscription_gets_200" time="26.476" /><testcase classname="tests.test_negative_security.TestMissingModelRef" name="test_authpolicy_with_nonexistent_model_ref" time="3.369" /><testcase classname="tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_other_user_forbidden" time="306.151" /><testcase classname="tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_multiple_subscriptions_separate_keys_gets_200" time="28.238" /><testcase classname="tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_missing_scope_returns_400" time="0.306" /><testcase classname="tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_group_based_subscription_but_no_auth_gets_403" time="16.052" /><testcase classname="tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_mint_api_key_denied_for_inaccessible_subscription" time="27.444" /><testcase classname="tests.test_subscription.TestSubscriptionEnforcement" name="test_subscribed_user_gets_200" time="0.416" /><testcase classname="tests.test_subscription.TestOrderingEdgeCases" name="test_subscription_before_auth_policy" time="18.707" /><testcase classname="tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_with_subscription_but_no_access_gets_403" time="16.001" /><testcase classname="tests.test_subscription.TestE2ESubscriptionFlow" name="test_e2e_group_based_access_gets_200" time="16.355" /><testcase classname="tests.test_subscription.TestDegradedSubscriptionFiltering" name="test_degraded_healthy_model_allows_inference" time="15.558" /><testcase classname="tests.test_api_keys.TestAPIKeyAuthorization" name="test_admin_manage_other_users_keys" time="386.228" /><testcase classname="tests.test_api_keys.TestAPIKeyBulkOperations" name="test_bulk_revoke_dry_run" time="2.358" /><testcase classname="tests.test_subscription.TestStatusReporting" name="test_authpolicy_active_status_with_valid_model" time="165.564" /><testcase classname="tests.test_subscription.TestStatusReporting" name="test_authpolicy_failed_status_with_missing_model" time="13.495" /><testcase classname="tests.test_subscription_list_endpoints.TestListSubscriptionsForModel" name="test_returns_subscriptions_for_model" time="36.217" /><testcase classname="tests.test_subscription_list_endpoints.TestListSubscriptionsForModel" name="test_unknown_model_returns_empty" time="1.009" /><testcase classname="tests.test_subscription_list_endpoints.TestSubscriptionModelAccessFiltering" name="test_filters_unauthorized_models" time="15.452" /><testcase classname="tests.test_subscription_list_endpoints.TestSubscriptionModelAccessFiltering" name="test_omits_subscription_with_no_authorized_models" time="11.793" /><testcase classname="tests.test_subscription.TestStatusReporting" name="test_subscription_active_status_with_valid_model" time="259.555" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_empty_subscription_header_value" time="28.360" /><testcase classname="tests.test_subscription.TestStatusReporting" name="test_subscription_degraded_status_with_partial_models" time="24.813" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_deduplication_same_model_multiple_refs" time="18.237" /><testcase classname="tests.test_subscription.TestStatusReporting" name="test_subscription_failed_status_with_missing_model" time="273.175" /><testcase classname="tests.test_subscription.TestStatusReporting" name="test_subscription_status_transitions_on_model_deletion" time="301.885" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_user_token_returns_all_models" time="39.921" /><testcase classname="tests.test_subscription.TestDegradedSubscriptionFiltering" name="test_models_endpoint_with_degraded_subscription_api_key" time="15.547" /><testcase classname="tests.test_subscription.TestStatusReporting" name="test_authpolicy_degraded_status_with_partial_models" time="267.886" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_multiple_distinct_models_in_subscription" time="23.163" /><testcase classname="tests.test_subscription_list_endpoints.TestListSubscriptions" name="test_returns_accessible_subscriptions" time="7.806" /><testcase classname="tests.test_subscription_list_endpoints.TestListSubscriptions" name="test_unauthenticated_returns_401[all-subscriptions]" time="259.578" /><testcase classname="tests.test_subscription_list_endpoints.TestListSubscriptions" name="test_unauthenticated_returns_401[subscriptions-for-model]" time="0.302" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_response_schema_and_metadata" time="1.047" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_api_key_with_deleted_subscription_403" time="56.578" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_api_key_scoped_to_subscription" time="24.519" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_multiple_api_keys_different_subscriptions" time="50.153" /><testcase classname="tests.test_subscription_list_endpoints.TestListSubscriptions" name="test_subscription_includes_model_refs" time="263.439" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_user_token_with_subscription_header_filters" time="15.559" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_access_denied_to_subscription_403" time="44.917" /><testcase classname="tests.test_subscription.TestDegradedSubscriptionFiltering" name="test_models_endpoint_with_degraded_subscription_kube_token" time="15.217" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_api_key_with_inaccessible_subscription_403" time="25.805" /><testcase classname="tests.test_subscription_list_endpoints.TestListSubscriptions" name="test_model_ref_display_name_and_description_enriched" time="16.253" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_service_account_token_multiple_subs_with_header" time="29.079" /><testcase classname="tests.test_smoke" name="test_tokens_endpoint_replaced_by_api_keys" time="2.254"><failure message="AssertionError: Expected 401/404/405 (endpoint replaced), got 500: Internal Server Error.&#10;  &#10;assert 500 in (401, 404, 405)&#10; +  where 500 = &lt;Response [500]&gt;.status_code">maas_api_base_url = 'https://maas.apps.9889fab16a1f2e4f7077.prod.konflux-ocp-ci.dev/maas-api'

    def test_tokens_endpoint_replaced_by_api_keys(maas_api_base_url: str):
        """
        Verify /v1/tokens endpoint has been replaced by the API key system.
        The endpoint may return 401 (auth required), 404 (removed), or 405 (method not allowed).
        Any of these indicates the old token minting is no longer available.
        """
        url = f"{maas_api_base_url}/v1/tokens"
        r = requests.post(url, json={"expiration": "1m"}, timeout=20, verify=TLS_VERIFY)
        msg = f"[token] POST {url} (no auth) -&gt; {r.status_code}"
        log.info(msg)
        print(msg)
    
        # 401/404/405 all indicate the old token minting endpoint is gone/protected
&gt;       assert r.status_code in (401, 404, 405), f"Expected 401/404/405 (endpoint replaced), got {r.status_code}: {r.text[:400]}"
E       AssertionError: Expected 401/404/405 (endpoint replaced), got 500: Internal Server Error.
E         
E       assert 500 in (401, 404, 405)
E        +  where 500 = &lt;Response [500]&gt;.status_code

test/maas-e2e/test/e2e/tests/test_smoke.py:50: AssertionError</failure></testcase><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_api_key_ignores_subscription_header" time="28.849" /><testcase classname="tests.test_smoke" name="test_chat_completions_streaming_sse" time="1.840" /><testcase classname="tests.test_tenant.TestTenantLifecycle" name="test_tenant_ready_and_phase_healthy" time="0.433" /><testcase classname="tests.test_tenant.TestTenantContract" name="test_status_has_phase_and_conditions" time="0.142" /><testcase classname="tests.test_tenant.TestTenantContract" name="test_spec_is_well_formed" time="0.133" /><testcase classname="tests.test_tenant.TestTenantContract" name="test_conditions_use_kubernetes_metav1_shape" time="0.141" /><testcase classname="tests.test_tenant.TestTenantNoFalseOwnership" name="test_maas_user_crs_not_owned_by_tenant" time="0.446" /><testcase classname="tests.test_smoke" name="test_healthz_or_404" time="0.207" /><testcase classname="tests.test_config_tenant.TestConfigAnchorPresence" name="test_cluster_config_default_exists" time="0.538" /><testcase classname="tests.test_config_tenant.TestConfigAnchorPresence" name="test_cluster_config_not_terminating" time="0.284" /><testcase classname="tests.test_config_tenant.TestConfigTenantOwnership" name="test_maas_controller_deployment_does_not_list_config_owner_reference" time="0.146" /><testcase classname="tests.test_config_tenant.TestConfigTenantOwnership" name="test_default_aitenant_lists_config_owner_reference" time="0.564" /><testcase classname="tests.test_tenant_discovery" name="test_tenant_discovery_requires_auth" time="6.325" /><testcase classname="tests.test_config_tenant.TestConfigTenantOwnership" name="test_tenant_config_lists_config_owner_reference" time="0.147" /><testcase classname="tests.test_tenant_discovery" name="test_tenant_discovery_authenticated" time="0.001"><skipped type="pytest.skip" message="Skipping when Gateway uses ClusterIP + OpenShift Route (unsupported configuration). This mixes incompatible routing paradigms. Gateway has no external hostname in spec.listeners, so /v1/tenants returns an error. Supported configuration: LoadBalancer service with hostname in spec.listeners.">/workspace/source/test/maas-e2e/test/e2e/tests/test_tenant_discovery.py:79: Skipping when Gateway uses ClusterIP + OpenShift Route (unsupported configuration). This mixes incompatible routing paradigms. Gateway has no external hostname in spec.listeners, so /v1/tenants returns an error. Supported configuration: LoadBalancer service with hostname in spec.listeners.</skipped></testcase><testcase classname="tests.test_tenant_discovery" name="test_tenant_discovery_gateway_matches_deployment" time="0.000"><skipped type="pytest.skip" message="Skipping when Gateway uses ClusterIP + OpenShift Route (unsupported configuration). This mixes incompatible routing paradigms. Gateway has no external hostname in spec.listeners, so /v1/tenants returns an error. Supported configuration: LoadBalancer service with hostname in spec.listeners.">/workspace/source/test/maas-e2e/test/e2e/tests/test_tenant_discovery.py:155: Skipping when Gateway uses ClusterIP + OpenShift Route (unsupported configuration). This mixes incompatible routing paradigms. Gateway has no external hostname in spec.listeners, so /v1/tenants returns an error. Supported configuration: LoadBalancer service with hostname in spec.listeners.</skipped></testcase><testcase classname="tests.test_tenant_discovery" name="test_tenant_discovery_not_exposed_through_gateway" time="0.209" /><testcase classname="tests.test_aitenant_lifecycle.TestAITenantLifecycle" name="test_default_aitenant_bootstraps_maas_tenant_config_without_gateway_mutation" time="2.101" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_unauthenticated_request_401" time="12.852" /><testcase classname="tests.test_smoke" name="test_chat_completions_gateway_alive" time="0.980" /><testcase classname="tests.test_aitenant_lifecycle.TestAITenantLifecycle" name="test_aitenant_rejected_outside_ai_tenants_namespace" time="8.554" /><testcase classname="tests.test_aitenant_lifecycle.TestAITenantLifecycle" name="test_aitenant_create_bootstrap_resources" time="172.522" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_invalid_subscription_header_403" time="24.266" /><testcase classname="tests.test_tenant_discovery" name="test_tenant_discovery_with_invalid_token" time="3.095" /><testcase classname="tests.test_models_endpoint.TestModelsEndpoint" name="test_service_account_token_multiple_subs_no_header" time="46.885" /><testcase classname="tests.test_aitenant_lifecycle.TestAITenantLifecycle" name="test_aitenant_derives_non_default_tenant_namespace" time="177.799"><failure message="AssertionError: aitenant/e2e-derive-6f787654 in ai-tenants did not satisfy condition. Last object: {'apiVersion': 'maas.opendatahub.io/v1alpha1', 'kind': 'AITenant', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{&quot;apiVersion&quot;:&quot;maas.opendatahub.io/v1alpha1&quot;,&quot;kind&quot;:&quot;AITenant&quot;,&quot;metadata&quot;:{&quot;annotations&quot;:{},&quot;name&quot;:&quot;e2e-derive-6f787654&quot;,&quot;namespace&quot;:&quot;ai-tenants&quot;},&quot;spec&quot;:{}}\n'}, 'creationTimestamp': '2026-10-30T21:35:37Z', 'finalizers': ['maas.opendatahub.io/aitenant...&#10;[truncated; full report in element text]">self = &lt;test_aitenant_lifecycle.TestAITenantLifecycle object at 0x7f2132497b90&gt;

    def test_aitenant_derives_non_default_tenant_namespace(self):
        """RHOAIENG-66836: non-default AITenant must not use models-as-a-service tenant namespace."""
        suffix = uuid.uuid4().hex[:8]
        aitenant_name = f"e2e-derive-{suffix}"
        reserved_ns = _ns()
        expected_ns = f"ai-tenant-{aitenant_name}"
        gateway_name = aitenant_name
    
        try:
            _apply_gateway_fixture({"gateway_name": gateway_name, "aitenant_name": aitenant_name})
            _apply(
                {
                    "apiVersion": "maas.opendatahub.io/v1alpha1",
                    "kind": "AITenant",
                    "metadata": {"name": aitenant_name, "namespace": AITENANT_NAMESPACE},
                    "spec": {},
                }
            )
&gt;           aitenant = _wait_for_json(
                AITENANT_KIND,
                aitenant_name,
                AITENANT_NAMESPACE,
                predicate=_aitenant_ready,
                timeout=120,
            )

test/maas-e2e/test/e2e/tests/test_aitenant_lifecycle.py:689: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kind = 'aitenant', name = 'e2e-derive-6f787654', namespace = 'ai-tenants'
predicate = &lt;function _aitenant_ready at 0x7f2132ab9f80&gt;, timeout = 120
interval = 5

    def _wait_for_json(kind, name, namespace=None, *, predicate=None, timeout=180, interval=5):
        deadline = time.time() + timeout
        last_obj = None
        while time.time() &lt; deadline:
            obj = _get_json_or_none(kind, name, namespace)
            if obj is not None:
                last_obj = obj
                if predicate is None or predicate(obj):
                    return obj
            time.sleep(interval)
&gt;       raise AssertionError(f"{kind}/{name} in {namespace or '&lt;cluster&gt;'} did not satisfy condition. Last object: {last_obj}")
E       AssertionError: aitenant/e2e-derive-6f787654 in ai-tenants did not satisfy condition. Last object: {'apiVersion': 'maas.opendatahub.io/v1alpha1', 'kind': 'AITenant', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{"apiVersion":"maas.opendatahub.io/v1alpha1","kind":"AITenant","metadata":{"annotations":{},"name":"e2e-derive-6f787654","namespace":"ai-tenants"},"spec":{}}\n'}, 'creationTimestamp': '2026-10-30T21:35:37Z', 'finalizers': ['maas.opendatahub.io/aitenant-cleanup'], 'generation': 1, 'name': 'e2e-derive-6f787654', 'namespace': 'ai-tenants', 'resourceVersion': '44582', 'uid': '0f350aef-70d4-4ad0-8ecb-a94acaab1d77'}, 'spec': {}, 'status': {'conditions': [{'lastTransitionTime': '2026-10-30T21:35:39Z', 'message': 'waiting for MaasTenantConfig to report Ready', 'observedGeneration': 1, 'reason': 'TenantConfigNotReady', 'status': 'False', 'type': 'Ready'}], 'gatewayRef': {'name': 'e2e-derive-6f787654', 'namespace': 'openshift-ingress'}, 'phase': 'Pending', 'tenantNamespace': 'ai-tenant-e2e-derive-6f787654'}}

test/maas-e2e/test/e2e/tests/test_aitenant_lifecycle.py:170: AssertionError</failure></testcase><testcase classname="tests.test_aitenant_lifecycle.TestAITenantLifecycle" name="test_aitenant_delete_cleans_maas_resources_and_preserves_user_objects" time="171.276" /><testcase classname="tests.test_smoke" name="test_models_catalog" time="2.290"><error message="failed on setup with &quot;requests.exceptions.HTTPError: 500 Server Error: Internal Server Error for url: https://maas.apps.9889fab16a1f2e4f7077.prod.konflux-ocp-ci.dev/maas-api/v1/models&quot;">maas_api_base_url = 'https://maas.apps.9889fab16a1f2e4f7077.prod.konflux-ocp-ci.dev/maas-api'
headers = {'Authorization': 'Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Ik1ULUVwNXpicnJVTERHTEVnUzFCVlp5TWhVUVhVVUNuMnMzRXdObUZsNlUifQ.e...Jx6QT_7F0MuJzEI0lTy-Vacg64spL0ai1na4VH1b2D3e3QITDtrmLnjNjMojtBwrOw0QmI_a0lIUxueZA', 'Content-Type': 'application/json'}

    @pytest.fixture(scope="session")
    def model_catalog(maas_api_base_url: str, headers: dict):
        r = requests.get(f"{maas_api_base_url}/v1/models", headers=headers, timeout=45, verify=TLS_VERIFY)
&gt;       r.raise_for_status()

test/maas-e2e/test/e2e/tests/conftest.py:289: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = &lt;Response [500]&gt;

    def raise_for_status(self) -&gt; None:
        """Raises :class:`HTTPError`, if one occurred."""
    
        http_error_msg = ""
        if isinstance(self.reason, bytes):
            # We attempt to decode utf-8 first because some servers
            # choose to localize their reason strings. If the string
            # isn't utf-8, we fall back to iso-8859-1 for all other
            # encodings. (See PR #3538)
            try:
                reason = self.reason.decode("utf-8")
            except UnicodeDecodeError:
                reason = self.reason.decode("iso-8859-1")
        else:
            reason = self.reason
    
        if 400 &lt;= self.status_code &lt; 500:
            http_error_msg = (
                f"{self.status_code} Client Error: {reason} for url: {self.url}"
            )
    
        elif 500 &lt;= self.status_code &lt; 600:
            http_error_msg = (
                f"{self.status_code} Server Error: {reason} for url: {self.url}"
            )
    
        if http_error_msg:
&gt;           raise HTTPError(http_error_msg, response=self)
E           requests.exceptions.HTTPError: 500 Server Error: Internal Server Error for url: https://maas.apps.9889fab16a1f2e4f7077.prod.konflux-ocp-ci.dev/maas-api/v1/models

test/e2e/.venv/lib64/python3.11/site-packages/requests/models.py:1167: HTTPError</error></testcase><testcase classname="tests.test_tenant_namespace_discovery.TestTenantNamespaceDiscovery" name="test_unlabeled_namespace_ignored" time="107.038" /><testcase classname="tests.test_smoke" name="test_legacy_completions_optionally" time="1.219" /><testcase classname="tests.test_tenant_namespace_discovery.TestTenantNamespaceDiscovery" name="test_namespace_qualified_collision_prevention" time="216.882" /><testcase classname="tests.test_aitenant_lifecycle.TestAITenantLifecycle" name="test_aitenant_delete_releases_tenantconfig_finalizer" time="178.182" /><testcase classname="tests.test_tenant_namespace_discovery.TestTenantNamespaceDiscovery" name="test_labeled_tenant_namespace_is_discovered" time="298.225"><failure message="AssertionError: maasauthpolicy/e2e-policy-433058a7 in ai-tenant-e2e-mt-433058a7 did not satisfy condition. Last object: {'apiVersion': 'maas.opendatahub.io/v1alpha1', 'kind': 'MaaSAuthPolicy', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{&quot;apiVersion&quot;:&quot;maas.opendatahub.io/v1alpha1&quot;,&quot;kind&quot;:&quot;MaaSAuthPolicy&quot;,&quot;metadata&quot;:{&quot;annotations&quot;:{},&quot;name&quot;:&quot;e2e-policy-433058a7&quot;,&quot;namespace&quot;:&quot;ai-tenant-e2e-mt-433058a7&quot;},&quot;spec&quot;:{&quot;modelRefs&quot;:[{&quot;name&quot;:&quot;facebook-opt-125m-simulated...&#10;[truncated; full report in element text]">self = &lt;test_tenant_namespace_discovery.TestTenantNamespaceDiscovery object at 0x7fdce6a547d0&gt;

    def test_labeled_tenant_namespace_is_discovered(self):
        """1.1: Controller discovers labeled tenant namespaces and reconciles CRs."""
        case = new_discovery_case(use_default_gateway=True)
        try:
            apply_discovery_labels(case["tenant_ns"], case["tenant_label_name"])
            apply_tenant_cr(case["tenant_ns"], DEFAULT_GATEWAY_NAME)
            apply_maas_auth_policy(case["policy_name"], case["tenant_ns"])
            apply_maas_subscription(case["subscription_name"], case["tenant_ns"])
    
            wait_for_finalizer("maasauthpolicy", case["policy_name"], case["tenant_ns"], FINALIZER_AUTHPOLICY)
            wait_for_finalizer("maassubscription", case["subscription_name"], case["tenant_ns"], FINALIZER_SUBSCRIPTION)
&gt;           auth = wait_for_status_phase(
                "maasauthpolicy",
                case["policy_name"],
                case["tenant_ns"],
                expected_phase="Active",
            )

test/maas-e2e/test/e2e/tests/test_tenant_namespace_discovery.py:92: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
test/maas-e2e/test/e2e/tests/multitenancy_helpers.py:343: in wait_for_status_phase
    return wait_for_json(kind, name, namespace, predicate=_predicate, timeout=timeout, interval=interval)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kind = 'maasauthpolicy', name = 'e2e-policy-433058a7'
namespace = 'ai-tenant-e2e-mt-433058a7'
predicate = &lt;function wait_for_status_phase.&lt;locals&gt;._predicate at 0x7fdce698d8a0&gt;
timeout = 180, interval = 5

    def wait_for_json(
        kind: str,
        name: str,
        namespace: Optional[str] = None,
        *,
        predicate=None,
        timeout: int = 180,
        interval: int = 5,
    ) -&gt; dict:
        deadline = time.time() + timeout
        last_obj = None
        while time.time() &lt; deadline:
            obj = get_json_or_none(kind, name, namespace)
            if obj is not None:
                last_obj = obj
                if predicate is None or predicate(obj):
                    return obj
            time.sleep(interval)
&gt;       raise AssertionError(
            f"{kind}/{name} in {namespace or '&lt;cluster&gt;'} did not satisfy condition. Last object: {last_obj}"
        )
E       AssertionError: maasauthpolicy/e2e-policy-433058a7 in ai-tenant-e2e-mt-433058a7 did not satisfy condition. Last object: {'apiVersion': 'maas.opendatahub.io/v1alpha1', 'kind': 'MaaSAuthPolicy', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{"apiVersion":"maas.opendatahub.io/v1alpha1","kind":"MaaSAuthPolicy","metadata":{"annotations":{},"name":"e2e-policy-433058a7","namespace":"ai-tenant-e2e-mt-433058a7"},"spec":{"modelRefs":[{"name":"facebook-opt-125m-simulated","namespace":"llm"}],"subjects":{"groups":[{"name":"system:authenticated"}]}}}\n'}, 'creationTimestamp': '2026-10-30T21:36:11Z', 'finalizers': ['maas.opendatahub.io/authpolicy-cleanup'], 'generation': 1, 'name': 'e2e-policy-433058a7', 'namespace': 'ai-tenant-e2e-mt-433058a7', 'resourceVersion': '51942', 'uid': '6f6d3a22-0315-4d8f-bfff-2e0133f469f0'}, 'spec': {'modelRefs': [{'name': 'facebook-opt-125m-simulated', 'namespace': 'llm'}], 'subjects': {'groups': [{'name': 'system:authenticated'}]}}, 'status': {'conditions': [{'lastTransitionTime': '2026-10-30T21:36:53Z', 'message': 'Waiting for gateway AuthPolicy openshift-ingress/maas-gateway-auth to be accepted and enforced: AuthPolicy status observed generation 3, current generation is 33', 'observedGeneration': 1, 'reason': 'Unknown', 'status': 'Unknown', 'type': 'Ready'}], 'phase': 'Pending'}}

test/maas-e2e/test/e2e/tests/multitenancy_helpers.py:288: AssertionError</failure></testcase><testcase classname="tests.test_tenant_namespace_discovery.TestTenantNamespaceDiscovery" name="test_per_tenant_oidc_configuration" time="0.001"><skipped type="pytest.skip" message="OIDC_ISSUER_URL not set; per-tenant OIDC E2E requires external OIDC deploy">/workspace/source/test/maas-e2e/test/e2e/tests/test_tenant_namespace_discovery.py:190: OIDC_ISSUER_URL not set; per-tenant OIDC E2E requires external OIDC deploy</skipped></testcase><testcase classname="tests.test_tenant_namespace_discovery.TestLegacyDefaultNamespaceStillWorks" name="test_models_as_a_service_namespace_reconciles" time="185.543"><failure message="AssertionError: maasauthpolicy/e2e-default-ns-fc87fbf4 in models-as-a-service did not satisfy condition. Last object: {'apiVersion': 'maas.opendatahub.io/v1alpha1', 'kind': 'MaaSAuthPolicy', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{&quot;apiVersion&quot;:&quot;maas.opendatahub.io/v1alpha1&quot;,&quot;kind&quot;:&quot;MaaSAuthPolicy&quot;,&quot;metadata&quot;:{&quot;annotations&quot;:{},&quot;name&quot;:&quot;e2e-default-ns-fc87fbf4&quot;,&quot;namespace&quot;:&quot;models-as-a-service&quot;},&quot;spec&quot;:{&quot;modelRefs&quot;:[{&quot;name&quot;:&quot;facebook-opt-125m-simulated&quot;,&quot;n...&#10;[truncated; full report in element text]">self = &lt;test_tenant_namespace_discovery.TestLegacyDefaultNamespaceStillWorks object at 0x7f90e745c8d0&gt;

    def test_models_as_a_service_namespace_reconciles(self):
        ns = legacy_default_namespace()
        suffix = uuid.uuid4().hex[:8]
        policy_name = f"e2e-default-ns-{suffix}"
        try:
            apply_maas_auth_policy(policy_name, ns)
            wait_for_finalizer("maasauthpolicy", policy_name, ns, FINALIZER_AUTHPOLICY)
&gt;           wait_for_status_phase("maasauthpolicy", policy_name, ns, expected_phase="Active")

test/maas-e2e/test/e2e/tests/test_tenant_namespace_discovery.py:394: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
test/maas-e2e/test/e2e/tests/multitenancy_helpers.py:343: in wait_for_status_phase
    return wait_for_json(kind, name, namespace, predicate=_predicate, timeout=timeout, interval=interval)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kind = 'maasauthpolicy', name = 'e2e-default-ns-fc87fbf4'
namespace = 'models-as-a-service'
predicate = &lt;function wait_for_status_phase.&lt;locals&gt;._predicate at 0x7f90e72a8d60&gt;
timeout = 180, interval = 5

    def wait_for_json(
        kind: str,
        name: str,
        namespace: Optional[str] = None,
        *,
        predicate=None,
        timeout: int = 180,
        interval: int = 5,
    ) -&gt; dict:
        deadline = time.time() + timeout
        last_obj = None
        while time.time() &lt; deadline:
            obj = get_json_or_none(kind, name, namespace)
            if obj is not None:
                last_obj = obj
                if predicate is None or predicate(obj):
                    return obj
            time.sleep(interval)
&gt;       raise AssertionError(
            f"{kind}/{name} in {namespace or '&lt;cluster&gt;'} did not satisfy condition. Last object: {last_obj}"
        )
E       AssertionError: maasauthpolicy/e2e-default-ns-fc87fbf4 in models-as-a-service did not satisfy condition. Last object: {'apiVersion': 'maas.opendatahub.io/v1alpha1', 'kind': 'MaaSAuthPolicy', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{"apiVersion":"maas.opendatahub.io/v1alpha1","kind":"MaaSAuthPolicy","metadata":{"annotations":{},"name":"e2e-default-ns-fc87fbf4","namespace":"models-as-a-service"},"spec":{"modelRefs":[{"name":"facebook-opt-125m-simulated","namespace":"llm"}],"subjects":{"groups":[{"name":"system:authenticated"}]}}}\n'}, 'creationTimestamp': '2026-10-30T21:37:17Z', 'finalizers': ['maas.opendatahub.io/authpolicy-cleanup'], 'generation': 1, 'name': 'e2e-default-ns-fc87fbf4', 'namespace': 'models-as-a-service', 'resourceVersion': '53244', 'uid': 'ea6fb7f6-164d-4d57-83cd-bf16bbdfda52'}, 'spec': {'modelRefs': [{'name': 'facebook-opt-125m-simulated', 'namespace': 'llm'}], 'subjects': {'groups': [{'name': 'system:authenticated'}]}}, 'status': {'conditions': [{'lastTransitionTime': '2026-10-30T21:39:46Z', 'message': 'Waiting for gateway AuthPolicy openshift-ingress/maas-gateway-auth to be accepted and enforced: AuthPolicy status observed generation 3, current generation is 44', 'observedGeneration': 1, 'reason': 'Unknown', 'status': 'Unknown', 'type': 'Ready'}], 'phase': 'Pending'}}

test/maas-e2e/test/e2e/tests/multitenancy_helpers.py:288: AssertionError</failure></testcase><testcase classname="tests.test_aitenant_lifecycle.TestAITenantLifecycle" name="test_aitenant_migrates_and_removes_legacy_tenant" time="145.670" /><testcase classname="tests.test_tenant_namespace_discovery.TestTenantNamespaceDiscovery" name="test_dynamic_discovery_after_label_added" time="302.313"><failure message="AssertionError: maasauthpolicy/e2e-policy-22b8defe in ai-tenant-e2e-mt-22b8defe did not satisfy condition. Last object: {'apiVersion': 'maas.opendatahub.io/v1alpha1', 'kind': 'MaaSAuthPolicy', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{&quot;apiVersion&quot;:&quot;maas.opendatahub.io/v1alpha1&quot;,&quot;kind&quot;:&quot;MaaSAuthPolicy&quot;,&quot;metadata&quot;:{&quot;annotations&quot;:{},&quot;name&quot;:&quot;e2e-policy-22b8defe&quot;,&quot;namespace&quot;:&quot;ai-tenant-e2e-mt-22b8defe&quot;},&quot;spec&quot;:{&quot;modelRefs&quot;:[{&quot;name&quot;:&quot;facebook-opt-125m-simulated...&#10;[truncated; full report in element text]">self = &lt;test_tenant_namespace_discovery.TestTenantNamespaceDiscovery object at 0x7f04782f4e90&gt;

    def test_dynamic_discovery_after_label_added(self):
        """1.2 variant: Pre-existing CRs reconcile after namespace gains discovery labels."""
        case = new_discovery_case(use_default_gateway=True)
        try:
            ensure_namespace(case["tenant_ns"])
            apply_tenant_cr(case["tenant_ns"], DEFAULT_GATEWAY_NAME)
            apply_maas_auth_policy(case["policy_name"], case["tenant_ns"])
            time.sleep(10)
            before = get_json_or_none("maasauthpolicy", case["policy_name"], case["tenant_ns"])
            assert before is not None
            assert FINALIZER_AUTHPOLICY not in ((before.get("metadata") or {}).get("finalizers") or [])
    
            apply_discovery_labels(case["tenant_ns"], case["tenant_label_name"])
            wait_for_finalizer("maasauthpolicy", case["policy_name"], case["tenant_ns"], FINALIZER_AUTHPOLICY)
&gt;           wait_for_status_phase(
                "maasauthpolicy",
                case["policy_name"],
                case["tenant_ns"],
                expected_phase="Active",
            )

test/maas-e2e/test/e2e/tests/test_tenant_namespace_discovery.py:177: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
test/maas-e2e/test/e2e/tests/multitenancy_helpers.py:343: in wait_for_status_phase
    return wait_for_json(kind, name, namespace, predicate=_predicate, timeout=timeout, interval=interval)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

kind = 'maasauthpolicy', name = 'e2e-policy-22b8defe'
namespace = 'ai-tenant-e2e-mt-22b8defe'
predicate = &lt;function wait_for_status_phase.&lt;locals&gt;._predicate at 0x7f047802bc40&gt;
timeout = 180, interval = 5

    def wait_for_json(
        kind: str,
        name: str,
        namespace: Optional[str] = None,
        *,
        predicate=None,
        timeout: int = 180,
        interval: int = 5,
    ) -&gt; dict:
        deadline = time.time() + timeout
        last_obj = None
        while time.time() &lt; deadline:
            obj = get_json_or_none(kind, name, namespace)
            if obj is not None:
                last_obj = obj
                if predicate is None or predicate(obj):
                    return obj
            time.sleep(interval)
&gt;       raise AssertionError(
            f"{kind}/{name} in {namespace or '&lt;cluster&gt;'} did not satisfy condition. Last object: {last_obj}"
        )
E       AssertionError: maasauthpolicy/e2e-policy-22b8defe in ai-tenant-e2e-mt-22b8defe did not satisfy condition. Last object: {'apiVersion': 'maas.opendatahub.io/v1alpha1', 'kind': 'MaaSAuthPolicy', 'metadata': {'annotations': {'kubectl.kubernetes.io/last-applied-configuration': '{"apiVersion":"maas.opendatahub.io/v1alpha1","kind":"MaaSAuthPolicy","metadata":{"annotations":{},"name":"e2e-policy-22b8defe","namespace":"ai-tenant-e2e-mt-22b8defe"},"spec":{"modelRefs":[{"name":"facebook-opt-125m-simulated","namespace":"llm"}],"subjects":{"groups":[{"name":"system:authenticated"}]}}}\n'}, 'creationTimestamp': '2026-10-30T21:38:20Z', 'finalizers': ['maas.opendatahub.io/authpolicy-cleanup'], 'generation': 1, 'name': 'e2e-policy-22b8defe', 'namespace': 'ai-tenant-e2e-mt-22b8defe', 'resourceVersion': '56454', 'uid': 'a410ed26-1b0c-4241-8b3f-fc1d4f896670'}, 'spec': {'modelRefs': [{'name': 'facebook-opt-125m-simulated', 'namespace': 'llm'}], 'subjects': {'groups': [{'name': 'system:authenticated'}]}}, 'status': {'conditions': [{'lastTransitionTime': '2026-10-30T21:38:31Z', 'message': 'Waiting for gateway AuthPolicy openshift-ingress/maas-gateway-auth to be accepted and enforced: AuthPolicy status observed generation 3, current generation is 61', 'observedGeneration': 1, 'reason': 'Unknown', 'status': 'Unknown', 'type': 'Ready'}], 'phase': 'Pending'}}

test/maas-e2e/test/e2e/tests/multitenancy_helpers.py:288: AssertionError</failure></testcase><testcase classname="tests.test_tenant_namespace_discovery.TestTenantNamespaceDiscovery" name="test_label_removal_stops_reconciliation" time="118.016" /><testcase classname="tests.test_tenant_namespace_discovery.TestTenantWebhookValidation" name="test_maassubscription_rejected_without_tenant_config_cr" time="17.634" /><testcase classname="tests.test_gateway_scoped_authpolicy.TestGatewayAuthPolicyStructure" name="test_gateway_auth_policy_contract" time="0.491" /><testcase classname="tests.test_gateway_scoped_authpolicy.TestGatewayAuthPolicyLifecycle" name="test_gateway_auth_rego_is_fixed_size" time="189.483" /><testcase classname="tests.test_tenant_namespace_discovery.TestTenantNamespaceDiscovery" name="test_tenant_admin_rbac_is_namespace_scoped" time="260.751" /><testcase classname="tests.test_tenant_namespace_discovery.TestTenantWebhookValidation" name="test_maasauthpolicy_rejected_without_tenant_config_cr" time="168.382" /><testcase classname="tests.test_gateway_scoped_authpolicy.TestGatewayAuthPolicyLifecycle" name="test_only_one_gateway_authpolicy_named_maas_gateway_auth" time="155.117" /><testcase classname="tests.test_gateway_scoped_authpolicy.TestGatewayAuthPolicyManagementEndpointAccess" name="test_management_rules_have_model_identity_guards" time="136.836" /><testcase classname="tests.test_tenant_discovery_isolation" name="test_tenant_discovery_each_tenant_returns_own_gateway" time="281.673"><skipped type="pytest.skip" message="Skipping when Gateway uses ClusterIP + OpenShift Route (unsupported configuration). This mixes incompatible routing paradigms. Gateway has no external hostname in spec.listeners, so /v1/tenants returns an error. Supported configuration: LoadBalancer service with hostname in spec.listeners.">/workspace/source/test/maas-e2e/test/e2e/tests/test_tenant_discovery_isolation.py:228: Skipping when Gateway uses ClusterIP + OpenShift Route (unsupported configuration). This mixes incompatible routing paradigms. Gateway has no external hostname in spec.listeners, so /v1/tenants returns an error. Supported configuration: LoadBalancer service with hostname in spec.listeners.</skipped></testcase><testcase classname="tests.test_gateway_scoped_authpolicy.TestGatewayAuthPolicyManagementEndpointAccess" name="test_gateway_default_auth_scoped_if_present" time="175.483"><skipped type="pytest.skip" message="legacy gateway-default-auth is scoped to the shared default gateway">/workspace/source/test/maas-e2e/test/e2e/tests/test_gateway_scoped_authpolicy.py:212: legacy gateway-default-auth is scoped to the shared default gateway</skipped></testcase><testcase classname="tests.test_tenant_discovery_isolation" name="test_tenant_discovery_same_tenant_access" time="282.468"><skipped type="pytest.skip" message="Skipping when Gateway uses ClusterIP + OpenShift Route (unsupported configuration). This mixes incompatible routing paradigms. Gateway has no external hostname in spec.listeners, so /v1/tenants returns an error. Supported configuration: LoadBalancer service with hostname in spec.listeners.">/workspace/source/test/maas-e2e/test/e2e/tests/test_tenant_discovery_isolation.py:93: Skipping when Gateway uses ClusterIP + OpenShift Route (unsupported configuration). This mixes incompatible routing paradigms. Gateway has no external hostname in spec.listeners, so /v1/tenants returns an error. Supported configuration: LoadBalancer service with hostname in spec.listeners.</skipped></testcase><testcase classname="tests.test_multi_tenant_integration.TestMultiTenantIntegration" name="test_full_tenant_lifecycle_create_to_delete" time="603.847"><failure message="AssertionError: API key from the deleted tenant remained valid after recreating the tenant with the same name: status=0 body=Command '['kubectl', 'exec', '-i', 'test-curl-7196-a2cb71', '-n', 'opendatahub', '--', 'sh', '-c', 'BODY=$(cat)\ncurl -s --proto =https --cacert /tmp/ca-bundle.crt -m 10 -X POST -H &quot;Content-Type: application/json&quot; -d &quot;$BODY&quot; -w &quot;\\nHTTP_CODE:%{http_code}&quot; https://maas-api-e2e-mt-3ab88248.odh-ai-gateway-infra.svc.cluster.local:8443/internal/v1/api-keys/validate']' timed out...&#10;[+2 more lines; full report in element text]">self = &lt;test_multi_tenant_integration.TestMultiTenantIntegration object at 0x7f90e797c710&gt;

    def test_full_tenant_lifecycle_create_to_delete(self):
        """7.1: Full tenant lifecycle from create through policy/subscription reconcile to delete."""
        case = new_discovery_case()
        role_name = f"aitenant-{case['tenant_label_name']}-tenant-admin"
        try:
            bootstrap_aitenant_tenant(case)
    
            tenant = wait_for_json(TENANT_CONFIG_KIND, TENANT_CR_NAME, case["tenant_ns"], timeout=180)
            tenant_labels = tenant["metadata"].get("labels") or {}
            tenant_annotations = tenant["metadata"].get("annotations") or {}
            assert tenant_labels[LABEL_MANAGED_BY_AITENANT] == "true"
            assert tenant_labels[LABEL_TENANT_NAME] == case["tenant_label_name"]
            assert tenant_labels[LABEL_TENANT_NAMESPACE] == case["tenant_ns"]
            assert tenant_annotations[ANNOTATION_AITENANT_NAME] == case["tenant_label_name"]
            assert tenant_annotations[ANNOTATION_AITENANT_NAMESPACE] == AITENANT_NAMESPACE
            aitenant = wait_for_json(AITENANT_KIND, case["tenant_label_name"], AITENANT_NAMESPACE, timeout=180)
            assert aitenant["status"]["gatewayRef"]["name"] == case["gateway_name"]
            assert get_json_or_none("role", role_name, case["tenant_ns"]) is not None
    
            model_name = f"e2e-lifecycle-model-{case['suffix']}"
            provision_tenant_model(model_name, case["tenant_ns"], case["gateway_name"])
    
            apply_maas_auth_policy(
                case["policy_name"],
                case["tenant_ns"],
                model_ref=model_name,
                model_namespace=case["tenant_ns"],
            )
            apply_maas_subscription(
                case["subscription_name"],
                case["tenant_ns"],
                model_ref=model_name,
                model_namespace=case["tenant_ns"],
            )
            wait_for_status_phase("maasauthpolicy", case["policy_name"], case["tenant_ns"], expected_phase="Active")
            wait_for_status_phase(
                "maassubscription",
                case["subscription_name"],
                case["tenant_ns"],
                expected_phase="Active",
            )
            wait_for_aitenant_cleanup_resources(case)
    
            # Bind the key to the tenant's subscription, then verify it remains
            # invalid after the tenant and its subscription are recreated.
            route = wait_for_route_admitted(f"{case['gateway_name']}-route")
            tenant_api_url = f"https://{route['spec']['host']}/maas-api"
            wait_for_deployment_available(f"maas-api-{case['tenant_label_name']}", timeout=180)
            key_response = create_api_key_at(
                tenant_api_url,
                _get_cluster_token(),
                f"e2e-aitenant-delete-{case['suffix']}",
                subscription=case["subscription_name"],
            )
            assert key_response.status_code in (200, 201), (
                f"failed to create tenant-scoped API key: {response_summary(key_response)}"
            )
            old_api_key = key_response.json().get("key")
            assert old_api_key, "tenant-scoped API key response did not include a key"
            validation = validate_api_key_at(
                tenant_internal_url(case["tenant_label_name"]),
                old_api_key,
            )
            assert validation.status_code == 200 and validation.json().get("valid") is True, (
                f"new tenant API key did not validate: status={validation.status_code} "
                f"body={validation.text}"
            )
    
            user_objects = apply_unrelated_tenant_objects(case)
    
            delete_best_effort(AITENANT_KIND, case["tenant_label_name"], AITENANT_NAMESPACE, timeout="180s")
            wait_for_not_found(TENANT_CONFIG_KIND, TENANT_CR_NAME, case["tenant_ns"], timeout=180)
            wait_for_not_found("maasauthpolicy", case["policy_name"], case["tenant_ns"], timeout=180)
            wait_for_not_found("maassubscription", case["subscription_name"], case["tenant_ns"], timeout=180)
            wait_for_not_found("role", role_name, case["tenant_ns"], timeout=180)
            namespace = wait_for_json(
                "namespace",
                case["tenant_ns"],
                predicate=lambda obj: LABEL_MANAGED_BY_AITENANT
                not in ((obj.get("metadata") or {}).get("labels") or {}),
                timeout=180,
            )
            namespace_metadata = namespace.get("metadata") or {}
            namespace_labels = namespace_metadata.get("labels") or {}
            namespace_annotations = namespace_metadata.get("annotations") or {}
            for key in (
                "app.kubernetes.io/managed-by",
                "app.kubernetes.io/part-of",
                LABEL_AI_GATEWAY_TENANT,
                LABEL_MANAGED_BY_AITENANT,
                LABEL_TENANT_NAME,
                LABEL_TENANT_NAMESPACE,
                "opendatahub.io/generated-namespace",
            ):
                assert key not in namespace_labels
            for key in (
                ANNOTATION_AITENANT_NAME,
                ANNOTATION_AITENANT_NAMESPACE,
                "maas.opendatahub.io/created-by-aitenant",
            ):
                assert key not in namespace_annotations
            assert get_json_or_none("secret", user_objects["secret"], case["tenant_ns"]) is not None
            assert get_json_or_none("rolebinding", user_objects["rolebinding"], case["tenant_ns"]) is not None
            wait_for_aitenant_cleanup_resources_deleted(case)
    
            # Recreate the same tenant name and verify the old key cannot be
            # used against the newly provisioned maas-api instance.
            bootstrap_aitenant_tenant(case)
            wait_for_aitenant_cleanup_resources(case)
            wait_for_deployment_available(f"maas-api-{case['tenant_label_name']}", timeout=180)
            validation = validate_api_key_at(
                tenant_internal_url(case["tenant_label_name"]),
                old_api_key,
            )
&gt;           assert validation.status_code == 200 and validation.json().get("valid") is False, (
                "API key from the deleted tenant remained valid after recreating "
                f"the tenant with the same name: status={validation.status_code} "
                f"body={validation.text}"
            )
E           AssertionError: API key from the deleted tenant remained valid after recreating the tenant with the same name: status=0 body=Command '['kubectl', 'exec', '-i', 'test-curl-7196-a2cb71', '-n', 'opendatahub', '--', 'sh', '-c', 'BODY=$(cat)\ncurl -s --proto =https --cacert /tmp/ca-bundle.crt -m 10 -X POST -H "Content-Type: application/json" -d "$BODY" -w "\\nHTTP_CODE:%{http_code}" https://maas-api-e2e-mt-3ab88248.odh-ai-gateway-infra.svc.cluster.local:8443/internal/v1/api-keys/validate']' timed out after 60 seconds
E           assert (0 == 200)
E            +  where 0 = &lt;multitenancy_helpers._InternalResponse object at 0x7f90e74b2cd0&gt;.status_code

test/maas-e2e/test/e2e/tests/test_multi_tenant_integration.py:196: AssertionError</failure></testcase><testcase classname="tests.test_tenant_discovery_isolation" name="test_tenant_discovery_cross_tenant_isolation" time="282.978"><skipped type="pytest.skip" message="Skipping when Gateway uses ClusterIP + OpenShift Route (unsupported configuration). This mixes incompatible routing paradigms. Gateway has no external hostname in spec.listeners, so /v1/tenants returns an error. Supported configuration: LoadBalancer service with hostname in spec.listeners.">/workspace/source/test/maas-e2e/test/e2e/tests/test_tenant_discovery_isolation.py:141: Skipping when Gateway uses ClusterIP + OpenShift Route (unsupported configuration). This mixes incompatible routing paradigms. Gateway has no external hostname in spec.listeners, so /v1/tenants returns an error. Supported configuration: LoadBalancer service with hostname in spec.listeners.</skipped></testcase><testcase classname="tests.test_multi_tenant_integration.TestMultiTenantIntegration" name="test_default_tenant_unaffected_by_multitenancy_enablement" time="237.529" /><testcase classname="tests.test_tenant_discovery_isolation" name="test_tenant_discovery_unauthorized_access" time="458.458"><failure message="AssertionError: e2e-shared-a-w0-fe5385 should reject no-auth request with 401, got 0&#10;assert 0 == 401">tenant_service_urls = {'tenant_a': {'name': 'e2e-shared-a-w0-fe5385', 'namespace': 'ai-tenant-e2e-shared-a-w0-fe5385', 'service_url': 'https...as-api-e2e-shared-b-w0-9085f4.odh-ai-gateway-infra.svc.cluster.local:8443', 'aitenant_name': 'e2e-shared-b-w0-9085f4'}}

    def test_tenant_discovery_unauthorized_access(tenant_service_urls):
        """
        Verify completely unauthorized access is rejected.
    
        Without any token or with an invalid token, all tenant endpoints should return 401.
        """
        for tenant_key in ["tenant_a", "tenant_b"]:
            tenant = tenant_service_urls[tenant_key]
            url = f"{tenant['service_url']}/v1/tenants"
    
            # Test 1: No auth header
            status_code, _ = _kubectl_curl(url)
&gt;           assert status_code == 401, \
                f"{tenant['name']} should reject no-auth request with 401, got {status_code}"
E           AssertionError: e2e-shared-a-w0-fe5385 should reject no-auth request with 401, got 0
E           assert 0 == 401

test/maas-e2e/test/e2e/tests/test_tenant_discovery_isolation.py:206: AssertionError</failure></testcase></testsuite></testsuites>