503 lines
16 KiB
PHP
503 lines
16 KiB
PHP
<?php
|
|
|
|
namespace Blax\Shop\Tests\Feature;
|
|
|
|
use Blax\Shop\Models\Cart;
|
|
use Blax\Shop\Models\CartItem;
|
|
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;
|
|
|
|
class CartManagementTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
/** @test */
|
|
public function it_can_create_a_cart()
|
|
{
|
|
$user = User::factory()->create();
|
|
|
|
$cart = Cart::create([
|
|
'customer_type' => get_class($user),
|
|
'customer_id' => $user->id,
|
|
'expires_at' => now()->addDays(7),
|
|
]);
|
|
|
|
$this->assertDatabaseHas('carts', [
|
|
'id' => $cart->id,
|
|
'customer_type' => get_class($user),
|
|
'customer_id' => $user->id,
|
|
]);
|
|
$this->assertNotNull($cart->id);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_automatically_generates_uuid()
|
|
{
|
|
$cart = Cart::create();
|
|
|
|
$this->assertNotNull($cart->id);
|
|
$this->assertIsString($cart->id);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_add_items_to_cart()
|
|
{
|
|
$product = Product::factory()->withPrices()->create();
|
|
$price = $product->defaultPrice()->first();
|
|
|
|
$cart = Cart::create();
|
|
|
|
$cartItem = $cart->addToCart($price, quantity: 2);
|
|
|
|
$this->assertCount(1, $cart->fresh()->items);
|
|
$this->assertEquals(2, $cart->items->first()->quantity);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_update_cart_item_quantity()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
|
$price = $product->defaultPrice()->first();
|
|
|
|
$cartItem = $cart->addToCart($price, quantity: 1);
|
|
$cartItem->update(['quantity' => 3]);
|
|
|
|
$this->assertEquals(3, $cartItem->fresh()->quantity);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_remove_items_from_cart()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 100.00)->create();
|
|
$price = $product->defaultPrice()->first();
|
|
|
|
$cartItem = $cart->addToCart($price, quantity: 1);
|
|
|
|
$this->assertCount(1, $cart->items);
|
|
|
|
$cartItem->delete();
|
|
|
|
$this->assertCount(0, $cart->refresh()->items);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_calculates_cart_total_correctly()
|
|
{
|
|
$cart = Cart::create();
|
|
$product1 = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
|
$product2 = Product::factory()->withPrices(unit_amount: 30.00)->create();
|
|
|
|
$productPrice1 = $product1->defaultPrice()->first();
|
|
$productPrice2 = $product2->defaultPrice()->first();
|
|
|
|
$cart->addToCart($productPrice1, quantity: 2);
|
|
$cart->addToCart($productPrice2, quantity: 1);
|
|
|
|
$total = $cart->fresh()->getTotal();
|
|
|
|
$this->assertEquals(130.00, $total); // (50 * 2) + (30 * 1)
|
|
}
|
|
|
|
/** @test */
|
|
public function it_calculates_total_items_correctly()
|
|
{
|
|
$cart = Cart::create();
|
|
$product1 = Product::factory()->withPrices(unit_amount: 10.00)->create();
|
|
$product2 = Product::factory()->withPrices(unit_amount: 20.00)->create();
|
|
|
|
$product1Price = $product1->defaultPrice()->first();
|
|
$product2Price = $product2->defaultPrice()->first();
|
|
|
|
$cart->addToCart($product1Price, quantity: 3);
|
|
$cart->addToCart($product2Price, quantity: 2);
|
|
|
|
$totalItems = $cart->fresh()->getTotalItems();
|
|
|
|
$this->assertEquals(5, $totalItems); // 3 + 2
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_check_if_cart_is_expired()
|
|
{
|
|
$expiredCart = Cart::create([
|
|
'expires_at' => now()->subDay(),
|
|
]);
|
|
|
|
$activeCart = Cart::create([
|
|
'expires_at' => now()->addDay(),
|
|
]);
|
|
|
|
$this->assertTrue($expiredCart->isExpired());
|
|
$this->assertFalse($activeCart->isExpired());
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_check_if_cart_is_converted()
|
|
{
|
|
$convertedCart = Cart::create([
|
|
'converted_at' => now(),
|
|
]);
|
|
|
|
$activeCart = Cart::create([
|
|
'converted_at' => null,
|
|
]);
|
|
|
|
$this->assertTrue($convertedCart->isConverted());
|
|
$this->assertFalse($activeCart->isConverted());
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_scope_active_carts()
|
|
{
|
|
Cart::create([
|
|
'expires_at' => now()->addDay(),
|
|
'converted_at' => null,
|
|
]);
|
|
|
|
Cart::create([
|
|
'expires_at' => now()->subDay(),
|
|
'converted_at' => null,
|
|
]);
|
|
|
|
Cart::create([
|
|
'expires_at' => now()->addDay(),
|
|
'converted_at' => now(),
|
|
]);
|
|
|
|
$activeCarts = Cart::active()->get();
|
|
|
|
$this->assertCount(1, $activeCarts);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_scope_carts_for_user()
|
|
{
|
|
$user = User::factory()->create();
|
|
$otherUser = User::factory()->create();
|
|
|
|
Cart::create(['customer_type' => get_class($user), 'customer_id' => $user->id]);
|
|
Cart::create(['customer_type' => get_class($user), 'customer_id' => $user->id]);
|
|
Cart::create(['customer_type' => get_class($otherUser), 'customer_id' => $otherUser->id]);
|
|
|
|
$userCarts = Cart::forUser($user)->get();
|
|
|
|
$this->assertCount(2, $userCarts);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_belongs_to_a_user()
|
|
{
|
|
$user = User::factory()->create();
|
|
$cart = Cart::create(['customer_type' => get_class($user), 'customer_id' => $user->id]);
|
|
|
|
$this->assertEquals($user->id, $cart->user->id);
|
|
}
|
|
|
|
/** @test */
|
|
public function cart_items_have_correct_relationships()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 45.00)->create();
|
|
$productPrice = $product->defaultPrice()->first();
|
|
|
|
$cartItem = $cart->addToCart($productPrice, quantity: 1);
|
|
|
|
$this->assertEquals($cart->id, $cartItem->cart->id);
|
|
$this->assertEquals($productPrice->id, $cartItem->purchasable_id);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_calculates_cart_item_subtotal()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 25.00)->create();
|
|
$productPrice = $product->defaultPrice()->first();
|
|
|
|
$cartItem = $cart->addToCart($productPrice, quantity: 4);
|
|
|
|
$this->assertEquals(100.00, $cartItem->getSubtotal()); // 25 * 4
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_store_cart_item_attributes()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
|
$productPrice = $product->defaultPrice()->first();
|
|
|
|
$cartItem = $cart->addToCart(
|
|
$productPrice,
|
|
quantity: 1,
|
|
parameters: [
|
|
'color' => 'blue',
|
|
'size' => 'large',
|
|
]
|
|
);
|
|
|
|
$this->assertEquals('blue', $cartItem->parameters['color']);
|
|
$this->assertEquals('large', $cartItem->parameters['size']);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_have_multiple_items_of_same_product_with_different_attributes()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 30.00)->create();
|
|
$productPrice = $product->defaultPrice()->first();
|
|
|
|
$cart->addToCart(
|
|
$productPrice,
|
|
quantity: 1,
|
|
parameters: ['size' => 'small']
|
|
);
|
|
|
|
$cart->addToCart(
|
|
$productPrice,
|
|
quantity: 2,
|
|
parameters: ['size' => 'large']
|
|
);
|
|
|
|
$this->assertCount(2, $cart->fresh()->items);
|
|
$this->assertEquals($productPrice->unit_amount * 3, $cart->getTotal());
|
|
}
|
|
|
|
/** @test */
|
|
public function it_deletes_cart_items_when_cart_is_deleted()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 75.00)->create();
|
|
$productPrice = $product->defaultPrice()->first();
|
|
|
|
$cartItem = $cart->addToCart(
|
|
$productPrice,
|
|
quantity: 1,
|
|
);
|
|
|
|
$cartItemId = $cartItem->id;
|
|
|
|
$cart->forceDelete();
|
|
|
|
$this->assertDatabaseMissing('cart_items', ['id' => $cartItemId]);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_calculats_unpaid_and_paid_and_can_scope()
|
|
{
|
|
$user = User::factory()->create();
|
|
$cart = $user->currentCart();
|
|
$product1 = Product::factory()->withStocks()->withPrices(1, 782)->create();
|
|
$product2 = Product::factory()->withStocks()->withPrices(1, 402)->create();
|
|
$product3 = Product::factory()->withStocks()->withPrices(1, 855)->create();
|
|
|
|
$cart->addToCart($product1);
|
|
$cart->addToCart($product2);
|
|
$cart->addToCart($product3);
|
|
|
|
$this->assertEquals(2039, $cart->getUnpaidAmount(), 'Unpaid amount should equal total cart amount initially.');
|
|
$this->assertEquals(0, $cart->getPaidAmount(), 'Paid amount should be zero initially.');
|
|
$this->assertEquals(2039, $cart->getTotal(), 'Total amount should equal sum of paid and unpaid amounts.');
|
|
$this->assertEquals($user->currentCart()->id, Cart::unpaid()->first()->id, 'Unpaid cart scope should return the current cart.');
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_create_cart_with_factory()
|
|
{
|
|
$cart = Cart::factory()->create();
|
|
|
|
$this->assertNotNull($cart);
|
|
$this->assertDatabaseHas('carts', [
|
|
'id' => $cart->id,
|
|
]);
|
|
|
|
$cartWithProduct = Cart::factory()
|
|
->withNewProductInCart(
|
|
quantity: 2,
|
|
unit_amount: 150.00,
|
|
sale_unit_amount: 120.00,
|
|
stocks: 10
|
|
)
|
|
->withNewProductInCart(
|
|
quantity: 2,
|
|
unit_amount: 150.00,
|
|
sale_unit_amount: 120.00,
|
|
stocks: 10,
|
|
sale_start: now()->subDay(),
|
|
sale_end: now()->addDay()
|
|
)
|
|
->create();
|
|
|
|
$this->assertCount(2, $cartWithProduct->items);
|
|
$this->assertEquals(4, $cartWithProduct->getTotalItems());
|
|
$this->assertEquals((150.00 * 2) + (120 * 2), $cartWithProduct->getTotal());
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_remove_entire_cart_item()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
|
$price = $product->defaultPrice()->first();
|
|
|
|
$cartItem = $cart->addToCart($price, quantity: 2);
|
|
$this->assertCount(1, $cart->items);
|
|
|
|
$result = $cart->removeFromCart($price, quantity: 2);
|
|
|
|
$this->assertCount(0, $cart->refresh()->items);
|
|
$this->assertTrue(true); // Item was deleted
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_decrease_cart_item_quantity()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 75.00)->create();
|
|
$price = $product->defaultPrice()->first();
|
|
|
|
$cartItem = $cart->addToCart($price, quantity: 5);
|
|
$this->assertEquals(5, $cartItem->quantity);
|
|
|
|
$result = $cart->removeFromCart($price, quantity: 2);
|
|
|
|
$updatedItem = $cart->items->first();
|
|
$this->assertEquals(3, $updatedItem->quantity);
|
|
$this->assertEquals(75.00 * 3, $updatedItem->subtotal);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_updates_subtotal_correctly_when_decreasing_quantity()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 100.00)->create();
|
|
$price = $product->defaultPrice()->first();
|
|
|
|
$cart->addToCart($price, quantity: 4);
|
|
|
|
$cart->removeFromCart($price, quantity: 1);
|
|
|
|
$cartItem = $cart->items->first();
|
|
$this->assertEquals(3, $cartItem->quantity);
|
|
$this->assertEquals(300.00, $cartItem->subtotal);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_respects_parameters_when_removing_from_cart()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
|
$price = $product->defaultPrice()->first();
|
|
|
|
// Add same product with different parameters
|
|
$cartItem1 = $cart->addToCart(
|
|
$price,
|
|
quantity: 2,
|
|
parameters: ['color' => 'blue']
|
|
);
|
|
|
|
$cartItem2 = $cart->addToCart(
|
|
$price,
|
|
quantity: 3,
|
|
parameters: ['color' => 'red']
|
|
);
|
|
|
|
$this->assertCount(2, $cart->items);
|
|
|
|
// Remove only the blue item
|
|
$cart->removeFromCart($price, quantity: 2, parameters: ['color' => 'blue']);
|
|
|
|
$this->assertCount(1, $cart->refresh()->items);
|
|
$this->assertEquals('red', $cart->items->first()->parameters['color']);
|
|
$this->assertEquals(3, $cart->items->first()->quantity);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_decreases_only_matching_parameters_when_removing()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
|
$price = $product->defaultPrice()->first();
|
|
|
|
$cart->addToCart(
|
|
$price,
|
|
quantity: 5,
|
|
parameters: ['size' => 'large']
|
|
);
|
|
|
|
$cart->removeFromCart($price, quantity: 2, parameters: ['size' => 'large']);
|
|
|
|
$cartItem = $cart->items->first();
|
|
$this->assertEquals(3, $cartItem->quantity);
|
|
$this->assertEquals('large', $cartItem->parameters['size']);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_returns_cart_item_when_quantity_is_decreased()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
|
$price = $product->defaultPrice()->first();
|
|
|
|
$cart->addToCart($price, quantity: 5);
|
|
|
|
$result = $cart->removeFromCart($price, quantity: 2);
|
|
|
|
$this->assertInstanceOf(CartItem::class, $result);
|
|
$this->assertEquals(3, $result->quantity);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_handles_removing_nonexistent_item_gracefully()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
|
$price = $product->defaultPrice()->first();
|
|
|
|
$result = $cart->removeFromCart($price, quantity: 1);
|
|
|
|
// Should return true when item doesn't exist
|
|
$this->assertTrue($result);
|
|
$this->assertCount(0, $cart->items);
|
|
}
|
|
|
|
/** @test */
|
|
public function it_updates_cart_total_after_removing_items()
|
|
{
|
|
$cart = Cart::create();
|
|
$product = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
|
$price = $product->defaultPrice()->first();
|
|
|
|
$cart->addToCart($price, quantity: 5);
|
|
$this->assertEquals(250.00, $cart->getTotal());
|
|
|
|
$cart->removeFromCart($price, quantity: 2);
|
|
|
|
$this->assertEquals(150.00, $cart->refresh()->getTotal());
|
|
}
|
|
|
|
/** @test */
|
|
public function it_can_remove_from_cart_with_multiple_items()
|
|
{
|
|
$cart = Cart::create();
|
|
$product1 = Product::factory()->withPrices(unit_amount: 50.00)->create();
|
|
$product2 = Product::factory()->withPrices(unit_amount: 75.00)->create();
|
|
|
|
$price1 = $product1->defaultPrice()->first();
|
|
$price2 = $product2->defaultPrice()->first();
|
|
|
|
$cart->addToCart($price1, quantity: 2);
|
|
$cart->addToCart($price2, quantity: 3);
|
|
$this->assertCount(2, $cart->items);
|
|
|
|
$cart->removeFromCart($price1, quantity: 2);
|
|
|
|
$this->assertCount(1, $cart->refresh()->items);
|
|
$this->assertEquals($price2->id, $cart->items->first()->purchasable_id);
|
|
$this->assertEquals(225.00, $cart->getTotal());
|
|
}
|
|
}
|