From 2513e07436c90569ef42ab230ff631875ed7da43 Mon Sep 17 00:00:00 2001 From: Joel Male Date: Wed, 27 May 2026 21:24:44 +1000 Subject: [PATCH] feat: add ShippingRate base class --- src/Shipping/ShippingRate.php | 48 +++++++++++++++++ tests/Unit/Shipping/ShippingRateTest.php | 68 ++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 src/Shipping/ShippingRate.php create mode 100644 tests/Unit/Shipping/ShippingRateTest.php diff --git a/src/Shipping/ShippingRate.php b/src/Shipping/ShippingRate.php new file mode 100644 index 0000000..3baa6f1 --- /dev/null +++ b/src/Shipping/ShippingRate.php @@ -0,0 +1,48 @@ +isApplicable()) { + throw new InvalidShippingRateException("Shipping rate [{$this->name}] is not applicable."); + } + + $data = [ + 'name' => $this->name, + 'type' => 'shipping', + '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/Shipping/ShippingRateTest.php b/tests/Unit/Shipping/ShippingRateTest.php new file mode 100644 index 0000000..918925a --- /dev/null +++ b/tests/Unit/Shipping/ShippingRateTest.php @@ -0,0 +1,68 @@ +toCondition(); + + expect($condition)->toBeInstanceOf(CartCondition::class); + expect($condition->getName())->toBe('Standard Shipping'); + expect($condition->getValue())->toBe('+10'); + expect($condition->getType())->toBe('shipping'); + expect($condition->getTarget())->toBe('total'); + }); + + test('isApplicable() returning false causes toCondition() to throw', function () { + expect(fn() => (new FreeShipping)->toCondition()) + ->toThrow(InvalidShippingRateException::class); + }); + + test('supports subtotal as target', function () { + $rate = new class extends ShippingRate { + protected string $name = 'Express'; + protected string $value = '+25'; + protected string $target = 'subtotal'; + public function isApplicable(): bool { return true; } + }; + + expect($rate->toCondition()->getTarget())->toBe('subtotal'); + }); + + test('supports maximum free shipping threshold', function () { + $rate = new class extends ShippingRate { + protected string $name = 'Conditional Shipping'; + protected string $value = '+15'; + protected ?float $maximum = 100.0; + public function isApplicable(): bool { return true; } + }; + + expect($rate->toCondition()->getMaximum())->toBe(100.0); + }); +});