Skip to content
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
43 changes: 43 additions & 0 deletions database/factories/DiscountRuleFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Cone\Bazar\Database\Factories;

use Cone\Bazar\Enums\DiscountRuleType;
use Cone\Bazar\Models\DiscountRule;
use Illuminate\Database\Eloquent\Factories\Factory;

class DiscountRuleFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var class-string<\Illuminate\Database\Eloquent\Model>
*/
protected $model = DiscountRule::class;

/**
* Get the name of the model that is generated by the factory.
*
* @return class-string<\Illuminate\Database\Eloquent\Model|TModel>
*/
public function modelName(): string
{
return $this->model::getProxiedClass();
}

/**
* Define the model's default state.
*/
public function definition(): array
{
return [
'active' => true,
'name' => fake()->words(3, true),
'rules' => [],
'stackable' => false,
'type' => DiscountRuleType::CART,
];
}
}
12 changes: 12 additions & 0 deletions src/Models/DiscountRule.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
<?php

Check warning on line 1 in src/Models/DiscountRule.php

View workflow job for this annotation

GitHub Actions / 4️⃣ Coding Standards

Found violation(s) of type: no_whitespace_in_blank_line

Check warning on line 1 in src/Models/DiscountRule.php

View workflow job for this annotation

GitHub Actions / 4️⃣ Coding Standards

Found violation(s) of type: ordered_imports

Check warning on line 1 in src/Models/DiscountRule.php

View workflow job for this annotation

GitHub Actions / 4️⃣ Coding Standards

Found violation(s) of type: not_operator_with_successor_space

Check warning on line 1 in src/Models/DiscountRule.php

View workflow job for this annotation

GitHub Actions / 4️⃣ Coding Standards

Found violation(s) of type: no_unused_imports

Check warning on line 1 in src/Models/DiscountRule.php

View workflow job for this annotation

GitHub Actions / 4️⃣ Coding Standards

Found violation(s) of type: unary_operator_spaces

declare(strict_types=1);

namespace Cone\Bazar\Models;

use Cone\Bazar\Database\Factories\DiscountRuleFactory;
use Cone\Bazar\Enums\DiscountRuleType;
use Cone\Bazar\Enums\DiscountRuleValueType;
use Cone\Bazar\Enums\DiscountType;
use Cone\Bazar\Exceptions\DiscountException;
Expand All @@ -12,6 +14,7 @@
use Cone\Bazar\Models\Discountable as DiscountablePivot;
use Cone\Root\Models\User;
use Cone\Root\Traits\InteractsWithProxy;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Attributes\Scope;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
Expand All @@ -21,6 +24,7 @@

