Skip to content

Commit 6ccf895

Browse files
authored
Add ability to delete test orders. (#11103)
1 parent 517cbbd commit 6ccf895

File tree

3 files changed

+289
-15
lines changed

3 files changed

+289
-15
lines changed

changelog/dev-delete-test-orders

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Significance: minor
2+
Type: add
3+
4+
Add WooCommerce Tool to delete test orders.

includes/class-wc-payments-status.php

Lines changed: 81 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -63,21 +63,87 @@ public function init_hooks() {
6363
* @param array $tools List of current available tools.
6464
*/
6565
public function debug_tools( $tools ) {
66-
$tools['clear_wcpay_account_cache'] = [
67-
'name' => sprintf(
68-
/* translators: %s: WooPayments */
69-
__( 'Clear %s account cache', 'woocommerce-payments' ),
70-
'WooPayments'
71-
),
72-
'button' => __( 'Clear', 'woocommerce-payments' ),
73-
'desc' => sprintf(
74-
/* translators: %s: WooPayments */
75-
__( 'This tool will clear the account cached values used in %s.', 'woocommerce-payments' ),
76-
'WooPayments'
77-
),
78-
'callback' => [ $this->account, 'refresh_account_data' ],
79-
];
80-
return $tools;
66+
return array_merge(
67+
$tools,
68+
[
69+
'clear_wcpay_account_cache' => [
70+
'name' => sprintf(
71+
/* translators: %s: WooPayments */
72+
__( 'Clear %s account cache', 'woocommerce-payments' ),
73+
'WooPayments'
74+
),
75+
'button' => __( 'Clear', 'woocommerce-payments' ),
76+
'desc' => sprintf(
77+
/* translators: %s: WooPayments */
78+
__( 'This tool will clear the cached account values used in %s.', 'woocommerce-payments' ),
79+
'WooPayments'
80+
),
81+
'callback' => [ $this->account, 'refresh_account_data' ],
82+
],
83+
'delete_wcpay_test_orders' => [
84+
'name' => __( 'Delete test orders', 'woocommerce-payments' ),
85+
'button' => __( 'Delete', 'woocommerce-payments' ),
86+
'desc' => sprintf(
87+
/* translators: %s: WooPayments */
88+
__( '<strong class="red">Note:</strong> This option will delete ALL orders created while %s test mode was enabled, use with caution. This action cannot be reversed.', 'woocommerce-payments' ),
89+
'WooPayments'
90+
),
91+
'callback' => [ $this, 'delete_test_orders' ],
92+
],
93+
]
94+
);
95+
}
96+
97+
/**
98+
* Deletes all test orders created with WooPayments.
99+
*
100+
* Test orders are identified by the '_wcpay_mode' meta key with value 'test'.
101+
*
102+
* @return string Success or error message.
103+
*/
104+
public function delete_test_orders() {
105+
try {
106+
// Get all orders with test mode meta.
107+
$test_orders = wc_get_orders(
108+
[
109+
'limit' => -1,
110+
// phpcs:ignore
111+
'meta_key' => WC_Payments_Order_Service::WCPAY_MODE_META_KEY,
112+
// phpcs:ignore
113+
'meta_value' => 'test',
114+
'return' => 'objects',
115+
]
116+
);
117+
118+
if ( empty( $test_orders ) ) {
119+
return __( 'No test orders found.', 'woocommerce-payments' );
120+
}
121+
122+
$deleted_count = 0;
123+
foreach ( $test_orders as $order ) {
124+
// Permanently delete the order (skip trash).
125+
if ( $order->delete() ) {
126+
++$deleted_count;
127+
}
128+
}
129+
130+
return sprintf(
131+
/* translators: %d: number of orders deleted */
132+
_n(
133+
'%d test order has been permanently deleted.',
134+
'%d test orders have been permanently deleted.',
135+
$deleted_count,
136+
'woocommerce-payments'
137+
),
138+
$deleted_count
139+
);
140+
} catch ( Exception $e ) {
141+
return sprintf(
142+
/* translators: %s: error message */
143+
__( 'Error deleting test orders: %s', 'woocommerce-payments' ),
144+
$e->getMessage()
145+
);
146+
}
81147
}
82148

83149
/**
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
<?php
2+
/**
3+
* Class WC_Payments_Status_Test
4+
*
5+
* @package WooCommerce\Payments\Tests
6+
*/
7+
8+
/**
9+
* WC_Payments_Status unit tests.
10+
*/
11+
class WC_Payments_Status_Test extends WCPAY_UnitTestCase {
12+
/**
13+
* System under test.
14+
*
15+
* @var WC_Payments_Status
16+
*/
17+
private $status;
18+
19+
/**
20+
* Mock gateway.
21+
*
22+
* @var WC_Payment_Gateway_WCPay|PHPUnit_Framework_MockObject_MockObject
23+
*/
24+
private $mock_gateway;
25+
26+
/**
27+
* Mock HTTP client.
28+
*
29+
* @var WC_Payments_Http_Interface|PHPUnit_Framework_MockObject_MockObject
30+
*/
31+
private $mock_http;
32+
33+
/**
34+
* Mock account service.
35+
*
36+
* @var WC_Payments_Account|PHPUnit_Framework_MockObject_MockObject
37+
*/
38+
private $mock_account;
39+
40+
/**
41+
* Pre-test setup
42+
*/
43+
public function set_up() {
44+
parent::set_up();
45+
46+
$this->mock_gateway = $this->createMock( WC_Payment_Gateway_WCPay::class );
47+
$this->mock_http = $this->createMock( WC_Payments_Http_Interface::class );
48+
$this->mock_account = $this->createMock( WC_Payments_Account::class );
49+
50+
$this->status = new WC_Payments_Status(
51+
$this->mock_gateway,
52+
$this->mock_http,
53+
$this->mock_account
54+
);
55+
}
56+
57+
/**
58+
* Test that debug_tools filter adds both tools.
59+
*/
60+
public function test_debug_tools_adds_wcpay_tools() {
61+
$tools = $this->status->debug_tools( [] );
62+
63+
$this->assertArrayHasKey( 'clear_wcpay_account_cache', $tools );
64+
$this->assertArrayHasKey( 'delete_wcpay_test_orders', $tools );
65+
}
66+
67+
/**
68+
* Test that the clear cache tool has correct structure.
69+
*/
70+
public function test_clear_cache_tool_structure() {
71+
$tools = $this->status->debug_tools( [] );
72+
73+
$clear_cache_tool = $tools['clear_wcpay_account_cache'];
74+
75+
$this->assertArrayHasKey( 'name', $clear_cache_tool );
76+
$this->assertArrayHasKey( 'button', $clear_cache_tool );
77+
$this->assertArrayHasKey( 'desc', $clear_cache_tool );
78+
$this->assertArrayHasKey( 'callback', $clear_cache_tool );
79+
80+
$this->assertStringContainsString( 'Clear', $clear_cache_tool['name'] );
81+
$this->assertEquals( 'Clear', $clear_cache_tool['button'] );
82+
}
83+
84+
/**
85+
* Test that the delete test orders tool has correct structure.
86+
*/
87+
public function test_delete_test_orders_tool_structure() {
88+
$tools = $this->status->debug_tools( [] );
89+
90+
$delete_tool = $tools['delete_wcpay_test_orders'];
91+
92+
$this->assertArrayHasKey( 'name', $delete_tool );
93+
$this->assertArrayHasKey( 'button', $delete_tool );
94+
$this->assertArrayHasKey( 'desc', $delete_tool );
95+
$this->assertArrayHasKey( 'callback', $delete_tool );
96+
97+
$this->assertEquals( 'Delete test orders', $delete_tool['name'] );
98+
$this->assertEquals( 'Delete', $delete_tool['button'] );
99+
$this->assertStringContainsString( 'Note:', $delete_tool['desc'] );
100+
$this->assertStringContainsString( 'strong class="red"', $delete_tool['desc'] );
101+
$this->assertStringContainsString( 'delete ALL orders created while', $delete_tool['desc'] );
102+
$this->assertStringContainsString( 'test mode was enabled', $delete_tool['desc'] );
103+
$this->assertStringContainsString( 'cannot be reversed', $delete_tool['desc'] );
104+
}
105+
106+
/**
107+
* Test delete_test_orders when no test orders exist.
108+
*/
109+
public function test_delete_test_orders_with_no_orders() {
110+
// Mock wc_get_orders to return empty array.
111+
add_filter(
112+
'woocommerce_order_data_store_cpt_get_orders_query',
113+
function ( $query, $query_vars ) {
114+
if ( isset( $query_vars['meta_key'] ) && '_wcpay_mode' === $query_vars['meta_key'] ) {
115+
$query['post__in'] = [ 0 ]; // Force no results.
116+
}
117+
return $query;
118+
},
119+
10,
120+
2
121+
);
122+
123+
$result = $this->status->delete_test_orders();
124+
125+
$this->assertEquals( 'No test orders found.', $result );
126+
}
127+
128+
/**
129+
* Test delete_test_orders successfully deletes test orders.
130+
*/
131+
public function test_delete_test_orders_deletes_orders() {
132+
// Create test orders with _wcpay_mode meta.
133+
$order1 = wc_create_order();
134+
$order1->update_meta_data( '_wcpay_mode', 'test' );
135+
$order1->save();
136+
137+
$order2 = wc_create_order();
138+
$order2->update_meta_data( '_wcpay_mode', 'test' );
139+
$order2->save();
140+
141+
// Create a non-test order that should not be deleted.
142+
$order3 = wc_create_order();
143+
$order3->update_meta_data( '_wcpay_mode', 'live' );
144+
$order3->save();
145+
146+
$result = $this->status->delete_test_orders();
147+
148+
// Verify result message.
149+
$this->assertStringContainsString( '2 test orders have been permanently deleted.', $result );
150+
151+
// Verify test orders were moved to trash.
152+
$trashed_order1 = wc_get_order( $order1->get_id() );
153+
$this->assertInstanceOf( WC_Order::class, $trashed_order1 );
154+
$this->assertEquals( 'trash', $trashed_order1->get_status() );
155+
156+
$trashed_order2 = wc_get_order( $order2->get_id() );
157+
$this->assertInstanceOf( WC_Order::class, $trashed_order2 );
158+
$this->assertEquals( 'trash', $trashed_order2->get_status() );
159+
160+
// Verify non-test order was not deleted.
161+
$order3_check = wc_get_order( $order3->get_id() );
162+
$this->assertInstanceOf( WC_Order::class, $order3_check );
163+
$this->assertNotEquals( 'trash', $order3_check->get_status() );
164+
}
165+
166+
/**
167+
* Test delete_test_orders with single order uses singular message.
168+
*/
169+
public function test_delete_test_orders_singular_message() {
170+
// Create single test order.
171+
$order = wc_create_order();
172+
$order->update_meta_data( '_wcpay_mode', 'test' );
173+
$order->save();
174+
175+
$result = $this->status->delete_test_orders();
176+
177+
$this->assertStringContainsString( '1 test order has been permanently deleted.', $result );
178+
179+
// Verify order was moved to trash.
180+
$trashed_order = wc_get_order( $order->get_id() );
181+
$this->assertInstanceOf( WC_Order::class, $trashed_order );
182+
$this->assertEquals( 'trash', $trashed_order->get_status() );
183+
}
184+
185+
/**
186+
* Test delete_test_orders handles exceptions.
187+
*/
188+
public function test_delete_test_orders_handles_exception() {
189+
// Mock wc_get_orders to throw an exception.
190+
add_filter(
191+
'woocommerce_order_data_store_cpt_get_orders_query',
192+
function ( $query, $query_vars ) {
193+
throw new Exception( 'Database error' );
194+
},
195+
10,
196+
2
197+
);
198+
199+
$result = $this->status->delete_test_orders();
200+
201+
$this->assertStringContainsString( 'Error deleting test orders:', $result );
202+
$this->assertStringContainsString( 'Database error', $result );
203+
}
204+
}

0 commit comments

Comments
 (0)