Skip to content
This repository was archived by the owner on Jun 15, 2022. It is now read-only.

Commit 3751e79

Browse files
authored
Merge pull request #25 from liquidweb/feature/tests-for-data-store
Tests for the order data store
2 parents 20fd3d7 + d272e29 commit 3751e79

12 files changed

+358
-21
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ insert_final_newline = true
1313
trim_trailing_whitespace = true
1414
indent_style = tab
1515

16-
[{package.json,*.yml}]
16+
[{*.json,*.yml}]
1717
indent_style = space
1818
indent_size = 2
1919

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
/tests/coverage
12
/vendor
2-
composer.lock
3+
composer.lock

CONTRIBUTING.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,13 @@ Once master has been updated, the release should be tagged, then `master` should
4747
WooCommerce Custom Order Tables uses [the WordPress core testing suite](https://make.wordpress.org/core/handbook/testing/automated-testing/writing-phpunit-tests/) to provide automated tests for its functionality.
4848

4949
When submitting pull requests, please include relevant tests for your new features and bugfixes. This helps prevent regressions in future iterations of the plugin, and helps instill confidence in store owners using this to enhance their WooCommerce stores.
50+
51+
#### Test coverage
52+
53+
To generate a code coverage report (test coverage percentage as well as areas of untested or under-tested code that could pose risk), you run the following:
54+
55+
```sh
56+
$ composer test-coverage
57+
```
58+
59+
The report will be saved to `tests/coverage/`. Please note that XDebug must be enabled in order to generate code coverage reports!

composer.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,16 @@
44
"type": "wordpress-plugin",
55
"license": "GPL-2.0",
66
"require": {
7+
"php": ">=5.2",
78
"composer/installers": "^1.4",
89
"xrstf/composer-php52": "^1.0"
910
},
1011
"autoload": {
1112
"classmap": ["includes"]
1213
},
14+
"autoload-dev": {
15+
"classmap": ["tests/test-tools"]
16+
},
1317
"scripts": {
1418
"post-install-cmd": [
1519
"xrstf\\Composer52\\Generator::onPostInstallCmd"
@@ -19,6 +23,9 @@
1923
],
2024
"post-autoload-dump": [
2125
"xrstf\\Composer52\\Generator::onPostInstallCmd"
26+
],
27+
"test-coverage": [
28+
"phpunit --coverage-html=tests/coverage"
2229
]
2330
}
2431
}

