laravel-shop/tests/Feature/Checkout/PurchaseFlowTest.php

342 lines
11 KiB
PHP
Raw Normal View History

2025-11-21 10:49:41 +00:00
<?php
2025-12-30 09:55:06 +00:00
namespace Blax\Shop\Tests\Feature\Checkout;
2025-11-21 10:49:41 +00:00
use Blax\Shop\Enums\PurchaseStatus;
use Blax\Shop\Exceptions\NotEnoughStockException;
2025-11-21 10:49:41 +00:00
use Blax\Shop\Models\Product;
use Blax\Shop\Models\ProductPurchase;
use Blax\Shop\Models\Cart;
use Blax\Shop\Models\CartItem;
2025-11-21 10:49:41 +00:00
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;
2025-11-21 10:49:41 +00:00
class PurchaseFlowTest extends TestCase
{
use RefreshDatabase;
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_can_purchase_a_product_directly()
{
$user = User::factory()->create();
2026-01-05 09:30:21 +00:00
$product = Product::factory()->withPrices(unit_amount: 4999)->create([
2025-11-21 10:49:41 +00:00
'manage_stock' => false,
]);
$price = $product->defaultPrice()->first();
2025-11-29 11:05:02 +00:00
$purchase = $user->purchase($price, quantity: 1);
2025-11-21 10:49:41 +00:00
$this->assertInstanceOf(ProductPurchase::class, $purchase);
$this->assertEquals($product->id, $purchase->purchasable_id);
$this->assertEquals($user->id, $purchase->purchaser_id);
2025-11-21 10:49:41 +00:00
$this->assertEquals(1, $purchase->quantity);
$this->assertEquals(PurchaseStatus::UNPAID, $purchase->status);
2025-11-21 10:49:41 +00:00
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_can_add_product_to_cart()
{
$user = User::factory()->create();
2025-11-28 09:24:07 +00:00
$product = Product::factory()->withPrices(1, 2999)->create([
2025-11-21 10:49:41 +00:00
'manage_stock' => false,
]);
2025-11-28 09:24:07 +00:00
$cartItem = $user->addToCart($product, quantity: 2);
2025-11-21 10:49:41 +00:00
$this->assertInstanceOf(CartItem::class, $cartItem);
2025-11-21 10:49:41 +00:00
$this->assertEquals(2, $cartItem->quantity);
2025-11-28 09:24:07 +00:00
$this->assertEquals($product->id, $cartItem->purchasable_id);
2025-11-21 10:49:41 +00:00
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_can_get_cart_items()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product1 = Product::factory()->withStocks(5)->withPrices()->create();
$product2 = Product::factory()->withStocks(5)->withPrices(2)->create();
$this->assertCount(1, $product1->prices);
$this->assertCount(2, $product2->prices);
2025-11-21 10:49:41 +00:00
$user->addToCart($product1->prices()->first(), quantity: 1);
$user->addToCart($product2->prices()->first(), quantity: 2);
2025-11-21 10:49:41 +00:00
$cartItems = $user->cartItems;
$this->assertCount(2, $cartItems);
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_can_update_cart_item_quantity()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withStocks(5)->withPrices()->create();
2025-11-21 10:49:41 +00:00
$cartItem = $user->addToCart($product->prices()->first(), quantity: 1);
2025-11-21 10:49:41 +00:00
$user->updateCartQuantity($cartItem, quantity: 5);
$this->assertEquals(5, $cartItem->fresh()->quantity);
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_can_remove_item_from_cart()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withStocks(5)->withPrices()->create();
2025-11-21 10:49:41 +00:00
$cartItem = $user->addToCart($product->prices()->first(), quantity: 1);
$this->assertCount(1, $user->cartItems);
2025-11-21 10:49:41 +00:00
$user->removeFromCart($cartItem);
$this->assertCount(0, $user->refresh()->cartItems);
2025-11-21 10:49:41 +00:00
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_can_checkout_cart()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product1 = Product::factory()->withStocks(5)->withPrices(3)->create(['manage_stock' => false]);
$product2 = Product::factory()->withStocks(5)->withPrices(3)->create(['manage_stock' => false]);
2025-11-21 10:49:41 +00:00
$user->addToCart($product1, quantity: 2);
$user->addToCart($product2, quantity: 1);
2025-11-25 11:33:42 +00:00
$product1->update(['manage_stock' => true]);
$product2->update(['manage_stock' => true]);
2025-11-29 11:05:02 +00:00
// Assert cart customer is user
$this->assertEquals($user->id, $user->currentCart()?->customer->id);
2025-11-28 09:24:07 +00:00
$this->assertThrows(fn() => $user->checkoutCart(), NotEnoughStockException::class);
$product1->update(['manage_stock' => false]);
$product2->increaseStock(5);
2025-11-28 09:24:07 +00:00
$cart = $user->checkoutCart();
$purchases = $cart->purchases;
2025-11-21 10:49:41 +00:00
$this->assertCount(2, $purchases);
$this->assertEquals(PurchaseStatus::UNPAID, $purchases[0]->status);
$this->assertEquals(PurchaseStatus::UNPAID, $purchases[1]->status);
2025-11-21 10:49:41 +00:00
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_can_get_cart_total()
{
$user = User::factory()->create();
2025-11-29 11:05:02 +00:00
$product1 = Product::factory()->withStocks()->withPrices(unit_amount: 40)->create();
$product2 = Product::factory()->withStocks()->withPrices(unit_amount: 60)->create();
2025-11-21 10:49:41 +00:00
2025-11-24 13:32:11 +00:00
$this->assertNotNull($product1->getCurrentPrice());
$this->assertNotNull($product2->getCurrentPrice());
2025-11-24 06:00:07 +00:00
$user->addToCart($product1, quantity: 2);
$user->addToCart($product2, quantity: 1);
2025-11-21 10:49:41 +00:00
$total = $user->getCartTotal();
2026-01-05 09:30:21 +00:00
$this->assertEquals(140, $total);
2025-11-21 10:49:41 +00:00
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_can_get_cart_items_count()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product1 = Product::factory()->withStocks()->withPrices()->create();
$product2 = Product::factory()->withStocks()->withPrices()->create();
2025-11-21 10:49:41 +00:00
$user->addToCart($product1, quantity: 3);
$user->addToCart($product2, quantity: 2);
$count = $user->getCartItemsCount();
$this->assertEquals(5, $count);
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_can_clear_cart()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product1 = Product::factory()->withStocks()->withPrices()->create();
$product2 = Product::factory()->withStocks()->withPrices()->create();
2025-11-21 10:49:41 +00:00
$user->addToCart($product1, quantity: 1);
$user->addToCart($product2, quantity: 1);
$this->assertCount(2, $user->cartItems);
$user->clearCart();
$this->assertCount(0, $user->fresh()->cartItems);
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_can_check_if_product_was_purchased()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$purchasedProduct = Product::factory()->withPrices()->create(['manage_stock' => false]);
$notPurchasedProduct = Product::factory()->withPrices()->create();
$productPurchase = $user->purchase($purchasedProduct, quantity: 1);
$productPurchase->update(['status' => 'completed']);
2025-11-21 10:49:41 +00:00
$this->assertTrue($user->hasPurchased($purchasedProduct));
$this->assertFalse($user->hasPurchased($notPurchasedProduct));
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_can_get_completed_purchases()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product1 = Product::factory()->withStocks()->withPrices()->create();
$product2 = Product::factory()->withStocks()->withPrices()->create();
$product3 = Product::factory()->withStocks()->withPrices()->create();
2025-11-21 10:49:41 +00:00
$user->purchase($product1, quantity: 1);
$user->purchase($product2, quantity: 1);
$user->addToCart($product3, quantity: 1);
2025-11-25 11:33:42 +00:00
$completed = $user->purchases;
2025-11-21 10:49:41 +00:00
$this->assertCount(2, $completed);
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function purchase_reduces_stock_when_managed()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withPrices()->create();
$product->increaseStock(10);
2025-11-21 10:49:41 +00:00
$user->purchase($product, quantity: 3);
2025-11-25 11:33:42 +00:00
$this->assertEquals(7, $product->AvailableStocks);
2025-11-21 10:49:41 +00:00
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function cannot_purchase_more_than_available_stock()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withPrices()->create();
2025-11-21 10:49:41 +00:00
2025-11-25 11:33:42 +00:00
$this->expectException(NotEnoughStockException::class);
2025-11-21 10:49:41 +00:00
$user->purchase($product, quantity: 10);
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function adding_to_cart_checks_stock_availability()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withPrices(2)->withStocks(3)->create();
2025-11-21 10:49:41 +00:00
2025-11-29 11:05:02 +00:00
$this->assertThrows(fn() => $user->addToCart($product, quantity: 5), NotEnoughStockException::class);
2025-11-21 10:49:41 +00:00
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function purchase_can_store_metadata()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withPrices()->create(['manage_stock' => false]);
2025-11-21 10:49:41 +00:00
2025-11-25 11:33:42 +00:00
$purchase = $user->purchase($product, quantity: 1, meta: [
'gift_message' => 'Happy Birthday!',
'gift_wrap' => true,
2025-11-21 10:49:41 +00:00
]);
2025-11-25 11:33:42 +00:00
$this->assertEquals('Happy Birthday!', $purchase->meta->gift_message ?? null);
2025-11-21 10:49:41 +00:00
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function purchase_can_be_associated_with_cart()
{
$user = User::factory()->create();
$cart = Cart::create(['user_id' => $user->id]);
2025-11-28 09:24:07 +00:00
$product = Product::factory()->create();
2025-11-21 10:49:41 +00:00
$purchase = ProductPurchase::create([
'user_id' => $user->id,
'purchasable_type' => get_class($user),
'purchasable_id' => $user->id,
'product_id' => $product->id,
'cart_id' => $cart->id,
'quantity' => 1,
'amount' => 5000,
'status' => 'cart',
]);
$this->assertEquals($cart->id, $purchase->cart_id);
$this->assertTrue($cart->purchases->contains($purchase));
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function checkout_marks_cart_as_converted()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withPrices()->create(['manage_stock' => false]);
2025-11-21 10:49:41 +00:00
2025-11-25 11:33:42 +00:00
$user->addToCart($product, quantity: 1);
$cart = $user->currentCart();
2025-11-21 10:49:41 +00:00
if ($cart) {
$this->assertNull($cart->converted_at);
2025-11-28 09:24:07 +00:00
$cart = $user->checkoutCart();
2025-11-21 10:49:41 +00:00
$this->assertNotNull($cart->fresh()->converted_at);
}
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function user_cannot_add_out_of_stock_product_to_cart()
{
$user = User::factory()->create();
$product = Product::factory()->create([
'manage_stock' => true,
'stock_quantity' => 0,
'in_stock' => false,
]);
$this->expectException(\Exception::class);
$user->addToCart($product, quantity: 1);
}
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-21 10:49:41 +00:00
public function purchase_stores_amount_correctly()
{
$user = User::factory()->create();
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withPrices()->create([
2025-11-21 10:49:41 +00:00
'manage_stock' => false,
]);
$purchase = $user->purchase($product, quantity: 2);
2025-11-25 11:33:42 +00:00
$this->assertEquals(2, $purchase->quantity);
$this->assertEquals(0, $purchase->amount_paid);
2025-11-21 10:49:41 +00:00
$this->assertGreaterThan(0, $purchase->amount);
}
2025-11-28 09:24:07 +00:00
2025-12-24 18:40:10 +00:00
#[Test]
2025-11-28 09:24:07 +00:00
public function cart_total_is_correct_after_checkout()
{
$user = User::factory()->create();
2025-11-29 11:05:02 +00:00
$product1 = Product::factory()->withStocks()->withPrices(1, unit_amount: 30)->create();
$product2 = Product::factory()->withStocks()->withPrices(1, unit_amount: 70)->create();
2025-11-28 09:24:07 +00:00
$user->addToCart($product1, quantity: 1);
$user->addToCart($product2, quantity: 2);
$cart = $user->checkoutCart();
2026-01-05 09:30:21 +00:00
$this->assertEquals(170, $cart->getTotal());
2025-11-28 09:24:07 +00:00
}
2025-11-21 10:49:41 +00:00
}