laravel-shop/tests/Feature/ProductManagementTest.php

290 lines
8.3 KiB
PHP
Raw Normal View History

2025-11-21 10:49:41 +00:00
<?php
namespace Blax\Shop\Tests\Feature;
use Blax\Shop\Enums\ProductRelationType;
use Blax\Shop\Enums\ProductStatus;
use Blax\Shop\Enums\ProductType;
2025-11-21 10:49:41 +00:00
use Blax\Shop\Models\Product;
use Blax\Shop\Models\ProductCategory;
use Blax\Shop\Models\ProductAttribute;
use Blax\Shop\Models\ProductPrice;
use Blax\Shop\Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class ProductManagementTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function it_can_create_a_product()
{
2025-11-24 06:00:07 +00:00
$product = Product::factory()
->withPrices(1, 99.99)
->create([
'slug' => 'test-product',
'type' => 'simple',
]);
2025-11-21 10:49:41 +00:00
$this->assertDatabaseHas('products', [
'id' => $product->id,
'slug' => 'test-product',
]);
2025-11-24 06:00:07 +00:00
$this->assertCount(1, $product->prices);
$this->assertEquals(99.99, $product->prices->first()->unit_amount);
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_automatically_generates_slug_if_not_provided()
{
$product = Product::factory()->create(['slug' => null]);
$this->assertNotNull($product->slug);
$this->assertStringStartsWith('new-product-', $product->slug);
}
/** @test */
public function it_can_manage_stock()
{
$product = Product::factory()->create([
'manage_stock' => true,
]);
$this->assertTrue($product->increaseStock(60));
$this->assertEquals(60, $product->AvailableStocks);
2025-11-21 10:49:41 +00:00
$this->assertTrue($product->decreaseStock(5));
$this->assertEquals(55, $product->AvailableStocks);
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_cannot_decrease_stock_below_zero()
{
$product = Product::factory()->create([
'manage_stock' => true,
]);
$this->assertThrows(function () use ($product) {
$product->decreaseStock(10);
}, \Blax\Shop\Exceptions\NotEnoughStockException::class);
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_returns_available_stock()
{
$product = Product::factory()->create([
'manage_stock' => true,
]);
$this->assertEquals(0, $product->getAvailableStock());
$product->increaseStock(20);
$this->assertEquals(20, $product->getAvailableStock());
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_can_check_if_in_stock()
{
$productInStock = Product::factory()->create([
'manage_stock' => true,
]);
$productInStock->increaseStock(10);
2025-11-21 10:49:41 +00:00
$productOutOfStock = Product::factory()->create([
'manage_stock' => true,
]);
$this->assertTrue($productInStock->isInStock());
$this->assertFalse($productOutOfStock->isInStock());
}
/** @test */
public function it_can_attach_categories()
{
$product = Product::factory()->create();
$category = ProductCategory::factory()->create();
$product->categories()->attach($category);
$this->assertTrue($product->categories->contains($category));
}
/** @test */
public function it_can_have_attributes()
{
$product = Product::factory()->create();
$attribute = ProductAttribute::create([
'product_id' => $product->id,
'key' => 'Color',
'value' => 'Blue',
]);
$this->assertCount(1, $product->fresh()->attributes);
$this->assertEquals('Color', $product->attributes->first()->key);
$this->assertEquals('Blue', $product->attributes->first()->value);
}
/** @test */
public function it_can_have_multiple_prices()
{
$product = Product::factory()->create();
ProductPrice::create([
'purchasable_id' => $product->id,
'purchasable_type' => get_class($product),
'type' => 'one_time',
'unit_amount' => 9999,
2025-11-21 10:49:41 +00:00
'currency' => 'USD',
'active' => true,
]);
ProductPrice::create([
'purchasable_id' => $product->id,
'purchasable_type' => get_class($product),
2025-11-21 10:49:41 +00:00
'type' => 'recurring',
'price' => 1999,
'currency' => 'USD',
'interval' => 'month',
'active' => true,
]);
$this->assertCount(2, $product->prices);
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_can_have_related_products()
{
$product = Product::factory()->create();
$relatedProduct = Product::factory()->create();
$product->productRelations()->attach($relatedProduct->id, [
'type' => ProductRelationType::RELATED->value,
2025-11-21 10:49:41 +00:00
]);
$this->assertTrue($product->relatedProducts()->get()->contains($relatedProduct));
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_can_have_upsell_products()
{
$product = Product::factory()->create();
$upsellProduct = Product::factory()->create();
$product->productRelations()->attach($upsellProduct->id, [
'type' => ProductRelationType::UPSELL->value,
2025-11-21 10:49:41 +00:00
]);
$this->assertTrue($product->upsellProducts()->get()->contains($upsellProduct));
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_can_have_cross_sell_products()
{
$product = Product::factory()->create();
$crossSellProduct = Product::factory()->create();
$product->productRelations()->attach($crossSellProduct->id, [
'type' => ProductRelationType::CROSS_SELL->value,
2025-11-21 10:49:41 +00:00
]);
$this->assertTrue($product->crossSellProducts()->get()->contains($crossSellProduct));
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_can_scope_published_products()
{
Product::factory()->create(['status' => 'published']);
Product::factory()->create(['status' => 'draft']);
$published = Product::published()->get();
$this->assertCount(1, $published);
$this->assertEquals(ProductStatus::PUBLISHED, $published->first()->status);
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_can_scope_in_stock_products()
{
Product::factory()->create([
'manage_stock' => false,
2025-11-21 10:49:41 +00:00
]);
$productInStock = Product::factory()->create([
2025-11-21 10:49:41 +00:00
'manage_stock' => true,
]);
$productInStock->increaseStock(10);
2025-11-21 10:49:41 +00:00
$inStock = Product::inStock()->get();
$this->assertCount(2, $inStock);
$this->assertTrue((bool) ($inStock->first()->isInStock()));
$this->assertNotEquals($inStock->reverse()->first()->id, $inStock->first()->id);
$this->assertTrue((bool) ($inStock->reverse()->first()->isInStock()));
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_can_scope_visible_products()
{
Product::factory()->create([
2025-11-22 08:55:58 +00:00
'is_visible' => true,
2025-11-21 10:49:41 +00:00
'status' => 'published',
]);
Product::factory()->create([
2025-11-22 08:55:58 +00:00
'is_visible' => false,
2025-11-21 10:49:41 +00:00
'status' => 'published',
]);
$visible = Product::visible()->get();
$this->assertCount(1, $visible);
2025-11-22 17:09:45 +00:00
$this->assertTrue($visible->first()->is_visible);
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_can_have_parent_child_relationships()
{
$parent = Product::factory()->create([
'type' => ProductType::VARIABLE,
2025-11-21 10:49:41 +00:00
]);
$child = Product::factory()->create([
'type' => ProductType::VARIABLE,
2025-11-21 10:49:41 +00:00
'parent_id' => $parent->id,
]);
$this->assertTrue($parent->children->contains($child));
$this->assertEquals($parent->id, $child->parent->id);
}
/** @test */
public function it_validates_virtual_and_downloadable_flags()
{
$virtualProduct = Product::factory()->create([
'virtual' => true,
'downloadable' => false,
]);
$downloadableProduct = Product::factory()->create([
'virtual' => false,
'downloadable' => true,
]);
$this->assertTrue($virtualProduct->virtual);
$this->assertFalse($virtualProduct->downloadable);
$this->assertTrue($downloadableProduct->downloadable);
$this->assertFalse($downloadableProduct->virtual);
}
/** @test */
public function it_can_check_featured_status()
{
$featured = Product::factory()->create(['featured' => true]);
$regular = Product::factory()->create(['featured' => false]);
$this->assertTrue($featured->featured);
$this->assertFalse($regular->featured);
}
}