Commit 3eeb64f25f2 for woocommerce
commit 3eeb64f25f242178a415e522f7f76f6c9cc012cc
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date: Sun Sep 13 16:00:08 2026 +0300
[tests] Pin the refund fixtures in the Orders Stats report tests (#68675)
test(admin): Pin the refund fixtures in the Orders Stats report tests
PR #68665 pinned `date_paid` on the order fixtures so a run crossing the
top of the hour could not drop them out of the queried window. It missed
the refunds, which reach the same window by a different route.
`Admin\API\Reports\Orders\Stats\DataStore::update()` backfills a refund
row's `date_paid` from the refund's own `date_created`, whenever the
parent order has a `date_paid` of its own -- which, since #68665, it
always does here. No refund here carries a date: `wc_create_refund()`
accepts none, so an explicit refund takes the wall clock, and an order
moved to `refunded` gets a second, implicit refund the same way, because
`wc_order_fully_refunded()` runs on that status change and calls
`wc_create_refund()` itself. The window still comes from the order's
`date_created`, so any refund created after the hour turns over lands
outside it and its amount goes missing from `refunds`, `total_sales`
and `net_revenue`.
Give an order's refunds the order's own date, through a
`pin_refund_dates()` helper on the shared test case. Applying it per
order rather than per `wc_create_refund()` call is what covers the
implicit refunds, which no call site in these tests names.
Three fixtures were exposed: `test_populate_and_query` and
`test_populate_and_query_statuses` in `DataStoreBasicsTest`, and
`test_populate_and_query_refunds` in `DataStoreRefundFiltersTest`. The
two full-refund tests in that same file query a window a day wide, so
they have slack the others do not. `DataStoreTest` holds eight more
`wc_create_refund()` calls and needs nothing: it asserts on
`wc_order_stats` rows by order id and queries no time window at all.
Refs WOOAIRR-307
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/fix-orders-stats-refund-fixture-hour-boundary b/plugins/woocommerce/changelog/fix-orders-stats-refund-fixture-hour-boundary
new file mode 100644
index 00000000000..1a5ba5083ff
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-orders-stats-refund-fixture-hour-boundary
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+
+Pin the refund fixtures' dates in the Orders Stats report tests, closing the half of the hour-boundary flake that pinning the orders alone left open.
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 4e554a7b9fa..fe6b050111c 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
@@ -57,6 +57,8 @@ class DataStoreBasicsTest extends OrdersStatsTestCase {
)
);
+ $this->pin_refund_dates( $order, $order->get_date_created() );
+
WC_Helper_Queue::run_all_pending( 'wc-admin-data' );
$start_time = gmdate( 'Y-m-d H:00:00', $order->get_date_created()->getOffsetTimestamp() );
@@ -150,6 +152,10 @@ class DataStoreBasicsTest extends OrdersStatsTestCase {
$order->set_shipping_total( 0 );
$order->set_cart_tax( 0 );
$order->save();
+
+ // The refunded order's save creates a refund of its own, via
+ // wc_order_fully_refunded(); it needs the fixture's date like any other.
+ $this->pin_refund_dates( $order, $time );
}
WC_Helper_Queue::run_all_pending( 'wc-admin-data' );
diff --git a/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreRefundFiltersTest.php b/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreRefundFiltersTest.php
index 00218f0d002..f1f927a8e02 100644
--- a/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreRefundFiltersTest.php
+++ b/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/DataStoreRefundFiltersTest.php
@@ -60,6 +60,10 @@ class DataStoreRefundFiltersTest extends OrdersStatsTestCase {
$order->set_shipping_total( 0 );
$order->set_cart_tax( 0 );
$order->save();
+
+ // The refunded order's save creates a refund of its own, via
+ // wc_order_fully_refunded(); it needs the fixture's date like any other.
+ $this->pin_refund_dates( $order, $time );
}
// Add a partial refund on the first item of the last order.
@@ -77,6 +81,8 @@ class DataStoreRefundFiltersTest extends OrdersStatsTestCase {
)
);
+ $this->pin_refund_dates( $order, $time );
+
WC_Helper_Queue::run_all_pending( 'wc-admin-data' );
$data_store = new OrdersStatsDataStore();
diff --git a/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/OrdersStatsTestCase.php b/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/OrdersStatsTestCase.php
index c30666a8f97..f06280fda58 100644
--- a/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/OrdersStatsTestCase.php
+++ b/plugins/woocommerce/tests/php/src/Admin/API/Reports/Orders/Stats/OrdersStatsTestCase.php
@@ -111,4 +111,24 @@ abstract class OrdersStatsTestCase extends WC_Unit_Test_Case {
'Query args: ' . print_r( $query_args, true ) . "; query: {$wpdb->last_query}" // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_print_r
);
}
+
+ /**
+ * Give an order's refunds the same date as the order itself.
+ *
+ * A refund reaches the report through its own date: the lookup table backfills a refund
+ * row's date_paid from the refund's date_created whenever the parent order has a date_paid.
+ * Neither `wc_create_refund()` nor the refund `wc_order_fully_refunded()` creates on a
+ * `refunded` status change takes a date, so both use the wall clock. A fixture that pins its
+ * order to a fixed hour and then queries that hour loses those refunds when the clock has
+ * moved on, which for a plain `time()` fixture means whenever the run crosses the hour.
+ *
+ * @param \WC_Order $order The order whose refunds should be pinned.
+ * @param int|string $date Date to apply, in any form set_date_created() accepts.
+ */
+ protected function pin_refund_dates( \WC_Order $order, $date ): void {
+ foreach ( $order->get_refunds() as $refund ) {
+ $refund->set_date_created( $date );
+ $refund->save();
+ }
+ }
}