Commit 028397d2398 for woocommerce

commit 028397d239893519ea94142ba0bd8e7228de7d72
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date:   Sat Sep 12 23:13:29 2026 +0300

    [tests] Fix the hour-boundary flake in the Orders Stats report tests (#68665)

    test(admin): Pin date_paid on Orders Stats report test fixtures

    The Orders Stats report queries the `date_paid` column by default:
    `Admin\API\Reports\Orders\Stats\DataStore::__construct` resolves
    `date_column_name` from the `woocommerce_date_type` option and falls
    back to `date_paid`, and no test sets that option.

    These fixtures pinned `date_created` to a deterministic timestamp but
    left `date_paid` alone. Reaching a paid status stamps it with the wall
    clock instead: `WC_Order::status_transition()` calls
    `maybe_set_date_paid()`, which sets `time()` when the prop is empty.
    The assertions then query a window derived from `date_created`, so a
    run whose fixture creation crosses the top of the hour leaves some
    orders paid in the next hour and outside that window. In
    `DataStoreFilterQueriesTest` that dropped 18 of 36 orders and failed 27
    of the 28 scenarios at once.

    Set `date_paid` alongside `date_created` wherever a fixture pins one,
    which is what the sibling `DataStoreZeroFillTest` already does. The
    fixtures keep exercising the default `date_paid` path; they just no
    longer depend on how long they take to build.

    Only `DataStoreFilterQueriesTest` has been observed failing. The two
    sites in `DataStoreBasicsTest` have the same cause and a narrower
    window, and are fixed here rather than left to surface later.

    Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/fix-orders-stats-report-test-hour-boundary b/plugins/woocommerce/changelog/fix-orders-stats-report-test-hour-boundary
new file mode 100644
index 00000000000..430ac44b245
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-orders-stats-report-test-hour-boundary
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Pin date_paid on the Orders Stats report test fixtures so a run that crosses the top of the hour no longer drops orders out of the queried window.
diff --git a/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreBasicsTest.php b/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreBasicsTest.php
index 837b8bfaec7..4e554a7b9fa 100644
--- a/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreBasicsTest.php
+++ b/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreBasicsTest.php
@@ -38,6 +38,9 @@ class DataStoreBasicsTest extends OrdersStatsTestCase {
 		$coupon->save();

 		$order = WC_Helper_Order::create_order( 1, $product );
+		// Pin date_paid to date_created, which the window below is derived from; otherwise
+		// the two can land in different hours when the save crosses the top of the hour.
+		$order->set_date_paid( $order->get_date_created() );
 		$order->set_status( OrderStatus::COMPLETED );
 		$order->set_shipping_total( 10 );
 		$order->apply_coupon( $coupon );
@@ -319,14 +322,18 @@ class DataStoreBasicsTest extends OrdersStatsTestCase {
 		$order_datetime->setTime( (int) $order_datetime->format( 'H' ), 10, 0 );
 		$order_time = (int) $order_datetime->format( 'U' );

+		// Both orders pin date_paid alongside date_created, because the report queries
+		// date_paid and the window below comes from date_created.
 		$customer_1_order = WC_Helper_Order::create_order( $customer_1->get_id() );
 		$customer_1_order->set_date_created( $order_time );
+		$customer_1_order->set_date_paid( $order_time );
 		$customer_1_order->set_status( OrderStatus::COMPLETED );
 		$customer_1_order->save();

 		// Offset by 1 second to keep both orders in the same hour but distinct.
 		$customer_2_order = WC_Helper_Order::create_order( $customer_2->get_id() );
 		$customer_2_order->set_date_created( $order_time + 1 );
+		$customer_2_order->set_date_paid( $order_time + 1 );
 		$customer_2_order->set_status( OrderStatus::COMPLETED );
 		$customer_2_order->save();

diff --git a/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreFilterQueriesTest.php b/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreFilterQueriesTest.php
index e3f4b2b94eb..f864244e06a 100644
--- a/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreFilterQueriesTest.php
+++ b/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreFilterQueriesTest.php
@@ -133,12 +133,19 @@ class DataStoreFilterQueriesTest extends OrdersStatsTestCase {
 		$order_time = (int) $order_datetime->format( 'U' );

 		$iterations = 1;
+		// The Orders Stats report queries date_paid by default, so each order pins it
+		// alongside date_created. Left unset, reaching a paid status stamps date_paid with
+		// the wall clock, and a run that crosses the hour puts orders outside the window
+		// the assertions query.
 		foreach ( $primary_products as $product ) {
 			foreach ( array( null, $small_coupon, $large_coupon ) as $coupon ) {
 				foreach ( array( OrderStatus::COMPLETED, OrderStatus::PROCESSING ) as $order_status ) {
-					$single_product_order = WC_Helper_Order::create_order( $customer->get_id(), $product );
 					// Offset each order by 1 second.
-					$single_product_order->set_date_created( $order_time + $iterations++ );
+					$single_order_time = $order_time + $iterations++;
+
+					$single_product_order = WC_Helper_Order::create_order( $customer->get_id(), $product );
+					$single_product_order->set_date_created( $single_order_time );
+					$single_product_order->set_date_paid( $single_order_time );
 					$single_product_order->set_status( $order_status );

 					if ( $coupon ) {
@@ -161,7 +168,9 @@ class DataStoreFilterQueriesTest extends OrdersStatsTestCase {
 					$item->save();
 					$two_product_order->add_item( $item );
 					// Offset each order by 1 second.
-					$two_product_order->set_date_created( $order_time + $iterations++ );
+					$two_order_time = $order_time + $iterations++;
+					$two_product_order->set_date_created( $two_order_time );
+					$two_product_order->set_date_paid( $two_order_time );
 					$two_product_order->set_status( $order_status );

 					if ( $coupon ) {