From 867daaf328ba5c94290c2d6f97ed025ba414db41 Mon Sep 17 00:00:00 2001 From: Joel Male Date: Wed, 27 May 2026 21:23:39 +1000 Subject: [PATCH] feat: add Coupon base class --- src/Coupons/Coupon.php | 53 +++++++++++++++++++++++ tests/Unit/Coupons/CouponTest.php | 72 +++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 src/Coupons/Coupon.php create mode 100644 tests/Unit/Coupons/CouponTest.php diff --git a/src/Coupons/Coupon.php b/src/Coupons/Coupon.php new file mode 100644 index 0000000..3303374 --- /dev/null +++ b/src/Coupons/Coupon.php @@ -0,0 +1,53 @@ +code; + } + + public function toCondition(): CartCondition + { + if (! $this->isValid()) { + throw new InvalidCouponException("Coupon [{$this->code}] is not valid."); + } + + $data = [ + 'name' => $this->code, + 'type' => 'coupon', + 'value' => $this->value, + 'target' => $this->target, + ]; + + if ($this->minimum !== null) { + $data['minimum'] = $this->minimum; + } + + if ($this->maximum !== null) { + $data['maximum'] = $this->maximum; + } + + return new CartCondition($data); + } +} diff --git a/tests/Unit/Coupons/CouponTest.php b/tests/Unit/Coupons/CouponTest.php new file mode 100644 index 0000000..120665e --- /dev/null +++ b/tests/Unit/Coupons/CouponTest.php @@ -0,0 +1,72 @@ +toCondition(); + + expect($condition)->toBeInstanceOf(CartCondition::class); + expect($condition->getName())->toBe('SAVE10'); + expect($condition->getValue())->toBe('-10%'); + expect($condition->getType())->toBe('coupon'); + expect($condition->getTarget())->toBe('subtotal'); + }); + + test('respects custom target', function () { + $coupon = new class extends Coupon { + protected string $code = 'TOTAL10'; + protected string $value = '-10%'; + protected string $target = 'total'; + public function isValid(): bool { return true; } + }; + + expect($coupon->toCondition()->getTarget())->toBe('total'); + }); + + test('includes minimum in condition when set', function () { + $coupon = new class extends Coupon { + protected string $code = 'BIG10'; + protected string $value = '-10%'; + protected ?float $minimum = 100.0; + public function isValid(): bool { return true; } + }; + + expect($coupon->toCondition()->getMinimum())->toBe(100.0); + }); + + test('isValid() returning false causes toCondition() to throw', function () { + expect(fn() => (new NeverValidCoupon)->toCondition()) + ->toThrow(InvalidCouponException::class); + }); + + test('getCode returns the coupon code', function () { + expect((new AlwaysValidCoupon)->getCode())->toBe('SAVE10'); + }); +});