class DiscountRule extends Model implements Contract
{
use HasFactory;
use InteractsWithProxy;

/**
Expand Down Expand Up @@ -79,6 +83,14 @@
return static::getProxiedClass();
}

/**
* Create a new factory instance for the model.
*/
protected static function newFactory(): DiscountRuleFactory
{
return DiscountRuleFactory::new();
}

/**
* Get the discountable types.
*/
Expand Down
69 changes: 69 additions & 0 deletions tests/Enums/DiscountRuleTypeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

declare(strict_types=1);

namespace Cone\Bazar\Tests\Enums;

use Cone\Bazar\Enums\DiscountRuleType;
use Cone\Bazar\Tests\TestCase;

class DiscountRuleTypeTest extends TestCase
{
public function test_discount_rule_type_has_cart_case(): void
{
$this->assertEquals('cart', DiscountRuleType::CART->value);
}

public function test_discount_rule_type_has_buyable_case(): void
{
$this->assertEquals('buyable', DiscountRuleType::BUYABLE->value);
}

public function test_discount_rule_type_has_shipping_case(): void
{
$this->assertEquals('shipping', DiscountRuleType::SHIPPING->value);
}

public function test_cart_type_has_highest_priority(): void
{
$this->assertEquals(3, DiscountRuleType::CART->priority());
}

public function test_buyable_type_has_medium_priority(): void
{
$this->assertEquals(2, DiscountRuleType::BUYABLE->priority());
}

public function test_shipping_type_has_lowest_priority(): void
{
$this->assertEquals(1, DiscountRuleType::SHIPPING->priority());
}

public function test_cart_type_has_correct_label(): void
{
$this->assertEquals('Cart Total', DiscountRuleType::CART->label());
}

public function test_buyable_type_has_correct_label(): void
{
$this->assertEquals('Buyable Item', DiscountRuleType::BUYABLE->label());
}

public function test_shipping_type_has_correct_label(): void
{
$this->assertEquals('Shipping', DiscountRuleType::SHIPPING->label());
}

public function test_priorities_are_ordered_correctly(): void
{
$this->assertGreaterThan(
DiscountRuleType::BUYABLE->priority(),
DiscountRuleType::CART->priority()
);

$this->assertGreaterThan(
DiscountRuleType::SHIPPING->priority(),
DiscountRuleType::BUYABLE->priority()
);
}
}
30 changes: 30 additions & 0 deletions tests/Enums/DiscountValueTypeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace Cone\Bazar\Tests\Enums;

use Cone\Bazar\Enums\DiscountValueType;
use Cone\Bazar\Tests\TestCase;

class DiscountValueTypeTest extends TestCase
{
public function test_discount_value_type_has_fix_case(): void
{
$this->assertEquals('fixed_amount', DiscountValueType::FIX->value);
}

public function test_discount_value_type_has_percent_case(): void
{
$this->assertEquals('percent', DiscountValueType::PERCENT->value);
}

public function test_all_cases_are_present(): void
{
$cases = DiscountValueType::cases();

$this->assertCount(2, $cases);
$this->assertContains(DiscountValueType::FIX, $cases);
$this->assertContains(DiscountValueType::PERCENT, $cases);
}
}
185 changes: 185 additions & 0 deletions tests/Models/DiscountRuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
<?php

Check warning on line 1 in tests/Models/DiscountRuleTest.php

View workflow job for this annotation

GitHub Actions / 4️⃣ Coding Standards

Found violation(s) of type: no_unused_imports

declare(strict_types=1);

namespace Cone\Bazar\Tests\Models;

use Cone\Bazar\Enums\DiscountRuleType;
use Cone\Bazar\Models\Cart;
use Cone\Bazar\Models\DiscountRule;
use Cone\Bazar\Models\Item;
use Cone\Bazar\Models\Product;
use Cone\Bazar\Tests\TestCase;
use Cone\Bazar\Tests\User;

class DiscountRuleTest extends TestCase
{
protected DiscountRule $discountRule;

protected Cart $cart;

protected function setUp(): void
{
parent::setUp();

$this->discountRule = DiscountRule::factory()->create();

$this->cart = Cart::factory()->create();

Product::factory(3)->create()->each(function ($product) {
$this->cart->items()->create([
'buyable_id' => $product->id,
'buyable_type' => Product::class,
'quantity' => mt_rand(1, 5),
'price' => $product->price,
'name' => $product->name,
]);
});
}

public function test_discount_rule_has_default_attributes(): void
{
$this->assertTrue($this->discountRule->active);
$this->assertFalse($this->discountRule->stackable);
$this->assertEquals(DiscountRuleType::CART, $this->discountRule->type);
$this->assertIsArray($this->discountRule->rules);
}

public function test_discount_rule_can_have_rules(): void
{
$this->discountRule->rules = ['min_amount' => 100];
$this->discountRule->save();

$this->assertSame(['min_amount' => 100], $this->discountRule->rules);
}

public function test_discount_rule_can_be_associated_with_users(): void
{
$users = User::factory()->count(2)->create();

$this->discountRule->users()->attach($users);

$this->assertCount(2, $this->discountRule->users);
$this->assertTrue($this->discountRule->users->contains($users[0]));
$this->assertTrue($this->discountRule->users->contains($users[1]));
}

public function test_discount_rule_can_calculate_discount(): void
{
$value = $this->discountRule->calculate($this->cart);

$this->assertIsFloat($value);
$this->assertEquals(0.0, $value);
}

public function test_discount_rule_can_be_applied_to_discountable(): void
{
$this->assertCount(0, $this->cart->discounts);

$this->discountRule->apply($this->cart);

$this->cart->refresh();

$this->assertCount(1, $this->cart->discounts);
$this->assertTrue($this->cart->discounts->contains($this->discountRule));
}

public function test_discount_rule_can_be_applied_to_item(): void
{
$item = $this->cart->items->first();

$this->assertCount(0, $item->discounts);

$this->discountRule->apply($item);

$item->refresh();

$this->assertCount(1, $item->discounts);
$this->assertTrue($item->discounts->contains($this->discountRule));
}

public function test_discount_rule_types_have_priorities(): void
{
$this->assertEquals(3, DiscountRuleType::CART->priority());
$this->assertEquals(2, DiscountRuleType::BUYABLE->priority());
$this->assertEquals(1, DiscountRuleType::SHIPPING->priority());
}

public function test_discount_rule_types_have_labels(): void
{
$this->assertEquals('Cart Total', DiscountRuleType::CART->label());
$this->assertEquals('Buyable Item', DiscountRuleType::BUYABLE->label());
$this->assertEquals('Shipping', DiscountRuleType::SHIPPING->label());
}

public function test_discount_rule_can_have_different_types(): void
{
$cartRule = DiscountRule::factory()->create(['type' => DiscountRuleType::CART]);
$buyableRule = DiscountRule::factory()->create(['type' => DiscountRuleType::BUYABLE]);
$shippingRule = DiscountRule::factory()->create(['type' => DiscountRuleType::SHIPPING]);

$this->assertEquals(DiscountRuleType::CART, $cartRule->type);
$this->assertEquals(DiscountRuleType::BUYABLE, $buyableRule->type);
$this->assertEquals(DiscountRuleType::SHIPPING, $shippingRule->type);
}

public function test_discount_rule_applies_to_shipping(): void
{
$shipping = $this->cart->shipping()->create([
'name' => 'Standard Shipping',
'cost' => 10.0,
'driver' => 'local-pickup',
]);

$this->assertCount(0, $shipping->discounts);

$this->discountRule->apply($shipping);

$shipping->refresh();

$this->assertCount(1, $shipping->discounts);
$this->assertTrue($shipping->discounts->contains($this->discountRule));
}

public function test_multiple_discount_rules_can_be_applied(): void
{
$rule1 = DiscountRule::factory()->create(['stackable' => true]);
$rule2 = DiscountRule::factory()->create(['stackable' => true]);

$rule1->apply($this->cart);
$rule2->apply($this->cart);

$this->cart->refresh();

$this->assertCount(2, $this->cart->discounts);
$this->assertTrue($this->cart->discounts->contains($rule1));
$this->assertTrue($this->cart->discounts->contains($rule2));
}

public function test_discount_rule_can_be_inactive(): void
{
$inactiveRule = DiscountRule::factory()->create(['active' => false]);

$this->assertFalse($inactiveRule->active);
}

public function test_discount_rule_stackable_attribute(): void
{
$stackableRule = DiscountRule::factory()->create(['stackable' => true]);
$nonStackableRule = DiscountRule::factory()->create(['stackable' => false]);

$this->assertTrue($stackableRule->stackable);
$this->assertFalse($nonStackableRule->stackable);
}

public function test_discount_rule_relationship_uses_correct_table(): void
{
$this->discountRule->apply($this->cart);

$this->assertDatabaseHas('bazar_discounts', [
'discount_rule_id' => $this->discountRule->id,
'discountable_id' => $this->cart->id,
'discountable_type' => get_class($this->cart),
]);
}
}
Loading
Loading