includes/class-wc-order-data-store-custom-table.php

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -532,15 +532,16 @@ private function get_orders_generate_customer_meta_query( $values, $relation = '
532532
public function get_unpaid_orders( $date ) {
533533
global $wpdb;
534534

535-
$unpaid_orders = $wpdb->get_col( $wpdb->prepare( "
536-
SELECT posts.ID
537-
FROM {$wpdb->posts} AS posts
538-
WHERE posts.post_type IN ('" . implode( "','", wc_get_order_types() ) . "')
539-
AND posts.post_status = 'wc-pending'
540-
AND posts.post_modified < %s
541-
", date( 'Y-m-d H:i:s', absint( $date ) ) ) );
542-
543-
return $unpaid_orders;
535+
$order_types = wc_get_order_types();
536+
537+
return $wpdb->get_col( $wpdb->prepare(
538+
"SELECT ID
539+
FROM $wpdb->posts
540+
WHERE post_type IN (" . implode( ',', array_fill( 0, count( $order_types ), '%s' ) ) . ")
541+
AND post_status = 'wc-pending'
542+
AND post_modified < %s",
543+
array_merge( $order_types, array( date( 'Y-m-d H:i:s', (int) $date ) ) )
544+
) );
544545
}
545546

546547
/**
@@ -555,24 +556,34 @@ public function search_orders( $term ) {
555556

556557
$order_ids = array();
557558

559+
// Treat a numeric search term as an order ID.
558560
if ( is_numeric( $term ) ) {
559561
$order_ids[] = absint( $term );
560562
}
561563

564+
// Search given post meta columns for the query.
565+
$postmeta_search = array();
566+
562567
/**
563568
* Searches on meta data can be slow - this lets you choose what fields to search.
564569
*
565570
* WooCommerce 2.7.0 added _billing_address and _shipping_address meta which contains all
566571
* address data to make this faster. However, this won't work on older orders unless they
567572
* are updated, so search a few others (expand this using the filter if needed).
568573
*/
569-
$meta_search_fields = array_map( 'wc_clean', apply_filters( 'woocommerce_shop_order_search_fields', array(
570-
// While we are searching the custom table, we will also search meta when filtered for backwards compatibility.
571-
) ) );
572-
573-
$postmeta_search = ! empty( $meta_search_fields ) ? $wpdb->get_col(
574-
$wpdb->prepare( "SELECT DISTINCT p1.post_id FROM {$wpdb->postmeta} p1 WHERE p1.meta_key IN ('" . implode( "','", array_map( 'esc_sql', $search_fields ) ) . "') AND p1.meta_value LIKE '%%%s%%';", wc_clean( $term ) )
575-
) : array();
574+
$meta_search_fields = array_map( 'wc_clean', apply_filters( 'woocommerce_shop_order_search_fields', array() ) );
575+
576+
// If we were given meta fields to search, make it happen.
577+
if ( ! empty( $meta_search_fields ) ) {
578+
$postmeta_search = $wpdb->get_col( $wpdb->prepare( "
579+
SELECT DISTINCT post_id
580+
FROM {$wpdb->postmeta}
581+
WHERE meta_key IN (" . implode( ',', array_fill( 0, count( $meta_search_fields ), '%s' ) ) . ')
582+
AND meta_value LIKE %s
583+
',
584+
array_merge( $meta_search_fields, array( '%' . $wpdb->esc_like( $term ) . '%' ) )
585+
) );
586+
}
576587

577588
return array_unique( array_merge(
578589
$order_ids,
@@ -581,9 +592,9 @@ public function search_orders( $term ) {
581592
$wpdb->prepare( "
582593
SELECT order_id
583594
FROM {$wpdb->prefix}woocommerce_order_items as order_items
584-
WHERE order_item_name LIKE '%%%s%%'
585-
",
586-
$term
595+
WHERE order_item_name LIKE %s
596+
",
597+
'%' . $wpdb->esc_like( $term ) . '%'
587598
)
588599
)
589600
) );

phpunit.xml.dist

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,10 @@
1313
<exclude>tests/test-sample.php</exclude>
1414
</testsuite>
1515
</testsuites>
16+
<filter>
17+
<whitelist>
18+
<directory suffix=".php">includes</directory>
19+
<file>wc-custom-order-table.php</file>
20+
</whitelist>
21+
</filter>
1622
</phpunit>

tests/bootstrap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ function _manually_load_plugin() {
2929

3030
// Start up the WP testing environment.
3131
require $_tests_dir . '/includes/bootstrap.php';
32+
require __DIR__ . '/../vendor/autoload_52.php';
3233
require __DIR__ . '/testcase.php';
3334

3435
/*

tests/test-data-store.php

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
<?php
2+
/**
3+
* Tests for the WC_Order_Data_Store_Custom_Table class.
4+
*
5+
* @package Woocommerce_Order_Tables
6+
* @author Liquid Web
7+
*/
8+
9+
class DataStoreTest extends TestCase {
10+
11+
/**
12+
* Fire the necessary actions to bootstrap WordPress.
13+
*
14+
* @before
15+
*/
16+
public function init() {
17+
do_action( 'init' );
18+
}
19+
20+
/**
21+
* Remove any closures that have been assigned to filters.
22+
*
23+
* @after
24+
*/
25+
public function remove_filter_callbacks() {
26+
remove_all_filters( 'woocommerce_shop_order_search_fields' );
27+
}
28+
29+
public function test_create() {
30+
$instance = new WC_Order_Data_Store_Custom_Table();
31+
$order = $this->factory()->order->create_and_get();
32+
$order_key = 'my_custom_order_key';
33+
34+
add_filter( 'woocommerce_generate_order_key', function () use ( $order_key ) {
35+
return $order_key;
36+
} );
37+
38+
$instance->create( $order );
39+
40+
$this->assertEquals( 'wc_' . $order_key, $order->get_order_key() );
41+
}
42+
43+
public function test_get_order_count() {
44+
$instance = new WC_Order_Data_Store_Custom_Table();
45+
$orders = $this->factory()->order->create_many( 5, array(
46+
'post_status' => 'wc-pending',
47+
) );
48+
49+
$this->assertEquals(
50+
count( $orders ),
51+
$instance->get_order_count( 'wc-pending' )
52+
);
53+
}
54+
55+
public function test_get_order_count_filters_by_status() {
56+
$instance = new WC_Order_Data_Store_Custom_Table();
57+
$this->factory()->order->create( array(
58+
'post_status' => 'not_a_pending_status',
59+
) );
60+
61+
$this->assertEquals(
62+
0,
63+
$instance->get_order_count( 'wc-pending' ),
64+
'The get_order_count() method should only count records matching $status.'
65+
);
66+
}
67+
68+
public function test_get_unpaid_orders() {
69+
$instance = new WC_Order_Data_Store_Custom_Table();
70+
$order = $this->factory()->order->create( array(
71+
'post_status' => 'wc-pending',
72+
) );
73+
$pending = $instance->get_unpaid_orders( time() + DAY_IN_SECONDS );
74+
75+
$this->assertCount( 1, $pending, 'There should be only one unpaid order.' );
76+
$this->assertEquals(
77+
$order,
78+
array_shift( $pending ),
79+
'The ID of the one unpaid order should be that of $order.'
80+
);
81+
}
82+
83+
public function test_get_unpaid_orders_uses_date_filtering() {
84+
$instance = new WC_Order_Data_Store_Custom_Table();
85+
$order = $this->factory()->order->create( array(
86+
'post_status' => 'wc-pending',
87+
) );
88+
$pending = $instance->get_unpaid_orders( time() - HOUR_IN_SECONDS );
89+
90+
$this->assertEmpty( $pending, 'No unpaid orders should match the time window.' );
91+
}
92+
93+
public function test_search_orders_can_search_by_order_id() {
94+
$instance = new WC_Order_Data_Store_Custom_Table();
95+
96+
$this->assertEquals(
97+
array( 123 ),
98+
$instance->search_orders( 123 ),
99+
'When given a numeric value, search_orders() should include that order ID.'
100+
);
101+
}
102+
103+
public function test_search_orders_can_check_post_meta() {
104+
$instance = new WC_Order_Data_Store_Custom_Table();
105+
$order = $this->factory()->order->create();
106+
$term = uniqid( 'search term ' );
107+
108+
add_post_meta( $order, 'some_custom_meta_key', $term );
109+
110+
add_filter( 'woocommerce_shop_order_search_fields', function () {
111+
return array( 'some_custom_meta_key' );
112+
} );
113+
114+
$this->assertEquals(
115+
array( $order ),
116+
$instance->search_orders( $term ),
117+
'If post meta keys are specified, they should also be searched.'
118+
);
119+
}
120+
121+
/**
122+
* Same as test_search_orders_can_check_post_meta(), but the filter is never added.
123+
*/
124+
public function test_search_orders_only_checks_post_meta_if_specified() {
125+
$instance = new WC_Order_Data_Store_Custom_Table();
126+
$order = $this->factory()->order->create();
127+
$term = uniqid( 'search term ' );
128+
129+
add_post_meta( $order, 'some_custom_meta_key', $term );
130+
131+
$this->assertEmpty(
132+
$instance->search_orders( $term ),
133+
'Only search post meta if keys are provided.'
134+
);
135+
}
136+
137+
public function test_search_orders_checks_table_for_product_item_matches() {
138+
$instance = new WC_Order_Data_Store_Custom_Table();
139+
$product = $this->factory()->product->create_and_get();
140+
$order = $this->factory()->order->create_and_get();
141+
$order->add_product( $product );
142+
$order->save();
143+
144+
$this->assertEquals(
145+
array( $order->get_id() ),
146+
$instance->search_orders( $product->get_name() ),
147+
'Order searches should extend to the names of product items.'
148+
);
149+
}
150+
151+
public function test_search_orders_checks_table_for_product_item_matches_with_like_comparison() {
152+
$instance = new WC_Order_Data_Store_Custom_Table();
153+
$product = $this->factory()->product->create_and_get( array(
154+
'post_title' => 'foo bar baz',
155+
) );
156+
$order = $this->factory()->order->create_and_get();
157+
$order->add_product( $product );
158+
$order->save();
159+
160+
$this->assertEquals(
161+
array( $order->get_id() ),
162+
$instance->search_orders( 'bar' ),
163+
'Product items should be searched using a LIKE comparison and wildcards.'
164+
);
165+
}
166+
167+
/**
168+
* @dataProvider order_type_provider()
169+
*/
170+
public function test_get_order_type( $order_type ) {
171+
$instance = new WC_Order_Data_Store_Custom_Table();
172+
$order = $this->factory()->order->create( array(
173+
'post_type' => $order_type,
174+
) );
175+
176+
$this->assertEquals(
177+
$order_type,
178+
$instance->get_order_type( $order )
179+
);
180+
}
181+
182+
/**
183+
* Provide a list of all available order types.
184+
*/
185+
public function order_type_provider() {
186+
$types = array();
187+
188+
foreach ( wc_get_order_types() as $type ) {
189+
$types[ $type ] = array( $type );
190+
}
191+
192+
return $types;
193+
}
194+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/**
4+
* Unit test factory for customers.
5+
*
6+
* Note: The below @method notations are defined solely for the benefit of IDEs,
7+
* as a way to indicate expected return values from the given factory methods.
8+
*
9+
* @method int create( $args = array(), $generation_definitions = null )
10+
* @method WP_User create_and_get( $args = array(), $generation_definitions = null )
11+
* @method int[] create_many( $count, $args = array(), $generation_definitions = null )
12+
*/
13+
class WP_UnitTest_Factory_For_Customer extends WP_UnitTest_Factory_For_User {
14+
15+
function __construct( $factory = null ) {
16+
parent::__construct( $factory );
17+
$this->default_generation_definitions = array(
18+
'user_login' => new WP_UnitTest_Generator_Sequence( 'Customer %s' ),
19+
'user_pass' => 'password',
20+
'user_email' => new WP_UnitTest_Generator_Sequence( 'customer_%[email protected]' ),
21+
);
22+
}
23+
24+
function get_object_by_id( $user_id ) {
25+
return new WC_Customer( $user_id );
26+
}
27+
}

0 commit comments

Comments
 (0)