Skip to content

Prevent the order count transient from being set before $wc_order_types is populated #52

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"@test:standards",
"@test:analysis"
],
"test:analysis": "vendor/bin/phpstan analyze",
"test:analysis": "vendor/bin/phpstan analyze -c phpstan.neon.dist",
"test:standards": "vendor/bin/phpcs",
"test:unit": "vendor/bin/phpunit --testdox --color=always"
},
Expand Down
8 changes: 7 additions & 1 deletion limit-orders.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,13 @@
} );

// Initialize the plugin.
add_action( 'woocommerce_loaded', function () {
add_action( 'init', function () {

// Abort if WooCommerce hasn't loaded.
if ( ! did_action( 'woocommerce_loaded' ) ) {
return;
}

$limiter = new OrderLimiter();
$admin = new Admin( $limiter );

Expand Down
12 changes: 12 additions & 0 deletions src/Exceptions/EmptyOrderTypesException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php
/**
* Thrown when wc_get_order_types() returns empty.
*
* @package Nexcess\LimitOrders
*/

namespace Nexcess\LimitOrders\Exceptions;

class EmptyOrderTypesException extends \Exception {

}
19 changes: 16 additions & 3 deletions src/OrderLimiter.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Nexcess\LimitOrders;

use Nexcess\LimitOrders\Exceptions\EmptyOrderTypesException;
use Nexcess\LimitOrders\Exceptions\OrdersNotAcceptedException;

class OrderLimiter {
Expand Down Expand Up @@ -367,7 +368,13 @@ public function customer_notice() {
* @return int The number of qualifying orders.
*/
public function regenerate_transient() {
$count = $this->count_qualifying_orders();
try {
$count = $this->count_qualifying_orders();
} catch ( EmptyOrderTypesException $e ) {
// Return 0 for now but try to populate this transient later, after $wc_order_types has been populated.
add_action( 'init', [ $this, 'regenerate_transient' ] );
return 0;
}

set_transient( self::TRANSIENT_NAME, $count, $this->get_seconds_until_next_interval() );

Expand Down Expand Up @@ -402,7 +409,7 @@ protected function count_qualifying_orders() {
/**
* Replace the logic used to count qualified orders.
*
* @param bool $preempt Whether the counting logic should be preempted. Returning
* @param bool $preempt Whether the counting logic should be preempted. Returning
* anything but FALSE will bypass the default logic.
* @param OrderLimiter $limiter The current OrderLimiter instance.
*/
Expand All @@ -412,8 +419,14 @@ protected function count_qualifying_orders() {
return (int) $count;
}

$order_types = wc_get_order_types( 'order-count' );

if ( empty( $order_types ) ) {
throw new EmptyOrderTypesException( 'No order types were found.' );
}

$orders = wc_get_orders( [
'type' => wc_get_order_types( 'order-count' ),
'type' => $order_types,
'date_created' => '>=' . $this->get_interval_start()->getTimestamp(),
'return' => 'ids',
'limit' => max( $this->get_limit(), 1000 ),
Expand Down
55 changes: 52 additions & 3 deletions tests/OrderLimiterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Tests;

use Nexcess\LimitOrders\Exceptions\EmptyOrderTypesException;
use Nexcess\LimitOrders\OrderLimiter;
use WC_Helper_Product;

Expand Down Expand Up @@ -698,9 +699,7 @@ public function has_orders_in_current_interval_should_compare_the_limit_to_remai
$limiter->init();

$this->assertFalse( $limiter->has_orders_in_current_interval() );

$this->generate_order();

$this->set_current_order_count( 1 );
$this->assertTrue( $limiter->has_orders_in_current_interval() );
}

Expand Down Expand Up @@ -888,6 +887,35 @@ public function regenerate_transient_should_create_the_site_transient() {
$this->assertSame( 3, get_transient( OrderLimiter::TRANSIENT_NAME ) );
}

/**
* @test
* @ticket https://github.com/nexcess/limit-orders/issues/48
*/
public function regenerate_transient_should_return_zero_if_no_count_is_available() {
update_option( OrderLimiter::OPTION_KEY, [
'enabled' => true,
'interval' => 'daily',
'limit' => 5,
] );

$this->assertFalse( get_transient( OrderLimiter::TRANSIENT_NAME ) );

$limiter = $this->getMockBuilder( OrderLimiter::class )
->setMethods( [ 'count_qualifying_orders' ] )
->getMock();
$limiter->expects( $this->once() )
->method( 'count_qualifying_orders' )
->will( $this->throwException( new EmptyOrderTypesException( 'No order types were found.' ) ) );

$this->assertSame( 0, $limiter->regenerate_transient() );
$this->assertFalse( get_transient( OrderLimiter::TRANSIENT_NAME ) );
$this->assertGreaterThan(
5,
has_action( 'init', [ $limiter, 'regenerate_transient' ] ),
'The function should attempt to save itself by hooking in after $wc_order_types is populated (init:5).'
);
}

/**
* @test
*/
Expand Down Expand Up @@ -1014,6 +1042,27 @@ public function count_qualifying_orders_should_be_filterable() {
$this->assertTrue( $called );
}

/**
* @test
* @ticket https://github.com/nexcess/limit-orders/issues/48
*/
public function count_qualifying_orders_should_throw_an_EmptyOrderTypesException_if_order_types_are_empty() {
global $wc_order_types;

$wc_order_types = [];
$instance = new OrderLimiter();
$method = new \ReflectionMethod( $instance, 'count_qualifying_orders' );
$method->setAccessible( true );

update_option( OrderLimiter::OPTION_KEY, [
'enabled' => true,
'limit' => 1,
] );

$this->expectException( EmptyOrderTypesException::class );
$method->invoke( $instance );
}

/**
* @test
* @testdox Return values from the limit_orders_pre_count_qualifying_orders filter should be cast as integers
Expand Down
4 changes: 2 additions & 2 deletions tests/SettingsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@ public function a_notice_should_be_shown_if_there_have_been_orders_in_the_curren

$limiter = new OrderLimiter();
$limiter->init();

$this->generate_order();
$this->set_current_order_count( 1 );

$this->assertContains(
'<div class="notice notice-info">',
Expand All @@ -110,6 +109,7 @@ public function a_notice_should_not_be_shown_if_there_have_not_been_any_orders_i

$limiter = new OrderLimiter();
$limiter->init();
$this->set_current_order_count( 0 );

$this->assertNotContains(
'<div class="notice notice-info">',
Expand Down
10 changes: 10 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Tests;

use Nexcess\LimitOrders\OrderLimiter;
use WC_Checkout;
use WC_Helper_Product;
use WP_UnitTestCase;
Expand All @@ -31,4 +32,13 @@ protected function generate_order() {
'payment_method' => 'dummy_payment_gateway',
] );
}

/**
* Explicitly set the current interval's order count.
*
* @param int $orders The current number of orders.
*/
protected function set_current_order_count( $orders ) {
set_transient( OrderLimiter::TRANSIENT_NAME, (int) $orders );
}
}