laravel-shop/tests/Feature/PoolMaxQuantityValidationTe...

251 lines
8.4 KiB
PHP
Raw Normal View History

2025-12-20 11:19:34 +00:00
<?php
namespace Blax\Shop\Tests\Feature;
use Blax\Shop\Enums\ProductType;
use Blax\Shop\Models\Cart;
use Blax\Shop\Models\Product;
use Blax\Shop\Models\ProductPrice;
use Blax\Shop\Tests\TestCase;
use Carbon\Carbon;
use Workbench\App\Models\User;
/**
* Tests to ensure pool products cannot exceed available single items
* even when adding without dates (flexible cart behavior)
*/
class PoolMaxQuantityValidationTest extends TestCase
{
protected User $user;
protected Cart $cart;
protected function setUp(): void
{
parent::setUp();
$this->user = User::factory()->create();
auth()->login($this->user);
$this->cart = Cart::factory()->create([
'customer_id' => $this->user->id,
'customer_type' => get_class($this->user),
]);
}
/**
* Create a pool with 7 single items (production scenario)
*/
protected function createPoolWith7Singles(): Product
{
$pool = Product::factory()->create([
'name' => 'Production Pool',
'type' => ProductType::POOL,
'manage_stock' => false,
]);
ProductPrice::factory()->create([
'purchasable_id' => $pool->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'currency' => 'USD',
'is_default' => true,
]);
$pool->setPoolPricingStrategy('lowest');
// Create 7 single items, each with 1 stock
for ($i = 1; $i <= 7; $i++) {
$single = Product::factory()->create([
'name' => "Single {$i}",
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$single->increaseStock(1);
ProductPrice::factory()->create([
'purchasable_id' => $single->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'currency' => 'USD',
'is_default' => true,
]);
$pool->attachSingleItems([$single->id]);
}
return $pool;
}
/** @test */
public function cannot_add_more_items_than_available_singles_without_dates()
{
$pool = $this->createPoolWith7Singles();
// Pool has 7 single items, each with 1 stock
$this->assertEquals(7, $pool->getPoolMaxQuantity());
// Should be able to add 7 items
$this->cart->addToCart($pool, 7);
$this->assertEquals(7, $this->cart->fresh()->items->sum('quantity'));
// Should NOT be able to add 8th item - should throw exception
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
$this->expectExceptionMessage('has only 0 items available');
$this->cart->addToCart($pool, 1);
}
/** @test */
public function cannot_add_more_items_than_available_singles_with_dates()
{
$pool = $this->createPoolWith7Singles();
$from = now()->addDays(1);
$until = now()->addDays(2);
// Pool has 7 single items, each with 1 stock
$this->assertEquals(7, $pool->getPoolMaxQuantity($from, $until));
// Should be able to add 7 items with dates
$this->cart->addToCart($pool, 7, [], $from, $until);
$this->assertEquals(7, $this->cart->fresh()->items->sum('quantity'));
// Should NOT be able to add 8th item - should throw exception
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
$this->expectExceptionMessage('has only 0 items available');
$this->cart->addToCart($pool, 1, [], $from, $until);
}
/** @test */
public function cannot_add_batch_exceeding_available_singles_without_dates()
{
$pool = $this->createPoolWith7Singles();
// Trying to add 8 items at once should fail
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
$this->expectExceptionMessage('has only 7 items available');
$this->cart->addToCart($pool, 8);
}
/** @test */
public function cannot_add_batch_exceeding_available_singles_with_dates()
{
$pool = $this->createPoolWith7Singles();
$from = now()->addDays(1);
$until = now()->addDays(2);
// Trying to add 8 items at once should fail
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
$this->expectExceptionMessage('has only 7 items available');
$this->cart->addToCart($pool, 8, [], $from, $until);
}
/** @test */
public function adding_items_without_dates_then_adding_more_validates_correctly()
{
$pool = $this->createPoolWith7Singles();
// Add 5 items without dates
$this->cart->addToCart($pool, 5);
$this->assertEquals(5, $this->cart->fresh()->items->sum('quantity'));
// Should be able to add 2 more (total 7)
$this->cart->addToCart($pool, 2);
$this->assertEquals(7, $this->cart->fresh()->items->sum('quantity'));
// Should NOT be able to add 1 more
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
$this->cart->addToCart($pool, 1);
}
/** @test */
public function checkoutSessionLink_throws_exception_when_cart_invalid()
{
$pool = $this->createPoolWith7Singles();
// Add items without dates - cart is not ready for checkout
$this->cart->addToCart($pool, 3);
// checkoutSessionLink should throw exception, not return null
// When items don't have dates, validation throws CartItemMissingInformationException
$this->expectException(\Blax\Shop\Exceptions\CartItemMissingInformationException::class);
$this->expectExceptionMessage('is missing required information: from, until');
$this->cart->checkoutSessionLink();
}
/** @test */
public function checkoutSessionLink_throws_exception_when_not_enough_stock()
{
$pool = $this->createPoolWith7Singles();
$from = now()->addDays(1);
$until = now()->addDays(2);
// Add 7 items with dates
$this->cart->addToCart($pool, 7, [], $from, $until);
// Simulate another cart claiming all stock for the same period
$otherCart = Cart::factory()->create([
'customer_id' => User::factory()->create()->id,
'customer_type' => User::class,
]);
$otherCart->addToCart($pool, 7, [], $from, $until);
$otherCart->checkout(); // This claims the stock
// Our cart should now fail validation when trying to create checkout session
// The validation throws NotEnoughStockException when checking availability
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
$this->expectExceptionMessage('has only 0 items available');
$this->cart->fresh()->checkoutSessionLink();
}
/** @test */
public function cart_aware_validation_accounts_for_items_already_in_cart()
{
$pool = $this->createPoolWith7Singles();
$from = now()->addDays(1);
$until = now()->addDays(2);
// Add 5 items to cart
$this->cart->addToCart($pool, 5, [], $from, $until);
// Pool has 7 total, 5 in cart, so 2 available for this request
$this->assertEquals(7, $pool->getPoolMaxQuantity($from, $until));
// Should be able to add 2 more
$this->cart->addToCart($pool, 2, [], $from, $until);
$this->assertEquals(7, $this->cart->fresh()->items->sum('quantity'));
// Should NOT be able to add 1 more
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
$this->expectExceptionMessage('has only 0 items available');
$this->cart->addToCart($pool, 1, [], $from, $until);
}
/** @test */
public function validation_message_shows_correct_remaining_availability()
{
$pool = $this->createPoolWith7Singles();
// Add 5 items without dates
$this->cart->addToCart($pool, 5);
try {
// Try to add 5 more (total would be 10, but max is 7)
// Should fail saying only 2 available
$this->cart->addToCart($pool, 5);
$this->fail('Should have thrown NotEnoughStockException');
} catch (\Blax\Shop\Exceptions\NotEnoughStockException $e) {
$this->assertStringContainsString('has only 2 items available', $e->getMessage());
$this->assertStringContainsString('Requested: 5', $e->getMessage());
}
}
}