tests/gateway/test_ntfy_plugin.py::test_platform_enum_resolves_via_plugin_scan tests/gateway/test_ntfy_plugin.py::TestNtfyRequirements::test_returns_false_when_httpx_unavailable tests/gateway/test_ntfy_plugin.py::TestNtfyRequirements::test_returns_false_when_topic_not_set tests/gateway/test_ntfy_plugin.py::TestNtfyRequirements::test_returns_true_when_topic_set_via_env tests/gateway/test_ntfy_plugin.py::TestNtfyRequirements::test_validate_config_requires_topic tests/gateway/test_ntfy_plugin.py::TestNtfyRequirements::test_is_connected_from_extra tests/gateway/test_ntfy_plugin.py::TestNtfyRequirements::test_is_connected_from_env tests/gateway/test_ntfy_plugin.py::TestNtfyAdapterInit::test_default_server_url tests/gateway/test_ntfy_plugin.py::TestNtfyAdapterInit::test_topic_read_from_extra tests/gateway/test_ntfy_plugin.py::TestNtfyAdapterInit::test_topic_read_from_env tests/gateway/test_ntfy_plugin.py::TestNtfyAdapterInit::test_publish_topic_falls_back_to_topic tests/gateway/test_ntfy_plugin.py::TestNtfyAdapterInit::test_publish_topic_uses_extra_value tests/gateway/test_ntfy_plugin.py::TestNtfyAdapterInit::test_token_read_from_extra tests/gateway/test_ntfy_plugin.py::TestNtfyAdapterInit::test_token_read_from_env tests/gateway/test_ntfy_plugin.py::TestNtfyAdapterInit::test_server_trailing_slash_stripped tests/gateway/test_ntfy_plugin.py::TestNtfyAdapterInit::test_initial_state tests/gateway/test_ntfy_plugin.py::TestAuthHeaders::test_no_token_returns_empty_dict tests/gateway/test_ntfy_plugin.py::TestAuthHeaders::test_bearer_token_for_plain_token tests/gateway/test_ntfy_plugin.py::TestAuthHeaders::test_basic_auth_for_user_colon_password tests/gateway/test_ntfy_plugin.py::TestAuthHeaders::test_bearer_token_used_when_no_colon tests/gateway/test_ntfy_plugin.py::TestAuthHeaders::test_auth_header_key_is_authorization tests/gateway/test_ntfy_plugin.py::TestDeduplication::test_first_message_not_duplicate tests/gateway/test_ntfy_plugin.py::TestDeduplication::test_second_occurrence_is_duplicate tests/gateway/test_ntfy_plugin.py::TestDeduplication::test_different_ids_not_duplicate tests/gateway/test_ntfy_plugin.py::TestDeduplication::test_many_messages_recorded tests/gateway/test_ntfy_plugin.py::TestDeduplication::test_cache_pruned_on_overflow tests/gateway/test_ntfy_plugin.py::TestDeduplication::test_expired_id_can_be_seen_again tests/gateway/test_ntfy_plugin.py::TestConnect::test_connect_fails_when_httpx_unavailable tests/gateway/test_ntfy_plugin.py::TestConnect::test_connect_fails_when_no_topic tests/gateway/test_ntfy_plugin.py::TestConnect::test_connect_starts_stream_task tests/gateway/test_ntfy_plugin.py::TestConnect::test_disconnect_clears_state tests/gateway/test_ntfy_plugin.py::TestConnect::test_disconnect_cancels_stream_task tests/gateway/test_ntfy_plugin.py::TestSend::test_send_fails_without_http_client tests/gateway/test_ntfy_plugin.py::TestSend::test_send_posts_to_publish_topic tests/gateway/test_ntfy_plugin.py::TestSend::test_send_falls_back_to_subscribe_topic tests/gateway/test_ntfy_plugin.py::TestSend::test_send_uses_metadata_publish_topic tests/gateway/test_ntfy_plugin.py::TestSend::test_send_handles_http_error_status tests/gateway/test_ntfy_plugin.py::TestSend::test_send_handles_timeout tests/gateway/test_ntfy_plugin.py::TestSend::test_send_truncates_to_max_length tests/gateway/test_ntfy_plugin.py::TestSend::test_send_typing_is_noop tests/gateway/test_ntfy_plugin.py::TestSend::test_get_chat_info_returns_dict tests/gateway/test_ntfy_plugin.py::TestSend::test_send_includes_bearer_auth_header tests/gateway/test_ntfy_plugin.py::TestSend::test_send_emits_markdown_header_when_enabled tests/gateway/test_ntfy_plugin.py::TestSend::test_send_omits_markdown_header_when_disabled tests/gateway/test_ntfy_plugin.py::TestSend::test_send_emits_echo_tag_header tests/gateway/test_ntfy_plugin.py::TestOnMessage::test_message_dispatched_to_handler tests/gateway/test_ntfy_plugin.py::TestOnMessage::test_empty_message_skipped tests/gateway/test_ntfy_plugin.py::TestOnMessage::test_duplicate_message_skipped tests/gateway/test_ntfy_plugin.py::TestOnMessage::test_own_tagged_message_skipped tests/gateway/test_ntfy_plugin.py::TestOnMessage::test_message_with_other_tags_still_dispatched tests/gateway/test_ntfy_plugin.py::TestOnMessage::test_timestamp_parsed_from_event tests/gateway/test_ntfy_plugin.py::TestOnMessage::test_message_id_set_from_event tests/gateway/test_ntfy_plugin.py::TestOnMessage::test_title_not_used_as_user_id tests/gateway/test_ntfy_plugin.py::TestOnMessage::test_unknown_publisher_cannot_impersonate_allowed_user tests/gateway/test_ntfy_plugin.py::TestOnMessage::test_source_chat_id_is_topic tests/gateway/test_ntfy_plugin.py::TestEnvEnablement::test_returns_none_without_topic tests/gateway/test_ntfy_plugin.py::TestEnvEnablement::test_seeds_topic_and_server tests/gateway/test_ntfy_plugin.py::TestEnvEnablement::test_custom_server_url tests/gateway/test_ntfy_plugin.py::TestEnvEnablement::test_publish_topic_seeded tests/gateway/test_ntfy_plugin.py::TestEnvEnablement::test_token_seeded tests/gateway/test_ntfy_plugin.py::TestEnvEnablement::test_markdown_truthy_values tests/gateway/test_ntfy_plugin.py::TestEnvEnablement::test_markdown_falsy_values tests/gateway/test_ntfy_plugin.py::TestEnvEnablement::test_home_channel_defaults_to_topic tests/gateway/test_ntfy_plugin.py::TestEnvEnablement::test_home_channel_override tests/gateway/test_ntfy_plugin.py::TestStandaloneSend::test_errors_without_topic tests/gateway/test_ntfy_plugin.py::TestStandaloneSend::test_posts_to_server tests/gateway/test_ntfy_plugin.py::TestStandaloneSend::test_emits_echo_tag_header tests/gateway/test_ntfy_plugin.py::TestStandaloneSend::test_emits_bearer_token_when_configured tests/gateway/test_ntfy_plugin.py::TestStandaloneSend::test_basic_auth_when_token_has_colon tests/gateway/test_ntfy_plugin.py::TestStandaloneSend::test_returns_error_on_http_failure tests/gateway/test_ntfy_plugin.py::test_register_calls_register_platform tests/gateway/test_ntfy_plugin.py::test_adapter_factory_returns_ntfy_adapter tests/gateway/test_ntfy_plugin.py::TestTokenHygiene::test_trailing_whitespace_stripped tests/gateway/test_ntfy_plugin.py::TestTokenHygiene::test_trailing_newline_stripped tests/gateway/test_ntfy_plugin.py::TestTokenHygiene::test_whitespace_only_returns_empty tests/gateway/test_ntfy_plugin.py::TestTokenHygiene::test_basic_auth_token_also_stripped tests/gateway/test_ntfy_plugin.py::TestTokenHygiene::test_adapter_strips_token_via_helper tests/gateway/test_ntfy_plugin.py::TestFatalErrorPropagation::test_401_sets_fatal_unauthorized tests/gateway/test_ntfy_plugin.py::TestFatalErrorPropagation::test_404_sets_fatal_topic_not_found tests/gateway/test_ntfy_plugin.py::TestTruncateHelper::test_short_message_passes_through tests/gateway/test_ntfy_plugin.py::TestTruncateHelper::test_long_message_truncated tests/gateway/test_ntfy_plugin.py::TestTruncateHelper::test_unicode_message_encoded