diff --git a/inc/Engine/Common/Clock/ClockInterface.php b/inc/Engine/Common/Clock/ClockInterface.php new file mode 100644 index 0000000000..4871732581 --- /dev/null +++ b/inc/Engine/Common/Clock/ClockInterface.php @@ -0,0 +1,27 @@ += 0 ) ) { + return $output; + } + return $current_time; + } +} diff --git a/inc/Engine/Optimization/RUCSS/Controller/UsedCSS.php b/inc/Engine/Optimization/RUCSS/Controller/UsedCSS.php index 6b634b281f..2bb0858d2d 100644 --- a/inc/Engine/Optimization/RUCSS/Controller/UsedCSS.php +++ b/inc/Engine/Optimization/RUCSS/Controller/UsedCSS.php @@ -4,6 +4,7 @@ namespace WP_Rocket\Engine\Optimization\RUCSS\Controller; use WP_Rocket\Admin\Options_Data; +use WP_Rocket\Engine\Common\Clock\WPRClock; use WP_Rocket\Engine\Common\Context\ContextInterface; use WP_Rocket\Engine\Common\Queue\QueueInterface; use WP_Rocket\Engine\Optimization\CSSTrait; @@ -12,6 +13,7 @@ use WP_Rocket\Engine\Optimization\RUCSS\Database\Queries\UsedCSS as UsedCSS_Query; use WP_Rocket\Engine\Optimization\RUCSS\Frontend\APIClient; use WP_Admin_Bar; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Factory\StrategyFactory; use WP_Rocket\Logger\LoggerAware; use WP_Rocket\Logger\LoggerAwareInterface; @@ -97,6 +99,20 @@ class UsedCSS implements LoggerAwareInterface { */ private $inline_content_exclusions = []; + /** + * Retry Strategy Factory + * + * @var StrategyFactory + */ + protected $strategy_factory; + + /** + * Clock instance. + * + * @var WPRClock + */ + protected $wpr_clock; + /** * Instantiate the class. * @@ -108,6 +124,8 @@ class UsedCSS implements LoggerAwareInterface { * @param Filesystem $filesystem Filesystem instance. * @param ContextInterface $context RUCSS context. * @param ContextInterface $optimize_url_context RUCSS optimize url context. + * @param StrategyFactory $strategy_factory Strategy Factory used for RUCSS retry process. + * @param WPRClock $clock Clock object instance. */ public function __construct( Options_Data $options, @@ -117,7 +135,9 @@ public function __construct( DataManager $data_manager, Filesystem $filesystem, ContextInterface $context, - ContextInterface $optimize_url_context + ContextInterface $optimize_url_context, + StrategyFactory $strategy_factory, + WPRClock $clock ) { $this->options = $options; $this->used_css_query = $used_css_query; @@ -127,6 +147,8 @@ public function __construct( $this->filesystem = $filesystem; $this->context = $context; $this->optimize_url_context = $optimize_url_context; + $this->strategy_factory = $strategy_factory; + $this->wpr_clock = $clock; } /** @@ -500,12 +522,14 @@ public function process_pending_jobs() { } foreach ( $pending_jobs as $used_css_row ) { - $this->logger::debug( "RUCSS: Send the job for url {$used_css_row->url} to Async task to check its job status." ); + $current_time = $this->wpr_clock->current_time( 'timestamp', true ); + if ( strtotime( $used_css_row->next_retry_time ) < $current_time ) { + $this->logger::debug( "RUCSS: Send the job for url {$used_css_row->url} to Async task to check its job status." ); - // Change status to in-progress. - $this->used_css_query->make_status_inprogress( (int) $used_css_row->id ); - - $this->queue->add_job_status_check_async( (int) $used_css_row->id ); + // Change status to in-progress. + $this->used_css_query->make_status_inprogress( (int) $used_css_row->id ); + $this->queue->add_job_status_check_async( (int) $used_css_row->id ); + } } } @@ -518,6 +542,7 @@ public function process_pending_jobs() { */ public function check_job_status( int $id ) { $this->logger::debug( 'RUCSS: Start checking job status for row ID: ' . $id ); + $row_details = $this->used_css_query->get_item( $id ); if ( ! $row_details ) { $this->logger::debug( 'RUCSS: Row ID not found ', compact( 'id' ) ); @@ -550,39 +575,9 @@ public function check_job_status( int $id ) { if ( 200 !== (int) $job_details['code'] - || - empty( $job_details['contents'] ) - || - ! isset( $job_details['contents']['shakedCSS'] ) ) { $this->logger::debug( 'RUCSS: Job status failed for url: ' . $row_details->url, $job_details ); - - // Failure, check the retries number. - if ( $row_details->retries >= 3 ) { - $this->logger::debug( 'RUCSS: Job failed 3 times for url: ' . $row_details->url ); - /** - * Unlock preload URL. - * - * @param string $url URL to unlock - */ - do_action( 'rocket_preload_unlock_url', $row_details->url ); - - $this->used_css_query->make_status_failed( $id, strval( $job_details['code'] ), $job_details['message'] ); - - return; - } - - // on timeout errors with code 408 create new job. - switch ( $job_details['code'] ) { - case 408: - $this->add_url_to_the_queue( $row_details->url, (bool) $row_details->is_mobile ); - return; - } - - // Increment the retries number with 1 , Change status to pending again and change job id on timeout. - $this->used_css_query->increment_retries( $id, (int) $job_details['code'], $job_details['message'] ); - - // @Todo: Maybe we can add this row to the async job to get the status before the next cron + $this->strategy_factory->manage( $row_details, $job_details ); return; } diff --git a/inc/Engine/Optimization/RUCSS/Database/Queries/UsedCSS.php b/inc/Engine/Optimization/RUCSS/Database/Queries/UsedCSS.php index 2656313745..3f36ea2313 100644 --- a/inc/Engine/Optimization/RUCSS/Database/Queries/UsedCSS.php +++ b/inc/Engine/Optimization/RUCSS/Database/Queries/UsedCSS.php @@ -106,6 +106,7 @@ public function get_pending_jobs( int $count = 100 ) { 'fields' => [ 'id', 'url', + 'next_retry_time', ], 'job_id__not_in' => [ 'not_in' => '', @@ -578,6 +579,50 @@ private function table_exists(): bool { return $exists; } + /** + * Update the error message. + * + * @param int $job_id Job ID. + * @param int $code Response code. + * @param string $message Response message. + * @param string $previous_message Previous saved message. + * + * @return bool + */ + public function update_message( int $job_id, int $code, string $message, string $previous_message = '' ): bool { + return $this->update_item( + $job_id, + [ + 'error_message' => $previous_message . ' - ' . current_time( 'mysql', true ) . " {$code}: {$message}", + ] + ); + } + + /** + * Updates the next_retry_time field + * + * @param mixed $job_id the job id. + * @param string|int $next_retry_time timestamp or mysql format date. + * + * @return bool either it is saved or not. + */ + public function update_next_retry_time( $job_id, $next_retry_time ): bool { + if ( is_string( $next_retry_time ) && strtotime( $next_retry_time ) ) { + // If $next_retry_time is a valid date string, convert it to a timestamp. + $next_retry_time = strtotime( $next_retry_time ); + } elseif ( ! is_numeric( $next_retry_time ) ) { + // If it's not numeric and not a valid date string, return false. + return false; + } + + return $this->update_item( + $job_id, + [ + 'next_retry_time' => gmdate( 'Y-m-d H:i:s', $next_retry_time ), + ] + ); + } + /** * Change the status to be pending. * diff --git a/inc/Engine/Optimization/RUCSS/Database/Row/UsedCSS.php b/inc/Engine/Optimization/RUCSS/Database/Row/UsedCSS.php index 612de39b72..69ef32cef9 100644 --- a/inc/Engine/Optimization/RUCSS/Database/Row/UsedCSS.php +++ b/inc/Engine/Optimization/RUCSS/Database/Row/UsedCSS.php @@ -113,6 +113,13 @@ class UsedCSS extends Row { */ public $submitted_at; + /** + * Tells when the retry has to be processed + * + * @var int + */ + public $next_retry_time; + /** * UsedCSS constructor. * @@ -122,19 +129,20 @@ public function __construct( $item ) { parent::__construct( $item ); // Set the type of each column, and prepare. - $this->id = (int) $this->id; - $this->url = (string) $this->url; - $this->css = (string) $this->css; - $this->hash = (string) $this->hash; - $this->error_code = (string) $this->error_code; - $this->error_message = (string) $this->error_message; - $this->retries = (int) $this->retries; - $this->is_mobile = (bool) $this->is_mobile; - $this->job_id = (string) $this->job_id; - $this->queue_name = (string) $this->queue_name; - $this->status = (string) $this->status; - $this->modified = empty( $this->modified ) ? 0 : strtotime( $this->modified ); - $this->last_accessed = empty( $this->last_accessed ) ? 0 : strtotime( $this->last_accessed ); - $this->submitted_at = empty( $this->submitted_at ) ? 0 : strtotime( $this->submitted_at ); + $this->id = (int) $this->id; + $this->url = (string) $this->url; + $this->css = (string) $this->css; + $this->hash = (string) $this->hash; + $this->error_code = (string) $this->error_code; + $this->error_message = (string) $this->error_message; + $this->retries = (int) $this->retries; + $this->is_mobile = (bool) $this->is_mobile; + $this->job_id = (string) $this->job_id; + $this->queue_name = (string) $this->queue_name; + $this->status = (string) $this->status; + $this->modified = empty( $this->modified ) ? 0 : strtotime( $this->modified ); + $this->last_accessed = empty( $this->last_accessed ) ? 0 : strtotime( $this->last_accessed ); + $this->submitted_at = empty( $this->submitted_at ) ? 0 : strtotime( $this->submitted_at ); + $this->next_retry_time = empty( $this->next_retry_time ) ? 0 : strtotime( $this->next_retry_time ); } } diff --git a/inc/Engine/Optimization/RUCSS/Database/Schemas/UsedCSS.php b/inc/Engine/Optimization/RUCSS/Database/Schemas/UsedCSS.php index 184f07d83f..3adf0b0027 100644 --- a/inc/Engine/Optimization/RUCSS/Database/Schemas/UsedCSS.php +++ b/inc/Engine/Optimization/RUCSS/Database/Schemas/UsedCSS.php @@ -163,5 +163,15 @@ class UsedCSS extends Schema { 'date_query' => true, 'sortable' => true, ], + + // NEXT_RETRY_TIME column. + [ + 'name' => 'next_retry_time', + 'type' => 'timestamp', + 'default' => '0000-00-00 00:00:00', + 'created' => true, + 'date_query' => true, + 'sortable' => true, + ], ]; } diff --git a/inc/Engine/Optimization/RUCSS/Database/Tables/UsedCSS.php b/inc/Engine/Optimization/RUCSS/Database/Tables/UsedCSS.php index 2ac29cf71f..18eaac6477 100644 --- a/inc/Engine/Optimization/RUCSS/Database/Tables/UsedCSS.php +++ b/inc/Engine/Optimization/RUCSS/Database/Tables/UsedCSS.php @@ -28,7 +28,7 @@ class UsedCSS extends Table { * * @var int */ - protected $version = 20231010; + protected $version = 20231031; /** * Key => value array of versions => methods. @@ -42,6 +42,7 @@ class UsedCSS extends Table { 20220920 => 'make_status_column_index_instead_queue_name', 20221104 => 'add_error_columns', 20231010 => 'add_submitted_at_column', + 20231031 => 'add_next_retry_time_column', ]; /** @@ -60,21 +61,22 @@ public function __construct() { */ protected function set_schema() { $this->schema = " - id bigint(20) unsigned NOT NULL AUTO_INCREMENT, - url varchar(2000) NOT NULL default '', - css longtext default NULL, - hash varchar(32) default '', - error_code varchar(32) NULL default NULL, - error_message longtext NULL default NULL, - unprocessedcss longtext NULL, - retries tinyint(1) NOT NULL default 1, - is_mobile tinyint(1) NOT NULL default 0, - job_id varchar(255) NOT NULL default '', - queue_name varchar(255) NOT NULL default '', - status varchar(255) NOT NULL default '', - modified timestamp NOT NULL default '0000-00-00 00:00:00', - last_accessed timestamp NOT NULL default '0000-00-00 00:00:00', - submitted_at timestamp NULL, + id bigint(20) unsigned NOT NULL AUTO_INCREMENT, + url varchar(2000) NOT NULL default '', + css longtext default NULL, + hash varchar(32) default '', + error_code varchar(32) NULL default NULL, + error_message longtext NULL default NULL, + unprocessedcss longtext NULL, + retries tinyint(1) NOT NULL default 1, + is_mobile tinyint(1) NOT NULL default 0, + job_id varchar(255) NOT NULL default '', + queue_name varchar(255) NOT NULL default '', + status varchar(255) NOT NULL default '', + modified timestamp NOT NULL default '0000-00-00 00:00:00', + last_accessed timestamp NOT NULL default '0000-00-00 00:00:00', + submitted_at timestamp NULL, + next_retry_time timestamp NOT NULL default '0000-00-00 00:00:00', PRIMARY KEY (id), KEY url (url(150), is_mobile), KEY modified (modified), @@ -342,4 +344,21 @@ protected function add_submitted_at_column() { return $this->is_success( $created ); } + + /** + * Adds the next_retry_time column + * + * @return bool + */ + protected function add_next_retry_time_column() { + $next_retry_time_exists = $this->column_exists( 'next_retry_time' ); + + $created = true; + + if ( ! $next_retry_time_exists ) { + $created &= $this->get_db()->query( "ALTER TABLE `{$this->table_name}` ADD COLUMN next_retry_time timestamp NOT NULL default '0000-00-00 00:00:00' AFTER submitted_at" ); + } + + return $this->is_success( $created ); + } } diff --git a/inc/Engine/Optimization/RUCSS/ServiceProvider.php b/inc/Engine/Optimization/RUCSS/ServiceProvider.php index 05f2aba46c..aeae9e5330 100644 --- a/inc/Engine/Optimization/RUCSS/ServiceProvider.php +++ b/inc/Engine/Optimization/RUCSS/ServiceProvider.php @@ -3,6 +3,7 @@ use WP_Rocket\Dependencies\League\Container\ServiceProvider\AbstractServiceProvider; use WP_Rocket\Engine\Optimization\RUCSS\Admin\{Database, OptionSubscriber, Settings}; +use WP_Rocket\Engine\Common\Clock\WPRClock; use WP_Rocket\Engine\Optimization\RUCSS\Admin\Subscriber as AdminSubscriber; use WP_Rocket\Engine\Optimization\RUCSS\Context\RUCSSContext; use WP_Rocket\Engine\Optimization\RUCSS\Context\RUCSSOptimizeContext; @@ -14,6 +15,11 @@ use WP_Rocket\Engine\Optimization\RUCSS\Database\Tables\UsedCSS as UsedCSSTable; use WP_Rocket\Engine\Optimization\RUCSS\Frontend\APIClient; use WP_Rocket\Engine\Optimization\RUCSS\Frontend\Subscriber as FrontendSubscriber; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Context\RetryContext; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Factory\StrategyFactory; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Strategies\DefaultProcess; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Strategies\JobSetFail; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Strategies\ResetRetryProcess; /** * Service provider for the WP Rocket RUCSS @@ -44,6 +50,11 @@ class ServiceProvider extends AbstractServiceProvider { 'rucss_filesystem', 'rucss_cron_subscriber', 'rucss_used_css_controller', + 'rucss_retry_strategy_factory', + 'rucss_retry_strategy_job_found_no_result', + 'rucss_retry_strategy_job_not_found', + 'rucss_retry_strategy_reset_retry', + 'rucss_retry_strategy_context', ]; /** @@ -77,6 +88,12 @@ public function register() { $this->getContainer()->add( 'rucss_optimize_context', RUCSSOptimizeContext::class ) ->addArgument( $this->getContainer()->get( 'options' ) ); + $this->getContainer()->add( 'wpr_clock', WPRClock::class ); + + $this->getContainer()->add( 'rucss_retry_strategy_factory', StrategyFactory::class ) + ->addArgument( $this->getContainer()->get( 'rucss_used_css_query' ) ) + ->addArgument( $this->getContainer()->get( 'wpr_clock' ) ); + $this->getContainer()->add( 'rucss_used_css_controller', UsedCSSController::class ) ->addArgument( $this->getContainer()->get( 'options' ) ) ->addArgument( $this->getContainer()->get( 'rucss_used_css_query' ) ) @@ -85,7 +102,20 @@ public function register() { ->addArgument( $this->getContainer()->get( 'dynamic_lists_defaultlists_data_manager' ) ) ->addArgument( $this->getContainer()->get( 'rucss_filesystem' ) ) ->addArgument( $this->getContainer()->get( 'rucss_context' ) ) - ->addArgument( $this->getContainer()->get( 'rucss_optimize_context' ) ); + ->addArgument( $this->getContainer()->get( 'rucss_optimize_context' ) ) + ->addArgument( $this->getContainer()->get( 'rucss_retry_strategy_factory' ) ) + ->addArgument( $this->getContainer()->get( 'wpr_clock' ) ); + + $this->getContainer()->add( 'rucss_retry_strategy_default_process', DefaultProcess::class ) + ->addArgument( $this->getContainer()->get( 'rucss_used_css_query' ) ); + + $this->getContainer()->add( 'rucss_retry_strategy_job_set_fail', JobSetFail::class ) + ->addArgument( $this->getContainer()->get( 'rucss_used_css_query' ) ); + + $this->getContainer()->add( 'rucss_retry_strategy_reset_retry', ResetRetryProcess::class ) + ->addArgument( $this->getContainer()->get( 'rucss_used_css_query' ) ); + + $this->getContainer()->add( 'rucss_retry_strategy_context', RetryContext::class ); $this->getContainer()->share( 'rucss_option_subscriber', OptionSubscriber::class ) ->addArgument( $this->getContainer()->get( 'rucss_settings' ) ); diff --git a/inc/Engine/Optimization/RUCSS/Strategy/Context/RetryContext.php b/inc/Engine/Optimization/RUCSS/Strategy/Context/RetryContext.php new file mode 100644 index 0000000000..87a8daafc9 --- /dev/null +++ b/inc/Engine/Optimization/RUCSS/Strategy/Context/RetryContext.php @@ -0,0 +1,37 @@ +strategy = $strategy; + } + + /** + * Execute the strategy. + * + * @param object $row_details row from the database. + * @param array $job_details job details. + * + * @return void + */ + public function execute( $row_details, $job_details ): void { + $this->strategy->execute( $row_details, $job_details ); + } +} diff --git a/inc/Engine/Optimization/RUCSS/Strategy/Factory/StrategyFactory.php b/inc/Engine/Optimization/RUCSS/Strategy/Factory/StrategyFactory.php new file mode 100644 index 0000000000..1fd4ecf088 --- /dev/null +++ b/inc/Engine/Optimization/RUCSS/Strategy/Factory/StrategyFactory.php @@ -0,0 +1,73 @@ +used_css_query = $used_css_query; + $this->clock = $clock; + } + /** + * Manage the whole process, to determine which strategy to adopt.. + * + * @param object $row_details DB Row of a job. + * @param array $job_details Job information from the API. + * + * @return void + */ + public function manage( $row_details, $job_details ): void { + + switch ( $job_details['code'] ) { + case 408: + $strategy = new ResetRetryProcess( $this->used_css_query ); + break; + case 500: + case 422: + case 404: + case 401: + $strategy = new JobSetFail( $this->used_css_query ); + break; + default: + $strategy = new DefaultProcess( $this->used_css_query, $this->clock ); + break; + } + + $context = new RetryContext(); + $context->set_strategy( $strategy ); + $context->execute( $row_details, $job_details ); + } +} diff --git a/inc/Engine/Optimization/RUCSS/Strategy/Strategies/DefaultProcess.php b/inc/Engine/Optimization/RUCSS/Strategy/Strategies/DefaultProcess.php new file mode 100644 index 0000000000..0195631cff --- /dev/null +++ b/inc/Engine/Optimization/RUCSS/Strategy/Strategies/DefaultProcess.php @@ -0,0 +1,111 @@ + 60, // 1 minutes + 1 => 120, // 2 minutes + 2 => 300, // 5 minutes + 3 => 600, // 10 minutes. + 4 => 1200, // 20 minutes. + 5 => 1800, // 30 minutes. + ]; + + /** + * Default value to wait before a retry. + * + * @var int + */ + private $default_waiting_retry = 1800; + + /** + * Strategy Constructor. + * + * @param UsedCSS_Query $used_css_query DB Table. + * @param WPRClock $clock Clock object. + */ + public function __construct( UsedCSS_Query $used_css_query, WPRClock $clock ) { + $this->used_css_query = $used_css_query; + $this->clock = $clock; + + /** + * Filter the array containing the time needed to wait for each retry. + * + * @param array $time_table_entry contains the number of retry and how long we have to wait. + */ + $time_table_retry = apply_filters( 'rocket_rucss_retry_table', $this->time_table_retry ); + + if ( is_array( $time_table_retry ) ) { + $this->time_table_retry = $time_table_retry; + } + } + + /** + * Execute the strategy process. + * + * @param object $row_details Row details of the job. + * @param array $job_details Job details from the API. + * + * @return void + */ + public function execute( object $row_details, array $job_details ): void { + if ( $row_details->retries >= count( $this->time_table_retry ) ) { + /** + * Unlock preload URL. + * + * @param string $url URL to unlock + */ + do_action( 'rocket_preload_unlock_url', $row_details->url ); + + $this->used_css_query->make_status_failed( $row_details->id, strval( $job_details['code'] ), $job_details['message'] ); + + return; + } + + $this->used_css_query->increment_retries( $row_details->id, (int) $row_details->retries, $job_details['message'] ); + + $rucss_retry_duration = $this->time_table_retry[ $row_details->retries ] ?? $this->default_waiting_retry; // Default to 30 minutes. + + /** + * Filter used css retry duration. + * + * @param int $duration Duration between each retry in seconds. + */ + $rucss_retry_duration = (int) apply_filters( 'rocket_rucss_retry_duration', $rucss_retry_duration ); + if ( $rucss_retry_duration < 0 ) { + $rucss_retry_duration = $this->default_waiting_retry; + } + + // update the `next_retry_time` column. + $next_retry_time = $this->clock->current_time( 'timestamp', true ) + $rucss_retry_duration; + + $this->used_css_query->update_message( $row_details->id, $job_details['code'], $job_details['message'], $row_details->error_message ); + $this->used_css_query->update_next_retry_time( (int) $row_details->id, $next_retry_time ); + } +} diff --git a/inc/Engine/Optimization/RUCSS/Strategy/Strategies/JobSetFail.php b/inc/Engine/Optimization/RUCSS/Strategy/Strategies/JobSetFail.php new file mode 100644 index 0000000000..a4e63fa2fd --- /dev/null +++ b/inc/Engine/Optimization/RUCSS/Strategy/Strategies/JobSetFail.php @@ -0,0 +1,45 @@ +used_css_query = $used_css_query; + } + + /** + * Execute the strategy process. + * + * @param object $row_details Row details of the job. + * @param array $job_details Job details from the API. + * + * @return void + */ + public function execute( object $row_details, array $job_details ): void { + /** + * Unlock preload URL. + * + * @param string $url URL to unlock + */ + do_action( 'rocket_preload_unlock_url', $row_details->url ); + + $this->used_css_query->make_status_failed( $row_details->id, strval( $job_details['code'] ), $job_details['message'] ); + } +} diff --git a/inc/Engine/Optimization/RUCSS/Strategy/Strategies/ResetRetryProcess.php b/inc/Engine/Optimization/RUCSS/Strategy/Strategies/ResetRetryProcess.php new file mode 100644 index 0000000000..3e531448f9 --- /dev/null +++ b/inc/Engine/Optimization/RUCSS/Strategy/Strategies/ResetRetryProcess.php @@ -0,0 +1,43 @@ +used_css_query = $used_css_query; + } + + /** + * Execute the strategy process. + * + * @param object $row_details Row details of the job. + * @param array $job_details Job details from the API. + * + * @return void + */ + public function execute( object $row_details, array $job_details ): void { + $used_css_row = $this->used_css_query->get_row( $row_details->url, (bool) $row_details->is_mobile ); + if ( empty( $used_css_row ) ) { + $this->used_css_query->create_new_job( $row_details->url, '', '', $row_details->is_mobile ); + return; + } + $this->used_css_query->reset_job( (int) $used_css_row->id ); + } +} diff --git a/inc/Engine/Optimization/RUCSS/Strategy/Strategies/StrategyInterface.php b/inc/Engine/Optimization/RUCSS/Strategy/Strategies/StrategyInterface.php new file mode 100644 index 0000000000..bd67ed0399 --- /dev/null +++ b/inc/Engine/Optimization/RUCSS/Strategy/Strategies/StrategyInterface.php @@ -0,0 +1,15 @@ + [ 'rows_count' => 100, 'in_progress' => null, + 'next_retry_time' => '2023-11-22 02:00:00', ] ], 'pendingShouldPassInProgress' => [ @@ -29,7 +30,8 @@ 'rows' => [ (object) [ 'id' => 10, - 'url' => 'http://example.org' + 'url' => 'http://example.org', + 'next_retry_time' => '2023-11-22 02:00:00', ] ] ], diff --git a/tests/Fixtures/inc/Engine/Optimization/RUCSS/Cron/Subscriber/checkJobStatus.php b/tests/Fixtures/inc/Engine/Optimization/RUCSS/Cron/Subscriber/checkJobStatus.php index 07b2fe4176..3d969e2cf5 100644 --- a/tests/Fixtures/inc/Engine/Optimization/RUCSS/Cron/Subscriber/checkJobStatus.php +++ b/tests/Fixtures/inc/Engine/Optimization/RUCSS/Cron/Subscriber/checkJobStatus.php @@ -177,6 +177,192 @@ [ 'status' => 'to-submit', 'job_id' => '', + 'retries' => 0, + 'queue_name' => 'queue', + 'hash' => '', + ] + ], + 'files' => [ + '/wp-content/cache/used-css/1/h/a/s/h.css.gz' => [ + 'exists' => false, + ] + ] + ] + ], + '400ShouldRetry' =>[ + 'config' => [ + 'hash' => 'hash', + 'row' => [ + 'url' => 'https://example.org', + 'job_id' => '123', + 'queue_name' => 'queue', + ], + 'request' => [ + 'url' => 'http://localhostrucss-job', + 'method' => 'GET', + 'response' => [ + 'response' => [ + 'code' => 400, + 'message' => 'bad json' + ], + 'body' => $error_response + ] + ], + 'create' => [ + 'url' => 'http://localhostrucss-job', + 'method' => 'POST', + 'response' => [ + 'response' => [ + 'code' => 200, + ], + 'body' => $success_response_create + ] + ] + ], + 'expected' => [ + 'rows' => [ + [ + 'status' => 'pending', + 'job_id' => '123', + 'queue_name' => 'queue', + 'hash' => '', + 'retries' => 1 + ] + ], + 'files' => [ + '/wp-content/cache/used-css/1/h/a/s/h.css.gz' => [ + 'exists' => false, + ] + ] + ] + ], + '404ShouldFail' =>[ + 'config' => [ + 'hash' => 'hash', + 'row' => [ + 'url' => 'https://example.org', + 'job_id' => '123', + 'queue_name' => 'queue', + ], + 'request' => [ + 'url' => 'http://localhostrucss-job', + 'method' => 'GET', + 'response' => [ + 'response' => [ + 'code' => 404, + 'message' => 'Job not found' + ], + 'body' => $error_response + ] + ], + 'create' => [ + 'url' => 'http://localhostrucss-job', + 'method' => 'POST', + 'response' => [ + 'response' => [ + 'code' => 200, + ], + 'body' => $success_response_create + ] + ] + ], + 'expected' => [ + 'rows' => [ + [ + 'status' => 'failed', + 'job_id' => '123', + 'queue_name' => 'queue', + 'hash' => '', + ] + ], + 'files' => [ + '/wp-content/cache/used-css/1/h/a/s/h.css.gz' => [ + 'exists' => false, + ] + ] + ] + ], + '500ShouldFail' =>[ + 'config' => [ + 'hash' => 'hash', + 'row' => [ + 'url' => 'https://example.org', + 'job_id' => '123', + 'queue_name' => 'queue', + ], + 'request' => [ + 'url' => 'http://localhostrucss-job', + 'method' => 'GET', + 'response' => [ + 'response' => [ + 'code' => 500, + 'message' => 'error' + ], + 'body' => $error_response + ] + ], + 'create' => [ + 'url' => 'http://localhostrucss-job', + 'method' => 'POST', + 'response' => [ + 'response' => [ + 'code' => 200, + ], + 'body' => $success_response_create + ] + ] + ], + 'expected' => [ + 'rows' => [ + [ + 'status' => 'failed', + 'job_id' => '123', + 'queue_name' => 'queue', + 'hash' => '', + ] + ], + 'files' => [ + '/wp-content/cache/used-css/1/h/a/s/h.css.gz' => [ + 'exists' => false, + ] + ] + ] + ], + '422ShouldFail' =>[ + 'config' => [ + 'hash' => 'hash', + 'row' => [ + 'url' => 'https://example.org', + 'job_id' => '123', + 'queue_name' => 'queue', + ], + 'request' => [ + 'url' => 'http://localhostrucss-job', + 'method' => 'GET', + 'response' => [ + 'response' => [ + 'code' => 422, + 'message' => 'error' + ], + 'body' => $error_response + ] + ], + 'create' => [ + 'url' => 'http://localhostrucss-job', + 'method' => 'POST', + 'response' => [ + 'response' => [ + 'code' => 200, + ], + 'body' => $success_response_create + ] + ] + ], + 'expected' => [ + 'rows' => [ + [ + 'status' => 'failed', + 'job_id' => '123', 'queue_name' => 'queue', 'hash' => '', ] diff --git a/tests/Fixtures/inc/Engine/Optimization/RUCSS/Strategy/Context/RetryContext/setStrategy.php b/tests/Fixtures/inc/Engine/Optimization/RUCSS/Strategy/Context/RetryContext/setStrategy.php new file mode 100644 index 0000000000..ca5d8ed549 --- /dev/null +++ b/tests/Fixtures/inc/Engine/Optimization/RUCSS/Strategy/Context/RetryContext/setStrategy.php @@ -0,0 +1,5 @@ + 180, // 3 minutes + 2 => 300, // 5 minutes + 3 => 600, // 10 minutes + 4 => 900, // 15 minutes. + 5 => 1200, // 20 minutes. + 6 => 1800, // 30 minutes. +]; + +return [ + 'ShouldRetryMore' => [ + 'config' => [ + 'job_id' => 1, + 'job_details' => [ + 'id' => '1', + 'code' => 400, + 'message' => 'Error', + ], + 'row_details' => (object) [ + 'id' => 1, + 'job_id' => 1, + 'status' => 'in-progress', + 'retries' => 0, + 'url' => 'https://example.org/page', + 'error_message' => '', + ], + 'time_table' => $time_table, + 'duration_retry' => 180 + ], + 'expected' => [ + 'row_details' => [ + 'id' => 1, + 'status' => 'pending', + 'retries' => 1, + 'not_process_before' => 180, + ] + ] + ], + 'ShouldFail' => [ + 'config' => [ + 'job_id' => 2, + 'job_details' => [ + 'id' => '2', + 'code' => 400, + 'message' => 'Error', + ], + 'row_details' => (object) [ + 'id' => 2, + 'job_id' => 2, + 'status' => 'in-progress', + 'url' => 'https://example.org/page', + 'error_message' => '', + 'retries' => 10 + ], + 'duration_retry' => 180, + 'time_table' => $time_table, + ], + 'expected' => [ + 'row_details' => [ + 'id' => 2, + 'status' => 'failed', + ], + + ] + ], +]; diff --git a/tests/Fixtures/inc/Engine/Optimization/RUCSS/Strategy/Strategies/JobSetFail/execute.php b/tests/Fixtures/inc/Engine/Optimization/RUCSS/Strategy/Strategies/JobSetFail/execute.php new file mode 100644 index 0000000000..a71d655301 --- /dev/null +++ b/tests/Fixtures/inc/Engine/Optimization/RUCSS/Strategy/Strategies/JobSetFail/execute.php @@ -0,0 +1,49 @@ + [ + 'config' => [ + 'job_id' => 1, + 'job_details' => [ + 'id' => '1', + 'code' => 404, + 'message' => 'Error', + ], + 'row_details' => [ + 'id' => 1, + 'status' => 'in-progress', + ], + ], + 'expected' => [], + ], + 'ApiGives422' => [ + 'config' => [ + 'job_id' => 2, + 'job_details' => [ + 'id' => '2', + 'code' => 422, + 'message' => 'Error', + ], + 'row_details' => [ + 'id' => 2, + 'status' => 'in-progress', + ], + ], + 'expected' => [] + ], + 'ApiGives500' => [ + 'config' => [ + 'job_id' => 3, + 'job_details' => [ + 'id' => '3', + 'code' => 500, + 'message' => 'Error' + ], + 'row_details' => [ + 'id' => 3, + 'status' => 'in-progress', + ] + ], + 'expected' => [], + ], +]; diff --git a/tests/Fixtures/inc/Engine/Optimization/RUCSS/Strategy/Strategies/ResetRetryProcess/execute.php b/tests/Fixtures/inc/Engine/Optimization/RUCSS/Strategy/Strategies/ResetRetryProcess/execute.php new file mode 100644 index 0000000000..81a7aab3b4 --- /dev/null +++ b/tests/Fixtures/inc/Engine/Optimization/RUCSS/Strategy/Strategies/ResetRetryProcess/execute.php @@ -0,0 +1,23 @@ + [ + 'config' => [ + 'job_id' => 1, + 'job_details' => [ + 'id' => '1', + 'code' => 408, + 'message' => 'Error', + ], + 'row_details' => (object) [ + 'id' => 1, + 'job_id' => '', + 'status' => 'in-progress', + 'url' => 'https://example.com/page', + 'is_mobile' => false, + 'queue_name' => '' + ], + ], + 'expected' => [], + ], +]; diff --git a/tests/Fixtures/inc/Engine/Preload/Controller/PreloadUrl/processPendingJobs.php b/tests/Fixtures/inc/Engine/Preload/Controller/PreloadUrl/processPendingJobs.php index b33125d6a4..6d97f12d37 100644 --- a/tests/Fixtures/inc/Engine/Preload/Controller/PreloadUrl/processPendingJobs.php +++ b/tests/Fixtures/inc/Engine/Preload/Controller/PreloadUrl/processPendingJobs.php @@ -7,6 +7,7 @@ 'is_mobile' => false, 'url' => 'http://example1', 'status' => 'pending', + 'next_retry_time' => '2023-11-22 02:00:00' ]); $row2 = new CacheRow([ @@ -14,6 +15,8 @@ 'is_mobile' => false, 'url' => 'http://example2', 'status' => 'pending', + 'next_retry_time' => '2023-11-22 02:00:00' + ]); $row3 = new CacheRow([ @@ -21,6 +24,8 @@ 'is_mobile' => false, 'url' => 'http://example3', 'status' => 'pending', + 'next_retry_time' => '2023-11-22 02:00:00' + ]); $outdated_row = new CacheRow([ @@ -28,6 +33,8 @@ 'is_mobile' => false, 'url' => 'http://example3', 'status' => 'in-progress', + 'next_retry_time' => '2023-11-22 02:00:00' + ]); return [ diff --git a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/UsedCSSTrait.php b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/UsedCSSTrait.php index 9224f6abde..a0bf9b4c59 100644 --- a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/UsedCSSTrait.php +++ b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/UsedCSSTrait.php @@ -5,6 +5,7 @@ use _PHPStan_7c8075089\Nette\Schema\Context; use Mockery; use WP_Rocket\Admin\Options_Data; +use WP_Rocket\Engine\Common\Clock\WPRClock; use WP_Rocket\Engine\Common\Context\ContextInterface; use WP_Rocket\Engine\Common\Queue\QueueInterface; use WP_Rocket\Engine\Optimization\DynamicLists\DefaultLists\DataManager; @@ -13,6 +14,7 @@ use WP_Rocket\Engine\Optimization\RUCSS\Controller\UsedCSS; use WP_Rocket\Engine\Optimization\RUCSS\Database\Queries\UsedCSS as UsedCSS_Query; use WP_Rocket\Engine\Optimization\RUCSS\Frontend\APIClient; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Factory\StrategyFactory; use WP_Rocket\Tests\Unit\HasLoggerTrait; trait UsedCSSTrait @@ -63,6 +65,9 @@ trait UsedCSSTrait protected $optimisedContext; + protected $strategy_factory; + protected $clock; + public function set_up() { parent::set_up(); $this->options = Mockery::mock(Options_Data::class); @@ -74,8 +79,10 @@ public function set_up() { $this->database = Mockery::mock(Database::class); $this->context = Mockery::mock(ContextInterface::class); $this->optimisedContext = Mockery::mock(ContextInterface::class); + $this->strategy_factory = Mockery::mock(StrategyFactory::class); + $this->clock = Mockery::mock(WPRClock::class); - $this->usedcss = new UsedCSS($this->options, $this->used_css_query, $this->api, $this->queue, $this->data_manager, $this->filesystem, $this->context, $this->optimisedContext); + $this->usedcss = new UsedCSS($this->options, $this->used_css_query, $this->api, $this->queue, $this->data_manager, $this->filesystem, $this->context, $this->optimisedContext, $this->strategy_factory, $this->clock); $this->set_logger($this->usedcss); } } diff --git a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/checkJobStatus.php b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/checkJobStatus.php index fd43248429..95e4cb5807 100644 --- a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/checkJobStatus.php +++ b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/checkJobStatus.php @@ -14,6 +14,8 @@ use Brain\Monkey\Functions; use Brain\Monkey\Filters; use Brain\Monkey\Actions; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Factory\StrategyFactory; +use WP_Rocket\Engine\Common\Clock\WPRClock; /** @@ -32,6 +34,13 @@ class Test_CheckJobStatus extends TestCase { protected $data_manager; protected $filesystem; + protected $strategy_factory; + + /** + * @var WPRClock + */ + protected $wpr_clock; + protected function setUp(): void { parent::setUp(); $this->options = Mockery::mock( Options_Data::class ); @@ -42,6 +51,9 @@ protected function setUp(): void { $this->filesystem = Mockery::mock( Filesystem::class ); $this->context = Mockery::mock(ContextInterface::class); $this->optimisedContext = Mockery::mock(ContextInterface::class); + $this->wpr_clock = Mockery::mock(WPRClock::class); + $this->strategy_factory = Mockery::mock(StrategyFactory::class, [$this->usedCssQuery, $this->wpr_clock]); + $this->usedCss = Mockery::mock( UsedCSS::class . '[is_allowed,update_last_accessed,add_url_to_the_queue]', [ @@ -53,9 +65,12 @@ protected function setUp(): void { $this->filesystem, $this->context, $this->optimisedContext, + $this->strategy_factory, + $this->wpr_clock, ] ); + $this->set_logger($this->usedCss); } @@ -110,39 +125,11 @@ public function testShouldReturnAsExpected( $config, $expected ) { } if ( 200 !== $job_details['code'] - || - empty( $job_details['contents'] ) - || - ! isset( $job_details['contents']['shakedCSS'] ) ) { - if ( $row_details->retries >= 3 ) { - Actions\expectDone('rocket_preload_unlock_url')->with($config['row_details']['url']); - $this->usedCssQuery->expects( self::once() ) - ->method( 'make_status_failed' ) - ->with( $config['job_id'], $job_details['code'], $job_details['message'] ); - - $this->usedCss->check_job_status( $config['job_id'] ); - - return; - } - - // on timeout errors with code 408 create new job. - if ( 408 === $job_details['code'] ) { - - $this->usedCss->expects()->add_url_to_the_queue( $config['row_details']['url'], (bool) $config['row_details']['is_mobile'] ); - - $this->usedCss->check_job_status( $config['job_id'] ); - return; - } - $this->usedCssQuery->expects( self::once() ) - ->method( 'increment_retries' ) - ->with( $config['job_id'], $job_details['code'], $job_details['message'] ); + $this->strategy_factory->expects( 'manage' )->with( $row_details, $job_details ); $this->usedCss->check_job_status( $config['job_id'] ); - return; - } else { - Actions\expectDone('rocket_preload_unlock_url')->with($config['row_details']['url']); } @@ -171,19 +158,4 @@ public function testShouldReturnAsExpected( $config, $expected ) { $this->usedCss->check_job_status( $config['job_id'] ); } - protected function configureCreateNewJob( $url, $is_mobile, $add_to_queue_response ) { - - $this->options->expects()->get( 'remove_unused_css_safelist', [] )->andReturn( [] ); - - Filters\expectApplied( 'rocket_rucss_safelist' )->with( [] )->andReturn( [] ); - $create_new_job_config = [ - 'treeshake' => 1, - 'rucss_safelist' => [], - 'is_mobile' => $is_mobile, - 'is_home' => $url, - ]; - $this->api->expects() - ->add_to_queue( $url, $create_new_job_config ) - ->andReturn( $add_to_queue_response ); - } } diff --git a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/clearFailedUrls.php b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/clearFailedUrls.php index 73639cd1a4..e7b7c7d866 100644 --- a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/clearFailedUrls.php +++ b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/clearFailedUrls.php @@ -8,6 +8,8 @@ use WP_Rocket\Engine\Optimization\RUCSS\Database\Queries\UsedCSS as UsedCSS_Query; use WP_Rocket\Engine\Optimization\RUCSS\Database\Row\UsedCSS as UsedCSS_Row; use WP_Rocket\Engine\Optimization\RUCSS\Frontend\APIClient; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Factory\StrategyFactory; +use WP_Rocket\Engine\Common\Clock\WPRClock; use WP_Rocket\Logger\Logger; use WP_Rocket\Tests\Unit\TestCase; use WP_Rocket\Engine\Optimization\DynamicLists\DefaultLists\DataManager; @@ -29,6 +31,16 @@ class Test_ClearFailedUrls extends TestCase { protected $data_manager; protected $filesystem; + /** + * @var StrategyFactory + */ + protected $strategy_factory; + + /** + * @var WPRClock + */ + protected $wpr_clock; + protected function setUp(): void { parent::setUp(); $this->options = Mockery::mock( Options_Data::class ); @@ -39,6 +51,8 @@ protected function setUp(): void { $this->filesystem = Mockery::mock( Filesystem::class ); $this->context = Mockery::mock(ContextInterface::class); $this->optimisedContext = Mockery::mock(ContextInterface::class); + $this->strategy_factory = Mockery::mock(StrategyFactory::class); + $this->wpr_clock = Mockery::mock(WPRClock::class); $this->usedCss = Mockery::mock( UsedCSS::class . '[is_allowed,update_last_accessed,add_url_to_the_queue]', [ @@ -50,6 +64,8 @@ protected function setUp(): void { $this->filesystem, $this->context, $this->optimisedContext, + $this->strategy_factory, + $this->wpr_clock, ] ); } diff --git a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/processOnSubmitJobs.php b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/processOnSubmitJobs.php index 7ce38cfc93..728ed629e2 100644 --- a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/processOnSubmitJobs.php +++ b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/processOnSubmitJobs.php @@ -3,6 +3,7 @@ namespace WP_Rocket\Tests\Unit\inc\Engine\Optimization\RUCSS\Controller\UsedCSS; use Mockery; +use WP_Rocket\Engine\Common\Clock\WPRClock; use WP_Rocket\Engine\Optimization\RUCSS\Controller\UsedCSS; use WP_Rocket\Admin\Options_Data; use WP_Rocket\Engine\Optimization\RUCSS\Database\Queries\UsedCSS as UsedCSS_Query; @@ -13,6 +14,7 @@ use WP_Rocket\Engine\Common\Context\ContextInterface; use Brain\Monkey\Filters; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Factory\StrategyFactory; use WP_Rocket\Tests\Unit\HasLoggerTrait; use WP_Rocket\Tests\Unit\TestCase; use Brain\Monkey\Functions; @@ -69,6 +71,16 @@ class Test_processOnSubmitJobs extends TestCase { */ protected $usedcss; + /** + * @var StrategyFactory + */ + protected $strategy_factory; + + /** + * @var WPRClock + */ + protected $wpr_clock; + public function set_up() { parent::set_up(); $this->options = Mockery::mock(Options_Data::class); @@ -79,8 +91,10 @@ public function set_up() { $this->filesystem = Mockery::mock(Filesystem::class); $this->context = Mockery::mock(ContextInterface::class); $this->optimize_url_context = Mockery::mock(ContextInterface::class); + $this->strategy_factory = Mockery::mock(StrategyFactory::class); + $this->wpr_clock = Mockery::mock(WPRClock::class); - $this->usedcss = new UsedCSS($this->options, $this->used_css_query, $this->api, $this->queue, $this->data_manager, $this->filesystem, $this->context, $this->optimize_url_context); + $this->usedcss = new UsedCSS($this->options, $this->used_css_query, $this->api, $this->queue, $this->data_manager, $this->filesystem, $this->context, $this->optimize_url_context, $this->strategy_factory, $this->wpr_clock); $this->set_logger($this->usedcss); } diff --git a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/processPendingJobs.php b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/processPendingJobs.php index bebcffea50..1b1b808612 100644 --- a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/processPendingJobs.php +++ b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/processPendingJobs.php @@ -3,6 +3,7 @@ namespace WP_Rocket\Tests\Unit\inc\Engine\Optimization\RUCSS\Controller\UsedCSS; use Mockery; +use WP_Rocket\Engine\Common\Clock\WPRClock; use WP_Rocket\Engine\Common\Context\ContextInterface; use WP_Rocket\Engine\Optimization\RUCSS\Controller\UsedCSS; use WP_Rocket\Admin\Options_Data; @@ -14,6 +15,7 @@ use WP_Rocket\Engine\Optimization\RUCSS\Admin\Database; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Factory\StrategyFactory; use WP_Rocket\Tests\Unit\HasLoggerTrait; use WP_Rocket\Tests\Unit\TestCase; use Brain\Monkey\Filters; @@ -64,6 +66,16 @@ class Test_processPendingJobs extends TestCase { */ protected $usedcss; + /** + * @var StrategyFactory + */ + protected $strategy_factory; + + /** + * @var WPRClock + */ + protected $wpr_clock; + public function set_up() { parent::set_up(); $this->options = Mockery::mock(Options_Data::class); @@ -75,8 +87,10 @@ public function set_up() { $this->database = Mockery::mock(Database::class); $this->context = Mockery::mock(ContextInterface::class); $this->optimisedContext = Mockery::mock(ContextInterface::class); + $this->strategy_factory = Mockery::mock(StrategyFactory::class); + $this->wpr_clock = Mockery::mock(WPRClock::class); - $this->usedcss = new UsedCSS($this->options, $this->used_css_query, $this->api, $this->queue, $this->data_manager, $this->filesystem, $this->context, $this->optimisedContext,); + $this->usedcss = new UsedCSS($this->options, $this->used_css_query, $this->api, $this->queue, $this->data_manager, $this->filesystem, $this->context, $this->optimisedContext, $this->strategy_factory, $this->wpr_clock); $this->set_logger($this->usedcss); } @@ -90,6 +104,11 @@ public function testShouldDoAsExpected( $config, $expected ) $this->configureDisabled($config, $expected); $this->configureEnabled($config, $expected); + $this->wpr_clock->shouldReceive( 'current_time' ) + ->with( 'timestamp', true ) + ->atMost() + ->once() + ->andReturn( 1700999999 ); $this->usedcss->process_pending_jobs(); } diff --git a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/treeshake.php b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/treeshake.php index ee49be2b43..7ffb7c3b79 100644 --- a/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/treeshake.php +++ b/tests/Unit/inc/Engine/Optimization/RUCSS/Controller/UsedCSS/treeshake.php @@ -8,6 +8,8 @@ use WP_Rocket\Engine\Optimization\RUCSS\Database\Queries\UsedCSS as UsedCSS_Query; use WP_Rocket\Engine\Optimization\RUCSS\Database\Row\UsedCSS as UsedCSS_Row; use WP_Rocket\Engine\Optimization\RUCSS\Frontend\APIClient; +use WP_Rocket\Engine\Optimization\RUCSS\Strategy\Factory\StrategyFactory; +use WP_Rocket\Engine\Common\Clock\WPRClock; use WP_Rocket\Logger\Logger; use WP_Rocket\Tests\Unit\HasLoggerTrait; use WP_Rocket\Tests\Unit\TestCase; @@ -30,6 +32,16 @@ class Test_Treeshake extends TestCase { protected $filesystem; protected $context; + /** + * @var StrategyFactory + */ + protected $strategy_factory; + + /** + * @var WPRClock + */ + protected $wpr_clock; + protected $optimisedContext; protected function setUp(): void { @@ -42,6 +54,8 @@ protected function setUp(): void $this->filesystem = Mockery::mock( Filesystem::class ); $this->context = Mockery::mock(ContextInterface::class); $this->optimisedContext = Mockery::mock(ContextInterface::class); + $this->strategy_factory = Mockery::mock(StrategyFactory::class); + $this->wpr_clock = Mockery::mock(WPRClock::class); $this->usedCss = Mockery::mock( UsedCSS::class . '[is_allowed,update_last_accessed]', @@ -53,6 +67,8 @@ protected function setUp(): void $this->filesystem, $this->context, $this->optimisedContext, + $this->strategy_factory, + $this->wpr_clock, ] ); diff --git a/tests/Unit/inc/Engine/Optimization/RUCSS/Strategy/Context/RetryContext/setStrategy.php b/tests/Unit/inc/Engine/Optimization/RUCSS/Strategy/Context/RetryContext/setStrategy.php new file mode 100644 index 0000000000..7876caa03e --- /dev/null +++ b/tests/Unit/inc/Engine/Optimization/RUCSS/Strategy/Context/RetryContext/setStrategy.php @@ -0,0 +1,27 @@ +used_css_query = $this->createMock( UsedCSS_Query::class ); + $this->wpr_clock = Mockery::mock(WPRClock::class); + + $this->strategy = new DefaultProcess($this->used_css_query, $this->wpr_clock); + + + } + + public function tearDown(): void { + parent::tearDown(); + } + + /** + * @dataProvider configTestData + */ + public function testShouldBehaveAsExpected( $config, $expected ) + { + if ( $config['row_details']->retries >= count( $config['time_table'] ) ) { + Actions\expectDone('rocket_preload_unlock_url')->with($config['row_details']->url); + + + $this->used_css_query->expects(self::once())->method('make_status_failed')->with($config['row_details']->id, strval($config['job_details']['code']), $config['job_details']['message']); + $this->strategy->execute($config['row_details'], $config['job_details']); + return; + } + + $this->used_css_query->expects(self::once())->method('increment_retries')->with( $config['row_details']->id, (int) $config['row_details']->retries); + + Filters\expectApplied('rocket_rucss_retry_duration')->andReturn($config['duration_retry']); + + $this->wpr_clock->expects('current_time')->with('timestamp', true)->andReturn(0); + // update the `next_retry_time` column. + + $this->used_css_query->expects(self::once())->method('update_message')->with($config['row_details']->id, $config['job_details']['code'], $config['job_details']['message'], $config['row_details']->error_message); + $this->used_css_query->expects(self::once())->method('update_next_retry_time')->with($config['job_id'], $config['duration_retry']); + + $this->strategy->execute($config['row_details'], $config['job_details']); + return; + } +} diff --git a/tests/Unit/inc/Engine/Optimization/RUCSS/Strategy/Strategies/JobSetFail/execute.php b/tests/Unit/inc/Engine/Optimization/RUCSS/Strategy/Strategies/JobSetFail/execute.php new file mode 100644 index 0000000000..2dea54bdbd --- /dev/null +++ b/tests/Unit/inc/Engine/Optimization/RUCSS/Strategy/Strategies/JobSetFail/execute.php @@ -0,0 +1,56 @@ +usedCssQuery = $this->createMock( UsedCSS_Query::class ); + + } + + public function tearDown(): void { + parent::tearDown(); + } + + /** + * @dataProvider configTestData + */ + public function testShouldBehaveAsExpected( $config, $expected ) + { + if ( $config['row_details'] ) { + $row_details = new UsedCSS_Row( $config['row_details'] ); + } else { + $row_details = null; + } + if ( isset( $config['job_details'] ) ) { + $job_details = $config['job_details']; + } + + Actions\expectDone('rocket_preload_unlock_url')->once(); + + $this->usedCssQuery->expects( self::once() ) + ->method( 'make_status_failed' ) + ->with( $config['job_id'], $job_details['code'], $job_details['message'] ); + + $strategy = new JobSetFail($this->usedCssQuery); + $strategy->execute($row_details, $job_details); + } +} diff --git a/tests/Unit/inc/Engine/Optimization/RUCSS/Strategy/Strategies/ResetRetryProcess/execute.php b/tests/Unit/inc/Engine/Optimization/RUCSS/Strategy/Strategies/ResetRetryProcess/execute.php new file mode 100644 index 0000000000..9b43ec3fde --- /dev/null +++ b/tests/Unit/inc/Engine/Optimization/RUCSS/Strategy/Strategies/ResetRetryProcess/execute.php @@ -0,0 +1,52 @@ +used_css_query = $this->createMock( UsedCSS_Query::class ); + $this->strategy = new ResetRetryProcess($this->used_css_query); + } + + public function tearDown(): void { + parent::tearDown(); + } + + /** + * @dataProvider configTestData + */ + public function testShouldBehaveAsExpected( $config, $expected ) + { + $this->used_css_query->expects(self::once()) + ->method('get_row') + ->with($config['row_details']->url, $config['row_details']->is_mobile) + ->willReturn($config['row_details']); + + + if ( empty( $config['row_details'] ) ) { + $this->used_css_query->expects(self::once()) + ->method('create_new_job') + ->with($config['row_details']->url, $config['row_details']->job_id, $config['row_details']->queue_name, $config['row_details']->is_mobile); + $this->strategy->execute($config['row_details'], $config['job_details']); + + return; + } + $this->used_css_query->expects(self::once())->method('reset_job')->with($config['row_details']->id); + + $this->strategy->execute($config['row_details'], $config['job_details']); + } +}