tests/gateway/test_google_chat.py::TestPlatformRegistration::test_enum_value tests/gateway/test_google_chat.py::TestPlatformRegistration::test_requirements_check_returns_true_when_available tests/gateway/test_google_chat.py::TestEnvConfigLoading::test_missing_subscription_does_not_enable tests/gateway/test_google_chat.py::TestEnvConfigLoading::test_missing_project_does_not_enable tests/gateway/test_google_chat.py::TestHelpers::test_mime_image_maps_to_photo tests/gateway/test_google_chat.py::TestHelpers::test_mime_audio_maps_to_audio tests/gateway/test_google_chat.py::TestHelpers::test_mime_video_maps_to_video tests/gateway/test_google_chat.py::TestHelpers::test_mime_other_maps_to_document tests/gateway/test_google_chat.py::TestHelpers::test_mime_empty_maps_to_document tests/gateway/test_google_chat.py::TestRedactSensitive::test_redacts_subscription_path tests/gateway/test_google_chat.py::TestRedactSensitive::test_redacts_topic_path tests/gateway/test_google_chat.py::TestRedactSensitive::test_redacts_service_account_email tests/gateway/test_google_chat.py::TestRedactSensitive::test_empty_text_passes_through tests/gateway/test_google_chat.py::TestGoogleOwnedHost::test_accepts_google_hosts[https://chat.googleapis.com/v1/x] tests/gateway/test_google_chat.py::TestGoogleOwnedHost::test_accepts_google_hosts[https://www.googleapis.com/upload/chat/v1/x] tests/gateway/test_google_chat.py::TestGoogleOwnedHost::test_accepts_google_hosts[https://drive.google.com/file/d/abc] tests/gateway/test_google_chat.py::TestGoogleOwnedHost::test_accepts_google_hosts[https://lh3.googleusercontent.com/photo.jpg] tests/gateway/test_google_chat.py::TestGoogleOwnedHost::test_rejects_non_google_or_insecure[https://evil.com/foo] tests/gateway/test_google_chat.py::TestGoogleOwnedHost::test_rejects_non_google_or_insecure[https://169.254.169.254/latest/meta-data/] tests/gateway/test_google_chat.py::TestGoogleOwnedHost::test_rejects_non_google_or_insecure[https://metadata.internal/computeMetadata/v1/] tests/gateway/test_google_chat.py::TestGoogleOwnedHost::test_rejects_non_google_or_insecure[https://chat.google.com.attacker.example/] tests/gateway/test_google_chat.py::TestGoogleOwnedHost::test_rejects_non_google_or_insecure[http://chat.googleapis.com/] tests/gateway/test_google_chat.py::TestGoogleOwnedHost::test_rejects_non_google_or_insecure[ftp://drive.google.com/x] tests/gateway/test_google_chat.py::TestGoogleOwnedHost::test_rejects_non_google_or_insecure[not a url] tests/gateway/test_google_chat.py::TestValidateConfig::test_missing_project_raises tests/gateway/test_google_chat.py::TestValidateConfig::test_missing_subscription_raises tests/gateway/test_google_chat.py::TestValidateConfig::test_subscription_format_rejected tests/gateway/test_google_chat.py::TestValidateConfig::test_subscription_project_mismatch_rejected tests/gateway/test_google_chat.py::TestValidateConfig::test_validate_config_happy tests/gateway/test_google_chat.py::TestChunkText::test_empty_returns_empty_list tests/gateway/test_google_chat.py::TestChunkText::test_short_returns_single_chunk tests/gateway/test_google_chat.py::TestChunkText::test_long_splits_into_multiple tests/gateway/test_google_chat.py::TestChunkText::test_splits_on_newline_near_boundary tests/gateway/test_google_chat.py::TestOnPubsubMessage::test_shutting_down_nacks tests/gateway/test_google_chat.py::TestOnPubsubMessage::test_malformed_json_acks_without_dispatch tests/gateway/test_google_chat.py::TestOnPubsubMessage::test_membership_created_caches_bot_user_id tests/gateway/test_google_chat.py::TestOnPubsubMessage::test_membership_deleted_acks_no_dispatch tests/gateway/test_google_chat.py::TestOnPubsubMessage::test_bot_sender_is_filtered tests/gateway/test_google_chat.py::TestOnPubsubMessage::test_relay_flat_bot_sender_is_filtered_end_to_end tests/gateway/test_google_chat.py::TestOnPubsubMessage::test_relay_flat_human_sender_dispatches tests/gateway/test_google_chat.py::TestOnPubsubMessage::test_duplicate_message_dropped tests/gateway/test_google_chat.py::TestOnPubsubMessage::test_text_message_submits_to_loop tests/gateway/test_google_chat.py::TestOnPubsubMessage::test_callback_exception_does_not_escape tests/gateway/test_google_chat.py::TestExtractMessagePayload::test_native_chat_api_format_extracts_msg_and_space tests/gateway/test_google_chat.py::TestExtractMessagePayload::test_native_chat_api_format_drops_non_message_events tests/gateway/test_google_chat.py::TestExtractMessagePayload::test_relay_flat_format_synthesizes_chat_api_shape tests/gateway/test_google_chat.py::TestExtractMessagePayload::test_relay_flat_honors_declared_sender_type_bot tests/gateway/test_google_chat.py::TestExtractMessagePayload::test_relay_flat_defaults_sender_type_human_when_absent tests/gateway/test_google_chat.py::TestExtractMessagePayload::test_relay_flat_coerces_unknown_sender_type_to_human tests/gateway/test_google_chat.py::TestExtractMessagePayload::test_unrecognized_envelope_returns_none tests/gateway/test_google_chat.py::TestBuildMessageEvent::test_dm_first_message_in_thread_is_main_flow tests/gateway/test_google_chat.py::TestBuildMessageEvent::test_dm_second_message_in_same_thread_is_side_thread tests/gateway/test_google_chat.py::TestBuildMessageEvent::test_dm_side_thread_caches_thread_for_outbound tests/gateway/test_google_chat.py::TestBuildMessageEvent::test_dm_main_flow_after_side_thread_clears_cache tests/gateway/test_google_chat.py::TestBuildMessageEvent::test_dm_different_top_level_threads_share_session tests/gateway/test_google_chat.py::TestBuildMessageEvent::test_group_keeps_thread_id_on_source tests/gateway/test_google_chat.py::TestBuildMessageEvent::test_slash_command_yields_command_type tests/gateway/test_google_chat.py::TestBuildMessageEvent::test_attachment_image_triggers_download tests/gateway/test_google_chat.py::TestSend::test_text_send_creates_message tests/gateway/test_google_chat.py::TestSend::test_create_message_passes_messageReplyOption_when_thread_set tests/gateway/test_google_chat.py::TestSend::test_create_message_omits_messageReplyOption_when_no_thread tests/gateway/test_google_chat.py::TestSend::test_with_typing_card_patches_instead_of_creating tests/gateway/test_google_chat.py::TestSend::test_long_text_splits_and_sends_multiple tests/gateway/test_google_chat.py::TestSend::test_403_sets_fatal_error tests/gateway/test_google_chat.py::TestSend::test_404_returns_target_not_found tests/gateway/test_google_chat.py::TestSend::test_429_increments_rate_limit_counter_and_raises tests/gateway/test_google_chat.py::TestTypingLifecycle::test_send_typing_posts_and_tracks tests/gateway/test_google_chat.py::TestTypingLifecycle::test_send_typing_skips_when_already_tracking tests/gateway/test_google_chat.py::TestTypingLifecycle::test_send_typing_inherits_inbound_thread tests/gateway/test_google_chat.py::TestTypingLifecycle::test_send_typing_no_thread_when_cache_empty tests/gateway/test_google_chat.py::TestTypingLifecycle::test_send_typing_concurrent_calls_create_only_one_card tests/gateway/test_google_chat.py::TestTypingLifecycle::test_send_typing_survives_caller_cancellation tests/gateway/test_google_chat.py::TestTypingLifecycle::test_orphan_typing_cards_reaped_on_completion tests/gateway/test_google_chat.py::TestTypingLifecycle::test_stop_typing_is_noop_for_live_card tests/gateway/test_google_chat.py::TestTypingLifecycle::test_stop_typing_pops_sentinel tests/gateway/test_google_chat.py::TestTypingLifecycle::test_stop_typing_noop_when_nothing_tracked tests/gateway/test_google_chat.py::TestTypingLifecycle::test_on_processing_complete_pops_sentinel_on_success tests/gateway/test_google_chat.py::TestTypingLifecycle::test_on_processing_complete_patches_stranded_card tests/gateway/test_google_chat.py::TestEditMessage::test_edit_message_patches_via_messages_patch tests/gateway/test_google_chat.py::TestEditMessage::test_edit_message_truncates_overlong_text tests/gateway/test_google_chat.py::TestEditMessage::test_edit_message_missing_id_returns_failure tests/gateway/test_google_chat.py::TestEditMessage::test_edit_message_429_increments_rate_limit_counter tests/gateway/test_google_chat.py::TestEditMessage::test_edit_message_overrides_base_so_progress_pipeline_runs tests/gateway/test_google_chat.py::TestDeleteMessage::test_delete_message_calls_api tests/gateway/test_google_chat.py::TestDeleteMessage::test_delete_message_swallows_404 tests/gateway/test_google_chat.py::TestDeleteMessage::test_delete_message_missing_id_returns_false tests/gateway/test_google_chat.py::TestNativeAttachmentDelivery::test_send_file_posts_setup_notice_when_no_user_oauth tests/gateway/test_google_chat.py::TestNativeAttachmentDelivery::test_send_file_two_step_native_upload_when_user_oauth_ready tests/gateway/test_google_chat.py::TestNativeAttachmentDelivery::test_send_file_falls_back_to_notice_on_401 tests/gateway/test_google_chat.py::TestNativeAttachmentDelivery::test_send_file_returns_error_on_unrelated_http_error tests/gateway/test_google_chat.py::TestSetupFilesSlashCommand::test_slash_command_intercepted_before_agent tests/gateway/test_google_chat.py::TestSetupFilesSlashCommand::test_no_arg_status_when_unconfigured tests/gateway/test_google_chat.py::TestSetupFilesSlashCommand::test_revoke_clears_in_memory_creds tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_load_user_credentials_returns_none_when_no_token tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_load_user_credentials_returns_none_on_corrupt_token tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_scopes_are_minimal tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_sanitize_email_lowercases_and_replaces_unsafe_chars tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_per_user_token_path_isolated_from_legacy tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_load_user_credentials_per_email_returns_none_when_missing tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_list_authorized_emails_lists_per_user_files tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_list_authorized_emails_empty_when_dir_missing tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_pending_auth_path_is_per_user_when_email_given tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_persist_credentials_writes_private_json tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_store_client_secret_writes_private_json tests/gateway/test_google_chat.py::TestUserOAuthHelper::test_save_pending_auth_writes_private_json tests/gateway/test_google_chat.py::TestPerUserAttachmentRouting::test_build_message_event_caches_sender_email tests/gateway/test_google_chat.py::TestPerUserAttachmentRouting::test_send_file_uses_per_user_token_when_sender_known tests/gateway/test_google_chat.py::TestPerUserAttachmentRouting::test_send_file_falls_back_to_legacy_when_per_user_missing tests/gateway/test_google_chat.py::TestPerUserAttachmentRouting::test_send_file_no_creds_anywhere_posts_setup_notice tests/gateway/test_google_chat.py::TestPerUserAttachmentRouting::test_send_file_per_user_401_evicts_only_that_user tests/gateway/test_google_chat.py::TestPerUserAttachmentRouting::test_setup_files_writes_to_per_user_path tests/gateway/test_google_chat.py::TestPerUserAttachmentRouting::test_setup_files_revoke_drops_only_that_user tests/gateway/test_google_chat.py::TestThreadCountStore::test_missing_file_returns_zero_counts tests/gateway/test_google_chat.py::TestThreadCountStore::test_corrupt_json_treated_as_empty tests/gateway/test_google_chat.py::TestThreadCountStore::test_incr_returns_pre_increment_value tests/gateway/test_google_chat.py::TestThreadCountStore::test_round_trip_persists_across_load tests/gateway/test_google_chat.py::TestThreadCountStore::test_invalid_shape_dropped_silently tests/gateway/test_google_chat.py::TestThreadCountStore::test_outbound_thread_tracked_for_user_reply_in_bot_thread tests/gateway/test_google_chat.py::TestThreadCountStore::test_side_thread_detection_survives_restart tests/gateway/test_google_chat.py::TestAttachmentSSRFGuard::test_drive_picker_only_skipped_when_no_resource_name tests/gateway/test_google_chat.py::TestAttachmentSSRFGuard::test_drive_file_with_resource_name_uses_bot_path tests/gateway/test_google_chat.py::TestAttachmentSSRFGuard::test_rejects_non_google_host tests/gateway/test_google_chat.py::TestAttachmentSSRFGuard::test_rejects_metadata_endpoint tests/gateway/test_google_chat.py::TestOutboundThreadRouting::test_resolve_uses_metadata_thread_id tests/gateway/test_google_chat.py::TestOutboundThreadRouting::test_resolve_falls_back_to_cached_thread_for_dm tests/gateway/test_google_chat.py::TestOutboundThreadRouting::test_resolve_metadata_overrides_cache tests/gateway/test_google_chat.py::TestOutboundThreadRouting::test_resolve_returns_none_when_no_inputs tests/gateway/test_google_chat.py::TestMediaDelegation::test_send_voice_delegates_to_document_with_audio_mime tests/gateway/test_google_chat.py::TestMediaDelegation::test_send_video_delegates_with_video_mime tests/gateway/test_google_chat.py::TestMediaDelegation::test_send_animation_delegates_to_image tests/gateway/test_google_chat.py::TestMediaDelegation::test_send_file_missing_path_returns_error tests/gateway/test_google_chat.py::TestOutboundRetry::test_retries_on_503_then_succeeds tests/gateway/test_google_chat.py::TestOutboundRetry::test_gives_up_after_max_attempts tests/gateway/test_google_chat.py::TestOutboundRetry::test_does_not_retry_on_400 tests/gateway/test_google_chat.py::TestOutboundRetry::test_is_retryable_error_classifier tests/gateway/test_google_chat.py::TestFormatMessage::test_bold_double_asterisk_to_single tests/gateway/test_google_chat.py::TestFormatMessage::test_bold_italic_combo_to_chat_dialect tests/gateway/test_google_chat.py::TestFormatMessage::test_markdown_link_to_chat_anglebracket tests/gateway/test_google_chat.py::TestFormatMessage::test_header_to_bold_at_line_start_only tests/gateway/test_google_chat.py::TestFormatMessage::test_fenced_code_block_protected tests/gateway/test_google_chat.py::TestFormatMessage::test_inline_code_protected tests/gateway/test_google_chat.py::TestFormatMessage::test_url_with_parens_in_path tests/gateway/test_google_chat.py::TestFormatMessage::test_mixed_bold_italic_orderings tests/gateway/test_google_chat.py::TestFormatMessage::test_strips_zwj_and_variation_selector tests/gateway/test_google_chat.py::TestFormatMessage::test_strips_bom_and_bidi_marks tests/gateway/test_google_chat.py::TestFormatMessage::test_empty_and_none_safe tests/gateway/test_google_chat.py::TestFormatMessage::test_unmatched_asterisks_left_alone tests/gateway/test_google_chat.py::TestADCFallback::test_load_credentials_uses_adc_when_no_sa_path tests/gateway/test_google_chat.py::TestADCFallback::test_load_credentials_raises_when_no_sa_and_adc_unavailable tests/gateway/test_google_chat.py::TestGoogleChatInteractiveSetup::test_interactive_setup_uses_shared_cli_prompt_helpers tests/gateway/test_google_chat.py::TestSupervisorReconnect::test_fatal_after_max_retries tests/gateway/test_google_chat.py::TestAuthorizationEmailMatch::test_allowlist_matches_when_user_id_is_email tests/gateway/test_google_chat.py::TestAuthorizationEmailMatch::test_allowlist_denies_wrong_email tests/gateway/test_google_chat.py::TestAuthorizationEmailMatch::test_allowlist_falls_back_to_resource_name_when_no_email tests/gateway/test_google_chat.py::TestCronSchedulerRegistry::test_google_chat_is_known_delivery_platform tests/gateway/test_google_chat.py::TestCronSchedulerRegistry::test_google_chat_home_env_var_resolves tests/gateway/test_google_chat.py::TestGoogleChatStandaloneSend::test_standalone_send_refreshes_token_and_posts_message tests/gateway/test_google_chat.py::TestGoogleChatStandaloneSend::test_standalone_send_returns_error_on_invalid_chat_id tests/gateway/test_google_chat.py::TestGoogleChatStandaloneSend::test_standalone_send_propagates_api_failure tests/gateway/test_google_chat.py::TestGoogleChatStandaloneSend::test_standalone_send_rejects_chat_id_with_path_traversal