Commit 5ccc1579d0d for woocommerce

commit 5ccc1579d0db0799bd4621c0666199946d9bb440
Author: Vlad Olaru <vlad.olaru@automattic.com>
Date:   Tue Oct 27 08:10:23 2026 +0200

    [tests] Stop the sales report and orders page tests leaking HPOS state into the run (#68930)

    * test(hpos): Stop the sales report test leaking HPOS into the run

    WC_Tests_API_Reports_Sales switched the store to HPOS from inside a
    test, through setup_cot(). That helper drops and recreates the orders
    tables, and DDL carries an implicit commit, so the switch was
    committed out of the transaction the parent opens for each test and
    the restore in tearDown could not undo it. The tearDown then truncated
    the four orders tables, committing a second time, before restoring the
    option.

    From that point on the whole run had HPOS enabled. A DISABLE_HPOS=1
    run stops exercising the posts-table path for every test that follows,
    which is most of the suite, and CI's "(HPOS:off)" PHPUnit job has been
    green without testing what its name says.

    Use the pattern the trait already provides and several other classes
    already follow: create the tables once in wpSetUpBeforeClass, where
    the DDL happens before any per-test transaction is open, and let the
    test switch only the option. Nothing commits mid-test, so the parent
    rollback covers both the fixture rows and the option, and the four
    TRUNCATE statements are no longer needed.

    The test leaves orders only in the orders tables, so switching back
    counts as switching while orders are out of sync. Allow that through
    wc_allow_changing_orders_storage_while_sync_is_pending, as
    wc-user-functions-test.php does; the rollback discards those rows
    immediately afterwards.

    With the leak gone, a full DISABLE_HPOS=1 run reports two failures out
    of 15,762, both in the Analytics report data-store tests, which build
    their fixture by relying on the database to truncate an overlength
    order status. Those are fixed in #68921.

    Refs #68921

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

    * test: Restore HPOS storage after the orders PageController tests

    PageControllerTest switches order storage inside each test's
    transaction. The rollback reverts the option row, but the object cache
    keeps the switched value, so tearDownAfterClass() compared against a
    stale value and skipped the restore. On a HPOS run, every class after
    it then ran with HPOS off.

    Flush the object cache and restore the stored state unconditionally.
    A probe placed after the class read HPOS off and the option as 'no'
    before this change, and HPOS on with 'yes' after it.

    This change comes from #68361, which is being closed because this PR
    fixes the sales report leak at its root.

    Refs #68361

    Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

diff --git a/plugins/woocommerce/changelog/hpos-sales-report-test-leak b/plugins/woocommerce/changelog/hpos-sales-report-test-leak
new file mode 100644
index 00000000000..919445fe475
--- /dev/null
+++ b/plugins/woocommerce/changelog/hpos-sales-report-test-leak
@@ -0,0 +1,4 @@
+Significance: patch
+Type: dev
+Comment: Stop the sales report REST test leaving HPOS enabled for the rest of the PHPUnit run; test-only change, no production code touched.
+
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/reports-sales.php b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/reports-sales.php
index bcefde9c36a..f776fa0934c 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/reports-sales.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/rest-api/Tests/Version3/reports-sales.php
@@ -28,11 +28,23 @@ class WC_Tests_API_Reports_Sales extends WC_REST_Unit_Test_Case {
 	private $original_hpos_usage;

 	/**
-	 * Whether the running test switched the store over to HPOS.
+	 * Ensure permanent HPOS tables exist before per-test transactions start.
 	 *
-	 * @var bool
+	 * Creating them here keeps the DDL, and its implicit commit, outside the
+	 * transaction the parent opens for each test.
 	 */
-	private $switched_to_hpos = false;
+	public static function wpSetUpBeforeClass(): void {
+		$previous_hpos_state = OrderUtil::custom_orders_table_usage_is_enabled();
+		add_filter( 'wc_allow_changing_orders_storage_while_sync_is_pending', '__return_true' );
+		try {
+			self::setup_cot_tables();
+			if ( OrderUtil::custom_orders_table_usage_is_enabled() !== $previous_hpos_state ) {
+				OrderHelper::toggle_cot_feature_and_usage( $previous_hpos_state );
+			}
+		} finally {
+			remove_filter( 'wc_allow_changing_orders_storage_while_sync_is_pending', '__return_true' );
+		}
+	}

 	/**
 	 * Setup our test server, endpoints, and user info.
@@ -40,7 +52,12 @@ class WC_Tests_API_Reports_Sales extends WC_REST_Unit_Test_Case {
 	public function setUp(): void {
 		parent::setUp();
 		$this->original_hpos_usage = OrderUtil::custom_orders_table_usage_is_enabled();
-		$this->user                = $this->factory->user->create(
+
+		// The HPOS title leaves orders only in the orders tables, so switching back
+		// counts as switching with orders out of sync. The parent rollback discards
+		// those rows straight after, so allow it.
+		add_filter( 'wc_allow_changing_orders_storage_while_sync_is_pending', '__return_true' );
+		$this->user = $this->factory->user->create(
 			array(
 				'role' => 'administrator',
 			)
@@ -52,25 +69,9 @@ class WC_Tests_API_Reports_Sales extends WC_REST_Unit_Test_Case {
 	 * Restore HPOS state and clear report caches.
 	 */
 	public function tearDown(): void {
-		global $wpdb;
-
-		// Scoped to the test that switches to HPOS, the only one writing to these tables.
-		// A test that never switches keeps an intact transaction, and TRUNCATE is DDL
-		// whose implicit commit would leak its fixtures past the parent rollback.
-		if ( $this->switched_to_hpos ) {
-			// phpcs:disable WordPress.DB.DirectDatabaseQuery
-			$wpdb->query( "TRUNCATE {$wpdb->prefix}wc_orders" );
-			$wpdb->query( "TRUNCATE {$wpdb->prefix}wc_orders_meta" );
-			$wpdb->query( "TRUNCATE {$wpdb->prefix}wc_order_operational_data" );
-			$wpdb->query( "TRUNCATE {$wpdb->prefix}wc_order_addresses" );
-			// phpcs:enable WordPress.DB.DirectDatabaseQuery
-		}
-
 		remove_all_actions( 'pre_option_' . DataSynchronizer::ORDERS_DATA_SYNC_ENABLED_OPTION );
 		OrderHelper::toggle_cot_feature_and_usage( $this->original_hpos_usage );
-
-		add_filter( 'query', array( $this, '_create_temporary_tables' ) );
-		add_filter( 'query', array( $this, '_drop_temporary_tables' ) );
+		remove_filter( 'wc_allow_changing_orders_storage_while_sync_is_pending', '__return_true' );

 		$this->clear_sales_report_cache();
 		parent::tearDown();
@@ -208,8 +209,7 @@ class WC_Tests_API_Reports_Sales extends WC_REST_Unit_Test_Case {
 	 * @testdox Should return sales totals from HPOS data when sync is disabled.
 	 */
 	public function test_get_sales_report_with_hpos_enabled_and_sync_off(): void {
-		$this->setup_cot();
-		$this->switched_to_hpos = true;
+		$this->toggle_cot_feature_and_usage( true );
 		$this->disable_cot_sync();

 		wp_set_current_user( $this->user );
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Orders/PageControllerTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Orders/PageControllerTest.php
index daf094dddea..2363495ba1b 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/Orders/PageControllerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Orders/PageControllerTest.php
@@ -45,9 +45,11 @@ namespace Automattic\WooCommerce\Tests\Internal\Admin\Orders {
 		 * Tear down class fixtures.
 		 */
 		public static function tearDownAfterClass(): void {
-			if ( OrderUtil::custom_orders_table_usage_is_enabled() !== self::$hpos_prev_state ) {
-				OrderHelper::toggle_cot_feature_and_usage( self::$hpos_prev_state );
-			}
+			// The tests switch storage inside a transaction, so the rolled back option value stays in
+			// the object cache. Flush it first, or update_option() compares against the cached value
+			// and skips the write, then restore unconditionally.
+			wp_cache_flush();
+			OrderHelper::toggle_cot_feature_and_usage( self::$hpos_prev_state );

 			remove_filter( 'wc_allow_changing_orders_storage_while_sync_is_pending', '__return_true' );