create([ 'slug' => 'test-product', 'type' => 'simple', 'price' => 99.99, 'regular_price' => 99.99, ]); $this->assertDatabaseHas('products', [ 'id' => $product->id, 'slug' => 'test-product', 'price' => 99.99, ]); } /** @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_returns_current_price_correctly() { $product = Product::factory()->create([ 'regular_price' => 100, 'sale_price' => null, ]); $this->assertEquals(100, $product->getCurrentPrice()); } /** @test */ public function it_applies_sale_price_when_active() { $product = Product::factory()->create([ 'regular_price' => 100, 'sale_price' => 75, 'sale_start' => now()->subDay(), 'sale_end' => now()->addDay(), ]); $this->assertEquals(75, $product->getCurrentPrice()); } /** @test */ public function it_ignores_sale_price_when_not_started() { $product = Product::factory()->create([ 'regular_price' => 100, 'sale_price' => 75, 'sale_start' => now()->addDay(), 'sale_end' => now()->addWeek(), ]); $this->assertEquals(100, $product->getCurrentPrice()); } /** @test */ public function it_ignores_sale_price_when_ended() { $product = Product::factory()->create([ 'regular_price' => 100, 'sale_price' => 75, 'sale_start' => now()->subWeek(), 'sale_end' => now()->subDay(), ]); $this->assertEquals(100, $product->getCurrentPrice()); } /** @test */ public function it_can_manage_stock() { $product = Product::factory()->create([ 'manage_stock' => true, 'stock_quantity' => 50, 'in_stock' => true, ]); $this->assertTrue($product->increaseStock(10)); $this->assertEquals(60, $product->fresh()->stock_quantity); $this->assertTrue($product->decreaseStock(5)); $this->assertEquals(55, $product->fresh()->stock_quantity); } /** @test */ public function it_cannot_decrease_stock_below_zero() { $product = Product::factory()->create([ 'manage_stock' => true, 'stock_quantity' => 5, ]); $this->assertFalse($product->decreaseStock(10)); $this->assertEquals(5, $product->fresh()->stock_quantity); } /** @test */ public function it_returns_available_stock() { $product = Product::factory()->create([ 'manage_stock' => true, 'stock_quantity' => 100, ]); $this->assertEquals(100, $product->getAvailableStock()); } /** @test */ public function it_can_check_if_in_stock() { $productInStock = Product::factory()->create([ 'manage_stock' => true, 'stock_quantity' => 10, 'in_stock' => true, ]); $productOutOfStock = Product::factory()->create([ 'manage_stock' => true, 'stock_quantity' => 0, 'in_stock' => false, ]); $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([ 'product_id' => $product->id, 'type' => 'one-time', 'price' => 9999, 'currency' => 'USD', 'active' => true, ]); ProductPrice::create([ 'product_id' => $product->id, 'type' => 'recurring', 'price' => 1999, 'currency' => 'USD', 'interval' => 'month', 'active' => true, ]); $this->assertCount(2, $product->fresh()->prices); } /** @test */ public function it_can_have_related_products() { $product = Product::factory()->create(); $relatedProduct = Product::factory()->create(); $product->relatedProducts()->attach($relatedProduct->id, [ 'type' => 'related', ]); $this->assertTrue($product->relatedProducts->contains($relatedProduct)); } /** @test */ public function it_can_have_upsell_products() { $product = Product::factory()->create(); $upsellProduct = Product::factory()->create(); $product->relatedProducts()->attach($upsellProduct->id, [ 'type' => 'upsell', ]); $this->assertTrue($product->upsells->contains($upsellProduct)); } /** @test */ public function it_can_have_cross_sell_products() { $product = Product::factory()->create(); $crossSellProduct = Product::factory()->create(); $product->relatedProducts()->attach($crossSellProduct->id, [ 'type' => 'cross-sell', ]); $this->assertTrue($product->crossSells->contains($crossSellProduct)); } /** @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('published', $published->first()->status); } /** @test */ public function it_can_scope_in_stock_products() { Product::factory()->create([ 'in_stock' => true, 'manage_stock' => true, 'stock_quantity' => 10, ]); Product::factory()->create([ 'in_stock' => false, 'manage_stock' => true, 'stock_quantity' => 0, ]); $inStock = Product::inStock()->get(); $this->assertCount(1, $inStock); $this->assertTrue($inStock->first()->in_stock); } /** @test */ public function it_can_scope_visible_products() { Product::factory()->create([ 'visible' => true, 'status' => 'published', ]); Product::factory()->create([ 'visible' => false, 'status' => 'published', ]); $visible = Product::visible()->get(); $this->assertCount(1, $visible); $this->assertTrue($visible->first()->visible); } /** @test */ public function it_can_have_parent_child_relationships() { $parent = Product::factory()->create([ 'type' => 'variable', ]); $child = Product::factory()->create([ 'type' => 'variation', '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); } }