From df15d08026fcae6650a783a729bd62ceea94347a Mon Sep 17 00:00:00 2001 From: Buba Suma Date: Thu, 13 Aug 2020 12:54:19 -0500 Subject: [PATCH] TP-34177: Out of stock label is not working correctly for different type of products - Backport 1.1.x --- ...tyGroupedProductOptionDefaultStockTest.xml | 95 +++++++++++++++ .../Queue/GetSalabilityDataForUpdate.php | 87 ++++++++++++++ .../Queue/UpdateIndexSalabilityStatus.php | 18 ++- .../IndexProcessor.php | 75 ++---------- .../UpdateLegacyStock.php | 53 ++++++++ .../ResourceModel/UpdateLegacyStockStatus.php | 58 +++++++++ .../Queue/GetSalabilityDataForUpdateTest.php | 113 ++++++++++++++++++ .../UpdateLegacyStockTest.php | 66 ++++++++++ .../Queue/UpdateIndexSalabilityStatusTest.php | 96 +++++++++++++++ .../UpdateLegacyStockStatusTest.php | 80 +++++++++++++ 10 files changed, 673 insertions(+), 68 deletions(-) create mode 100644 InventoryAdminUi/Test/Mftf/Test/StorefrontCreateOrderAllQuantityGroupedProductOptionDefaultStockTest.xml create mode 100644 InventoryIndexer/Model/Queue/GetSalabilityDataForUpdate.php create mode 100644 InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/UpdateLegacyStock.php create mode 100644 InventoryIndexer/Model/ResourceModel/UpdateLegacyStockStatus.php create mode 100644 InventoryIndexer/Test/Unit/Model/Queue/GetSalabilityDataForUpdateTest.php create mode 100644 InventoryIndexer/Test/Unit/Model/Queue/UpdateIndexSalabilityStatus/UpdateLegacyStockTest.php create mode 100644 InventoryIndexer/Test/Unit/Model/Queue/UpdateIndexSalabilityStatusTest.php create mode 100644 InventoryIndexer/Test/Unit/Model/ResourceModel/UpdateLegacyStockStatusTest.php diff --git a/InventoryAdminUi/Test/Mftf/Test/StorefrontCreateOrderAllQuantityGroupedProductOptionDefaultStockTest.xml b/InventoryAdminUi/Test/Mftf/Test/StorefrontCreateOrderAllQuantityGroupedProductOptionDefaultStockTest.xml new file mode 100644 index 000000000000..1ed1f6633f8e --- /dev/null +++ b/InventoryAdminUi/Test/Mftf/Test/StorefrontCreateOrderAllQuantityGroupedProductOptionDefaultStockTest.xml @@ -0,0 +1,95 @@ + + + + + + + + + <description value="Verify, grouped product option will change status after order placement with all it's quantity on default stock"/> + <severity value="CRITICAL"/> + <group value="msi"/> + <group value="multi_mode"/> + </annotations> + <before> + <!--Login to admin--> + <actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/> + <!--Disable all custom sources--> + <actionGroup ref="DisableAllSourcesActionGroup" stepKey="disableSource"/> + <!--Create category--> + <createData entity="ApiCategory" stepKey="createCategory"/> + <!--Create grouped product--> + <createData entity="ApiGroupedProduct" stepKey="groupedProduct"/> + <!--Create simple product (1) with qty 10--> + <createData entity="ApiSimplePrice10Qty10" stepKey="product1"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!--Create simple product (2) with qty 10--> + <createData entity="ApiSimplePrice10Qty10" stepKey="product2"> + <requiredEntity createDataKey="createCategory"/> + </createData> + <!--Add simple product (1) to grouped product--> + <createData entity="OneSimpleProductLink" stepKey="addProductOne"> + <requiredEntity createDataKey="groupedProduct"/> + <requiredEntity createDataKey="product1"/> + </createData> + <!--Add simple product (2) to grouped product--> + <updateData entity="OneMoreSimpleProductLink" createDataKey="addProductOne" stepKey="addProductTwo"> + <requiredEntity createDataKey="groupedProduct"/> + <requiredEntity createDataKey="product2"/> + </updateData> + <!--Create customer--> + <createData entity="MsiCustomer1" stepKey="customer"/> + </before> + <after> + <!--Logout from admin--> + <actionGroup ref="logout" stepKey="logoutOfAdmin"/> + <!--Delete category--> + <deleteData createDataKey="createCategory" stepKey="deleteCategory"/> + <!--Delete grouped product--> + <deleteData createDataKey="groupedProduct" stepKey="deleteGroupedProduct"/> + <!--Delete simple product (1)--> + <deleteData createDataKey="product1" stepKey="deleteSimpleProduct1"/> + <!--Delete simple product (2)--> + <deleteData createDataKey="product2" stepKey="deleteSimpleProduct2"/> + <!--Delete customer--> + <deleteData createDataKey="customer" stepKey="deleteCustomer"/> + </after> + + <!--Login To storefront as Customer--> + <actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginToStorefront"> + <argument name="Customer" value="$$customer$$"/> + </actionGroup> + <!--Navigate to group product PDP.--> + <amOnPage url="{{StorefrontProductPage.url($groupedProduct.custom_attributes[url_key]$)}}" stepKey="goToProductPage"/> + <waitForPageLoad stepKey="waitForProductPageLoaded"/> + <!--Add grouped product to cart with all available quantity of product (1).--> + <actionGroup ref="StorefrontAddGroupedProductWithTwoLinksToCartActionGroup" stepKey="addGroupedProductToCart"> + <argument name="product" value="$groupedProduct$"/> + <argument name="linkedProduct1Name" value="$product1.name$"/> + <argument name="linkedProduct2Name" value="$product2.name$"/> + <argument name="linkedProduct1Qty" value="{{Qty_10.qty}}"/> + <argument name="linkedProduct2Qty" value="{{Qty_1.qty}}"/> + </actionGroup> + <!--Place order.--> + <actionGroup ref="GoToCheckoutFromMinicartActionGroup" stepKey="navigateToCheckoutPage"/> + <click selector="{{CheckoutShippingSection.next}}" stepKey="clickNextButton"/> + <actionGroup ref="ClickPlaceOrderActionGroup" stepKey="clickOnPlaceOrder"/> + <!--Run queue consumer.--> + <magentoCLI command="queue:consumers:start" arguments="inventory.reservations.updateSalabilityStatus" stepKey="startSalabilityUpdate" timeout="30"/> + <!--Navigate to group product PDP.--> + <amOnPage url="{{StorefrontProductPage.url($groupedProduct.custom_attributes[url_key]$)}}" stepKey="navigateToGroupedProductPDPAfterOrderPlacement"/> + <waitForPageLoad stepKey="waitForProductPageLoaded2"/> + <!--Verify that product1 is not present.--> + <dontSee selector="{{StorefrontProductInfoMainSection.groupedProductsTable}}" userInput="$product1.name$" stepKey="dontSeeProduct1"/> + <!--Verify that product2 is present.--> + <actionGroup ref="AssertLinkPresenceOnGroupedProductPage" stepKey="verifySecondOptionIsStillPresentedOnPage"> + <argument name="productName" value="$product2.name$"/> + </actionGroup> + </test> +</tests> diff --git a/InventoryIndexer/Model/Queue/GetSalabilityDataForUpdate.php b/InventoryIndexer/Model/Queue/GetSalabilityDataForUpdate.php new file mode 100644 index 000000000000..2bda26c3bfab --- /dev/null +++ b/InventoryIndexer/Model/Queue/GetSalabilityDataForUpdate.php @@ -0,0 +1,87 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryIndexer\Model\Queue; + +use Magento\Framework\Exception\LocalizedException; +use Magento\InventorySalesApi\Api\IsProductSalableInterface; +use Magento\InventorySalesApi\Model\GetStockItemDataInterface; + +/** + * Get stock status changes for given reservation. + */ +class GetSalabilityDataForUpdate +{ + /** + * @var IsProductSalableInterface + */ + private $isProductSalable; + + /** + * @var GetStockItemDataInterface + */ + private $getStockItemData; + + /** + * @param IsProductSalableInterface $isProductSalable + * @param GetStockItemDataInterface $getStockItemData + */ + public function __construct( + IsProductSalableInterface $isProductSalable, + GetStockItemDataInterface $getStockItemData + ) { + $this->isProductSalable = $isProductSalable; + $this->getStockItemData = $getStockItemData; + } + + /** + * Get stock status changes for given reservation. + * + * @param ReservationData $reservationData + * @return bool[] - ['sku' => bool] + */ + public function execute(ReservationData $reservationData): array + { + $salabilityData = []; + foreach ($reservationData->getSkus() as $sku) { + $salabilityData[$sku] = $this->isProductSalable->execute($sku, $reservationData->getStock()); + } + + $data = []; + foreach ($salabilityData as $sku => $isSalable) { + $currentStatus = $this->isCurrentlySalable( + $sku, + $reservationData->getStock() + ); + if ($isSalable !== $currentStatus) { + $data[$sku] = $isSalable; + } + } + + return $data; + } + + /** + * Get current is_salable value. + * + * @param string $sku + * @param int $stockId + * + * @return bool + */ + private function isCurrentlySalable(string $sku, int $stockId): bool + { + try { + $data = $this->getStockItemData->execute($sku, $stockId); + $isSalable = $data ? (bool)$data[GetStockItemDataInterface::IS_SALABLE] : false; + } catch (LocalizedException $e) { + $isSalable = false; + } + + return $isSalable; + } +} diff --git a/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus.php b/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus.php index 87ac385753ba..13c832de6762 100644 --- a/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus.php +++ b/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus.php @@ -9,6 +9,7 @@ use Magento\Framework\Exception\StateException; use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface; +use Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus\UpdateLegacyStock; use Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus\IndexProcessor; /** @@ -25,17 +26,24 @@ class UpdateIndexSalabilityStatus * @var IndexProcessor */ private $indexProcessor; + /** + * @var UpdateLegacyStock + */ + private $updateLegacyStock; /** * @param DefaultStockProviderInterface $defaultStockProvider * @param IndexProcessor $indexProcessor + * @param UpdateLegacyStock $updateLegacyStock */ public function __construct( DefaultStockProviderInterface $defaultStockProvider, - IndexProcessor $indexProcessor + IndexProcessor $indexProcessor, + UpdateLegacyStock $updateLegacyStock ) { $this->defaultStockProvider = $defaultStockProvider; $this->indexProcessor = $indexProcessor; + $this->updateLegacyStock = $updateLegacyStock; } /** @@ -50,8 +58,12 @@ public function execute(ReservationData $reservationData): array { $stockId = $reservationData->getStock(); $dataForUpdate = []; - if ($stockId !== $this->defaultStockProvider->getId() && $reservationData->getSkus()) { - $dataForUpdate = $this->indexProcessor->execute($reservationData, $stockId); + if ($reservationData->getSkus()) { + if ($stockId !== $this->defaultStockProvider->getId()) { + $dataForUpdate = $this->indexProcessor->execute($reservationData, $stockId); + } else { + $dataForUpdate = $this->updateLegacyStock->execute($reservationData); + } } return $dataForUpdate; diff --git a/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/IndexProcessor.php b/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/IndexProcessor.php index cebf1e8cdac9..db4c40f1a480 100644 --- a/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/IndexProcessor.php +++ b/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/IndexProcessor.php @@ -8,16 +8,14 @@ namespace Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus; use Magento\Framework\App\ResourceConnection; -use Magento\Framework\Exception\LocalizedException; use Magento\Framework\Exception\StateException; use Magento\InventoryIndexer\Indexer\InventoryIndexer; +use Magento\InventoryIndexer\Model\Queue\GetSalabilityDataForUpdate; use Magento\InventoryIndexer\Model\Queue\ReservationData; use Magento\InventoryIndexer\Model\ResourceModel\UpdateIsSalable; use Magento\InventoryMultiDimensionalIndexerApi\Model\Alias; use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexNameBuilder; use Magento\InventoryMultiDimensionalIndexerApi\Model\IndexStructureInterface; -use Magento\InventorySalesApi\Api\IsProductSalableInterface; -use Magento\InventorySalesApi\Model\GetStockItemDataInterface; /** * Update 'is salable' index data processor. @@ -35,39 +33,31 @@ class IndexProcessor private $indexStructure; /** - * @var IsProductSalableInterface - */ - private $isProductSalable; - - /** - * @var GetStockItemDataInterface + * @var UpdateIsSalable */ - private $getStockItemData; + private $updateIsSalable; /** - * @var UpdateIsSalable + * @var GetSalabilityDataForUpdate */ - private $updateIsSalable; + private $getSalabilityDataForUpdate; /** * @param IndexNameBuilder $indexNameBuilder * @param IndexStructureInterface $indexStructure - * @param GetStockItemDataInterface $getStockItemData * @param UpdateIsSalable $updateIsSalable - * @param IsProductSalableInterface $isProductSalable + * @param GetSalabilityDataForUpdate $getSalabilityDataForUpdate */ public function __construct( IndexNameBuilder $indexNameBuilder, IndexStructureInterface $indexStructure, - GetStockItemDataInterface $getStockItemData, UpdateIsSalable $updateIsSalable, - IsProductSalableInterface $isProductSalable + GetSalabilityDataForUpdate $getSalabilityDataForUpdate ) { $this->indexNameBuilder = $indexNameBuilder; $this->indexStructure = $indexStructure; - $this->isProductSalable = $isProductSalable; - $this->getStockItemData = $getStockItemData; $this->updateIsSalable = $updateIsSalable; + $this->getSalabilityDataForUpdate = $getSalabilityDataForUpdate; } /** @@ -77,6 +67,7 @@ public function __construct( * @param int $stockId * @return bool[] * @throws StateException + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function execute(ReservationData $reservationData, int $stockId): array { @@ -89,12 +80,7 @@ public function execute(ReservationData $reservationData, int $stockId): array $this->indexStructure->create($mainIndexName, ResourceConnection::DEFAULT_CONNECTION); } - $salabilityData = []; - foreach ($reservationData->getSkus() as $sku) { - $salabilityData[$sku] = $this->isProductSalable->execute($sku, $reservationData->getStock()); - } - - $dataForUpdate = $this->getDataForUpdate($salabilityData, $stockId); + $dataForUpdate = $this->getSalabilityDataForUpdate->execute($reservationData); $this->updateIsSalable->execute( $mainIndexName, $dataForUpdate, @@ -103,45 +89,4 @@ public function execute(ReservationData $reservationData, int $stockId): array return $dataForUpdate; } - - /** - * Build data for index update. - * - * @param array $salabilityData - * @param int $stockId - * - * @return bool[] - ['sku' => bool] - */ - private function getDataForUpdate(array $salabilityData, int $stockId): array - { - $data = []; - foreach ($salabilityData as $sku => $isSalable) { - $currentStatus = $this->getIndexSalabilityStatus($sku, $stockId); - if ($isSalable != $currentStatus && $currentStatus !== null) { - $data[$sku] = $isSalable; - } - } - - return $data; - } - - /** - * Get current index is_salable value. - * - * @param string $sku - * @param int $stockId - * - * @return bool|null - */ - private function getIndexSalabilityStatus(string $sku, int $stockId): ?bool - { - try { - $data = $this->getStockItemData->execute($sku, $stockId); - $isSalable = $data ? (bool)$data[GetStockItemDataInterface::IS_SALABLE] : false; - } catch (LocalizedException $e) { - $isSalable = null; - } - - return $isSalable; - } } diff --git a/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/UpdateLegacyStock.php b/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/UpdateLegacyStock.php new file mode 100644 index 000000000000..d480aad6e817 --- /dev/null +++ b/InventoryIndexer/Model/Queue/UpdateIndexSalabilityStatus/UpdateLegacyStock.php @@ -0,0 +1,53 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus; + +use Magento\InventoryIndexer\Model\Queue\GetSalabilityDataForUpdate; +use Magento\InventoryIndexer\Model\Queue\ReservationData; +use Magento\InventoryIndexer\Model\ResourceModel\UpdateLegacyStockStatus; + +/** + * Update legacy stock status for given reservation. + */ +class UpdateLegacyStock +{ + /** + * @var GetSalabilityDataForUpdate + */ + private $getSalabilityDataForUpdate; + /** + * @var UpdateLegacyStockStatus + */ + private $updateLegacyStockStatus; + + /** + * @param GetSalabilityDataForUpdate $getSalabilityDataForUpdate + * @param UpdateLegacyStockStatus $updateLegacyStockStatus + */ + public function __construct( + GetSalabilityDataForUpdate $getSalabilityDataForUpdate, + UpdateLegacyStockStatus $updateLegacyStockStatus + ) { + $this->getSalabilityDataForUpdate = $getSalabilityDataForUpdate; + $this->updateLegacyStockStatus = $updateLegacyStockStatus; + } + + /** + * Update legacy stock status for given reservation. + * + * @param ReservationData $reservationData + * @return bool[] + */ + public function execute(ReservationData $reservationData): array + { + $dataForUpdate = $this->getSalabilityDataForUpdate->execute($reservationData); + $this->updateLegacyStockStatus->execute($dataForUpdate); + + return $dataForUpdate; + } +} diff --git a/InventoryIndexer/Model/ResourceModel/UpdateLegacyStockStatus.php b/InventoryIndexer/Model/ResourceModel/UpdateLegacyStockStatus.php new file mode 100644 index 000000000000..ec828683a056 --- /dev/null +++ b/InventoryIndexer/Model/ResourceModel/UpdateLegacyStockStatus.php @@ -0,0 +1,58 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryIndexer\Model\ResourceModel; + +use Magento\Framework\App\ResourceConnection; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; + +/** + * Update legacy stock status for given skus. + */ +class UpdateLegacyStockStatus +{ + /** + * @var ResourceConnection + */ + private $resource; + + /** + * @var GetProductIdsBySkusInterface + */ + private $getProductIdsBySkus; + + /** + * @param ResourceConnection $resource + * @param GetProductIdsBySkusInterface $getProductIdsBySkus + */ + public function __construct( + ResourceConnection $resource, + GetProductIdsBySkusInterface $getProductIdsBySkus + ) { + $this->resource = $resource; + $this->getProductIdsBySkus = $getProductIdsBySkus; + } + + /** + * Update legacy stock status for given skus. + * + * @param array $dataForUpdate + */ + public function execute(array $dataForUpdate): void + { + $connection = $this->resource->getConnection(); + $tableName = $connection->getTableName('cataloginventory_stock_status'); + $productIds = $this->getProductIdsBySkus->execute(array_keys($dataForUpdate)); + foreach ($dataForUpdate as $sku => $isSalable) { + $connection->update( + $tableName, + ['stock_status' => $isSalable], + ['product_id = ?' => (int) $productIds[$sku]] + ); + } + } +} diff --git a/InventoryIndexer/Test/Unit/Model/Queue/GetSalabilityDataForUpdateTest.php b/InventoryIndexer/Test/Unit/Model/Queue/GetSalabilityDataForUpdateTest.php new file mode 100644 index 000000000000..b624e3b9bf1a --- /dev/null +++ b/InventoryIndexer/Test/Unit/Model/Queue/GetSalabilityDataForUpdateTest.php @@ -0,0 +1,113 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryIndexer\Test\Unit\Model\Queue; + +use Magento\InventoryIndexer\Model\Queue\GetSalabilityDataForUpdate; +use Magento\InventoryIndexer\Model\Queue\ReservationData; +use Magento\InventorySalesApi\Api\IsProductSalableInterface; +use Magento\InventorySalesApi\Model\GetStockItemDataInterface; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test for GetSalabilityDataForUpdate + */ +class GetSalabilityDataForUpdateTest extends TestCase +{ + /** + * @var IsProductSalableInterface|MockObject + */ + private $isProductSalable; + /** + * @var GetStockItemDataInterface|MockObject + */ + private $getStockItemData; + /** + * @var GetSalabilityDataForUpdate + */ + private $model; + + /** + * @var array + */ + private $salability = [ + 'P1' => true, + 'P2' => true + ]; + + /** + * @var array + */ + private $actualSalability = [ + 'P1' => false, + 'P2' => true + ]; + + /** + * @inheridoc + */ + protected function setUp() + { + parent::setUp(); + $this->isProductSalable = $this->createMock(IsProductSalableInterface::class); + $this->getStockItemData = $this->createMock(GetStockItemDataInterface::class); + $this->model = new GetSalabilityDataForUpdate( + $this->isProductSalable, + $this->getStockItemData + ); + $this->isProductSalable->method('execute') + ->willReturnCallback( + function ($sku) { + return $this->actualSalability[$sku] ?? false; + } + ); + $this->getStockItemData->method('execute') + ->willReturnCallback( + function ($sku) { + return isset($this->salability[$sku]) + ? ['is_salable' => $this->salability[$sku]] + : null; + } + ); + } + + /** + * @param array $skus + * @param array $result + * @dataProvider executeDataProvider + */ + public function testExecute( + array $skus, + array $result + ): void { + $stockId = 1; + $reservation = new ReservationData($skus, $stockId); + $this->assertEquals($result, $this->model->execute($reservation)); + } + + /** + * @return array + */ + public function executeDataProvider(): array + { + return [ + [ + [], + [] + ], + [ + ['P1', 'P2'], + ['P1' => false] + ], + [ + ['P3', 'P2'], + [] + ] + ]; + } +} diff --git a/InventoryIndexer/Test/Unit/Model/Queue/UpdateIndexSalabilityStatus/UpdateLegacyStockTest.php b/InventoryIndexer/Test/Unit/Model/Queue/UpdateIndexSalabilityStatus/UpdateLegacyStockTest.php new file mode 100644 index 000000000000..6b4450f2023a --- /dev/null +++ b/InventoryIndexer/Test/Unit/Model/Queue/UpdateIndexSalabilityStatus/UpdateLegacyStockTest.php @@ -0,0 +1,66 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryIndexer\Test\Unit\Model\Queue\UpdateIndexSalabilityStatus; + +use Magento\InventoryIndexer\Model\Queue\GetSalabilityDataForUpdate; +use Magento\InventoryIndexer\Model\Queue\ReservationData; +use Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus\UpdateLegacyStock; +use Magento\InventoryIndexer\Model\ResourceModel\UpdateLegacyStockStatus; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test for UpdateLegacyStock + */ +class UpdateLegacyStockTest extends TestCase +{ + /** + * @var GetSalabilityDataForUpdate|MockObject + */ + private $getSalabilityDataForUpdate; + /** + * @var UpdateLegacyStockStatus|MockObject + */ + private $updateLegacyStockStatus; + /** + * @var UpdateLegacyStock + */ + private $model; + + /** + * @inheridoc + */ + protected function setUp() + { + parent::setUp(); + $this->getSalabilityDataForUpdate = $this->createMock(GetSalabilityDataForUpdate::class); + $this->updateLegacyStockStatus = $this->createMock(UpdateLegacyStockStatus::class); + $this->model = new UpdateLegacyStock( + $this->getSalabilityDataForUpdate, + $this->updateLegacyStockStatus + ); + } + + /** + * Test that stock status changes are saved in the database + */ + public function testExecute(): void + { + $skus = ['P1', 'P2']; + $stockId = 1; + $reservation = new ReservationData($skus, $stockId); + $changes = ['P1' => false]; + $this->getSalabilityDataForUpdate->expects($this->once()) + ->method('execute') + ->willReturn($changes); + $this->updateLegacyStockStatus->expects($this->once()) + ->method('execute') + ->with($changes); + $this->assertEquals($changes, $this->model->execute($reservation)); + } +} diff --git a/InventoryIndexer/Test/Unit/Model/Queue/UpdateIndexSalabilityStatusTest.php b/InventoryIndexer/Test/Unit/Model/Queue/UpdateIndexSalabilityStatusTest.php new file mode 100644 index 000000000000..35ceb848c00f --- /dev/null +++ b/InventoryIndexer/Test/Unit/Model/Queue/UpdateIndexSalabilityStatusTest.php @@ -0,0 +1,96 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryIndexer\Test\Unit\Model\Queue; + +use Magento\InventoryCatalogApi\Api\DefaultStockProviderInterface; +use Magento\InventoryIndexer\Model\Queue\ReservationData; +use Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus; +use Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus\UpdateLegacyStock; +use Magento\InventoryIndexer\Model\Queue\UpdateIndexSalabilityStatus\IndexProcessor; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test for UpdateIndexSalabilityStatus + */ +class UpdateIndexSalabilityStatusTest extends TestCase +{ + /** + * @var DefaultStockProviderInterface|MockObject + */ + private $defaultStockProvider; + /** + * @var IndexProcessor|MockObject + */ + private $indexProcessor; + /** + * @var UpdateLegacyStock|MockObject + */ + private $updateLegacyStock; + /** + * @var UpdateIndexSalabilityStatus + */ + private $model; + + /** + * @inheridoc + */ + protected function setUp() + { + parent::setUp(); + $this->defaultStockProvider = $this->createMock(DefaultStockProviderInterface::class); + $this->defaultStockProvider->method('getId') + ->willReturn(1); + $this->indexProcessor = $this->createMock(IndexProcessor::class); + $this->updateLegacyStock = $this->createMock(UpdateLegacyStock::class); + $this->model = new UpdateIndexSalabilityStatus( + $this->defaultStockProvider, + $this->indexProcessor, + $this->updateLegacyStock + ); + } + + /** + * Test that legacy stock indexer is executed if the stock is default otherwise custom stock indexer is executed + * + * @param int $stockId + * @param int $updateLegacyStockInvokeCount + * @param int $indexProcessorInvokeCount + * @dataProvider executeDataProvider + */ + public function testExecute( + int $stockId, + int $updateLegacyStockInvokeCount, + int $indexProcessorInvokeCount + ): void { + $skus = ['P1', 'P2']; + $changes = ['P1' => false]; + $reservation = new ReservationData($skus, $stockId); + $this->updateLegacyStock->expects($this->exactly($updateLegacyStockInvokeCount)) + ->method('execute') + ->with($reservation) + ->willReturn($changes); + $this->indexProcessor->expects($this->exactly($indexProcessorInvokeCount)) + ->method('execute') + ->with($reservation, $stockId) + ->willReturn($changes); + $this->assertEquals($changes, $this->model->execute($reservation)); + } + + /** + * @return array + */ + public function executeDataProvider(): array + { + return [ + [1, 1, 0], + [2, 0, 1], + [3, 0, 1] + ]; + } +} diff --git a/InventoryIndexer/Test/Unit/Model/ResourceModel/UpdateLegacyStockStatusTest.php b/InventoryIndexer/Test/Unit/Model/ResourceModel/UpdateLegacyStockStatusTest.php new file mode 100644 index 000000000000..0c13c87be5e7 --- /dev/null +++ b/InventoryIndexer/Test/Unit/Model/ResourceModel/UpdateLegacyStockStatusTest.php @@ -0,0 +1,80 @@ +<?php +/** + * Copyright © Magento, Inc. All rights reserved. + * See COPYING.txt for license details. + */ +declare(strict_types=1); + +namespace Magento\InventoryIndexer\Test\Unit\Model\ResourceModel; + +use Magento\Framework\App\ResourceConnection; +use Magento\Framework\DB\Adapter\AdapterInterface; +use Magento\InventoryCatalogApi\Model\GetProductIdsBySkusInterface; +use Magento\InventoryIndexer\Model\ResourceModel\UpdateLegacyStockStatus; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test for UpdateLegacyStock + */ +class UpdateLegacyStockStatusTest extends TestCase +{ + /** + * @var ResourceConnection|MockObject + */ + private $resource; + /** + * @var GetProductIdsBySkusInterface|MockObject + */ + private $getProductIdsBySkus; + /** + * @var AdapterInterface|MockObject + */ + private $connection; + /** + * @var UpdateLegacyStockStatus + */ + private $model; + + /** + * @inheridoc + */ + protected function setUp() + { + parent::setUp(); + $this->resource = $this->createMock(ResourceConnection::class); + $this->connection = $this->createMock(AdapterInterface::class); + $this->resource->method('getConnection')->willReturn($this->connection); + $this->connection->method('getTableName')->willReturnArgument(0); + $this->getProductIdsBySkus = $this->createMock(GetProductIdsBySkusInterface::class); + $this->getProductIdsBySkus->method('execute')->willReturnCallback('array_flip'); + $this->model = new UpdateLegacyStockStatus( + $this->resource, + $this->getProductIdsBySkus + ); + } + + /** + * Test that stock status changes are saved in the database + */ + public function testExecute(): void + { + $salability = ['P1' => false, 'P2' => true]; + $tableName = 'cataloginventory_stock_status'; + $this->connection->expects($this->exactly(2)) + ->method('update') + ->withConsecutive( + [ + $tableName, + ['stock_status' => false], + ['product_id = ?' => 0] + ], + [ + $tableName, + ['stock_status' => true], + ['product_id = ?' => 1] + ] + ); + $this->model->execute($salability); + } +}