Make WordPress Core

Changeset 64314


Ignore:
Timestamp:
10/12/2026 01:26:22 PM (less than one hour ago)
Author:
lancewillett
Message:

Tests: Cover scheduled publication and the Scheduled posts view.

Add PHPUnit coverage for the registered publish_future_post callback: publish due posts, reschedule early calls, and ignore draft or missing posts.

Verify the Scheduled view link, filtered post IDs, and date-column labels for scheduled and past-due posts. Exercise real WordPress behavior without waiting for scheduled times.

Developed in: ​https://github.com/WordPress/wordpress-develop/pull/12668

Props csmcneill, lucatume.
Fixes #65772.

Location:
trunk/tests/phpunit/tests
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/admin/wpPostsListTable.php

    r62814 r64314  
    409409
    410410        /**
     411         * @ticket 65772
     412         * @covers WP_Posts_List_Table::get_views
     413         */
     414        public function test_get_views_should_include_scheduled_posts_link() {
     415                $global_names = array( 'avail_post_stati', 'locked_post_status', 'post_type_object' );
     416                $backup       = array_intersect_key( $GLOBALS, array_flip( $global_names ) );
     417                $future_date  = gmdate( 'Y-m-d H:i:s', time() + DAY_IN_SECONDS );
     418                self::factory()->post->create(
     419                        array(
     420                                'post_status'   => 'future',
     421                                'post_date'     => get_date_from_gmt( $future_date ),
     422                                'post_date_gmt' => $future_date,
     423                        )
     424                );
     425
     426                try {
     427                        $GLOBALS['locked_post_status'] = '';
     428                        $GLOBALS['avail_post_stati']   = get_available_post_statuses( 'post' );
     429                        $table                         = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => 'edit-post' ) );
     430
     431                        $views = $table->get_views();
     432
     433                        $this->assertArrayHasKey( 'future', $views );
     434                        $this->assertSame(
     435                                '<a href="edit.php?post_status=future&#038;post_type=post">Scheduled <span class="count">(1)</span></a>',
     436                                $views['future']
     437                        );
     438                } finally {
     439                        foreach ( $global_names as $name ) {
     440                                if ( array_key_exists( $name, $backup ) ) {
     441                                        $GLOBALS[ $name ] = $backup[ $name ];
     442                                } else {
     443                                        unset( $GLOBALS[ $name ] );
     444                                }
     445                        }
     446                }
     447        }
     448
     449        /**
     450         * @ticket 65772
     451         * @covers WP_Posts_List_Table::prepare_items
     452         */
     453        public function test_prepare_items_should_only_include_scheduled_posts_when_filtered() {
     454                $global_names = array( 'mode', 'avail_post_stati', 'per_page', 'post_type_object' );
     455                $backup       = array_intersect_key( $GLOBALS, array_flip( $global_names ) );
     456                $future_date  = gmdate( 'Y-m-d H:i:s', time() + DAY_IN_SECONDS );
     457                $scheduled_id = self::factory()->post->create(
     458                        array(
     459                                'post_status'   => 'future',
     460                                'post_date'     => get_date_from_gmt( $future_date ),
     461                                'post_date_gmt' => $future_date,
     462                        )
     463                );
     464                self::factory()->post->create( array( 'post_status' => 'publish' ) );
     465                wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
     466
     467                try {
     468                        $_GET     = array(
     469                                'post_type'   => 'post',
     470                                'post_status' => 'future',
     471                        );
     472                        $_REQUEST = $_GET;
     473                        $table    = _get_list_table( 'WP_Posts_List_Table', array( 'screen' => 'edit-post' ) );
     474
     475                        $table->prepare_items();
     476
     477                        $post_ids = wp_list_pluck( $GLOBALS['wp_query']->posts, 'ID' );
     478                        $this->assertSame( array( $scheduled_id ), $post_ids );
     479                } finally {
     480                        foreach ( $global_names as $name ) {
     481                                if ( array_key_exists( $name, $backup ) ) {
     482                                        $GLOBALS[ $name ] = $backup[ $name ];
     483                                } else {
     484                                        unset( $GLOBALS[ $name ] );
     485                                }
     486                        }
     487                }
     488        }
     489
     490        /**
     491         * @ticket 65772
     492         * @dataProvider data_scheduled_post_date_column_status
     493         * @covers WP_Posts_List_Table::column_date
     494         *
     495         * @param int    $offset   Seconds from now to the scheduled date.
     496         * @param string $expected Expected status markup.
     497         */
     498        public function test_column_date_should_show_scheduled_post_status( $offset, $expected ) {
     499                $future_date = gmdate( 'Y-m-d H:i:s', time() + DAY_IN_SECONDS );
     500                $post        = self::factory()->post->create_and_get(
     501                        array(
     502                                'post_status'   => 'future',
     503                                'post_date'     => get_date_from_gmt( $future_date ),
     504                                'post_date_gmt' => $future_date,
     505                        )
     506                );
     507
     508                // Model an overdue date without letting wp_update_post() publish the fixture.
     509                $date                = gmdate( 'Y-m-d H:i:s', time() + $offset );
     510                $post->post_date     = get_date_from_gmt( $date );
     511                $post->post_date_gmt = $date;
     512
     513                ob_start();
     514                try {
     515                        $this->table->column_date( $post );
     516                        $output = ob_get_contents();
     517                } finally {
     518                        ob_end_clean();
     519                }
     520
     521                $this->assertStringStartsWith( $expected . '<br />', $output );
     522        }
     523
     524        /**
     525         * @return array[]
     526         */
     527        public function data_scheduled_post_date_column_status() {
     528                return array(
     529                        'future date'   => array( DAY_IN_SECONDS, 'Scheduled' ),
     530                        'past due date' => array( -DAY_IN_SECONDS, '<strong class="error-message">Missed schedule</strong>' ),
     531                );
     532        }
     533
     534        /**
    411535         * Renders the title column for a post in a given list view mode.
    412536         *
  • trunk/tests/phpunit/tests/cron.php

    r64262 r64314  
    2929                _set_cron_array( array() );
    3030                parent::tear_down();
     31        }
     32
     33        /**
     34         * Tests that the scheduled publication hook publishes a post whose date is due.
     35         *
     36         * @ticket 65772
     37         * @group post
     38         * @covers ::check_and_publish_future_post
     39         */
     40        public function test_scheduled_post_should_publish_when_due() {
     41                global $wpdb;
     42
     43                $future_date = gmdate( 'Y-m-d H:i:s', $this->plus_thirty_minutes );
     44                $post_id     = self::factory()->post->create(
     45                        array(
     46                                'post_status'   => 'future',
     47                                'post_date'     => get_date_from_gmt( $future_date ),
     48                                'post_date_gmt' => $future_date,
     49                        )
     50                );
     51                $event       = wp_get_scheduled_event( 'publish_future_post', array( $post_id ) );
     52
     53                $this->assertNotFalse( $event, 'Creating a future post should schedule publication.' );
     54
     55                // Make the post due without letting wp_update_post() publish it during setup.
     56                $past_date = gmdate( 'Y-m-d H:i:s', time() - HOUR_IN_SECONDS );
     57                $this->assertSame(
     58                        1,
     59                        $wpdb->update(
     60                                $wpdb->posts,
     61                                array(
     62                                        'post_date'     => get_date_from_gmt( $past_date ),
     63                                        'post_date_gmt' => $past_date,
     64                                ),
     65                                array( 'ID' => $post_id )
     66                        )
     67                );
     68                clean_post_cache( $post_id );
     69                $this->assertSame( 'future', get_post_status( $post_id ), 'Setup must not publish the post.' );
     70
     71                // Cron removes a single event before calling its hook.
     72                $this->assertTrue( wp_unschedule_event( $event->timestamp, $event->hook, $event->args ) );
     73
     74                // Exercise the registered callback with the scheduled event's real arguments.
     75                do_action_ref_array( $event->hook, $event->args );
     76
     77                clean_post_cache( $post_id );
     78                $this->assertSame( 'publish', get_post_status( $post_id ) );
     79                $this->assertFalse( wp_next_scheduled( 'publish_future_post', array( $post_id ) ) );
     80        }
     81
     82        /**
     83         * Tests that an early publication hook leaves the post scheduled for its date.
     84         *
     85         * @ticket 65772
     86         * @group post
     87         * @covers ::check_and_publish_future_post
     88         */
     89        public function test_scheduled_post_should_not_publish_before_due() {
     90                $future_date = gmdate( 'Y-m-d H:i:s', $this->plus_thirty_minutes );
     91                $post_id     = self::factory()->post->create(
     92                        array(
     93                                'post_status'   => 'future',
     94                                'post_date'     => get_date_from_gmt( $future_date ),
     95                                'post_date_gmt' => $future_date,
     96                        )
     97                );
     98                $event       = wp_get_scheduled_event( 'publish_future_post', array( $post_id ) );
     99
     100                $this->assertNotFalse( $event );
     101                $this->assertSame( 'future', get_post_status( $post_id ) );
     102
     103                // Cron removes a single event before calling its hook. An early call must reschedule it.
     104                $this->assertTrue( wp_unschedule_event( $event->timestamp, $event->hook, $event->args ) );
     105                $this->assertFalse( wp_next_scheduled( 'publish_future_post', array( $post_id ) ) );
     106
     107                do_action_ref_array( $event->hook, $event->args );
     108
     109                clean_post_cache( $post_id );
     110                $this->assertSame( 'future', get_post_status( $post_id ) );
     111                $this->assertSame( $this->plus_thirty_minutes, wp_next_scheduled( 'publish_future_post', array( $post_id ) ) );
     112        }
     113
     114        /**
     115         * @ticket 65772
     116         * @group post
     117         * @covers ::check_and_publish_future_post
     118         */
     119        public function test_draft_post_should_not_publish_when_scheduled_hook_fires() {
     120                $post_id = self::factory()->post->create( array( 'post_status' => 'draft' ) );
     121
     122                do_action_ref_array( 'publish_future_post', array( $post_id ) );
     123
     124                clean_post_cache( $post_id );
     125                $this->assertSame( 'draft', get_post_status( $post_id ) );
     126        }
     127
     128        /**
     129         * @ticket 65772
     130         * @group post
     131         * @covers ::check_and_publish_future_post
     132         */
     133        public function test_missing_post_should_be_ignored_when_scheduled_hook_fires() {
     134                do_action_ref_array( 'publish_future_post', array( PHP_INT_MAX ) );
     135
     136                $this->assertFalse( wp_next_scheduled( 'publish_future_post', array( PHP_INT_MAX ) ) );
    31137        }
    32138
Note: See TracChangeset for help on using the changeset viewer.