@@ -75,19 +75,82 @@ public function test_add_local_post_to_queue(): void {
7575 * @return void
7676 */
7777 public function test_add_local_post_to_queue_multiple_times (): void {
78- $ post_id = 1 ;
78+ $ post_id = 999 ;
7979
8080 // Create the event.
8181 $ event = new Process_Local_Post_Event ();
8282
83- $ event ::add_to_queue ( $ post_id );
84- $ event ::add_to_queue ( $ post_id );
85- $ event ::add_to_queue ( $ post_id );
86- $ event ::add_to_queue ( $ post_id );
87- $ event ::add_to_queue ( $ post_id );
83+ Process_Local_Post_Event ::add_to_queue ( $ post_id );
84+ Process_Local_Post_Event ::add_to_queue ( $ post_id );
85+ Process_Local_Post_Event ::add_to_queue ( $ post_id );
86+ Process_Local_Post_Event ::add_to_queue ( $ post_id );
87+ Process_Local_Post_Event ::add_to_queue ( $ post_id );
8888
8989 // Check that the action has been added to the queue.
90- $ actions = $ this ->wpdb ->get_results ( "SELECT * FROM {$ this ->wpdb ->prefix }actionscheduler_actions where status='pending' " );
90+ $ actions = $ this ->wpdb ->get_results ( "SELECT * FROM {$ this ->wpdb ->prefix }actionscheduler_actions " );
91+ $ this ->assertCount ( 1 , $ actions );
92+
93+ // Check to ensure the hard delete handler is removed, to prevent interference with other events.
94+ $ this ->assertFalse ( has_action ( 'action_scheduler_canceled_action ' , array ( Process_Local_Post_Event::class, 'handle_hard_delete ' ) ) );
95+ }
96+
97+ /**
98+ * @testdox If there are many pending rows for the same post, scheduling should collapse them to one.
99+ *
100+ * @see https://github.com/a8cteam51/internet-archive-wayback-machine-link-fixer/issues/294
101+ * @return void
102+ */
103+ public function test_add_local_post_to_queue_with_existing_duplicates (): void {
104+ $ post_id = 42 ;
105+
106+ // Insert 10 duplicate pending scheduled actions for the same post to
107+ // simulate the DB flood described in the issue.
108+ $ table = $ this ->wpdb ->prefix . 'actionscheduler_actions ' ;
109+ $ now = time ();
110+ for ( $ i = 0 ; $ i < 10 ; $ i ++ ) {
111+ $ ts = $ now + $ i ;
112+
113+ $ schedule = \str_replace (
114+ '11111111 ' ,
115+ "$ ts " ,
116+ '"O:30:"ActionScheduler_SimpleSchedule":2:{s:22:"\x00*\x00scheduled_timestamp";i:11111111;s:41:"\x00ActionScheduler_SimpleSchedule\x00timestamp";i:11111111;}" ' ,
117+ );
118+
119+ $ this ->wpdb ->insert (
120+ $ table ,
121+ array (
122+ 'action_id ' => time () + $ i ,
123+ 'hook ' => Process_Local_Post_Event::HANDLE ,
124+ 'status ' => 'pending ' ,
125+ 'scheduled_date_gmt ' => gmdate ( 'Y-m-d H:i:s ' , $ ts ),
126+ 'scheduled_date_local ' => gmdate ( 'Y-m-d H:i:s ' , $ ts ),
127+ 'priority ' => 10 ,
128+ 'args ' => wp_json_encode ( array ( 'post_id ' => $ post_id ) ),
129+ 'schedule ' => $ schedule ,
130+ 'group_id ' => 3 ,
131+ 'attempts ' => 0 ,
132+ 'last_attempt_gmt ' => '0000-00-00 00:00:00 ' ,
133+ 'last_attempt_local ' => '0000-00-00 00:00:00 ' ,
134+ 'claim_id ' => 0 ,
135+ 'extended_args ' => null ,
136+ ),
137+ array ( '%s ' , '%s ' , '%s ' , '%s ' , '%d ' , '%s ' , '%d ' , '%d ' , '%s ' , '%s ' , '%d ' , '%s ' )
138+ );
139+ }
140+
141+ // Call the method which should remove duplicates and add a single
142+ // scheduled action.
143+ Process_Local_Post_Event::add_to_queue_with_delay ( $ post_id , 0 );
144+
145+ // Check there is only one pending action for that post.
146+ $ actions = $ this ->wpdb ->get_results (
147+ $ this ->wpdb ->prepare (
148+ "SELECT * FROM {$ table } WHERE status = 'pending' AND hook = %s AND args = %s " ,
149+ Process_Local_Post_Event::HANDLE ,
150+ wp_json_encode ( array ( 'post_id ' => $ post_id ) )
151+ )
152+ );
153+
91154 $ this ->assertCount ( 1 , $ actions );
92155 }
93156
@@ -265,4 +328,59 @@ function ( string $url ) use ( &$processed ): string {
265328 $ this ->assertGreaterThan ( -10 , abs ( time () - $ meta ) );
266329 }
267330
331+ /**
332+ * @testdox When we run the event, any other pending events for the same post should be removed from the queue.
333+ *
334+ * @see https://github.com/a8cteam51/internet-archive-wayback-machine-link-fixer/issues/294
335+ *
336+ * @return void
337+ */
338+ public function test_process_local_post_event_removes_duplicates_from_queue (): void {
339+ // Mock the snapshot client to return success.
340+ $ this ->create_service (
341+ true ,
342+ function ( array $ config ): array {
343+ $ config ['snapshot ' ]
344+ ->method ( 'create_snapshot ' )
345+ ->willReturnCallback ( fn ( $ url ) => $ url );
346+ return $ config ;
347+ }
348+ );
349+
350+ // Create a post.
351+ $ post_id = \wp_insert_post (
352+ array (
353+ 'post_title ' => 'Test Post ' ,
354+ 'post_content ' => 'Test Content ' ,
355+ 'post_status ' => 'publish ' ,
356+ )
357+ );
358+
359+ // Add 10 events to the queue for the same post ID.
360+ $ now = time ();
361+ for ( $ i = 0 ; $ i < 10 ; $ i ++ ) {
362+ $ ts = $ now + $ i ;
363+
364+ as_schedule_single_action (
365+ $ ts ,
366+ Process_Local_Post_Event::HANDLE ,
367+ array (
368+ 'post_id ' => $ post_id ,
369+ )
370+ );
371+ }
372+
373+ // Add the event to the queue.
374+ Process_Local_Post_Event::add_to_queue_with_delay ( $ post_id , 0 );
375+
376+ // Process the event.
377+ $ event = new Process_Local_Post_Event ();
378+ $ event ( $ post_id );
379+
380+ // Check there is NO pending actions for that post.
381+ $ actions = $ actions = $ this ->wpdb ->get_results ( "SELECT * FROM {$ this ->wpdb ->prefix }actionscheduler_actions where status='pending' " );
382+ $ this ->assertEmpty ( $ actions );
383+
384+
385+ }
268386}
0 commit comments