diff --git a/.github/workflows/pr-unit-tests.yml b/.github/workflows/pr-unit-tests.yml index 18da726ee35..ac92b092793 100644 --- a/.github/workflows/pr-unit-tests.yml +++ b/.github/workflows/pr-unit-tests.yml @@ -14,12 +14,14 @@ permissions: {} jobs: test: - name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} + name: PHP ${{ matrix.php }} WP ${{ matrix.wp }} ${{ matrix.hpos && 'HPOS' || '' }} timeout-minutes: 30 runs-on: ubuntu-20.04 permissions: contents: read continue-on-error: ${{ matrix.wp == 'nightly' }} + env: + HPOS: ${{ matrix.hpos }} strategy: fail-fast: false matrix: @@ -32,6 +34,9 @@ jobs: php: 7.4 - wp: '5.9' php: 7.4 + - wp: 'latest' + php: '7.4' + hpos: true services: database: image: mysql:5.6 diff --git a/plugins/woocommerce/changelog/fix-36682 b/plugins/woocommerce/changelog/fix-36682 new file mode 100644 index 00000000000..57e3e2c4011 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-36682 @@ -0,0 +1,5 @@ +Significance: patch +Type: fix +Comment: Changes are only in unit tests, no functionality is affected. + + diff --git a/plugins/woocommerce/changelog/fix-36684 b/plugins/woocommerce/changelog/fix-36684 new file mode 100644 index 00000000000..46751fa0216 --- /dev/null +++ b/plugins/woocommerce/changelog/fix-36684 @@ -0,0 +1,4 @@ +Significance: patch +Type: fix + +Add HPOS compat for wc-user-functions.php. diff --git a/plugins/woocommerce/changelog/fix-36685-2 b/plugins/woocommerce/changelog/fix-36685-2 index 6a89ac8e78f..9a8084aeee3 100644 --- a/plugins/woocommerce/changelog/fix-36685-2 +++ b/plugins/woocommerce/changelog/fix-36685-2 @@ -1,4 +1,4 @@ Significance: patch Type: fix -Directly fetch order prop from DB for compatibility with CPT store. +Fetch order first to refresh cache before returning prop. diff --git a/plugins/woocommerce/includes/class-wc-post-data.php b/plugins/woocommerce/includes/class-wc-post-data.php index e35de400ef1..c1e3aa6b5f0 100644 --- a/plugins/woocommerce/includes/class-wc-post-data.php +++ b/plugins/woocommerce/includes/class-wc-post-data.php @@ -53,6 +53,7 @@ class WC_Post_Data { add_action( 'delete_post', array( __CLASS__, 'delete_post' ) ); add_action( 'wp_trash_post', array( __CLASS__, 'trash_post' ) ); add_action( 'untrashed_post', array( __CLASS__, 'untrash_post' ) ); + add_action( 'before_delete_post', array( __CLASS__, 'before_delete_order' ) ); add_action( 'woocommerce_before_delete_order', array( __CLASS__, 'before_delete_order' ) ); // Meta cache flushing. diff --git a/plugins/woocommerce/includes/wc-user-functions.php b/plugins/woocommerce/includes/wc-user-functions.php index f1c2c43706a..7b53871b454 100644 --- a/plugins/woocommerce/includes/wc-user-functions.php +++ b/plugins/woocommerce/includes/wc-user-functions.php @@ -716,11 +716,11 @@ function wc_reset_order_customer_id_on_deleted_user( $user_id ) { if ( OrderUtil::custom_orders_table_usage_is_enabled() ) { $order_table_ds = wc_get_container()->get( OrdersTableDataStore::class ); - $order_table = $order_table_ds::get_orders_table_name(); + $order_table = $order_table_ds::get_orders_table_name(); $wpdb->update( $order_table, array( - 'customer_id' => 0, + 'customer_id' => 0, 'date_updated_gmt' => current_time( 'mysql', true ), ), array( @@ -740,13 +740,13 @@ function wc_reset_order_customer_id_on_deleted_user( $user_id ) { $wpdb->update( $wpdb->postmeta, array( - 'meta_value' => 0, + 'meta_value' => 0, //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value ), array( - 'meta_key' => '_customer_user', - 'meta_value' => $user_id, + 'meta_key' => '_customer_user', //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key + 'meta_value' => $user_id, //phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_value ) - ); // WPCS: slow query ok. + ); } } diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php index 2d4038adbed..8683d1c366e 100644 --- a/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php +++ b/plugins/woocommerce/src/Internal/DataStores/Orders/OrdersTableDataStore.php @@ -573,7 +573,8 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements */ public function get_download_permissions_granted( $order ) { $order_id = is_int( $order ) ? $order : $order->get_id(); - return $this->get_field_value( $order_id, 'download_permission_granted', self::get_operational_data_table_name() ); + $order = wc_get_order( $order_id ); + return $order->get_download_permissions_granted(); } /** @@ -599,7 +600,8 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements */ public function get_recorded_sales( $order ) { $order_id = is_int( $order ) ? $order : $order->get_id(); - return $this->get_field_value( $order_id, 'recorded_sales', self::get_operational_data_table_name() ); + $order = wc_get_order( $order_id ); + return $order->get_recorded_sales(); } /** @@ -625,7 +627,8 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements */ public function get_recorded_coupon_usage_counts( $order ) { $order_id = is_int( $order ) ? $order : $order->get_id(); - return $this->get_field_value( $order_id, 'coupon_usages_are_counted', self::get_operational_data_table_name() ); + $order = wc_get_order( $order_id ); + return $order->get_recorded_coupon_usage_counts(); } /** @@ -651,7 +654,8 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements */ public function get_email_sent( $order ) { $order_id = is_int( $order ) ? $order : $order->get_id(); - return $this->get_field_value( $order_id, 'new_order_email_sent', self::get_operational_data_table_name() ); + $order = wc_get_order( $order_id ); + return $order->get_new_order_email_sent(); } /** @@ -702,7 +706,8 @@ class OrdersTableDataStore extends \Abstract_WC_Order_Data_Store_CPT implements */ public function get_stock_reduced( $order ) { $order_id = is_int( $order ) ? $order : $order->get_id(); - return $this->get_field_value( $order_id, 'order_stock_reduced', self::get_operational_data_table_name() ); + $order = wc_get_order( $order_id ); + return $order->get_order_stock_reduced(); } /** @@ -899,31 +904,6 @@ WHERE // phpcs:enable } - /** - * Returns field value for an order directly from the database, skipping the value stored in order prop. - * Useful when you are not sure if the order prop is manipulated by a callee function without having to refresh the order. - * - * @param int $order_id Order ID. - * @param string $column_name Name of the column to fetch value from. - * @param string $table_name Name of the table to fetch value from. - * - * @return string|null Field value or null if not found. - */ - public function get_field_value( $order_id, $column_name, $table_name ) { - global $wpdb; - $where_clause = ' WHERE 1=1 '; - if ( self::get_addresses_table_name() === $table_name ) { - $address_type = explode( '_', $column_name )[0]; - $column_name = str_replace( $address_type . '_', '', $column_name ); - $where_clause .= $wpdb->prepare( ' AND type = %s', $address_type ); - } - $select_clause = 'SELECT `' . esc_sql( $column_name ) . '` FROM `' . esc_sql( $table_name ) . '` '; - $where_clause .= $wpdb->prepare( ' AND order_id = %d', $order_id ); - - // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Both select_clause and where_clause are escaped. - return $wpdb->get_var( "$select_clause $where_clause LIMIT 1" ); - } - /** * Search order data for a term and return matching order IDs. * diff --git a/plugins/woocommerce/tests/legacy/bootstrap.php b/plugins/woocommerce/tests/legacy/bootstrap.php index e920c396ce3..7463227d793 100644 --- a/plugins/woocommerce/tests/legacy/bootstrap.php +++ b/plugins/woocommerce/tests/legacy/bootstrap.php @@ -89,7 +89,9 @@ class WC_Unit_Tests_Bootstrap { // re-initialize dependency injection, this needs to be the last operation after everything else is in place. $this->initialize_dependency_injection(); - $this->initialize_hpos(); + if ( getenv( 'HPOS' ) ) { + $this->initialize_hpos(); + } error_reporting(error_reporting() & ~E_DEPRECATED); } @@ -142,6 +144,11 @@ class WC_Unit_Tests_Bootstrap { CodeHacker::enable(); } + /** + * Initialize HPOS if tests need to run in HPOS context. + * + * @return void + */ private function initialize_hpos() { \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::delete_order_custom_tables(); \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_order_custom_table_if_not_exist(); diff --git a/plugins/woocommerce/tests/legacy/unit-tests/crud/meta.php b/plugins/woocommerce/tests/legacy/unit-tests/crud/meta.php index 427739ca95e..00c34fd5eac 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/crud/meta.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/crud/meta.php @@ -115,7 +115,7 @@ class WC_Tests_CRUD_Meta_Data extends WC_Unit_Test_Case { $this->assertTrue( in_array( 'random', wp_list_pluck( $new_order->get_meta_data(), 'key' ) ) ); $this->assertTrue( in_array( 'random_other', wp_list_pluck( $new_order->get_meta_data(), 'key' ) ) ); if ( ! OrderUtil::custom_orders_table_usage_is_enabled() ) { - $this->assertTrue( in_array( 'random_other_pre_crud', wp_list_pluck( $new_order->get_meta_data(), 'key' ) ) ); + $this->assertTrue( in_array( 'random_other_pre_crud', wp_list_pluck( $new_order->get_meta_data(), 'key' ), true ) ); } } diff --git a/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php b/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php index b0f8897821a..e85e8f0fe50 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-crud-orders.php @@ -5,6 +5,8 @@ * @package WooCommerce\Tests\CRUD */ +use Automattic\WooCommerce\Utilities\OrderUtil; + /** * Meta * @@ -882,9 +884,9 @@ class WC_Tests_CRUD_Orders extends WC_Unit_Test_Case { $object = new WC_Order(); // Save + create. - $save_id = $object->save(); - $post = get_post( $save_id ); - $expected_post_type = \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled() ? 'shop_order_placehold' : 'shop_order'; + $save_id = $object->save(); + $post = get_post( $save_id ); + $expected_post_type = OrderUtil::custom_orders_table_usage_is_enabled() ? 'shop_order_placehold' : 'shop_order'; $this->assertEquals( $expected_post_type, $post->post_type ); // Update. diff --git a/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-order-functions.php b/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-order-functions.php index 6ba56763594..696668decd9 100644 --- a/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-order-functions.php +++ b/plugins/woocommerce/tests/legacy/unit-tests/order/class-wc-tests-order-functions.php @@ -109,10 +109,10 @@ class WC_Tests_Order_Functions extends WC_Unit_Test_Case { ->will( $this->returnValueMap( $test_counts[ $order_type ] ) ); } - $add_mock_datastores = function( $stores ) use ( $mock_datastores ) { + $add_mock_datastores = function ( $stores ) use ( $mock_datastores ) { return array_merge( $stores, $mock_datastores ); }; - $add_mock_order_type = function( $order_types ) use ( $mock_datastores ) { + $add_mock_order_type = function ( $order_types ) use ( $mock_datastores ) { return array( 'shop_order', 'order-fake-type' ); }; $return_mock_order_data_store = function ( $stores ) use ( $mock_datastores ) { diff --git a/plugins/woocommerce/tests/php/includes/class-wc-order-factory-test.php b/plugins/woocommerce/tests/php/includes/class-wc-order-factory-test.php index e3af7d32052..56cda5d51c8 100644 --- a/plugins/woocommerce/tests/php/includes/class-wc-order-factory-test.php +++ b/plugins/woocommerce/tests/php/includes/class-wc-order-factory-test.php @@ -5,6 +5,35 @@ */ class WC_Order_Factory_Test extends WC_Unit_Test_Case { + /** + * Store COT state at the start of the test so we can restore it later. + * + * @var bool + */ + private $cot_state; + + /** + * Disable COT before the test. + * + * @return void + */ + public function setUp(): void { + parent::setUp(); + $this->cot_state = \Automattic\WooCommerce\Utilities\OrderUtil::custom_orders_table_usage_is_enabled(); + \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::toggle_cot( false ); + } + + /** + * Restore COT state after the test. + * + * @return void + */ + public function tearDown(): void { + parent::tearDown(); + wp_cache_flush(); + \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::toggle_cot( $this->cot_state ); + } + /** * @testDox get_orders should be able to return multiple orders of different types. */ diff --git a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php index 2d7b5685c55..04e70cc9c90 100644 --- a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php +++ b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/OrdersTableDataStoreTests.php @@ -7,6 +7,7 @@ use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableDataStore; use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableQuery; use Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper; use Automattic\WooCommerce\RestApi\UnitTests\HPOSToggleTrait; +use Automattic\WooCommerce\Utilities\OrderUtil; /** * Class OrdersTableDataStoreTests. @@ -37,6 +38,12 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case { */ private $cpt_data_store; + /** + * Whether COT was enabled before the test. + * @var bool + */ + private $cot_state; + /** * Initializes system under test. */ @@ -47,6 +54,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case { parent::setUp(); // Remove the Test Suiteā€™s use of temporary tables https://wordpress.stackexchange.com/a/220308. $this->setup_cot(); + $this->cot_state = OrderUtil::custom_orders_table_usage_is_enabled(); $this->toggle_cot( false ); $this->sut = wc_get_container()->get( OrdersTableDataStore::class ); $this->migrator = wc_get_container()->get( PostsToOrdersMigrationController::class ); @@ -59,6 +67,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case { public function tearDown(): void { //phpcs:ignore WordPress.DateTime.RestrictedFunctions.timezone_change_date_default_timezone_set -- We need to change the timezone to test the date sync fields. update_option( 'timezone_string', $this->original_time_zone ); + $this->toggle_cot( $this->cot_state ); $this->clean_up_cot_setup(); parent::tearDown(); } @@ -205,6 +214,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case { wp_cache_flush(); $order = new WC_Order(); $order->set_id( $post_order->get_id() ); + $this->toggle_cot( true ); $this->switch_data_store( $order, $this->sut ); $this->sut->read( $order ); @@ -239,6 +249,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case { foreach ( $datastore_updates as $prop => $value ) { $this->assertEquals( $value, $this->sut->{"get_$prop"}( $order ), "Unable to match prop $prop" ); } + $this->toggle_cot( false ); } /** @@ -1767,6 +1778,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case { * Ideally, this should be possible only from getters and setters for objects, but for backward compatibility, earlier ways are also supported. */ public function test_internal_ds_getters_and_setters() { + $this->toggle_cot( true ); $props_to_test = array( '_download_permissions_granted', '_recorded_sales', @@ -1813,6 +1825,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case { $order->save(); } $this->assert_get_prop_via_ds_object_and_metadata( $props_to_test, $order, false, $ds_getter_setter_names ); + $this->toggle_cot( false ); } /** @@ -1855,7 +1868,8 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case { * @testDox Legacy getters and setters for props migrated from data stores should be set/reset properly. */ public function test_legacy_getters_setters() { - $order_id = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_complex_wp_post_order(); + $this->toggle_cot( true ); + $order_id = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_complex_data_store_order( $this->sut ); $order = wc_get_order( $order_id ); $this->switch_data_store( $order, $this->sut ); $bool_props = array( @@ -1887,7 +1901,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case { $this->assert_props_value_via_data_store( $order, $bool_props, true ); $this->assert_props_value_via_order_object( $order, $bool_props, true ); - + $this->toggle_cot( false ); } /** @@ -1984,8 +1998,9 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case { */ public function test_read_multiple_dont_sync_again_for_same_order() { $this->toggle_cot( true ); - $this->enable_cot_sync(); $order = $this->create_complex_cot_order(); + $this->sut->backfill_post_record( $order ); + $this->enable_cot_sync(); $order_id = $order->get_id(); @@ -1999,6 +2014,7 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case { $this->assertTrue( $should_sync_callable->call( $this->sut, $order ) ); $this->sut->read_multiple( $orders ); $this->assertFalse( $should_sync_callable->call( $this->sut, $order ) ); + $this->toggle_cot( false ); } /**