laravel-shop/tests/Feature/Product/ProductPricingValidationTes...

319 lines
9.5 KiB
PHP
Raw Permalink Normal View History

<?php
2025-12-30 09:55:06 +00:00
namespace Blax\Shop\Tests\Feature\Product;
use Blax\Shop\Enums\ProductType;
use Blax\Shop\Exceptions\HasNoDefaultPriceException;
use Blax\Shop\Exceptions\HasNoPriceException;
use Blax\Shop\Facades\Cart;
use Blax\Shop\Models\Product;
use Blax\Shop\Models\ProductPrice;
use Blax\Shop\Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Workbench\App\Models\User;
2025-12-24 18:40:10 +00:00
use PHPUnit\Framework\Attributes\Test;
class ProductPricingValidationTest extends TestCase
{
use RefreshDatabase;
protected User $user;
protected function setUp(): void
{
parent::setUp();
$this->user = User::factory()->create();
auth()->login($this->user);
}
2025-12-24 18:40:10 +00:00
#[Test]
public function it_throws_exception_when_product_has_no_prices()
{
$product = Product::factory()->create([
'name' => 'No Price Product',
'type' => ProductType::SIMPLE,
]);
$this->expectException(HasNoPriceException::class);
$this->expectExceptionMessage('has no pricing configured');
Cart::add($product, 1);
}
2025-12-24 18:40:10 +00:00
#[Test]
public function it_throws_exception_when_product_has_multiple_prices_but_no_default()
{
$product = Product::factory()->create([
'name' => 'Multi Price Product',
'type' => ProductType::SIMPLE,
]);
// Create multiple prices, none marked as default
ProductPrice::factory()->create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'is_default' => false,
]);
ProductPrice::factory()->create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'unit_amount' => 7000,
'is_default' => false,
]);
$this->expectException(HasNoDefaultPriceException::class);
$this->expectExceptionMessage('none are marked as default');
Cart::add($product, 1);
}
2025-12-24 18:40:10 +00:00
#[Test]
public function it_throws_exception_when_product_has_single_price_not_marked_as_default()
{
$product = Product::factory()->create([
'name' => 'Single Non-Default Price',
'type' => ProductType::SIMPLE,
]);
ProductPrice::factory()->create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'is_default' => false, // Not marked as default
]);
$this->expectException(HasNoDefaultPriceException::class);
$this->expectExceptionMessage("not marked as default");
Cart::add($product, 1);
}
2025-12-24 18:40:10 +00:00
#[Test]
public function it_throws_exception_when_product_has_multiple_default_prices()
{
$product = Product::factory()->create([
'name' => 'Multiple Defaults',
'type' => ProductType::SIMPLE,
]);
// Create multiple prices, all marked as default
ProductPrice::factory()->create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'is_default' => true,
]);
ProductPrice::factory()->create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'unit_amount' => 7000,
'is_default' => true,
]);
ProductPrice::factory()->create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'unit_amount' => 9000,
'is_default' => true,
]);
$this->expectException(HasNoDefaultPriceException::class);
$this->expectExceptionMessage('3 prices marked as default');
Cart::add($product, 1);
}
2025-12-24 18:40:10 +00:00
#[Test]
public function it_allows_adding_product_with_valid_default_price()
{
$product = Product::factory()->create([
'name' => 'Valid Product',
'type' => ProductType::SIMPLE,
'manage_stock' => true,
]);
$product->increaseStock(10);
ProductPrice::factory()->create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'is_default' => true,
]);
$cartItem = Cart::add($product, 1);
$this->assertNotNull($cartItem);
$this->assertEquals($product->id, $cartItem->purchasable_id);
}
2025-12-24 18:40:10 +00:00
#[Test]
public function it_allows_product_with_one_default_and_multiple_non_default_prices()
{
$product = Product::factory()->create([
'name' => 'Mixed Prices',
'type' => ProductType::SIMPLE,
'manage_stock' => true,
]);
$product->increaseStock(10);
// One default price
ProductPrice::factory()->create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'is_default' => true,
]);
// Multiple non-default prices
ProductPrice::factory()->create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'unit_amount' => 4500,
'is_default' => false,
]);
ProductPrice::factory()->create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'unit_amount' => 4000,
'is_default' => false,
]);
$cartItem = Cart::add($product, 1);
$this->assertNotNull($cartItem);
$this->assertEquals($product->id, $cartItem->purchasable_id);
}
2025-12-24 18:40:10 +00:00
#[Test]
public function it_throws_exception_when_pool_has_no_price_and_single_items_have_no_prices()
{
$pool = Product::factory()->create([
'name' => 'Pool Without Prices',
'type' => ProductType::POOL,
]);
$spot1 = Product::factory()->create([
'name' => 'Spot 1',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$spot1->increaseStock(1);
$pool->productRelations()->attach($spot1->id, [
'type' => \Blax\Shop\Enums\ProductRelationType::SINGLE->value,
]);
$this->expectException(HasNoPriceException::class);
2025-12-17 15:43:22 +00:00
$this->expectExceptionMessage('Cannot add pool product');
$this->expectExceptionMessage('No pricing available');
Cart::add($pool, 1);
}
2025-12-24 18:40:10 +00:00
#[Test]
public function it_allows_pool_with_no_direct_price_but_single_items_have_prices()
{
$pool = Product::factory()->create([
'name' => 'Pool With Inherited Prices',
'type' => ProductType::POOL,
'manage_stock' => false,
]);
$spot1 = Product::factory()->create([
'name' => 'Spot 1',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$spot1->increaseStock(1);
ProductPrice::factory()->create([
'purchasable_id' => $spot1->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'is_default' => true,
]);
$pool->productRelations()->attach($spot1->id, [
'type' => \Blax\Shop\Enums\ProductRelationType::SINGLE->value,
]);
$cartItem = Cart::add($pool, 1);
$this->assertNotNull($cartItem);
$this->assertEquals($pool->id, $cartItem->purchasable_id);
}
2025-12-24 18:40:10 +00:00
#[Test]
public function it_allows_pool_with_direct_price_even_if_single_items_have_no_prices()
{
$pool = Product::factory()->create([
'name' => 'Pool With Direct Price',
'type' => ProductType::POOL,
'manage_stock' => false,
]);
ProductPrice::factory()->create([
'purchasable_id' => $pool->id,
'purchasable_type' => Product::class,
'unit_amount' => 6000,
'is_default' => true,
]);
$spot1 = Product::factory()->create([
'name' => 'Spot 1',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$spot1->increaseStock(1);
$pool->productRelations()->attach($spot1->id, [
'type' => \Blax\Shop\Enums\ProductRelationType::SINGLE->value,
]);
$cartItem = Cart::add($pool, 1);
$this->assertNotNull($cartItem);
$this->assertEquals($pool->id, $cartItem->purchasable_id);
}
2025-12-24 18:40:10 +00:00
#[Test]
public function validate_pricing_returns_errors_array_without_throwing()
{
$product = Product::factory()->create([
'name' => 'Test Product',
'type' => ProductType::SIMPLE,
]);
$result = $product->validatePricing(throwExceptions: false);
$this->assertFalse($result['valid']);
$this->assertNotEmpty($result['errors']);
$this->assertStringContainsString('no prices', $result['errors'][0]);
}
2025-12-24 18:40:10 +00:00
#[Test]
public function validate_pricing_with_valid_price_returns_valid()
{
$product = Product::factory()->create([
'name' => 'Valid Product',
'type' => ProductType::SIMPLE,
]);
ProductPrice::factory()->create([
'purchasable_id' => $product->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'is_default' => true,
]);
$result = $product->validatePricing(throwExceptions: false);
$this->assertTrue($result['valid']);
$this->assertEmpty($result['errors']);
}
}