@@ -852,6 +852,13 @@ public function test_mention_with_array() {
852852 'object ' => array (
853853 'id ' => 'https://example.com/post/1 ' ,
854854 'content ' => 'Test mention ' ,
855+ 'tag ' => array (
856+ array (
857+ 'type ' => 'Mention ' ,
858+ 'href ' => Actors::get_by_id ( $ user_id )->get_id (),
859+ 'name ' => '@test ' ,
860+ ),
861+ ),
855862 ),
856863 'cc ' => array ( Actors::get_by_id ( $ user_id )->get_id () ),
857864 );
@@ -978,6 +985,13 @@ public function test_mention_filters_recipients() {
978985 'object ' => array (
979986 'id ' => 'https://example.com/post/1 ' ,
980987 'content ' => 'Test mention ' ,
988+ 'tag ' => array (
989+ array (
990+ 'type ' => 'Mention ' ,
991+ 'href ' => Actors::get_by_id ( $ user_id )->get_id (),
992+ 'name ' => '@test ' ,
993+ ),
994+ ),
981995 ),
982996 // Only user_id is in CC, not other_user_id.
983997 'cc ' => array ( Actors::get_by_id ( $ user_id )->get_id () ),
@@ -1018,4 +1032,122 @@ function ( $args ) use ( $user_id, $other_user_id ) {
10181032 \remove_all_filters ( 'wp_mail ' );
10191033 \wp_delete_user ( $ other_user_id );
10201034 }
1035+
1036+ /**
1037+ * Test that users in CC without actual mention tags do not receive mention notifications.
1038+ *
1039+ * This tests the bug fix where users added to CC (e.g., because they follow the actor)
1040+ * were incorrectly receiving mention notifications even when not actually mentioned.
1041+ *
1042+ * @covers ::mention
1043+ */
1044+ public function test_mention_requires_tag_not_just_cc () {
1045+ $ user_id = self ::$ user_id ;
1046+
1047+ // Activity with user in CC but NOT mentioned in tags.
1048+ $ activity = array (
1049+ 'actor ' => 'https://example.com/sports-account ' ,
1050+ 'object ' => array (
1051+ 'id ' => 'https://example.com/sports-account/posts/123 ' ,
1052+ 'type ' => 'Note ' ,
1053+ 'content ' => '<p>Join @user1 and @user2 on our stream...</p> ' ,
1054+ 'tag ' => array (
1055+ // Other users mentioned, but NOT the local user.
1056+ array (
1057+ 'type ' => 'Mention ' ,
1058+ 'href ' => 'https://example.com/user1 ' ,
1059+ 'name ' => 'user1@example.com ' ,
1060+ ),
1061+ array (
1062+ 'type ' => 'Mention ' ,
1063+ 'href ' => 'https://example.com/user2 ' ,
1064+ 'name ' => 'user2@example.com ' ,
1065+ ),
1066+ ),
1067+ ),
1068+ // User is in CC (e.g., because they follow the actor).
1069+ 'cc ' => array ( Actors::get_by_id ( $ user_id )->get_id () ),
1070+ );
1071+
1072+ // Mock remote metadata.
1073+ $ metadata_filter = function () {
1074+ return array (
1075+ 'name ' => 'Sports Account ' ,
1076+ 'url ' => 'https://example.com/sports-account ' ,
1077+ );
1078+ };
1079+ \add_filter ( 'pre_get_remote_metadata_by_actor ' , $ metadata_filter );
1080+
1081+ $ mock = new \MockAction ();
1082+ \add_filter ( 'wp_mail ' , array ( $ mock , 'filter ' ), 1 );
1083+
1084+ // Trigger mention notification.
1085+ Mailer::mention ( $ activity , $ user_id );
1086+
1087+ // Should NOT send any email because user is not actually mentioned in tags.
1088+ $ this ->assertEquals ( 0 , $ mock ->get_call_count (), 'User in CC without mention tag should not receive notification ' );
1089+
1090+ // Clean up.
1091+ \remove_filter ( 'pre_get_remote_metadata_by_actor ' , $ metadata_filter );
1092+ \remove_filter ( 'wp_mail ' , array ( $ mock , 'filter ' ), 1 );
1093+ }
1094+
1095+ /**
1096+ * Test that users with actual mention tags DO receive mention notifications.
1097+ *
1098+ * @covers ::mention
1099+ */
1100+ public function test_mention_with_tag_sends_notification () {
1101+ $ user_id = self ::$ user_id ;
1102+
1103+ // Activity with user properly mentioned in both CC and tags.
1104+ $ activity = array (
1105+ 'actor ' => 'https://example.com/author ' ,
1106+ 'object ' => array (
1107+ 'id ' => 'https://example.com/post/1 ' ,
1108+ 'type ' => 'Note ' ,
1109+ 'content ' => '<p>Hello @testuser, how are you?</p> ' ,
1110+ 'tag ' => array (
1111+ array (
1112+ 'type ' => 'Mention ' ,
1113+ 'href ' => Actors::get_by_id ( $ user_id )->get_id (),
1114+ 'name ' => '@testuser ' ,
1115+ ),
1116+ ),
1117+ ),
1118+ 'cc ' => array ( Actors::get_by_id ( $ user_id )->get_id () ),
1119+ );
1120+
1121+ // Mock remote metadata.
1122+ $ metadata_filter = function () {
1123+ return array (
1124+ 'name ' => 'Test Author ' ,
1125+ 'url ' => 'https://example.com/author ' ,
1126+ );
1127+ };
1128+ \add_filter ( 'pre_get_remote_metadata_by_actor ' , $ metadata_filter );
1129+
1130+ $ mock = new \MockAction ();
1131+ \add_filter ( 'wp_mail ' , array ( $ mock , 'filter ' ), 1 );
1132+
1133+ // Capture email.
1134+ $ mail_filter = function ( $ args ) use ( $ user_id ) {
1135+ $ this ->assertStringContainsString ( 'Mention ' , $ args ['subject ' ] );
1136+ $ this ->assertStringContainsString ( 'Test Author ' , $ args ['subject ' ] );
1137+ $ this ->assertEquals ( \get_user_by ( 'id ' , $ user_id )->user_email , $ args ['to ' ] );
1138+ return $ args ;
1139+ };
1140+ \add_filter ( 'wp_mail ' , $ mail_filter );
1141+
1142+ // Trigger mention notification.
1143+ Mailer::mention ( $ activity , $ user_id );
1144+
1145+ // Should send 1 email because user is properly mentioned.
1146+ $ this ->assertEquals ( 1 , $ mock ->get_call_count (), 'User properly mentioned in tags should receive notification ' );
1147+
1148+ // Clean up.
1149+ \remove_filter ( 'pre_get_remote_metadata_by_actor ' , $ metadata_filter );
1150+ \remove_filter ( 'wp_mail ' , array ( $ mock , 'filter ' ), 1 );
1151+ \remove_filter ( 'wp_mail ' , $ mail_filter );
1152+ }
10211153}
0 commit comments