Evolve the member discount into a sitewide stepped promo
Replaces the flat "$10 off $100+, members only" discount with $5 off per $50 spent, capped at $200 spend ($20 max) — an evolution of the same idea, not an addition to it. Now sitewide by default: the guest tier carries the same step_discount benefit as free_member, so anonymous shoppers get it too, not just members. Toggling it on/off, or switching between sitewide and members-only, needs no code change — an admin unchecks the benefit on one tier's row in the settings screen. Migration (schema v3->v4) overwrites guest/free_member's benefits to the new shape directly rather than merging, per the established pre-launch convention (no real customer/tier data exists to preserve yet). Live-verified beyond the unit suite: the full discount table checked against the real DB for both a guest and a real member, and the sitewide/members-only toggle exercised through a real authenticated settings-screen form POST (not just read back from code). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MqhgnCRjjVLFnZ3Jpn93Vg
This commit is contained in:
1 parent
13e7362579
commit
773883a0f3
7 files changed
+122
-62
No files matched your search
@@ -8,11 +8,14 @@ use Brain\Monkey\Functions;
|
||||
use Mockery;
|
||||
|
||||
/**
|
||||
* "$10 off orders $100+, members only" (design doc §07 extension). The
|
||||
* real Membership\Tier class runs unmocked — only $wpdb (underneath
|
||||
* Tier's queries), get_current_user_id(), and a WC_Cart mock are
|
||||
* stubbed, so these tests exercise the actual threshold/amount lookup,
|
||||
* not just a canned discount value.
|
||||
* The stepped, sitewide promo: $5 off per $50 spent, capped at $200
|
||||
* spend for a max $20 off (evolved from an earlier flat "$10 off $100+,
|
||||
* members only" version — design doc §07 extension). "Sitewide" means
|
||||
* both `guest` and `free_member` carry the same `step_discount` benefit
|
||||
* by default, so a guest (user_id 0) getting the discount is the thing
|
||||
* that specifically proves this isn't members-only anymore. The real
|
||||
* Membership\Tier class runs unmocked — only $wpdb (underneath Tier's
|
||||
* queries), get_current_user_id(), and a WC_Cart mock are stubbed.
|
||||
*/
|
||||
class MemberDiscountTest extends TestCase
|
||||
{
|
||||
@@ -23,40 +26,52 @@ class MemberDiscountTest extends TestCase
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function test_guest_gets_no_discount(): void
|
||||
{
|
||||
Functions\when('get_current_user_id')->justReturn(0);
|
||||
// Guest tier has no spend_discount benefit configured.
|
||||
$this->stub_wpdb_anonymous(['free_shipping_threshold' => ['threshold' => 25.00]]);
|
||||
|
||||
$cart = Mockery::mock(\WC_Cart::class);
|
||||
$cart->shouldReceive('get_subtotal')->andReturn('500.00');
|
||||
$cart->shouldNotReceive('add_fee');
|
||||
|
||||
MemberDiscount::apply($cart);
|
||||
}
|
||||
|
||||
public function test_member_below_min_spend_gets_no_discount(): void
|
||||
/**
|
||||
* @dataProvider subtotalCases
|
||||
*/
|
||||
public function test_member_gets_the_stepped_discount(float $subtotal, float $expected_discount): void
|
||||
{
|
||||
Functions\when('get_current_user_id')->justReturn(7);
|
||||
$this->stub_wpdb_with_member_lookup(5, [
|
||||
'free_shipping_threshold' => ['threshold' => 15.00],
|
||||
'spend_discount' => ['min_spend' => 100.00, 'discount_amount' => 10.00],
|
||||
'step_discount' => ['step_amount' => 50, 'discount_per_step' => 5, 'max_spend' => 200],
|
||||
]);
|
||||
|
||||
$cart = Mockery::mock(\WC_Cart::class);
|
||||
$cart->shouldReceive('get_subtotal')->andReturn('80.00');
|
||||
$cart->shouldNotReceive('add_fee');
|
||||
$cart->shouldReceive('get_subtotal')->andReturn((string) $subtotal);
|
||||
|
||||
if ($expected_discount > 0) {
|
||||
$cart->shouldReceive('add_fee')->once()->with('Member discount', -1 * $expected_discount, false);
|
||||
} else {
|
||||
$cart->shouldNotReceive('add_fee');
|
||||
}
|
||||
|
||||
MemberDiscount::apply($cart);
|
||||
}
|
||||
|
||||
public function test_member_at_min_spend_gets_the_discount(): void
|
||||
public static function subtotalCases(): array
|
||||
{
|
||||
Functions\when('get_current_user_id')->justReturn(7);
|
||||
$this->stub_wpdb_with_member_lookup(5, [
|
||||
'free_shipping_threshold' => ['threshold' => 15.00],
|
||||
'spend_discount' => ['min_spend' => 100.00, 'discount_amount' => 10.00],
|
||||
return [
|
||||
'nothing in cart' => [0.00, 0.00],
|
||||
'below the first step' => [30.00, 0.00],
|
||||
'exactly the first step' => [50.00, 5.00],
|
||||
'just under the second step' => [99.99, 5.00],
|
||||
'exactly the second step' => [100.00, 10.00],
|
||||
'exactly the third step' => [150.00, 15.00],
|
||||
'just under the cap' => [199.99, 15.00],
|
||||
'exactly at the cap' => [200.00, 20.00],
|
||||
'above the cap stays capped' => [250.00, 20.00],
|
||||
];
|
||||
}
|
||||
|
||||
public function test_guest_gets_the_discount_too_sitewide_by_default(): void
|
||||
{
|
||||
Functions\when('get_current_user_id')->justReturn(0);
|
||||
// guest tier carries the same step_discount benefit as
|
||||
// free_member by default -- this is what makes it sitewide.
|
||||
$this->stub_wpdb_anonymous([
|
||||
'free_shipping_threshold' => ['threshold' => 25.00],
|
||||
'step_discount' => ['step_amount' => 50, 'discount_per_step' => 5, 'max_spend' => 200],
|
||||
]);
|
||||
|
||||
$cart = Mockery::mock(\WC_Cart::class);
|
||||
@@ -66,16 +81,19 @@ class MemberDiscountTest extends TestCase
|
||||
MemberDiscount::apply($cart);
|
||||
}
|
||||
|
||||
public function test_member_above_min_spend_gets_the_discount(): void
|
||||
public function test_guest_gets_nothing_when_admin_disables_it_on_the_guest_tier(): void
|
||||
{
|
||||
Functions\when('get_current_user_id')->justReturn(7);
|
||||
$this->stub_wpdb_with_member_lookup(5, [
|
||||
'spend_discount' => ['min_spend' => 100.00, 'discount_amount' => 10.00],
|
||||
Functions\when('get_current_user_id')->justReturn(0);
|
||||
// Admin unchecked step_discount on the guest row via the
|
||||
// settings screen -- members-only mode, proven by the absence
|
||||
// of the benefit key here (not a hardcoded "guest excluded").
|
||||
$this->stub_wpdb_anonymous([
|
||||
'free_shipping_threshold' => ['threshold' => 25.00],
|
||||
]);
|
||||
|
||||
$cart = Mockery::mock(\WC_Cart::class);
|
||||
$cart->shouldReceive('get_subtotal')->andReturn('142.50');
|
||||
$cart->shouldReceive('add_fee')->once()->with('Member discount', -10.00, false);
|
||||
$cart->shouldReceive('get_subtotal')->andReturn('250.00');
|
||||
$cart->shouldNotReceive('add_fee');
|
||||
|
||||
MemberDiscount::apply($cart);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user