Merge pull request woocommerce/woocommerce-admin#1426 from woocommerce/update/1323-handle-report-regeneration-fail

Use Action Scheduler for lookup table updates & schedule retries.
This commit is contained in:
Jeff Stieler 2019-02-04 17:12:35 -07:00 committed by GitHub
commit 0711ed85f8
26 changed files with 305 additions and 82 deletions

View File

@ -37,6 +37,11 @@ class WC_Admin_Api_Init {
*/
const ORDERS_LOOKUP_BATCH_INIT = 'wc-admin_orders_lookup_batch_init';
/**
* Action hook for processing a batch of orders.
*/
const SINGLE_ORDER_ACTION = 'wc-admin_process_order';
/**
* Queue instance.
*
@ -59,19 +64,16 @@ class WC_Admin_Api_Init {
add_filter( 'rest_endpoints', array( 'WC_Admin_Api_Init', 'filter_rest_endpoints' ), 10, 1 );
add_filter( 'woocommerce_debug_tools', array( 'WC_Admin_Api_Init', 'add_regenerate_tool' ) );
// Initialize Orders data store class's static vars.
add_action( 'woocommerce_after_register_post_type', array( 'WC_Admin_Api_Init', 'orders_data_store_init' ), 20 );
// Initialize Customers Report data store sync hooks.
// Note: we need to hook in before `wc_current_user_is_active`.
// See: https://github.com/woocommerce/woocommerce/blob/942615101ba00c939c107c3a4820c3d466864872/includes/wc-user-functions.php#L749.
add_action( 'wp_loaded', array( 'WC_Admin_Api_Init', 'customers_report_data_store_init' ) );
// Initialize syncing hooks.
add_action( 'wp_loaded', array( __CLASS__, 'orders_lookup_update_init' ) );
// Initialize scheduled action handlers.
add_action( self::QUEUE_BATCH_ACTION, array( __CLASS__, 'queue_batches' ), 10, 3 );
add_action( self::QUEUE_DEPEDENT_ACTION, array( __CLASS__, 'queue_dependent_action' ), 10, 2 );
add_action( self::QUEUE_DEPEDENT_ACTION, array( __CLASS__, 'queue_dependent_action' ), 10, 3 );
add_action( self::CUSTOMERS_BATCH_ACTION, array( __CLASS__, 'customer_lookup_process_batch' ) );
add_action( self::ORDERS_BATCH_ACTION, array( __CLASS__, 'orders_lookup_process_batch' ) );
add_action( self::ORDERS_LOOKUP_BATCH_INIT, array( __CLASS__, 'orders_lookup_batch_init' ) );
add_action( self::SINGLE_ORDER_ACTION, array( __CLASS__, 'orders_lookup_process_order' ) );
// Add currency symbol to orders endpoint response.
add_filter( 'woocommerce_rest_prepare_shop_order_object', array( __CLASS__, 'add_currency_symbol_to_order_response' ) );
@ -433,7 +435,7 @@ class WC_Admin_Api_Init {
// so that the orders can be associated with the `customer_id` column.
self::customer_lookup_batch_init();
// Queue orders lookup to occur after customers lookup generation is done.
self::queue_dependent_action( self::ORDERS_LOOKUP_BATCH_INIT, self::CUSTOMERS_BATCH_ACTION );
self::queue_dependent_action( self::ORDERS_LOOKUP_BATCH_INIT, array(), self::CUSTOMERS_BATCH_ACTION );
}
/**
@ -457,16 +459,58 @@ class WC_Admin_Api_Init {
}
/**
* Init orders data store.
* Schedule an action to process a single Order.
*
* @param int $order_id Order ID.
* @return void
*/
public static function orders_data_store_init() {
public static function schedule_single_order_process( $order_id ) {
if ( 'shop_order' !== get_post_type( $order_id ) ) {
return;
}
// This can get called multiple times for a single order, so we look
// for existing pending jobs for the same order to avoid duplicating efforts.
$existing_jobs = self::queue()->search(
array(
'status' => 'pending',
'per_page' => 1,
'claimed' => false,
'search' => "[{$order_id}]",
)
);
if ( $existing_jobs ) {
$existing_job = current( $existing_jobs );
// Bail out if there's a pending single order action, or a pending dependent action.
if (
( self::SINGLE_ORDER_ACTION === $existing_job->get_hook() ) ||
(
self::QUEUE_DEPEDENT_ACTION === $existing_job->get_hook() &&
in_array( self::SINGLE_ORDER_ACTION, $existing_job->get_args() )
)
) {
return;
}
}
// We want to ensure that customer lookup updates are scheduled before order updates.
self::queue_dependent_action( self::SINGLE_ORDER_ACTION, array( $order_id ), self::CUSTOMERS_BATCH_ACTION );
}
/**
* Attach order lookup update hooks.
*/
public static function orders_lookup_update_init() {
// Activate WC_Order extension.
WC_Admin_Order::add_filters();
// Initialize data stores.
add_action( 'save_post_shop_order', array( __CLASS__, 'schedule_single_order_process' ) );
add_action( 'woocommerce_order_refunded', array( __CLASS__, 'schedule_single_order_process' ) );
WC_Admin_Reports_Orders_Stats_Data_Store::init();
WC_Admin_Reports_Products_Data_Store::init();
WC_Admin_Reports_Taxes_Data_Store::init();
WC_Admin_Reports_Coupons_Data_Store::init();
WC_Admin_Reports_Customers_Data_Store::init();
}
/**
@ -512,19 +556,35 @@ class WC_Admin_Api_Init {
$order_ids = $order_query->get_orders();
foreach ( $order_ids as $order_id ) {
// @todo: schedule single order update if this fails?
WC_Admin_Reports_Orders_Stats_Data_Store::sync_order( $order_id );
WC_Admin_Reports_Products_Data_Store::sync_order_products( $order_id );
WC_Admin_Reports_Coupons_Data_Store::sync_order_coupons( $order_id );
WC_Admin_Reports_Taxes_Data_Store::sync_order_taxes( $order_id );
self::orders_lookup_process_order( $order_id );
}
}
/**
* Init customers report data store.
* Process a single order to update lookup tables for.
* If an error is encountered in one of the updates, a retry action is scheduled.
*
* @param int $order_id Order ID.
* @return void
*/
public static function customers_report_data_store_init() {
WC_Admin_Reports_Customers_Data_Store::init();
public static function orders_lookup_process_order( $order_id ) {
$result = array_sum(
array(
WC_Admin_Reports_Orders_Stats_Data_Store::sync_order( $order_id ),
WC_Admin_Reports_Products_Data_Store::sync_order_products( $order_id ),
WC_Admin_Reports_Coupons_Data_Store::sync_order_coupons( $order_id ),
WC_Admin_Reports_Taxes_Data_Store::sync_order_taxes( $order_id ),
)
);
// If all updates were either skipped or successful, we're done.
// The update methods return -1 for skip, or a boolean success indicator.
if ( 4 === absint( $result ) ) {
return;
}
// Otherwise assume an error occurred and reschedule.
self::schedule_single_order_process( $order_id );
}
/**
@ -592,9 +652,10 @@ class WC_Admin_Api_Init {
* Queue an action to run after another.
*
* @param string $action Action to run after prerequisite.
* @param array $action_args Action arguments.
* @param string $prerequisite_action Prerequisite action.
*/
public static function queue_dependent_action( $action, $prerequisite_action ) {
public static function queue_dependent_action( $action, $action_args, $prerequisite_action ) {
$blocking_jobs = self::queue()->search(
array(
'status' => 'pending',
@ -613,10 +674,10 @@ class WC_Admin_Api_Init {
self::queue()->schedule_single(
$after_blocking_job,
self::QUEUE_DEPEDENT_ACTION,
array( $action, $prerequisite_action )
array( $action, $action_args, $prerequisite_action )
);
} else {
self::queue()->schedule_single( time() + 5, $action );
self::queue()->schedule_single( time() + 5, $action, $action_args );
}
}

View File

@ -51,15 +51,6 @@ class WC_Admin_Reports_Coupons_Data_Store extends WC_Admin_Reports_Data_Store im
$this->report_columns['orders_count'] = str_replace( 'order_id', $table_name . '.order_id', $this->report_columns['orders_count'] );
}
/**
* Set up all the hooks for maintaining and populating table data.
*/
public static function init() {
add_action( 'save_post', array( __CLASS__, 'sync_order_coupons' ) );
add_action( 'clean_post_cache', array( __CLASS__, 'sync_order_coupons' ) );
add_action( 'woocommerce_order_refunded', array( __CLASS__, 'sync_order_coupons' ) );
}
/**
* Returns comma separated ids of included coupons, based on query arguments from the user.
*
@ -315,19 +306,21 @@ class WC_Admin_Reports_Coupons_Data_Store extends WC_Admin_Reports_Data_Store im
*
* @since 3.5.0
* @param int $order_id Order ID.
* @return void
* @return int|bool Returns -1 if order won't be processed, or a boolean indicating processing success.
*/
public static function sync_order_coupons( $order_id ) {
global $wpdb;
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
return -1;
}
$coupon_items = $order->get_items( 'coupon' );
$num_updated = 0;
foreach ( $coupon_items as $coupon_item ) {
$wpdb->replace(
$result = $wpdb->replace(
$wpdb->prefix . self::TABLE_NAME,
array(
'order_id' => $order_id,
@ -342,7 +335,11 @@ class WC_Admin_Reports_Coupons_Data_Store extends WC_Admin_Reports_Data_Store im
'%s',
)
);
$num_updated += intval( $result );
}
return ( count( $coupon_items ) === $num_updated );
}
}

View File

@ -73,10 +73,6 @@ class WC_Admin_Reports_Orders_Stats_Data_Store extends WC_Admin_Reports_Data_Sto
* Set up all the hooks for maintaining and populating table data.
*/
public static function init() {
add_action( 'save_post', array( __CLASS__, 'sync_order' ) );
// @todo: this is required as order update skips save_post.
add_action( 'clean_post_cache', array( __CLASS__, 'sync_order' ) );
add_action( 'woocommerce_order_refunded', array( __CLASS__, 'sync_order' ) );
add_action( 'woocommerce_refund_deleted', array( __CLASS__, 'sync_on_refund_delete' ), 10, 2 );
add_action( 'delete_post', array( __CLASS__, 'delete_order' ) );
}
@ -360,18 +356,19 @@ class WC_Admin_Reports_Orders_Stats_Data_Store extends WC_Admin_Reports_Data_Sto
* Add order information to the lookup table when orders are created or modified.
*
* @param int $post_id Post ID.
* @return int|bool Returns -1 if order won't be processed, or a boolean indicating processing success.
*/
public static function sync_order( $post_id ) {
if ( 'shop_order' !== get_post_type( $post_id ) ) {
return;
return -1;
}
$order = wc_get_order( $post_id );
if ( ! $order ) {
return;
return -1;
}
self::update( $order );
return self::update( $order );
}
/**
@ -388,14 +385,14 @@ class WC_Admin_Reports_Orders_Stats_Data_Store extends WC_Admin_Reports_Data_Sto
* Update the database with stats data.
*
* @param WC_Order $order Order to update row for.
* @return int|bool|null Number or rows modified or false on failure.
* @return int|bool Returns -1 if order won't be processed, or a boolean indicating processing success.
*/
public static function update( $order ) {
global $wpdb;
$table_name = $wpdb->prefix . self::TABLE_NAME;
if ( ! $order->get_id() || ! $order->get_date_created() ) {
return false;
return -1;
}
$data = array(
@ -450,7 +447,9 @@ class WC_Admin_Reports_Orders_Stats_Data_Store extends WC_Admin_Reports_Data_Sto
}
// Update or add the information to the DB.
return $wpdb->replace( $table_name, $data, $format );
$result = $wpdb->replace( $table_name, $data, $format );
return ( 1 === $result );
}
/**

View File

@ -82,15 +82,6 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i
$this->report_columns['orders_count'] = str_replace( 'order_id', $table_name . '.order_id', $this->report_columns['orders_count'] );
}
/**
* Set up all the hooks for maintaining and populating table data.
*/
public static function init() {
add_action( 'save_post', array( __CLASS__, 'sync_order_products' ) );
add_action( 'clean_post_cache', array( __CLASS__, 'sync_order_products' ) );
add_action( 'woocommerce_order_refunded', array( __CLASS__, 'sync_order_products' ) );
}
/**
* Fills ORDER BY clause of SQL request based on user supplied parameters.
*
@ -319,7 +310,7 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i
*
* @since 3.5.0
* @param int $order_id Order ID.
* @return void
* @return int|bool Returns -1 if order won't be processed, or a boolean indicating processing success.
*/
public static function sync_order_products( $order_id ) {
global $wpdb;
@ -328,10 +319,13 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i
// This hook gets called on refunds as well, so return early to avoid errors.
if ( ! $order || 'shop_order_refund' === $order->get_type() ) {
return;
return -1;
}
foreach ( $order->get_items() as $order_item ) {
$order_items = $order->get_items();
$num_updated = 0;
foreach ( $order_items as $order_item ) {
$order_item_id = $order_item->get_id();
$quantity_refunded = $order->get_item_quantity_refunded( $order_item );
$amount_refunded = $order->get_item_amount_refunded( $order_item );
@ -355,13 +349,13 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i
$net_revenue = $order_item->get_subtotal( 'edit' ) - $amount_refunded;
if ( $quantity_refunded >= $order_item->get_quantity( 'edit' ) ) {
$wpdb->delete(
$result = $wpdb->delete(
$wpdb->prefix . self::TABLE_NAME,
array( 'order_item_id' => $order_item_id ),
array( '%d' )
); // WPCS: cache ok, DB call ok.
} else {
$wpdb->replace(
$result = $wpdb->replace(
$wpdb->prefix . self::TABLE_NAME,
array(
'order_item_id' => $order_item_id,
@ -398,7 +392,11 @@ class WC_Admin_Reports_Products_Data_Store extends WC_Admin_Reports_Data_Store i
)
); // WPCS: cache ok, DB call ok, unprepared SQL ok.
}
$num_updated += intval( $result );
}
return ( count( $order_items ) === $num_updated );
}
}

View File

@ -66,15 +66,6 @@ class WC_Admin_Reports_Taxes_Data_Store extends WC_Admin_Reports_Data_Store impl
$this->report_columns['orders_count'] = str_replace( 'order_id', $table_name . '.order_id', $this->report_columns['orders_count'] );
}
/**
* Set up all the hooks for maintaining and populating table data.
*/
public static function init() {
add_action( 'save_post', array( __CLASS__, 'sync_order_taxes' ) );
add_action( 'clean_post_cache', array( __CLASS__, 'sync_order_taxes' ) );
add_action( 'woocommerce_order_refunded', array( __CLASS__, 'sync_order_taxes' ) );
}
/**
* Updates the database query with parameters used for Taxes report: categories and order status.
*
@ -255,17 +246,20 @@ class WC_Admin_Reports_Taxes_Data_Store extends WC_Admin_Reports_Data_Store impl
* Create or update an entry in the wc_order_tax_lookup table for an order.
*
* @param int $order_id Order ID.
* @return void
* @return int|bool Returns -1 if order won't be processed, or a boolean indicating processing success.
*/
public static function sync_order_taxes( $order_id ) {
global $wpdb;
$order = wc_get_order( $order_id );
if ( ! $order ) {
return;
return -1;
}
foreach ( $order->get_items( 'tax' ) as $tax_item ) {
$wpdb->replace(
$tax_items = $order->get_items( 'tax' );
$num_updated = 0;
foreach ( $tax_items as $tax_item ) {
$result = $wpdb->replace(
$wpdb->prefix . self::TABLE_NAME,
array(
'order_id' => $order->get_id(),
@ -284,7 +278,11 @@ class WC_Admin_Reports_Taxes_Data_Store extends WC_Admin_Reports_Data_Store impl
'%f',
)
);
$num_updated += intval( $result );
}
return ( count( $tax_items ) === $num_updated );
}
}

View File

@ -0,0 +1,85 @@
<?php
/**
* REST API Init Class Test
*
* @package WooCommerce\Tests\API
* @since 3.5.0
*/
/**
* Class WC_Tests_API_Init
*/
class WC_Tests_API_Init extends WC_REST_Unit_Test_Case {
/**
* Set up.
*/
public function setUp() {
parent::setUp();
$this->queue = new WC_Admin_Test_Action_Queue();
WC_Admin_Api_Init::set_queue( $this->queue );
}
/**
* Tear down.
*/
public function tearDown() {
parent::tearDown();
WC_Admin_Api_Init::set_queue( null );
$this->queue->actions = array();
}
/**
* Cause a failure when updating order stats for the test order, without deleting it.
*
* @param string $query Query.
* @return string
*/
public function filter_order_query( $query ) {
if (
0 === strpos( $query, 'REPLACE INTO' ) &&
false !== strpos( $query, WC_Admin_Reports_Orders_Stats_Data_Store::TABLE_NAME )
) {
remove_filter( 'query', array( $this, 'filter_order_query' ) );
return 'THIS WONT MATCH';
}
return $query;
}
/**
* Test that a retry job is scheduled for a failed sync.
*
* @return void
*/
public function test_order_sync_retries_on_failure() {
// Create a test Order.
$product = new WC_Product_Simple();
$product->set_name( 'Test Product' );
$product->set_regular_price( 25 );
$product->save();
$order = WC_Helper_Order::create_order( 1, $product );
$order->set_status( 'completed' );
$order->set_total( 100 ); // $25 x 4.
$order->save();
// Clear the existing action queue (the above save adds an action).
$this->queue->actions = array();
// Force a failure by sabotaging the query run after retreiving order coupons.
add_filter( 'query', array( $this, 'filter_order_query' ) );
// Initiate sync.
WC_Admin_Api_Init::orders_lookup_process_order( $order->get_id() );
// Verify that a retry job was scheduled.
$this->assertCount( 1, $this->queue->actions );
$this->assertArraySubset(
array(
'hook' => WC_Admin_Api_Init::SINGLE_ORDER_ACTION,
'args' => array( $order->get_id() ),
),
$this->queue->actions[0]
);
}
}

View File

@ -64,6 +64,8 @@ class WC_Tests_API_Reports_Categories extends WC_REST_Unit_Test_Case {
$order->set_total( 100 ); // $25 x 4.
$order->save();
WC_Helper_Queue::run_all_pending();
$uncategorized_term = get_term_by( 'slug', 'uncategorized', 'product_cat' );
$response = $this->server->dispatch( new WP_REST_Request( 'GET', $this->endpoint ) );

View File

@ -87,6 +87,8 @@ class WC_Tests_API_Reports_Coupons_Stats extends WC_REST_Unit_Test_Case {
$order_2c->set_date_created( $time );
$order_2c->save();
WC_Helper_Queue::run_all_pending();
$request = new WP_REST_Request( 'GET', $this->endpoint );
$request->set_query_params(
array(

View File

@ -82,6 +82,8 @@ class WC_Tests_API_Reports_Coupons extends WC_REST_Unit_Test_Case {
$order_2c->calculate_totals();
$order_2c->save();
WC_Helper_Queue::run_all_pending();
$response = $this->server->dispatch( new WP_REST_Request( 'GET', $this->endpoint ) );
$coupon_reports = $response->get_data();

View File

@ -126,6 +126,8 @@ class WC_Tests_API_Reports_Customers_Stats extends WC_REST_Unit_Test_Case {
$order->set_total( 9.12 );
$order->save();
WC_Helper_Queue::run_all_pending();
$request = new WP_REST_Request( 'GET', $this->endpoint );
$response = $this->server->dispatch( $request );
$reports = $response->get_data();

View File

@ -134,6 +134,8 @@ class WC_Tests_API_Reports_Customers extends WC_REST_Unit_Test_Case {
$order->set_total( 100 );
$order->save();
WC_Helper_Queue::run_all_pending();
$request = new WP_REST_Request( 'GET', $this->endpoint );
$request->set_query_params(
array(

View File

@ -67,6 +67,8 @@ class WC_Tests_API_Reports_Orders extends WC_REST_Unit_Test_Case {
$order->set_total( 100 ); // $25 x 4.
$order->save();
WC_Helper_Queue::run_all_pending();
$expected_customer_id = WC_Admin_Reports_Customers_Data_Store::get_customer_id_by_user_id( 1 );
$response = $this->server->dispatch( new WP_REST_Request( 'GET', $this->endpoint ) );

View File

@ -84,6 +84,8 @@ class WC_Tests_API_Reports_Performance_Indicators extends WC_REST_Unit_Test_Case
$object->set_user_ip_address( '1.2.3.4' );
$object->save();
WC_Helper_Queue::run_all_pending();
$time = time();
$request = new WP_REST_Request( 'GET', $this->endpoint );
$request->set_query_params(

View File

@ -71,6 +71,8 @@ class WC_Tests_API_Reports_Products_Stats extends WC_REST_Unit_Test_Case {
$order->set_total( 97 ); // $25x4 products + $10 shipping - $20 discount + $7 tax.
$order->save();
WC_Helper_Queue::run_all_pending();
$request = new WP_REST_Request( 'GET', $this->endpoint );
$request->set_query_params(
array(

View File

@ -67,6 +67,8 @@ class WC_Tests_API_Reports_Products extends WC_REST_Unit_Test_Case {
$order->set_total( 100 ); // $25 x 4.
$order->save();
WC_Helper_Queue::run_all_pending();
$response = $this->server->dispatch( new WP_REST_Request( 'GET', $this->endpoint ) );
$reports = $response->get_data();

View File

@ -91,6 +91,8 @@ class WC_Tests_API_Reports_Taxes extends WC_REST_Unit_Test_Case {
)
);
WC_Helper_Queue::run_all_pending();
$response = $this->server->dispatch( new WP_REST_Request( 'GET', $this->endpoint ) );
$reports = $response->get_data();

View File

@ -65,6 +65,8 @@ class WC_Tests_API_Reports_Variations extends WC_REST_Unit_Test_Case {
$order->set_total( 100 ); // $25 x 4.
$order->save();
WC_Helper_Queue::run_all_pending();
$response = $this->server->dispatch( new WP_REST_Request( 'GET', $this->endpoint ) );
$reports = $response->get_data();

View File

@ -136,7 +136,7 @@ class WC_Tests_Reports_Regenerate_Batching extends WC_REST_Unit_Test_Case {
// insert a blocking job.
WC_Admin_Api_Init::queue()->schedule_single( time(), 'blocking_job', array( 'stuff' ) );
// queue an action that depends on blocking job.
WC_Admin_Api_Init::queue_dependent_action( 'dependent_action', 'blocking_job' );
WC_Admin_Api_Init::queue_dependent_action( 'dependent_action', array(), 'blocking_job' );
// verify that the action was properly blocked.
$this->assertEmpty(
WC_Admin_Api_Init::queue()->search(
@ -151,13 +151,13 @@ class WC_Tests_Reports_Regenerate_Batching extends WC_REST_Unit_Test_Case {
WC_Admin_Api_Init::queue()->search(
array(
'hook' => WC_Admin_Api_Init::QUEUE_DEPEDENT_ACTION,
'args' => array( 'dependent_action', 'blocking_job' ),
'args' => array( 'dependent_action', array(), 'blocking_job' ),
)
)
);
// queue an action that isn't blocked.
WC_Admin_Api_Init::queue_dependent_action( 'another_dependent_action', 'nonexistant_blocking_job' );
WC_Admin_Api_Init::queue_dependent_action( 'another_dependent_action', array(), 'nonexistant_blocking_job' );
// verify that the dependent action was queued.
$this->assertCount(
1,
@ -172,7 +172,7 @@ class WC_Tests_Reports_Regenerate_Batching extends WC_REST_Unit_Test_Case {
WC_Admin_Api_Init::queue()->search(
array(
'hook' => WC_Admin_Api_Init::QUEUE_DEPEDENT_ACTION,
'args' => array( 'another_dependent_action', 'nonexistant_blocking_job' ),
'args' => array( 'another_dependent_action', array(), 'nonexistant_blocking_job' ),
)
)
);

View File

@ -123,3 +123,4 @@ wc_test_includes();
require_once dirname( __FILE__ ) . '/framework/helpers/class-wc-helper-reports.php';
require_once dirname( __FILE__ ) . '/framework/helpers/class-wc-helper-admin-notes.php';
require_once dirname( __FILE__ ) . '/framework/helpers/class-wc-test-action-queue.php';
require_once dirname( __FILE__ ) . '/framework/helpers/class-wc-helper-queue.php';

View File

@ -0,0 +1,32 @@
<?php
/**
* Helper code for wc-admin unit tests.
*
* @package WooCommerce\Tests\Framework\Helpers
*/
/**
* Class WC_Helper_Queue.
*
* This helper class should ONLY be used for unit tests!.
*/
class WC_Helper_Queue {
/**
* Run all pending queued actions.
*
* @return void
*/
public static function run_all_pending() {
$jobs = WC()->queue()->search(
array(
'per_page' => -1,
'status' => 'pending',
'claimed' => false,
)
);
foreach ( $jobs as $job ) {
$job->execute();
}
}
}

View File

@ -54,6 +54,8 @@ class WC_Tests_Reports_Coupons_Stats extends WC_Unit_Test_Case {
$order_2c->calculate_totals();
$order_2c->save();
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Coupons_Stats_Data_Store();
$start_time = date( 'Y-m-d 00:00:00', $order->get_date_created()->getOffsetTimestamp() );
$end_time = date( 'Y-m-d 23:59:59', $order->get_date_created()->getOffsetTimestamp() );

View File

@ -54,6 +54,8 @@ class WC_Tests_Reports_Coupons extends WC_Unit_Test_Case {
$order_2c->calculate_totals();
$order_2c->save();
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Coupons_Data_Store();
$start_time = date( 'Y-m-d 00:00:00', $order->get_date_created()->getOffsetTimestamp() );
$end_time = date( 'Y-m-d 23:59:59', $order->get_date_created()->getOffsetTimestamp() );

View File

@ -41,6 +41,8 @@ class WC_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case {
)
);
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Orders_Stats_Data_Store();
$start_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() );
@ -176,10 +178,13 @@ class WC_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case {
$order->set_shipping_total( 0 );
$order->set_cart_tax( 0 );
$order->save();
// Wait one second to avoid potentially ambiguous new/returning customer.
sleep( 1 );
}
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Orders_Stats_Data_Store();
$start_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() );
@ -203,8 +208,8 @@ class WC_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case {
'taxes' => 0,
'shipping' => 0,
'net_revenue' => 100,
'num_returning_customers' => 0,
'num_new_customers' => 1,
'num_returning_customers' => 1,
'num_new_customers' => 0,
'products' => 1,
'segments' => array(),
),
@ -226,8 +231,8 @@ class WC_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case {
'num_items_sold' => 4,
'avg_items_per_order' => 4,
'avg_order_value' => 100,
'num_returning_customers' => 0,
'num_new_customers' => 1,
'num_returning_customers' => 1,
'num_new_customers' => 0,
'segments' => array(),
),
),
@ -483,6 +488,8 @@ class WC_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case {
}
}
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Orders_Stats_Data_Store();
// Tests for before & after set to current hour.
@ -1697,6 +1704,8 @@ class WC_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case {
$returning_order->set_date_created( $order_1_time + 1 ); // This is guaranteed to belong to the same hour by the adjustment to $order_1_time.
$returning_order->save();
WC_Helper_Queue::run_all_pending();
$query_args = array(
'after' => $current_hour_start->format( WC_Admin_Reports_Interval::$sql_datetime_format ), // I don't think this makes sense.... date( 'Y-m-d H:i:s', $orders[0]->get_date_created()->getOffsetTimestamp() + 1 ), // Date after initial order to get a returning customer.
'before' => $current_hour_end->format( WC_Admin_Reports_Interval::$sql_datetime_format ),
@ -3324,6 +3333,8 @@ class WC_Tests_Reports_Orders_Stats extends WC_Unit_Test_Case {
$order_2->calculate_totals();
$order_2->save();
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Orders_Stats_Data_Store();
// Tests for before & after set to current hour.

View File

@ -38,6 +38,8 @@ class WC_Tests_Reports_Products extends WC_Unit_Test_Case {
$order->set_total( 97 ); // $25x4 products + $10 shipping - $20 discount + $7 tax.
$order->save();
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Products_Data_Store();
$start_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() );
$end_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() + HOUR_IN_SECONDS );
@ -113,6 +115,8 @@ class WC_Tests_Reports_Products extends WC_Unit_Test_Case {
$order_2->set_date_created( $date_created_2 );
$order_2->save();
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Products_Data_Store();
$start_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() );
$end_time = date( 'Y-m-d H:00:00', $order_2->get_date_created()->getOffsetTimestamp() + HOUR_IN_SECONDS );
@ -211,6 +215,9 @@ class WC_Tests_Reports_Products extends WC_Unit_Test_Case {
$order->set_shipping_tax( 2 );
$order->set_total( 97 ); // $25x4 products + $10 shipping - $20 discount + $7 tax.
$order->save();
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Products_Data_Store();
$start_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() );
$end_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() + HOUR_IN_SECONDS );
@ -289,6 +296,8 @@ class WC_Tests_Reports_Products extends WC_Unit_Test_Case {
break;
}
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Products_Data_Store();
$start_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() );
$end_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() + HOUR_IN_SECONDS );

View File

@ -37,6 +37,8 @@ class WC_Admin_Tests_Reports_Revenue_Stats extends WC_Unit_Test_Case {
$order->set_total( 97 ); // $25x4 products + $10 shipping - $20 discount + $7 tax.
$order->save();
WC_Helper_Queue::run_all_pending();
// /reports/revenue/stats is mapped to Orders_Data_Store.
$data_store = new WC_Admin_Reports_Orders_Stats_Data_Store();

View File

@ -39,6 +39,8 @@ class WC_Tests_Reports_Variations extends WC_Unit_Test_Case {
$order->set_status( 'completed' );
$order->save();
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Variations_Data_Store();
$start_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() );
$end_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() + HOUR_IN_SECONDS );
@ -106,6 +108,8 @@ class WC_Tests_Reports_Variations extends WC_Unit_Test_Case {
$order->set_status( 'completed' );
$order->save();
WC_Helper_Queue::run_all_pending();
$data_store = new WC_Admin_Reports_Variations_Data_Store();
$start_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() );
$end_time = date( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() + HOUR_IN_SECONDS );