diff --git a/tests/VariantTest.php b/tests/VariantTest.php new file mode 100644 index 0000000..653e9ee --- /dev/null +++ b/tests/VariantTest.php @@ -0,0 +1,107 @@ +variants = new VariantRepository($this->db); + $this->attributes = new AttributeRepository($this->db); + $this->products = new ProductRepository($this->db); + } + + private function makeVariant(int $productId, string $sku, int $priceCents, int $stock, string $sizeValue): int + { + $attributeId = $this->attributes->findBySlug('size') + ? (int) $this->attributes->findBySlug('size')['id'] + : $this->attributes->create('Size', 'size'); + $valueRow = $this->attributes->findValueBySlug($attributeId, slugify($sizeValue)); + $valueId = $valueRow ? (int) $valueRow['id'] : $this->attributes->createValue($attributeId, $sizeValue, slugify($sizeValue)); + + $variantId = $this->variants->create($productId, $sku, $priceCents, $stock); + $this->variants->setOption($variantId, $attributeId, $valueId); + return $variantId; + } + + public function testVariantCarriesOwnPriceStockAndLabel(): void + { + $productId = $this->seedProduct('Tee', 2000, 0); + $variantId = $this->makeVariant($productId, 'TEE-S', 2500, 7, 'Small'); + + $variant = $this->variants->find($variantId); + $this->assertSame(2500, (int) $variant['priceCents']); + $this->assertSame(7, (int) $variant['stockQuantity']); + + $forProduct = $this->variants->forProduct($productId); + $this->assertCount(1, $forProduct); + $this->assertSame('Small', $forProduct[0]['label']); + } + + public function testCartUsesVariantPriceStockNotProduct(): void + { + $productId = $this->seedProduct('Tee', 2000, 100); + $variantId = $this->makeVariant($productId, 'TEE-M', 2500, 3, 'Medium'); + + $cart = new Cart(new Session(), $this->products, $this->variants); + $cart->add($productId, 10, $variantId); + + $lines = $cart->lines(); + $this->assertCount(1, $lines); + $this->assertSame(3, $lines[0]['quantity'], 'clamped to variant stock, not product stock'); + $this->assertSame(2500, $lines[0]['unitPriceCents']); + $this->assertSame('Medium', $lines[0]['variantLabel']); + $this->assertSame(2500 * 3, $cart->totalCents()); + } + + public function testProductAndVariantLinesAreSeparate(): void + { + $productId = $this->seedProduct('Tee', 2000, 50); + $variantId = $this->makeVariant($productId, 'TEE-L', 2500, 20, 'Large'); + + $cart = new Cart(new Session(), $this->products, $this->variants); + $cart->add($productId, 2); + $cart->add($productId, 1, $variantId); + + $this->assertCount(2, $cart->lines()); + $this->assertSame(3, $cart->count()); + } + + public function testOrderDecrementsVariantStockNotProductStock(): void + { + $productId = $this->seedProduct('Tee', 2000, 50); + $variantId = $this->makeVariant($productId, 'TEE-XL', 2500, 8, 'Extra Large'); + $userId = (new UserRepository($this->db))->create('v@bazaar.test', password_hash('correcthorse', PASSWORD_DEFAULT), 'V'); + + $cart = new Cart(new Session(), $this->products, $this->variants); + $cart->add($productId, 2, $variantId); + + $orders = new OrderRepository($this->db); + $service = new OrderService($this->db, $orders, $this->products, $this->variants); + $orderId = $service->createFromCart($userId, $cart, 'usd'); + + $items = $orders->items($orderId); + $this->assertSame($variantId, (int) $items[0]['variantId']); + $this->assertSame('Extra Large', $items[0]['variantLabel']); + $this->assertSame(2500, (int) $items[0]['unitPriceCents']); + + $service->markPaid($orderId); + + $this->assertSame(6, (int) $this->variants->find($variantId)['stockQuantity']); + $this->assertSame(50, (int) $this->products->find($productId)['stockQuantity'], 'product stock untouched'); + } +}