tests/gateway/test_signal.py::TestSignalConfigLoading::test_apply_env_overrides_signal tests/gateway/test_signal.py::TestSignalConfigLoading::test_signal_not_loaded_without_both_vars tests/gateway/test_signal.py::TestSignalAdapterInit::test_init_parses_config tests/gateway/test_signal.py::TestSignalAdapterInit::test_init_empty_allowlist tests/gateway/test_signal.py::TestSignalAdapterInit::test_init_strips_trailing_slash tests/gateway/test_signal.py::TestSignalAdapterInit::test_self_message_filtering tests/gateway/test_signal.py::TestSignalConnectCleanup::test_releases_lock_and_closes_client_on_healthcheck_failure tests/gateway/test_signal.py::TestSignalHelpers::test_redact_phone_long tests/gateway/test_signal.py::TestSignalHelpers::test_redact_phone_short tests/gateway/test_signal.py::TestSignalHelpers::test_redact_phone_empty tests/gateway/test_signal.py::TestSignalHelpers::test_parse_comma_list tests/gateway/test_signal.py::TestSignalHelpers::test_guess_extension_png tests/gateway/test_signal.py::TestSignalHelpers::test_guess_extension_jpeg tests/gateway/test_signal.py::TestSignalHelpers::test_guess_extension_pdf tests/gateway/test_signal.py::TestSignalHelpers::test_guess_extension_zip tests/gateway/test_signal.py::TestSignalHelpers::test_guess_extension_mp4 tests/gateway/test_signal.py::TestSignalHelpers::test_guess_extension_aac_adts_unprotected tests/gateway/test_signal.py::TestSignalHelpers::test_guess_extension_aac_adts_protected tests/gateway/test_signal.py::TestSignalHelpers::test_guess_extension_mp3_mpeg1_layer3 tests/gateway/test_signal.py::TestSignalHelpers::test_guess_extension_mp3_mpeg2_layer3 tests/gateway/test_signal.py::TestSignalHelpers::test_guess_extension_aac_routes_to_audio_cache tests/gateway/test_signal.py::TestSignalHelpers::test_remux_aac_to_m4a_round_trip tests/gateway/test_signal.py::TestSignalHelpers::test_remux_aac_to_m4a_handles_garbage tests/gateway/test_signal.py::TestSignalHelpers::test_guess_extension_unknown tests/gateway/test_signal.py::TestSignalHelpers::test_is_image_ext tests/gateway/test_signal.py::TestSignalHelpers::test_is_audio_ext tests/gateway/test_signal.py::TestSignalHelpers::test_check_requirements tests/gateway/test_signal.py::TestSignalHelpers::test_render_mentions tests/gateway/test_signal.py::TestSignalHelpers::test_render_mentions_no_mentions tests/gateway/test_signal.py::TestSignalHelpers::test_check_requirements_missing tests/gateway/test_signal.py::TestSignalSSEUrlEncoding::test_sse_url_encodes_plus_in_account tests/gateway/test_signal.py::TestSignalSSEUrlEncoding::test_sse_url_encoding_preserves_digits tests/gateway/test_signal.py::TestSignalAttachmentFetch::test_fetch_attachment_uses_id_parameter tests/gateway/test_signal.py::TestSignalAttachmentFetch::test_fetch_attachment_returns_none_on_empty tests/gateway/test_signal.py::TestSignalAttachmentFetch::test_fetch_attachment_handles_dict_response tests/gateway/test_signal.py::TestSignalSessionSource::test_session_source_alt_fields tests/gateway/test_signal.py::TestSignalSessionSource::test_session_source_roundtrip tests/gateway/test_signal.py::TestSignalPhoneRedaction::test_us_number tests/gateway/test_signal.py::TestSignalPhoneRedaction::test_uk_number tests/gateway/test_signal.py::TestSignalPhoneRedaction::test_multiple_numbers tests/gateway/test_signal.py::TestSignalPhoneRedaction::test_short_number_not_matched tests/gateway/test_signal.py::TestSignalAuthorization::test_signal_in_allowlist_maps tests/gateway/test_signal.py::TestSignalSendImageFile::test_send_image_file_sends_via_rpc tests/gateway/test_signal.py::TestSignalSendImageFile::test_send_image_file_to_group tests/gateway/test_signal.py::TestSignalSendImageFile::test_send_image_file_missing tests/gateway/test_signal.py::TestSignalSendImageFile::test_send_image_file_too_large tests/gateway/test_signal.py::TestSignalSendImageFile::test_send_image_file_rpc_failure tests/gateway/test_signal.py::TestSignalRecipientResolution::test_send_prefers_cached_uuid_for_direct_messages tests/gateway/test_signal.py::TestSignalRecipientResolution::test_send_looks_up_uuid_via_list_contacts tests/gateway/test_signal.py::TestSignalRecipientResolution::test_send_falls_back_to_phone_when_no_uuid_found tests/gateway/test_signal.py::TestSignalRecipientResolution::test_send_typing_uses_cached_uuid tests/gateway/test_signal.py::TestSignalSendVoice::test_send_voice_sends_via_rpc tests/gateway/test_signal.py::TestSignalSendVoice::test_send_voice_missing_file tests/gateway/test_signal.py::TestSignalSendVoice::test_send_voice_to_group tests/gateway/test_signal.py::TestSignalSendVoice::test_send_voice_too_large tests/gateway/test_signal.py::TestSignalSendVoice::test_send_voice_rpc_failure tests/gateway/test_signal.py::TestSignalSendVideo::test_send_video_sends_via_rpc tests/gateway/test_signal.py::TestSignalSendVideo::test_send_video_missing_file tests/gateway/test_signal.py::TestSignalSendVideo::test_send_video_too_large tests/gateway/test_signal.py::TestSignalSendVideo::test_send_video_rpc_failure tests/gateway/test_signal.py::TestSignalMediaExtraction::test_extract_media_finds_image_tag tests/gateway/test_signal.py::TestSignalMediaExtraction::test_extract_media_finds_audio_tag tests/gateway/test_signal.py::TestSignalMediaExtraction::test_signal_has_all_media_methods tests/gateway/test_signal.py::TestSignalInboundMessageTypeClassification::test_pdf_attachment_sets_document_type tests/gateway/test_signal.py::TestSignalInboundMessageTypeClassification::test_text_plain_attachment_sets_document_type tests/gateway/test_signal.py::TestSignalInboundMessageTypeClassification::test_text_html_attachment_sets_document_type tests/gateway/test_signal.py::TestSignalInboundMessageTypeClassification::test_video_attachment_sets_video_type tests/gateway/test_signal.py::TestSignalInboundMessageTypeClassification::test_unknown_mime_attachment_falls_back_to_document tests/gateway/test_signal.py::TestSignalSendDocumentViaHelper::test_send_document_too_large tests/gateway/test_signal.py::TestSignalSendDocumentViaHelper::test_send_document_error_includes_path tests/gateway/test_signal.py::TestSignalStreamingCapabilities::test_signal_declares_no_message_editing tests/gateway/test_signal.py::TestSignalSendReturnsMessageId::test_send_returns_none_message_id_even_with_timestamp tests/gateway/test_signal.py::TestSignalSendReturnsMessageId::test_send_returns_none_message_id_when_no_timestamp tests/gateway/test_signal.py::TestSignalSendReturnsMessageId::test_send_returns_none_message_id_for_non_dict tests/gateway/test_signal.py::TestSignalSendResultValidation::test_send_success_when_results_has_success tests/gateway/test_signal.py::TestSignalSendResultValidation::test_send_failure_when_results_has_failure_type tests/gateway/test_signal.py::TestSignalSendResultValidation::test_send_failure_when_results_has_success_false tests/gateway/test_signal.py::TestSignalSendResultValidation::test_rpc_raises_rate_limit_on_results_failure tests/gateway/test_signal.py::TestSignalStopTyping::test_stop_typing_calls_private_method tests/gateway/test_signal.py::TestSignalTypingBackoff::test_first_failure_logs_at_warning_subsequent_at_debug tests/gateway/test_signal.py::TestSignalTypingBackoff::test_three_consecutive_failures_trigger_cooldown tests/gateway/test_signal.py::TestSignalTypingBackoff::test_cooldown_is_per_chat_not_global tests/gateway/test_signal.py::TestSignalTypingBackoff::test_success_resets_failure_counter_and_cooldown tests/gateway/test_signal.py::TestSignalTypingBackoff::test_stop_typing_indicator_clears_backoff_state tests/gateway/test_signal.py::TestSignalStopTypingExplicitRPC::test_stop_typing_indicator_sends_stop_rpc_for_dm tests/gateway/test_signal.py::TestSignalStopTypingExplicitRPC::test_stop_typing_indicator_sends_stop_rpc_for_group tests/gateway/test_signal.py::TestSignalStopTypingExplicitRPC::test_stop_typing_indicator_best_effort_on_rpc_failure tests/gateway/test_signal.py::TestSignalStopTypingExplicitRPC::test_stop_typing_indicator_best_effort_on_recipient_failure tests/gateway/test_signal.py::TestSignalQuoteExtraction::test_handle_envelope_sets_reply_context_from_quote tests/gateway/test_signal.py::TestSignalQuoteExtraction::test_handle_envelope_marks_quote_to_own_sent_timestamp tests/gateway/test_signal.py::TestSignalQuoteExtraction::test_handle_envelope_marks_quote_to_own_account_author tests/gateway/test_signal.py::TestSignalQuoteExtraction::test_track_sent_timestamp_keeps_reply_detection_cache_after_echo_discard tests/gateway/test_signal.py::TestSignalQuoteExtraction::test_handle_envelope_without_quote_leaves_reply_fields_none tests/gateway/test_signal.py::TestSignalQuoteExtraction::test_handle_envelope_quote_without_text_sets_only_reply_id tests/gateway/test_signal.py::TestSignalRpcRateLimit::test_raises_on_429_when_opted_in tests/gateway/test_signal.py::TestSignalRpcRateLimit::test_raises_on_rate_limit_exception_substring tests/gateway/test_signal.py::TestSignalRpcRateLimit::test_default_swallows_rate_limit_returns_none tests/gateway/test_signal.py::TestSignalRpcRateLimit::test_non_rate_limit_error_does_not_raise_when_opted_in tests/gateway/test_signal.py::TestSignalRpcRateLimit::test_raises_with_retry_after_from_v0_14_3_payload tests/gateway/test_signal.py::TestSignalRpcRateLimit::test_raises_with_retry_after_none_for_old_signal_cli tests/gateway/test_signal.py::TestSignalRpcRateLimit::test_raises_on_retry_later_inside_attachment_invalid tests/gateway/test_signal.py::TestSignalSendMultipleImages::test_empty_list_is_noop tests/gateway/test_signal.py::TestSignalSendMultipleImages::test_all_bad_files_no_rpc tests/gateway/test_signal.py::TestSignalSendMultipleImages::test_single_batch_under_limit tests/gateway/test_signal.py::TestSignalSendMultipleImages::test_skips_bad_images_in_mixed_batch tests/gateway/test_signal.py::TestSignalSendMultipleImages::test_429_calibrates_scheduler_then_retries tests/gateway/test_signal.py::TestSignalSendMultipleImages::test_429_without_retry_after_uses_default_rate tests/gateway/test_signal.py::TestSignalSendMultipleImages::test_rate_limit_exhaust_continues_to_next_batch tests/gateway/test_signal.py::TestSignalSendMultipleImages::test_full_batch_emits_pacing_notice_for_followup tests/gateway/test_signal.py::TestSignalSendMultipleImages::test_short_followup_wait_skips_pacing_notice tests/gateway/test_signal.py::TestSignalSendMultipleImages::test_single_batch_send_does_not_pace tests/gateway/test_signal.py::TestSignalRateLimitDetection::test_detect_typed_code tests/gateway/test_signal.py::TestSignalRateLimitDetection::test_detect_substring_fallback tests/gateway/test_signal.py::TestSignalRateLimitDetection::test_detect_non_rate_limit tests/gateway/test_signal.py::TestSignalRateLimitDetection::test_extract_retry_after_from_results tests/gateway/test_signal.py::TestSignalRateLimitDetection::test_extract_retry_after_missing tests/gateway/test_signal.py::TestSignalRateLimitDetection::test_detect_retry_later_exception_substring tests/gateway/test_signal.py::TestSignalRateLimitDetection::test_extract_retry_after_parses_message_string tests/gateway/test_signal.py::TestSignalSendTimeout::test_zero_attachments_uses_default tests/gateway/test_signal.py::TestSignalSendTimeout::test_floor_at_60s tests/gateway/test_signal.py::TestSignalSendTimeout::test_scales_with_batch_size tests/gateway/test_signal.py::TestSignalContentlessEnvelope::test_skips_profile_key_update_no_message_field tests/gateway/test_signal.py::TestSignalContentlessEnvelope::test_skips_empty_message tests/gateway/test_signal.py::TestSignalContentlessEnvelope::test_skips_whitespace_only_message tests/gateway/test_signal.py::TestSignalContentlessEnvelope::test_allows_message_with_attachment_no_text tests/gateway/test_signal.py::TestSignalContentlessEnvelope::test_allows_normal_text_message