diff --git a/tests/Stubs/Lunar/TestTaxDriver.php b/tests/Stubs/Lunar/TestTaxDriver.php new file mode 100644 index 0000000..fd92022 --- /dev/null +++ b/tests/Stubs/Lunar/TestTaxDriver.php @@ -0,0 +1,119 @@ +shippingAddress = $address; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setCurrency(Currency $currency): self + { + $this->currency = $currency; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setBillingAddress(?Addressable $address = null): self + { + $this->billingAddress = $address; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function setPurchasable(Purchasable $purchasable): self + { + $this->purchasable = $purchasable; + + return $this; + } + + /** + * Set the cart line. + */ + public function setCartLine(CartLine $cartLine): self + { + $this->cartLine = $cartLine; + + return $this; + } + + /** + * {@inheritDoc} + */ + public function getBreakdown($subTotal): TaxBreakdown + { + $breakdown = new TaxBreakdown; + + $currency = Currency::first() ?: Currency::factory()->create(); + + $taxAmount = TaxRateAmount::factory()->create(); + + $result = round($subTotal * ($taxAmount->percentage / 100)); + + $variant = ProductVariant::factory()->create(); + + $amount = new TaxBreakdownAmount( + price: new Price((int) $result, $currency, $variant->getUnitQuantity()), + description: $taxAmount->taxRate->name, + identifier: "tax_rate_{$taxAmount->taxRate->id}", + percentage: $taxAmount->percentage + ); + + $breakdown->addAmount($amount); + + return $breakdown; + } +} diff --git a/tests/Stubs/Lunar/TestUrlGenerator.php b/tests/Stubs/Lunar/TestUrlGenerator.php new file mode 100644 index 0000000..663d610 --- /dev/null +++ b/tests/Stubs/Lunar/TestUrlGenerator.php @@ -0,0 +1,23 @@ + 'datetime', + ]; + + /** + * Return a new factory instance for the model. + */ + protected static function newFactory(): UserFactory + { + return UserFactory::new(); + } +} diff --git a/tests/Stubs/Users/UserFactory.php b/tests/Stubs/Users/UserFactory.php new file mode 100644 index 0000000..52d52c6 --- /dev/null +++ b/tests/Stubs/Users/UserFactory.php @@ -0,0 +1,48 @@ + + */ +class UserFactory extends Factory +{ + /** + * The name of the factory's corresponding model. + * + * @var string + */ + protected $model = User::class; + + /** + * Define the model's default state. + */ + public function definition(): array + { + return [ + 'name' => $this->faker->name(), + 'email' => $this->faker->unique()->safeEmail(), + 'email_verified_at' => now(), + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password + 'remember_token' => Str::random(10), + ]; + } + + /** + * Indicate that the model's email address should be unverified. + * + * @return Factory + */ + public function unverified(): UserFactory + { + return $this->state(function (array $attributes) { + return [ + 'email_verified_at' => null, + ]; + }); + } +} diff --git a/tests/Stubs/Users/UserSchema.php b/tests/Stubs/Users/UserSchema.php new file mode 100644 index 0000000..6315c6a --- /dev/null +++ b/tests/Stubs/Users/UserSchema.php @@ -0,0 +1,32 @@ +make(TestTaxDriver::class); + }); activity()->disableLogging(); } diff --git a/tests/Traits/CreatesTestingModels.php b/tests/Traits/CreatesTestingModels.php new file mode 100644 index 0000000..d4636ab --- /dev/null +++ b/tests/Traits/CreatesTestingModels.php @@ -0,0 +1,182 @@ + $models + */ + public function createCart(...$models): Cart + { + $customerGroup = CustomerGroup::factory()->create([ + 'default' => true, + ]); + + $currency = $this->modelInParams('currency', $models) + ?? Currency::factory()->create([ + 'decimal_places' => 2, + ]); + + $cart = Cart::factory()->create([ + 'currency_id' => $currency->id, + ]); + + $taxClass = TaxClass::factory()->create([ + 'name' => 'Foobar', + ]); + + $taxClass->taxRateAmounts()->create( + TaxRateAmount::factory()->make([ + 'percentage' => 20, + 'tax_class_id' => $taxClass->id, + ])->toArray() + ); + + $purchasable = ProductVariant::factory()->create([ + 'tax_class_id' => $taxClass->id, + 'unit_quantity' => 1, + ]); + + $price = Price::factory()->create([ + 'price' => 2000, + 'tier' => 1, + 'currency_id' => $currency->id, + 'priceable_type' => get_class($purchasable), + 'priceable_id' => $purchasable->id, + ]); + + $cart->lines()->create([ + 'purchasable_type' => get_class($purchasable), + 'purchasable_id' => $purchasable->id, + 'quantity' => 1, + ]); + + return $cart->calculate(); + } + + /** + * @param array $models + */ + public function createOrder(...$models): Order + { + $customerGroup = CustomerGroup::factory()->create([ + 'default' => true, + ]); + + $billing = CartAddress::factory()->make([ + 'type' => 'billing', + 'country_id' => Country::factory(), + 'first_name' => 'Santa', + 'line_one' => '123 Elf Road', + 'city' => 'Lapland', + 'postcode' => 'BILL', + ]); + + $shipping = CartAddress::factory()->make([ + 'type' => 'shipping', + 'country_id' => Country::factory(), + 'first_name' => 'Santa', + 'line_one' => '123 Elf Road', + 'city' => 'Lapland', + 'postcode' => 'SHIPP', + ]); + + $currency = $this->modelInParams('currency', $models) + ?? Currency::factory()->create([ + 'decimal_places' => 2, + ]); + + $cart = Cart::factory()->create([ + 'currency_id' => $currency->id, + ]); + + $taxClass = TaxClass::factory()->create([ + 'name' => 'Foobar', + ]); + + $taxClass->taxRateAmounts()->create( + TaxRateAmount::factory()->make([ + 'percentage' => 20, + 'tax_class_id' => $taxClass->id, + ])->toArray() + ); + + $purchasable = ProductVariant::factory()->create([ + 'tax_class_id' => $taxClass->id, + 'unit_quantity' => 1, + ]); + + $price = Price::factory()->create([ + 'price' => 1000, + 'tier' => 1, + 'currency_id' => $currency->id, + 'priceable_type' => get_class($purchasable), + 'priceable_id' => $purchasable->id, + ]); + + $cart->lines()->create([ + 'purchasable_type' => get_class($purchasable), + 'purchasable_id' => $purchasable->id, + 'quantity' => 1, + ]); + + $cart->addresses()->createMany([ + $billing->toArray(), + $shipping->toArray(), + ]); + + $shippingOption = new ShippingOption( + name: 'Basic Delivery', + description: 'Basic Delivery', + identifier: 'BASDEL', + price: new PriceDataType(500, $cart->currency, 1), + taxClass: $taxClass + ); + + ShippingManifest::addOption($shippingOption); + + $cart->shippingAddress->update([ + 'shipping_option' => $shippingOption->getIdentifier(), + ]); + + $cart->shippingAddress->shippingOption = $shippingOption; + + $order = $cart->createOrder(); + + return $order; + } + + /** + * Get model from function parameters. + * + * @param array $params + */ + private function modelInParams(string $model, array $params): ?Model + { + if (empty($params)) { + return null; + } + + if (array_key_exists($model, $params)) { + return $params[$model]; + } + + return null; + } +} diff --git a/tests/Unit/Domain/Rewards/Actions/RewardPointsCalculatorTest.php b/tests/Unit/Domain/Rewards/Actions/RewardPointsCalculatorTest.php new file mode 100644 index 0000000..debfdb2 --- /dev/null +++ b/tests/Unit/Domain/Rewards/Actions/RewardPointsCalculatorTest.php @@ -0,0 +1,94 @@ +create([ + 'decimal_places' => 2, + 'exchange_rate' => 1.0, + ]); + + // Order subtotal is 10 + $order = $this->createOrder( + currency: $currency, + ); + + $points = RewardPointsCalculator::for($order) + ->setCoefficient(2) + ->calculate(); + + $this->assertEquals(20, $points); + +})->group('rewards', 'point-calculator'); + +it('can calculate points from order while respecting currency exchange rate', function () { + + /** @var TestCase $this */ + $currency = Currency::factory()->create([ + 'decimal_places' => 2, + 'exchange_rate' => 1.5, + ]); + + // Order subtotal is 10 + $order = $this->createOrder( + currency: $currency, + ); + + $points = RewardPointsCalculator::for($order) + ->setCoefficient(10) + ->calculate(); + + $this->assertEquals(150, $points); + +})->group('rewards', 'point-calculator'); + +it('can calculate points from order respecting configured', function () { + + /** @var TestCase $this */ + $currency = Currency::factory()->create([ + 'decimal_places' => 2, + 'exchange_rate' => 1, + ]); + + Config::set('lunar-rewards.reward_point_coefficient', 10); + + // Order subtotal is 10 + $order = $this->createOrder( + currency: $currency, + ); + + $points = RewardPointsCalculator::for($order) + ->calculate(); + + $this->assertEquals(100, $points); + +})->group('rewards', 'point-calculator'); + +it('can calculate points from cart correctly', function () { + + /** @var TestCase $this */ + $currency = Currency::factory()->create([ + 'decimal_places' => 2, + 'exchange_rate' => 1.0, + ]); + + // Cart subtotal is 20 + $cart = $this->createCart( + currency: $currency, + ); + + $points = RewardPointsCalculator::for($cart) + ->setCoefficient(3) + ->calculate(); + + $this->assertEquals(60, $points); + +})->group('rewards', 'point-calculator');