Add experimental support for FTS indexes in HPOS. (#46130)

* Added key for sku

* Add experimental support for FTS indexes in HPOS. Additionally, revert existing HPOS search queries to use post like structure.

* Add unit tests

* Escape the error messages as per new phpcs rules.

* Fix query with index + added test.

* remove uninteded change.

* Unit test fixes.

* Remove unit test since the commit command breaks other tests.

Use message API instead of notices with defensive checks.
This commit is contained in:
Vedanshu Jain 2024-04-26 21:55:03 +05:30 committed by GitHub
parent 3d8e20c62c
commit 94e438f524
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 274 additions and 48 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: performance
Add experimental support for FTS indexes in HPOS. Additionally, revert existing HPOS search queries to use post like structure.

View File

@ -10,6 +10,8 @@ use Automattic\WooCommerce\Caches\OrderCacheController;
use Automattic\WooCommerce\Internal\BatchProcessing\BatchProcessingController;
use Automattic\WooCommerce\Internal\Features\FeaturesController;
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
use Automattic\WooCommerce\Utilities\OrderUtil;
use Automattic\WooCommerce\Utilities\PluginUtil;
use WC_Admin_Settings;
@ -46,6 +48,12 @@ class CustomOrdersTableController {
public const DEFAULT_DB_TRANSACTIONS_ISOLATION_LEVEL = 'READ UNCOMMITTED';
public const HPOS_FTS_INDEX_OPTION = 'woocommerce_hpos_fts_index_enabled';
public const HPOS_FTS_ADDRESS_INDEX_CREATED_OPTION = 'woocommerce_hpos_address_fts_index_created';
public const HPOS_FTS_ORDER_ITEM_INDEX_CREATED_OPTION = 'woocommerce_hpos_order_item_fts_index_created';
/**
* The data store object to use.
*
@ -109,6 +117,13 @@ class CustomOrdersTableController {
*/
private $plugin_util;
/**
* The db util object to use.
*
* @var DatabaseUtil;
*/
private $db_util;
/**
* Class constructor.
*/
@ -124,6 +139,7 @@ class CustomOrdersTableController {
self::add_filter( 'woocommerce_order-refund_data_store', array( $this, 'get_refunds_data_store' ), 999, 1 );
self::add_filter( 'woocommerce_debug_tools', array( $this, 'add_hpos_tools' ), 999 );
self::add_filter( 'updated_option', array( $this, 'process_updated_option' ), 999, 3 );
self::add_filter( 'updated_option', array( $this, 'process_updated_option_fts_index' ), 999, 3 );
self::add_filter( 'pre_update_option', array( $this, 'process_pre_update_option' ), 999, 3 );
self::add_action( 'woocommerce_after_register_post_type', array( $this, 'register_post_type_for_order_placeholders' ), 10, 0 );
self::add_action( 'woocommerce_sections_advanced', array( $this, 'sync_now' ) );
@ -144,6 +160,7 @@ class CustomOrdersTableController {
* @param OrderCache $order_cache The order cache engine to use.
* @param OrderCacheController $order_cache_controller The order cache controller to use.
* @param PluginUtil $plugin_util The plugin util to use.
* @param DatabaseUtil $db_util The database util to use.
*/
final public function init(
OrdersTableDataStore $data_store,
@ -154,7 +171,8 @@ class CustomOrdersTableController {
FeaturesController $features_controller,
OrderCache $order_cache,
OrderCacheController $order_cache_controller,
PluginUtil $plugin_util
PluginUtil $plugin_util,
DatabaseUtil $db_util
) {
$this->data_store = $data_store;
$this->data_synchronizer = $data_synchronizer;
@ -165,6 +183,7 @@ class CustomOrdersTableController {
$this->order_cache = $order_cache;
$this->order_cache_controller = $order_cache_controller;
$this->plugin_util = $plugin_util;
$this->db_util = $db_util;
}
/**
@ -291,6 +310,61 @@ class CustomOrdersTableController {
}
}
/**
* Process option that enables FTS index on orders table. Tries to create an FTS index when option is enabled.
*
* @param string $option Option name.
* @param string $old_value Old value of the option.
* @param string $value New value of the option.
*
* @return void
*/
private function process_updated_option_fts_index( $option, $old_value, $value ) {
if ( self::HPOS_FTS_INDEX_OPTION !== $option ) {
return;
}
if ( 'yes' !== $value ) {
return;
}
if ( ! $this->custom_orders_table_usage_is_enabled() ) {
update_option( self::HPOS_FTS_INDEX_OPTION, 'no', true );
if ( class_exists( 'WC_Admin_Settings' ) ) {
WC_Admin_Settings::add_error( __( 'Failed to create FTS index on orders table. This feature is only available when High-performance order storage is enabled.', 'woocommerce' ) );
}
return;
}
if ( ! $this->db_util->fts_index_on_order_address_table_exists() ) {
$this->db_util->create_fts_index_order_address_table();
}
// Check again to see if index was actually created.
if ( $this->db_util->fts_index_on_order_address_table_exists() ) {
update_option( self::HPOS_FTS_ADDRESS_INDEX_CREATED_OPTION, 'yes', true );
} else {
update_option( self::HPOS_FTS_ADDRESS_INDEX_CREATED_OPTION, 'no', true );
if ( class_exists( 'WC_Admin_Settings ' ) ) {
WC_Admin_Settings::add_error( __( 'Failed to create FTS index on address table', 'woocommerce' ) );
}
}
if ( ! $this->db_util->fts_index_on_order_item_table_exists() ) {
$this->db_util->create_fts_index_order_item_table();
}
// Check again to see if index was actually created.
if ( $this->db_util->fts_index_on_order_item_table_exists() ) {
update_option( self::HPOS_FTS_ORDER_ITEM_INDEX_CREATED_OPTION, 'yes', true );
} else {
update_option( self::HPOS_FTS_ORDER_ITEM_INDEX_CREATED_OPTION, 'no', true );
if ( class_exists( 'WC_Admin_Settings ' ) ) {
WC_Admin_Settings::add_error( __( 'Failed to create FTS index on order item table', 'woocommerce' ) );
}
}
}
/**
* Handler for the setting pre-update hook.
* We use it to verify that authoritative orders table switch doesn't happen while sync is pending.

View File

@ -51,7 +51,7 @@ class OrdersTableSearchQuery {
*
* @return array Array of search filters.
*/
private function sanitize_search_filters( string $search_filter ) : array {
private function sanitize_search_filters( string $search_filter ): array {
$core_filters = array(
'order_id',
'transaction_id',
@ -112,15 +112,7 @@ class OrdersTableSearchQuery {
*
* @return string JOIN clause.
*/
private function generate_join_for_search_filter( $search_filter ) : string {
if ( 'products' === $search_filter ) {
$orders_table = $this->query->get_table_name( 'orders' );
$items_table = $this->query->get_table_name( 'items' );
return "
LEFT JOIN $items_table AS search_query_items ON search_query_items.order_id = $orders_table.id
";
}
private function generate_join_for_search_filter( $search_filter ): string {
/**
* Filter to support adding a custom order search filter.
* Provide a JOIN clause for a new search filter. This should be used along with `woocommerce_hpos_admin_search_filters`
@ -181,7 +173,7 @@ class OrdersTableSearchQuery {
*
* @return string WHERE clause.
*/
private function generate_where_for_search_filter( string $search_filter ) : string {
private function generate_where_for_search_filter( string $search_filter ): string {
global $wpdb;
$order_table = $this->query->get_table_name( 'orders' );
@ -208,15 +200,11 @@ class OrdersTableSearchQuery {
}
if ( 'products' === $search_filter ) {
return $wpdb->prepare(
'search_query_items.order_item_name LIKE %s',
'%' . $wpdb->esc_like( $this->search_term ) . '%'
);
return $this->get_where_for_products();
}
if ( 'customers' === $search_filter ) {
$meta_sub_query = $this->generate_where_for_meta_table();
return "`$order_table`.id IN ( $meta_sub_query ) ";
return $this->get_where_for_customers();
}
/**
@ -243,6 +231,74 @@ class OrdersTableSearchQuery {
);
}
/**
* Helper function to generate the WHERE clause for products search. Uses FTS when available.
*
* @return string|null WHERE clause for products search.
*/
private function get_where_for_products() {
global $wpdb;
$items_table = $this->query->get_table_name( 'items' );
$orders_table = $this->query->get_table_name( 'orders' );
$fts_enabled = get_option( CustomOrdersTableController::HPOS_FTS_INDEX_OPTION ) === 'yes' && get_option( CustomOrdersTableController::HPOS_FTS_ORDER_ITEM_INDEX_CREATED_OPTION ) === 'yes';
if ( $fts_enabled ) {
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $orders_table and $items_table are hardcoded.
return $wpdb->prepare(
"
$orders_table.id in (
SELECT order_id FROM $items_table search_query_items WHERE
MATCH ( search_query_items.order_item_name ) AGAINST ( %s IN BOOLEAN MODE )
)
",
'*' . $wpdb->esc_like( $this->search_term ) . '*'
);
// phpcs:enable
}
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $orders_table and $items_table are hardcoded.
return $wpdb->prepare(
"
$orders_table.id in (
SELECT order_id FROM $items_table search_query_items WHERE
search_query_items.order_item_name LIKE %s
)
",
'%' . $wpdb->esc_like( $this->search_term ) . '%'
);
// phpcs:enable
}
/**
* Helper function to generate the WHERE clause for customers search. Uses FTS when available.
*
* @return string|null WHERE clause for customers search.
*/
private function get_where_for_customers() {
global $wpdb;
$order_table = $this->query->get_table_name( 'orders' );
$address_table = $this->query->get_table_name( 'addresses' );
$fts_enabled = get_option( CustomOrdersTableController::HPOS_FTS_INDEX_OPTION ) === 'yes' && get_option( CustomOrdersTableController::HPOS_FTS_ADDRESS_INDEX_CREATED_OPTION ) === 'yes';
if ( $fts_enabled ) {
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $order_table and $address_table are hardcoded.
return $wpdb->prepare(
"
$order_table.id IN (
SELECT order_id FROM $address_table WHERE
MATCH( $address_table.first_name, $address_table.last_name, $address_table.company, $address_table.address_1, $address_table.address_2, $address_table.city, $address_table.state, $address_table.postcode, $address_table.country, $address_table.email ) AGAINST ( %s IN BOOLEAN MODE )
)
",
'*' . $wpdb->esc_like( $this->search_term ) . '*'
);
// phpcs:enable
}
$meta_sub_query = $this->generate_where_for_meta_table();
return "`$order_table`.id IN ( $meta_sub_query ) ";
}
/**
* Generates where clause for meta table.
*

View File

@ -76,6 +76,7 @@ class OrdersDataStoreServiceProvider extends AbstractServiceProvider {
OrderCache::class,
OrderCacheController::class,
PluginUtil::class,
DatabaseUtil::class,
)
);
$this->share( OrderCache::class );

View File

@ -7,6 +7,7 @@ namespace Automattic\WooCommerce\Internal\Features;
use Automattic\WooCommerce\Internal\Admin\Analytics;
use Automattic\WooCommerce\Admin\Features\Navigation\Init;
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
use Automattic\WooCommerce\Internal\Traits\AccessiblePrivateMethods;
use Automattic\WooCommerce\Proxies\LegacyProxy;
use Automattic\WooCommerce\Utilities\ArrayUtil;
@ -232,6 +233,17 @@ class FeaturesController {
'is_legacy' => true,
'is_experimental' => false,
),
'hpos_fts_indexes' => array(
'name' => __( 'HPOS Full text search indexes', 'woocommerce' ),
'description' => __(
'Create and use full text search indexes for orders. This feature only works with high-performance order storage.',
'woocommerce'
),
'is_experimental' => true,
'enabled_by_default' => false,
'is_legacy' => true,
'option_key' => CustomOrdersTableController::HPOS_FTS_INDEX_OPTION,
),
);
foreach ( $legacy_features as $slug => $definition ) {
@ -392,7 +404,7 @@ class FeaturesController {
ArrayUtil::ensure_key_is_array( $this->compatibility_info_by_plugin[ $plugin_name ], $opposite_key );
if ( in_array( $feature_id, $this->compatibility_info_by_plugin[ $plugin_name ][ $opposite_key ], true ) ) {
throw new \Exception( "Plugin $plugin_name is trying to declare itself as $key with the '$feature_id' feature, but it already declared itself as $opposite_key" );
throw new \Exception( esc_html( "Plugin $plugin_name is trying to declare itself as $key with the '$feature_id' feature, but it already declared itself as $opposite_key" ) );
}
if ( ! in_array( $feature_id, $this->compatibility_info_by_plugin[ $plugin_name ][ $key ], true ) ) {
@ -487,14 +499,15 @@ class FeaturesController {
/**
* Check if the 'woocommerce_init' has run or is running, do a 'wc_doing_it_wrong' if not.
*
* @param string|null $function Name of the invoking method, if not null, 'wc_doing_it_wrong' will be invoked if 'woocommerce_init' has not run and is not running.
* @param string|null $function_name Name of the invoking method, if not null, 'wc_doing_it_wrong' will be invoked if 'woocommerce_init' has not run and is not running.
*
* @return bool True if 'woocommerce_init' has run or is running, false otherwise.
*/
private function verify_did_woocommerce_init( string $function = null ): bool {
private function verify_did_woocommerce_init( string $function_name = null ): bool {
if ( ! $this->proxy->call_function( 'did_action', 'woocommerce_init' ) &&
! $this->proxy->call_function( 'doing_action', 'woocommerce_init' ) ) {
if ( ! is_null( $function ) ) {
$class_and_method = ( new \ReflectionClass( $this ) )->getShortName() . '::' . $function;
if ( ! is_null( $function_name ) ) {
$class_and_method = ( new \ReflectionClass( $this ) )->getShortName() . '::' . $function_name;
/* translators: 1: class::method 2: plugins_loaded */
$this->proxy->call_function( 'wc_doing_it_wrong', $class_and_method, sprintf( __( '%1$s should not be called before the %2$s action.', 'woocommerce' ), $class_and_method, 'woocommerce_init' ), '7.0' );
}
@ -869,41 +882,41 @@ class FeaturesController {
* if we are in the plugins page and the query string of the current request
* looks like '?plugin_status=incompatible_with_feature&feature_id=<feature id>'.
*
* @param array $list The original list of plugins.
* @param array $plugin_list The original list of plugins.
*/
private function filter_plugins_list( $list ): array {
private function filter_plugins_list( $plugin_list ): array {
if ( ! $this->verify_did_woocommerce_init() ) {
return $list;
return $plugin_list;
}
// phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput
if ( ! function_exists( 'get_current_screen' ) || get_current_screen() && 'plugins' !== get_current_screen()->id || 'incompatible_with_feature' !== ArrayUtil::get_value_or_default( $_GET, 'plugin_status' ) ) {
return $list;
return $plugin_list;
}
$feature_id = $_GET['feature_id'] ?? 'all';
if ( 'all' !== $feature_id && ! $this->feature_exists( $feature_id ) ) {
return $list;
return $plugin_list;
}
return $this->get_incompatible_plugins( $feature_id, $list );
return $this->get_incompatible_plugins( $feature_id, $plugin_list );
}
/**
* Returns the list of plugins incompatible with a given feature.
*
* @param string $feature_id ID of the feature. Can also be `all` to denote all features.
* @param array $list List of plugins to filter.
* @param array $plugin_list List of plugins to filter.
*
* @return array List of plugins incompatible with the given feature.
*/
private function get_incompatible_plugins( $feature_id, $list ) {
private function get_incompatible_plugins( $feature_id, $plugin_list ) {
$incompatibles = array();
$list = array_diff_key( $list, array_flip( $this->plugins_excluded_from_compatibility_ui ) );
$plugin_list = array_diff_key( $plugin_list, array_flip( $this->plugins_excluded_from_compatibility_ui ) );
// phpcs:enable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
foreach ( array_keys( $list ) as $plugin_name ) {
foreach ( array_keys( $plugin_list ) as $plugin_name ) {
if ( ! $this->plugin_util->is_woocommerce_aware_plugin( $plugin_name ) || ! $this->proxy->call_function( 'is_plugin_active', $plugin_name ) ) {
continue;
}
@ -921,7 +934,7 @@ class FeaturesController {
}
}
return array_intersect_key( $list, array_flip( $incompatibles ) );
return array_intersect_key( $plugin_list, array_flip( $incompatibles ) );
}
/**

View File

@ -170,7 +170,7 @@ class DatabaseUtil {
$value = $value ? ( new DateTime( "@{$value}" ) )->format( 'Y-m-d H:i:s' ) : null;
break;
default:
throw new \Exception( 'Invalid type received: ' . $type );
throw new \Exception( esc_html( 'Invalid type received: ' . $type ) );
}
return $value;
@ -194,7 +194,7 @@ class DatabaseUtil {
);
if ( ! isset( $wpdb_placeholder_for_type[ $type ] ) ) {
throw new \Exception( 'Invalid column type: ' . $type );
throw new \Exception( esc_html( 'Invalid column type: ' . $type ) );
}
return $wpdb_placeholder_for_type[ $type ];
@ -231,7 +231,7 @@ class DatabaseUtil {
*
* @return int Returns the value of DB's ON DUPLICATE KEY UPDATE clause.
*/
public function insert_on_duplicate_key_update( $table_name, $data, $format ) : int {
public function insert_on_duplicate_key_update( $table_name, $data, $format ): int {
global $wpdb;
if ( empty( $data ) ) {
return 0;
@ -249,7 +249,7 @@ class DatabaseUtil {
$values[] = $value;
$value_format[] = $format[ $index ];
}
$index++;
++$index;
}
$column_clause = '`' . implode( '`, `', $columns ) . '`';
$value_format_clause = implode( ', ', $value_format );
@ -273,7 +273,7 @@ $on_duplicate_clause
*
* @return int Max index length.
*/
public function get_max_index_length() : int {
public function get_max_index_length(): int {
/**
* Filters the maximum index length in the database.
*
@ -291,4 +291,52 @@ $on_duplicate_clause
// Index length cannot be more than 768, which is 3078 bytes in utf8mb4 and max allowed by InnoDB engine.
return min( absint( $max_index_length ), 767 );
}
/**
* Create a fulltext index on order address table.
*
* @return void
*/
public function create_fts_index_order_address_table(): void {
global $wpdb;
$address_table = $wpdb->prefix . 'wc_order_addresses';
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $address_table is hardcoded.
$wpdb->query( "CREATE FULLTEXT INDEX order_addresses_fts ON $address_table (first_name, last_name, company, address_1, address_2, city, state, postcode, country, email)" );
}
/**
* Check if fulltext index with key `order_addresses_fts` on order address table exists.
*
* @return bool
*/
public function fts_index_on_order_address_table_exists(): bool {
global $wpdb;
$address_table = $wpdb->prefix . 'wc_order_addresses';
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $address_table is hardcoded.
return ! empty( $wpdb->get_results( "SHOW INDEX FROM $address_table WHERE Key_name = 'order_addresses_fts'" ) );
}
/**
* Create a fulltext index on order item table.
*
* @return void
*/
public function create_fts_index_order_item_table(): void {
global $wpdb;
$order_item_table = $wpdb->prefix . 'woocommerce_order_items';
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $order_item_table is hardcoded.
$wpdb->query( "CREATE FULLTEXT INDEX order_item_fts ON $order_item_table (order_item_name)" );
}
/**
* Check if fulltext index with key `order_item_fts` on order item table exists.
*
* @return bool
*/
public function fts_index_on_order_item_table_exists(): bool {
global $wpdb;
$order_item_table = $wpdb->prefix . 'woocommerce_order_items';
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $order_item_table is hardcoded.
return ! empty( $wpdb->get_results( "SHOW INDEX FROM $order_item_table WHERE Key_name = 'order_item_fts'" ) );
}
}

View File

@ -1,5 +1,6 @@
<?php
use Automattic\WooCommerce\Internal\DataStores\Orders\CustomOrdersTableController;
use Automattic\WooCommerce\Internal\DataStores\Orders\OrdersTableQuery;
use Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper;
use Automattic\WooCommerce\RestApi\UnitTests\HPOSToggleTrait;
@ -24,8 +25,8 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
public function setUp(): void {
parent::setUp();
add_filter( 'wc_allow_changing_orders_storage_while_sync_is_pending', '__return_true' );
$this->setup_cot();
$this->cot_state = OrderUtil::custom_orders_table_usage_is_enabled();
$this->setup_cot();
$this->toggle_cot_feature_and_usage( true );
}
@ -148,7 +149,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
$filters_called = 0;
$filter_callback = function ( $arg ) use ( &$filters_called ) {
$filters_called++;
++$filters_called;
return $arg;
};
@ -193,7 +194,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
$this->assertCount( 2, wc_get_orders( array() ) );
// Force a query that returns nothing.
$filter_callback = function( $clauses ) {
$filter_callback = function ( $clauses ) {
$clauses['where'] .= ' AND 1=0 ';
return $clauses;
};
@ -203,7 +204,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
remove_all_filters( 'woocommerce_orders_table_query_clauses' );
// Force a query that sorts orders by id ASC (as opposed to the default date DESC) if a query arg is present.
$filter_callback = function( $clauses, $query, $query_args ) {
$filter_callback = function ( $clauses, $query, $query_args ) {
if ( ! empty( $query_args['my_custom_arg'] ) ) {
$clauses['orderby'] = $query->get_table_name( 'orders' ) . '.id ASC';
}
@ -254,7 +255,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
$this->assertEquals( 2, $query->found_orders );
$this->assertEquals( 0, $query->max_num_pages );
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
$callback = function ( $result, $query_object, $sql ) use ( $order1 ) {
$this->assertNull( $result );
$this->assertInstanceOf( OrdersTableQuery::class, $query_object );
$this->assertStringContainsString( 'SELECT ', $sql );
@ -295,7 +296,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
$this->assertEquals( 2, $query->found_orders );
$this->assertEquals( 0, $query->max_num_pages );
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
$callback = function ( $result, $query_object, $sql ) use ( $order1 ) {
$this->assertNull( $result );
$this->assertInstanceOf( OrdersTableQuery::class, $query_object );
$this->assertStringContainsString( 'SELECT ', $sql );
@ -330,7 +331,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
$order1->set_date_created( time() - HOUR_IN_SECONDS );
$order1->save();
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
$callback = function () use ( $order1 ) {
// Do not return found_orders or max_num_pages so as to provoke a warning.
$order_ids = array( $order1->get_id() );
return array( $order_ids, 10, null );
@ -385,7 +386,7 @@ class OrdersTableQueryTests extends WC_Unit_Test_Case {
$order1->set_date_created( time() - HOUR_IN_SECONDS );
$order1->save();
$callback = function( $result, $query_object, $sql ) use ( $order1 ) {
$callback = function () use ( $order1 ) {
// Just return null.
return null;
};

View File

@ -402,7 +402,7 @@ class FeaturesControllerTest extends \WC_Unit_Test_Case {
$this->simulate_inside_before_woocommerce_init_hook();
$this->ExpectException( \Exception::class );
$this->ExpectExceptionMessage( "Plugin the_plugin is trying to declare itself as incompatible with the 'mature1' feature, but it already declared itself as compatible" );
$this->ExpectExceptionMessage( esc_html( "Plugin the_plugin is trying to declare itself as incompatible with the 'mature1' feature, but it already declared itself as compatible" ) );
$this->sut->declare_compatibility( 'mature1', 'the_plugin', true );
$this->sut->declare_compatibility( 'mature1', 'the_plugin', false );

View File

@ -3,6 +3,7 @@
* Tests for the DatabaseUtil utility.
*/
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
/**
@ -57,4 +58,32 @@ class DatabaseUtilTest extends WC_Unit_Test_Case {
$this->sut->get_index_columns( $wpdb->prefix . 'wc_product_meta_lookup', 'invalid_index_name' )
);
}
/**
* @test Test that we are able to create FTS index on order address table.
*/
public function test_create_fts_index_order_address_table() {
$db = wc_get_container()->get( DataSynchronizer::class );
// Remove the Test Suites use of temporary tables https://wordpress.stackexchange.com/a/220308.
remove_filter( 'query', array( $this, '_create_temporary_tables' ) );
remove_filter( 'query', array( $this, '_drop_temporary_tables' ) );
$db->create_database_tables();
// Add back removed filter.
add_filter( 'query', array( $this, '_create_temporary_tables' ) );
add_filter( 'query', array( $this, '_drop_temporary_tables' ) );
if ( ! $this->sut->fts_index_on_order_item_table_exists() ) {
$this->sut->create_fts_index_order_address_table();
}
$this->assertTrue( $this->sut->fts_index_on_order_address_table_exists() );
}
/**
* @test Test that we are able to create FTS index on order item table.
*/
public function test_create_fts_index_order_item_table() {
if ( ! $this->sut->fts_index_on_order_item_table_exists() ) {
$this->sut->create_fts_index_order_item_table();
}
$this->assertTrue( $this->sut->fts_index_on_order_item_table_exists() );
}
}