tests/gateway/test_qqbot.py::TestQQRequirements::test_returns_bool tests/gateway/test_qqbot.py::TestQQAdapterInit::test_basic_attributes tests/gateway/test_qqbot.py::TestQQAdapterInit::test_env_fallback tests/gateway/test_qqbot.py::TestQQAdapterInit::test_env_fallback_extra_wins tests/gateway/test_qqbot.py::TestQQAdapterInit::test_dm_policy_default tests/gateway/test_qqbot.py::TestQQAdapterInit::test_dm_policy_explicit tests/gateway/test_qqbot.py::TestQQAdapterInit::test_group_policy_default tests/gateway/test_qqbot.py::TestQQAdapterInit::test_allow_from_parsing_string tests/gateway/test_qqbot.py::TestQQAdapterInit::test_allow_from_parsing_list tests/gateway/test_qqbot.py::TestQQAdapterInit::test_allow_from_default_empty tests/gateway/test_qqbot.py::TestQQAdapterInit::test_group_allow_from tests/gateway/test_qqbot.py::TestQQAdapterInit::test_markdown_support_default tests/gateway/test_qqbot.py::TestQQAdapterInit::test_markdown_support_false tests/gateway/test_qqbot.py::TestQQAdapterInit::test_name_property tests/gateway/test_qqbot.py::TestCoerceList::test_none tests/gateway/test_qqbot.py::TestCoerceList::test_string tests/gateway/test_qqbot.py::TestCoerceList::test_list tests/gateway/test_qqbot.py::TestCoerceList::test_empty_string tests/gateway/test_qqbot.py::TestCoerceList::test_tuple tests/gateway/test_qqbot.py::TestCoerceList::test_single_item_string tests/gateway/test_qqbot.py::TestIsVoiceContentType::test_voice_content_type tests/gateway/test_qqbot.py::TestIsVoiceContentType::test_audio_content_type tests/gateway/test_qqbot.py::TestIsVoiceContentType::test_voice_extension tests/gateway/test_qqbot.py::TestIsVoiceContentType::test_non_voice tests/gateway/test_qqbot.py::TestIsVoiceContentType::test_audio_extension_amr tests/gateway/test_qqbot.py::TestVoiceAttachmentSSRFProtection::test_stt_blocks_unsafe_download_url tests/gateway/test_qqbot.py::TestVoiceAttachmentSSRFProtection::test_connect_uses_redirect_guard_hook tests/gateway/test_qqbot.py::TestQQWebSocketProxy::test_open_ws_honors_proxy_env tests/gateway/test_qqbot.py::TestStripAtMention::test_removes_mention tests/gateway/test_qqbot.py::TestStripAtMention::test_no_mention tests/gateway/test_qqbot.py::TestStripAtMention::test_empty_string tests/gateway/test_qqbot.py::TestStripAtMention::test_only_mention tests/gateway/test_qqbot.py::TestDmAllowed::test_open_policy tests/gateway/test_qqbot.py::TestDmAllowed::test_disabled_policy tests/gateway/test_qqbot.py::TestDmAllowed::test_allowlist_match tests/gateway/test_qqbot.py::TestDmAllowed::test_allowlist_no_match tests/gateway/test_qqbot.py::TestDmAllowed::test_allowlist_wildcard tests/gateway/test_qqbot.py::TestGroupAllowed::test_open_policy tests/gateway/test_qqbot.py::TestGroupAllowed::test_allowlist_match tests/gateway/test_qqbot.py::TestGroupAllowed::test_allowlist_no_match tests/gateway/test_qqbot.py::TestResolveSTTConfig::test_no_config tests/gateway/test_qqbot.py::TestResolveSTTConfig::test_env_config tests/gateway/test_qqbot.py::TestResolveSTTConfig::test_extra_config tests/gateway/test_qqbot.py::TestDetectMessageType::test_no_media tests/gateway/test_qqbot.py::TestDetectMessageType::test_image tests/gateway/test_qqbot.py::TestDetectMessageType::test_voice tests/gateway/test_qqbot.py::TestDetectMessageType::test_video tests/gateway/test_qqbot.py::TestQQCloseError::test_attributes tests/gateway/test_qqbot.py::TestQQCloseError::test_code_none tests/gateway/test_qqbot.py::TestQQCloseError::test_string_to_int tests/gateway/test_qqbot.py::TestQQCloseError::test_message_format tests/gateway/test_qqbot.py::TestDispatchPayload::test_unknown_op tests/gateway/test_qqbot.py::TestDispatchPayload::test_op10_updates_heartbeat_interval tests/gateway/test_qqbot.py::TestDispatchPayload::test_op11_heartbeat_ack tests/gateway/test_qqbot.py::TestDispatchPayload::test_seq_tracking tests/gateway/test_qqbot.py::TestDispatchPayload::test_seq_increments tests/gateway/test_qqbot.py::TestReadyHandling::test_ready_stores_session tests/gateway/test_qqbot.py::TestReadyHandling::test_resumed_preserves_session tests/gateway/test_qqbot.py::TestParseJson::test_valid_json tests/gateway/test_qqbot.py::TestParseJson::test_invalid_json tests/gateway/test_qqbot.py::TestParseJson::test_none_input tests/gateway/test_qqbot.py::TestParseJson::test_non_dict_json tests/gateway/test_qqbot.py::TestParseJson::test_empty_dict tests/gateway/test_qqbot.py::TestBuildTextBody::test_plain_text tests/gateway/test_qqbot.py::TestBuildTextBody::test_markdown_text tests/gateway/test_qqbot.py::TestBuildTextBody::test_truncation tests/gateway/test_qqbot.py::TestBuildTextBody::test_empty_string tests/gateway/test_qqbot.py::TestBuildTextBody::test_reply_to tests/gateway/test_qqbot.py::TestWaitForReconnection::test_send_waits_and_succeeds_on_reconnect tests/gateway/test_qqbot.py::TestWaitForReconnection::test_send_returns_retryable_after_timeout tests/gateway/test_qqbot.py::TestWaitForReconnection::test_send_succeeds_immediately_when_connected tests/gateway/test_qqbot.py::TestWaitForReconnection::test_send_media_waits_for_reconnect tests/gateway/test_qqbot.py::TestChunkedUploadFormatSize::test_bytes tests/gateway/test_qqbot.py::TestChunkedUploadFormatSize::test_kilobytes tests/gateway/test_qqbot.py::TestChunkedUploadFormatSize::test_megabytes tests/gateway/test_qqbot.py::TestChunkedUploadFormatSize::test_gigabytes tests/gateway/test_qqbot.py::TestChunkedUploadErrors::test_daily_limit_has_human_size tests/gateway/test_qqbot.py::TestChunkedUploadErrors::test_too_large_includes_limit tests/gateway/test_qqbot.py::TestChunkedUploadErrors::test_too_large_unknown_limit tests/gateway/test_qqbot.py::TestChunkedUploadHelpers::test_read_chunk_exact_bytes tests/gateway/test_qqbot.py::TestChunkedUploadHelpers::test_read_chunk_short_read_raises tests/gateway/test_qqbot.py::TestChunkedUploadHelpers::test_compute_hashes_small_file tests/gateway/test_qqbot.py::TestChunkedUploadHelpers::test_compute_hashes_large_file_has_distinct_md5_10m tests/gateway/test_qqbot.py::TestChunkedUploadHelpers::test_parse_prepare_response_wrapped_in_data tests/gateway/test_qqbot.py::TestChunkedUploadHelpers::test_parse_prepare_response_missing_upload_id_raises tests/gateway/test_qqbot.py::TestChunkedUploadHelpers::test_parse_prepare_response_missing_parts_raises tests/gateway/test_qqbot.py::TestChunkedUploaderFlow::test_full_upload_two_parts_success tests/gateway/test_qqbot.py::TestChunkedUploaderFlow::test_group_paths tests/gateway/test_qqbot.py::TestChunkedUploaderFlow::test_daily_limit_raises_structured_error tests/gateway/test_qqbot.py::TestChunkedUploaderFlow::test_part_finish_retries_on_40093001_then_succeeds tests/gateway/test_qqbot.py::TestChunkedUploaderFlow::test_put_retries_transient_failure tests/gateway/test_qqbot.py::TestApprovalButtonData::test_parse_allow_once tests/gateway/test_qqbot.py::TestApprovalButtonData::test_parse_allow_always tests/gateway/test_qqbot.py::TestApprovalButtonData::test_parse_deny tests/gateway/test_qqbot.py::TestApprovalButtonData::test_parse_invalid_prefix_returns_none tests/gateway/test_qqbot.py::TestApprovalButtonData::test_parse_unknown_decision_returns_none tests/gateway/test_qqbot.py::TestApprovalButtonData::test_parse_empty_returns_none tests/gateway/test_qqbot.py::TestUpdatePromptButtonData::test_parse_yes tests/gateway/test_qqbot.py::TestUpdatePromptButtonData::test_parse_no tests/gateway/test_qqbot.py::TestUpdatePromptButtonData::test_parse_unknown_returns_none tests/gateway/test_qqbot.py::TestUpdatePromptButtonData::test_parse_wrong_prefix tests/gateway/test_qqbot.py::TestBuildApprovalKeyboard::test_three_buttons_in_single_row tests/gateway/test_qqbot.py::TestBuildApprovalKeyboard::test_button_data_embeds_session_key tests/gateway/test_qqbot.py::TestBuildApprovalKeyboard::test_buttons_share_group_id_for_mutual_exclusion tests/gateway/test_qqbot.py::TestBuildApprovalKeyboard::test_to_dict_has_expected_shape tests/gateway/test_qqbot.py::TestBuildApprovalKeyboard::test_round_trip_parse_matches_build tests/gateway/test_qqbot.py::TestBuildUpdatePromptKeyboard::test_two_buttons tests/gateway/test_qqbot.py::TestBuildUpdatePromptKeyboard::test_button_data_shape tests/gateway/test_qqbot.py::TestBuildApprovalText::test_exec_approval_includes_command_preview tests/gateway/test_qqbot.py::TestBuildApprovalText::test_plugin_approval_uses_severity_icon tests/gateway/test_qqbot.py::TestBuildApprovalText::test_truncates_long_commands tests/gateway/test_qqbot.py::TestInteractionEventParsing::test_parse_c2c_interaction tests/gateway/test_qqbot.py::TestInteractionEventParsing::test_parse_group_interaction tests/gateway/test_qqbot.py::TestInteractionEventParsing::test_parse_missing_data_gracefully tests/gateway/test_qqbot.py::TestAdapterInteractionDispatch::test_callback_invoked_with_parsed_event tests/gateway/test_qqbot.py::TestAdapterInteractionDispatch::test_missing_id_skips_ack tests/gateway/test_qqbot.py::TestAdapterInteractionDispatch::test_callback_exception_does_not_propagate tests/gateway/test_qqbot.py::TestAdapterInteractionDispatch::test_explicit_no_callback_is_harmless tests/gateway/test_qqbot.py::TestProcessQuotedContext::test_non_quote_message_returns_empty tests/gateway/test_qqbot.py::TestProcessQuotedContext::test_quote_type_but_no_elements_returns_empty tests/gateway/test_qqbot.py::TestProcessQuotedContext::test_quote_with_text_only tests/gateway/test_qqbot.py::TestProcessQuotedContext::test_quote_with_voice_attachment_runs_stt tests/gateway/test_qqbot.py::TestProcessQuotedContext::test_quote_with_file_preserves_filename tests/gateway/test_qqbot.py::TestProcessQuotedContext::test_quote_with_image_returns_cached_paths tests/gateway/test_qqbot.py::TestProcessQuotedContext::test_quote_with_image_only_no_text tests/gateway/test_qqbot.py::TestProcessQuotedContext::test_multiple_elements_concatenated tests/gateway/test_qqbot.py::TestProcessQuotedContext::test_invalid_message_type_string_returns_empty tests/gateway/test_qqbot.py::TestMergeQuoteInto::test_empty_quote_returns_original tests/gateway/test_qqbot.py::TestMergeQuoteInto::test_empty_text_returns_only_quote tests/gateway/test_qqbot.py::TestMergeQuoteInto::test_both_present_joined_with_blank_line tests/gateway/test_qqbot.py::TestDefaultInteractionDispatch::test_default_callback_installed_on_init tests/gateway/test_qqbot.py::TestDefaultInteractionDispatch::test_send_exec_approval_is_a_class_method tests/gateway/test_qqbot.py::TestDefaultInteractionDispatch::test_approval_click_once_maps_to_once tests/gateway/test_qqbot.py::TestDefaultInteractionDispatch::test_approval_click_always_maps_to_always tests/gateway/test_qqbot.py::TestDefaultInteractionDispatch::test_approval_click_deny_maps_to_deny tests/gateway/test_qqbot.py::TestDefaultInteractionDispatch::test_approval_click_rejects_unauthorized_operator tests/gateway/test_qqbot.py::TestDefaultInteractionDispatch::test_update_prompt_click_writes_response_file tests/gateway/test_qqbot.py::TestDefaultInteractionDispatch::test_update_prompt_click_no_writes_n tests/gateway/test_qqbot.py::TestDefaultInteractionDispatch::test_unknown_button_data_is_harmless tests/gateway/test_qqbot.py::TestDefaultInteractionDispatch::test_empty_button_data_is_harmless tests/gateway/test_qqbot.py::TestDefaultInteractionDispatch::test_resolve_exception_is_swallowed tests/gateway/test_qqbot.py::TestSendExecApproval::test_delegates_to_send_approval_request tests/gateway/test_qqbot.py::TestSendExecApproval::test_accepts_metadata_arg tests/gateway/test_qqbot.py::TestSendUpdatePrompt::test_delegates_to_send_with_keyboard tests/gateway/test_qqbot.py::TestSendUpdatePrompt::test_empty_default_has_no_hint tests/gateway/test_qqbot.py::TestIdentifyIntents::test_intents_include_interaction_bit tests/gateway/test_qqbot.py::TestProcessAttachmentsPathExposure::test_video_attachment_includes_path tests/gateway/test_qqbot.py::TestProcessAttachmentsPathExposure::test_file_attachment_includes_path tests/gateway/test_qqbot.py::TestProcessAttachmentsPathExposure::test_video_without_filename_falls_back_to_content_type tests/gateway/test_qqbot.py::TestProcessAttachmentsPathExposure::test_download_failure_produces_no_attachment_info tests/gateway/test_qqbot.py::TestProcessAttachmentsPathExposure::test_quoted_video_includes_path_in_quote_block tests/gateway/test_qqbot.py::TestProcessAttachmentsPathExposure::test_quoted_file_includes_path_in_quote_block tests/gateway/test_qqbot.py::TestOp7ServerReconnect::test_op7_closes_websocket tests/gateway/test_qqbot.py::TestOp7ServerReconnect::test_op7_close_task_executes tests/gateway/test_qqbot.py::TestOp9InvalidSession::test_op9_not_resumable_clears_session tests/gateway/test_qqbot.py::TestOp9InvalidSession::test_op9_resumable_preserves_session tests/gateway/test_qqbot.py::TestOp9InvalidSession::test_op9_non_resumable_triggers_ws_close tests/gateway/test_qqbot.py::TestCloseCodeClassification::test_4009_preserves_session tests/gateway/test_qqbot.py::TestCloseCodeClassification::test_fatal_codes_include_intent_errors tests/gateway/test_qqbot.py::TestReadEventsClosedWsGuard::test_read_events_raises_when_ws_closed_on_entry tests/gateway/test_qqbot.py::TestReadEventsClosedWsGuard::test_read_events_raises_when_ws_none