first commit

This commit is contained in:
2025-08-02 16:30:27 +02:00
commit 23646bfcee
14851 changed files with 1750626 additions and 0 deletions

View File

@@ -0,0 +1,162 @@
<?php
declare(strict_types=1);
namespace KupShop\ProductReservationBundle\Tests;
use KupShop\CatalogBundle\ProductList\ProductList;
use KupShop\KupShopBundle\Context\UserContext;
use KupShop\KupShopBundle\Util\Contexts;
use KupShop\ProductReservationBundle\Util\ProductReservationUtil;
use Query\Operator;
class ProductReservationTest extends \DatabaseTestCase
{
private ProductReservationUtil $productReservationUtil;
private ProductList $productList;
protected function setUp(): void
{
parent::setUp();
$this->productReservationUtil = $this->get(ProductReservationUtil::class);
$this->productList = $this->get(ProductList::class);
}
public function testCreateProductReservation(): void
{
$this->assertNotEmpty(
$this->productReservationUtil->createProductReservation(3, null, 5, ProductReservationUtil::TYPE_RETAIL_RESERVE)
);
$this->assertEquals(5, $this->productReservationUtil->getProductReservedQuantity(3), 'Assert that reservation quantity is same as we set');
}
public function testCreateProductReservationWithInvalidType(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->productReservationUtil->createProductReservation(3, null, 5, 'nesmysl');
}
/** @dataProvider data_testProductsReservationMultiFetch */
public function testProductsReservationMultiFetch(int $productId, ?int $quantity): void
{
if ($quantity !== null) {
$this->productReservationUtil->createProductReservation($productId, null, $quantity, ProductReservationUtil::TYPE_RETAIL_RESERVE);
}
$products = $this->productList->andSpec(Operator::equals(['p.id' => $productId]))
->getProducts();
$products->fetchReservations();
$product = $products->current();
if ($quantity !== null) {
$this->assertNotEmpty($product->reservations);
$this->assertEquals($quantity, array_sum(array_map(fn ($x) => $x['quantity'], $product->reservations)));
return;
}
$this->assertEmpty($product->reservations);
}
public function data_testProductsReservationMultiFetch(): iterable
{
yield 'Multi fetch for product with existing reservation' => [3, 5];
yield 'Multi fetch for product with zero quantity reservation' => [3, 0];
yield 'Multi fetch for product without existing reservation' => [3, null];
}
/**
* @dataProvider data_testProductReservationIsAppliedOnProduct
*/
public function testProductReservationIsAppliedOnProduct(int $productId, ?int $variationId, int $quantity, string $reservationType, bool $withIsTypeB2B, int $expectedInStore): void
{
if ($withIsTypeB2B) {
Contexts::clear();
$this->mockUserContextWithIsTypeCallable(fn () => true);
}
$this->productReservationUtil->createProductReservation($productId, $variationId, $quantity, $reservationType);
$product = $variationId ? new \Variation($productId, $variationId) : new \Product($productId);
$product->createFromDB();
$this->assertEquals($expectedInStore, $product->inStore);
}
/**
* @dataProvider data_testProductReservationIsAppliedOnProduct
*/
public function testProductReservationIsAppliedOnProductList(int $productId, ?int $variationId, int $quantity, string $reservationType, bool $withIsTypeB2B, int $expectedInStore): void
{
if ($withIsTypeB2B) {
Contexts::clear();
$this->mockUserContextWithIsTypeCallable(fn () => true);
}
$this->productReservationUtil->createProductReservation($productId, $variationId, $quantity, $reservationType);
$productList = $this->productList;
// reset `resultModifiers` to remove call of `fetchDeliveryText` that changes inStore value to max(inStore, 0), so negative pieces are removed
$reflectionClass = new \ReflectionClass($productList);
$property = $reflectionClass->getProperty('resultModifiers');
$property->setAccessible(true);
$property->setValue($productList, []);
$product = $productList
->andSpec(Operator::equals(['p.id' => $productId]))
->fetchVariations(true, false)
->getProducts()
->current();
if ($variationId) {
$this->assertEquals($expectedInStore, $product->variations[$variationId]['in_store']);
return;
}
$this->assertEquals($expectedInStore, $product->inStore);
}
public function data_testProductReservationIsAppliedOnProduct(): iterable
{
yield 'Product store is 19 pcs, we are creating reservation for 10 pcs so expected store should be 9 pcs' => [9, null, 10, ProductReservationUtil::TYPE_RESERVATION, false, 9];
yield 'Product store is 0 pcs, we are creating reservation for 5 pcs so expected store should be -5 pcs' => [8, null, 5, ProductReservationUtil::TYPE_RESERVATION, false, -5];
yield 'Variation store is 2 pcs, we are creating reservation for 2 pcs so expected store should be 0 pcs' => [2, 9, 2, ProductReservationUtil::TYPE_RESERVATION, false, 0];
yield 'Variation store is 2 pcs, we are creating reservation for 2 pcs, but type TYPE_RETAIL_RESERVE is not active so expected store is still 2 pcs' => [2, 9, 2, ProductReservationUtil::TYPE_RETAIL_RESERVE, false, 2];
yield 'Variation store is 2 pcs, we are creating reservation for 2 pcs and type TYPE_RETAIL_RESERVE is active so expected store is 0 pcs' => [2, 9, 2, ProductReservationUtil::TYPE_RETAIL_RESERVE, true, 0];
}
private function mockUserContextWithIsTypeCallable(callable $fn): void
{
$mock = $this->autowire(UserContextMock::class);
$mock->_setIsTypeCallable($fn);
$this->set(UserContext::class, $mock);
}
}
class UserContextMock extends UserContext
{
private ?\Closure $_isTypeCallable = null;
public function _setIsTypeCallable(?callable $isTypeCallable): void
{
$this->_isTypeCallable = $isTypeCallable;
}
public function isType(string $type): bool
{
if ($this->_isTypeCallable) {
return ($this->_isTypeCallable)($type);
}
return parent::isType($type);
}
}