Issues (3641)

Zed/Availability/Business/Model/SellableTest.php (1 issue)

1
<?php
2
3
/**
4
 * Copyright © 2016-present Spryker Systems GmbH. All rights reserved.
5
 * Use of this software requires acceptance of the Evaluation License Agreement. See LICENSE file.
6
 */
7
8
namespace SprykerTest\Zed\Availability\Business\Model;
9
10
use Codeception\Test\Unit;
11
use Generated\Shared\Transfer\ProductAvailabilityCriteriaTransfer;
12
use Generated\Shared\Transfer\ProductConcreteAvailabilityTransfer;
13
use Generated\Shared\Transfer\SellableItemRequestTransfer;
14
use Generated\Shared\Transfer\SellableItemsRequestTransfer;
15
use Generated\Shared\Transfer\SellableItemsResponseTransfer;
16
use Generated\Shared\Transfer\StoreTransfer;
17
use Spryker\DecimalObject\Decimal;
18
use Spryker\Zed\Availability\Business\Model\AvailabilityHandlerInterface;
19
use Spryker\Zed\Availability\Business\Model\Sellable;
20
use Spryker\Zed\Availability\Business\Model\SellableInterface;
21
use Spryker\Zed\Availability\Dependency\Facade\AvailabilityToStoreFacadeInterface;
22
use Spryker\Zed\Availability\Persistence\AvailabilityRepositoryInterface;
23
use Spryker\Zed\AvailabilityExtension\Dependency\Plugin\AvailabilityStrategyPluginInterface;
24
25
/**
26
 * Auto-generated group annotations
27
 *
28
 * @group SprykerTest
29
 * @group Zed
30
 * @group Availability
31
 * @group Business
32
 * @group Model
33
 * @group SellableTest
34
 * Add your own group annotations below this line
35
 */
36
class SellableTest extends Unit
37
{
38
    /**
39
     * @var string
40
     */
41
    public const SKU_PRODUCT = 'sku-123-321';
42
43
    /**
44
     * @var string
45
     */
46
    public const SKU_PRODUCT_SECOND = 'sku-123-222';
47
48
    /**
49
     * @var string
50
     */
51
    protected const PRODUCT_OFFER_REFERENCE = 'offer0';
52
53
    /**
54
     * @dataProvider isProductSellableStoresDataProvider
55
     *
56
     * @param \Generated\Shared\Transfer\StoreTransfer $storeTransfer
57
     *
58
     * @return void
59
     */
60
    public function testIsProductIsSellableWhenNeverOutOfStockShouldReturnIsSellable(StoreTransfer $storeTransfer): void
61
    {
62
        $availabilityRepositoryMock = $this->createAvailabilityRepositoryMock();
63
        $availabilityRepositoryMock->method('findProductConcreteAvailabilityBySkuAndStore')
0 ignored issues
show
The method method() does not exist on Spryker\Zed\Availability...lityRepositoryInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

63
        $availabilityRepositoryMock->/** @scrutinizer ignore-call */ 
64
                                     method('findProductConcreteAvailabilityBySkuAndStore')

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
64
            ->with(static::SKU_PRODUCT, $this->createStoreTransfer())
65
            ->willReturn(
66
                (new ProductConcreteAvailabilityTransfer())
67
                    ->setIsNeverOutOfStock(true),
68
            );
69
70
        $sellable = $this->createSellable($availabilityRepositoryMock);
71
        $isSellable = $sellable->isProductSellableForStore(static::SKU_PRODUCT, new Decimal(1), $storeTransfer);
72
73
        $this->assertTrue($isSellable);
74
    }
75
76
    /**
77
     * @return array
78
     */
79
    public function isProductSellableStoresDataProvider(): array
80
    {
81
        return [
82
            'store with ID' => [$this->createStoreTransfer()],
83
            'store without ID' => [(new StoreTransfer())->setName('DE')],
84
        ];
85
    }
86
87
    /**
88
     * @dataProvider reservedItemsAndExistingStockDataProvider
89
     *
90
     * @param \Spryker\DecimalObject\Decimal $existingAvailability
91
     *
92
     * @return void
93
     */
94
    public function testIsProductSellableWhenProductHaveInStockShouldReturnIsSellable(Decimal $existingAvailability): void
95
    {
96
        $storeTransfer = $this->createStoreTransfer();
97
        $availabilityRepositoryMock = $this->createAvailabilityRepositoryMock();
98
        $availabilityRepositoryMock->method('findProductConcreteAvailabilityBySkuAndStore')
99
            ->with(static::SKU_PRODUCT, $storeTransfer)
100
            ->willReturn(
101
                (new ProductConcreteAvailabilityTransfer())
102
                    ->setAvailability($existingAvailability)
103
                    ->setIsNeverOutOfStock(false),
104
            );
105
106
        $sellable = $this->createSellable($availabilityRepositoryMock);
107
        $isSellable = $sellable->isProductSellableForStore(static::SKU_PRODUCT, new Decimal(1), $storeTransfer);
108
109
        $this->assertTrue($isSellable);
110
    }
111
112
    /**
113
     * @return void
114
     */
115
    public function testAreProductConcretesSellableForStoreWhenProductOutOfStockShouldReturnIsNotSellable(): void
116
    {
117
        // Arrange
118
        $storeTransfer = $this->createStoreTransfer();
119
        $availabilityRepositoryMock = $this->createAvailabilityRepositoryMock();
120
        $availabilityRepositoryMock->method('findProductConcreteAvailabilityBySkusAndStore')
121
            ->with([static::SKU_PRODUCT, static::SKU_PRODUCT_SECOND], $storeTransfer)
122
            ->willReturn(
123
                [
124
                    (new ProductConcreteAvailabilityTransfer())
125
                        ->setSku(static::SKU_PRODUCT)
126
                        ->setAvailability(6)
127
                        ->setIsNeverOutOfStock(false),
128
                    (new ProductConcreteAvailabilityTransfer())
129
                        ->setSku(static::SKU_PRODUCT_SECOND)
130
                        ->setAvailability(6)
131
                        ->setIsNeverOutOfStock(false),
132
                ],
133
            );
134
        $batchRequestData = [
135
            [
136
                'sku' => static::SKU_PRODUCT,
137
                'quantity' => 6,
138
            ],
139
            [
140
                'sku' => static::SKU_PRODUCT_SECOND,
141
                'quantity' => 7,
142
            ],
143
        ];
144
145
        $sellableItemsRequestTransfer = $this->createSellableItemsRequestTransfer($storeTransfer);
146
        $sellableItemsRequestTransfer = $this->addSellableItemRequestTransfersFromArray(
147
            $sellableItemsRequestTransfer,
148
            $batchRequestData,
149
        );
150
        $sellableItemsResponseTransfer = $this->createSellableItemsResponseTransfer();
151
152
        $sellable = $this->createSellable($availabilityRepositoryMock);
153
154
        //Act
155
        $sellableItemsResponseTransfer = $sellable->areProductConcretesSellableForStore(
156
            $sellableItemsRequestTransfer,
157
            $sellableItemsResponseTransfer,
158
        );
159
160
        //Assert
161
        $sellableItemsResponseTransferMap = $this->getSellableItemResponseTransfersMapBySku($sellableItemsResponseTransfer);
162
163
        $this->assertSame(2, count($sellableItemsResponseTransfer->getSellableItemResponses()));
164
        $this->assertTrue($sellableItemsResponseTransferMap[static::SKU_PRODUCT]->getIsSellable());
165
166
        $this->assertFalse($sellableItemsResponseTransferMap[static::SKU_PRODUCT_SECOND]->getIsSellable());
167
    }
168
169
    /**
170
     * @return void
171
     */
172
    public function testAreProductConcretesSellableForStoreWhenProductsInStockShouldReturnIsSellable(): void
173
    {
174
        // Arrange
175
        $storeTransfer = $this->createStoreTransfer();
176
        $availabilityRepositoryMock = $this->createAvailabilityRepositoryMock();
177
        $availabilityRepositoryMock->method('findProductConcreteAvailabilityBySkusAndStore')
178
            ->with([static::SKU_PRODUCT, static::SKU_PRODUCT_SECOND], $storeTransfer)
179
            ->willReturn(
180
                [
181
                    (new ProductConcreteAvailabilityTransfer())
182
                        ->setSku(static::SKU_PRODUCT)
183
                        ->setAvailability(6)
184
                        ->setIsNeverOutOfStock(false),
185
                    (new ProductConcreteAvailabilityTransfer())
186
                        ->setSku(static::SKU_PRODUCT_SECOND)
187
                        ->setAvailability(6)
188
                        ->setIsNeverOutOfStock(false),
189
                ],
190
            );
191
        $batchRequestData = [
192
            [
193
                'sku' => static::SKU_PRODUCT,
194
                'quantity' => 5,
195
            ],
196
            [
197
                'sku' => static::SKU_PRODUCT_SECOND,
198
                'quantity' => 5,
199
            ],
200
        ];
201
202
        $sellableItemsRequestTransfer = $this->createSellableItemsRequestTransfer($storeTransfer);
203
        $sellableItemsRequestTransfer = $this->addSellableItemRequestTransfersFromArray(
204
            $sellableItemsRequestTransfer,
205
            $batchRequestData,
206
        );
207
        $sellableItemsResponseTransfer = $this->createSellableItemsResponseTransfer();
208
209
        $sellable = $this->createSellable($availabilityRepositoryMock);
210
211
        //Act
212
        $sellableItemsResponseTransfer = $sellable->areProductConcretesSellableForStore(
213
            $sellableItemsRequestTransfer,
214
            $sellableItemsResponseTransfer,
215
        );
216
217
        //Assert
218
        $sellableItemsResponseTransferMap = $this->getSellableItemResponseTransfersMapBySku($sellableItemsResponseTransfer);
219
220
        $this->assertSame(2, count($sellableItemsResponseTransfer->getSellableItemResponses()));
221
        $this->assertTrue($sellableItemsResponseTransferMap[static::SKU_PRODUCT]->getIsSellable());
222
223
        $this->assertTrue($sellableItemsResponseTransferMap[static::SKU_PRODUCT_SECOND]->getIsSellable());
224
    }
225
226
    /**
227
     * @return void
228
     */
229
    public function testAreProductConcretesSellableForStoreWhenProductsAreNeverOutOfStockShouldReturnIsSellable(): void
230
    {
231
        // Arrange
232
        $storeTransfer = $this->createStoreTransfer();
233
        $availabilityRepositoryMock = $this->createAvailabilityRepositoryMock();
234
        $availabilityRepositoryMock->method('findProductConcreteAvailabilityBySkusAndStore')
235
            ->with([static::SKU_PRODUCT, static::SKU_PRODUCT_SECOND], $storeTransfer)
236
            ->willReturn(
237
                [
238
                    (new ProductConcreteAvailabilityTransfer())
239
                        ->setSku(static::SKU_PRODUCT)
240
                        ->setIsNeverOutOfStock(true),
241
                    (new ProductConcreteAvailabilityTransfer())
242
                        ->setSku(static::SKU_PRODUCT_SECOND)
243
                        ->setIsNeverOutOfStock(true),
244
                ],
245
            );
246
        $batchRequestData = [
247
            [
248
                'sku' => static::SKU_PRODUCT,
249
                'quantity' => 5,
250
            ],
251
            [
252
                'sku' => static::SKU_PRODUCT_SECOND,
253
                'quantity' => 5,
254
            ],
255
        ];
256
257
        $sellableItemsRequestTransfer = $this->createSellableItemsRequestTransfer($storeTransfer);
258
        $sellableItemsRequestTransfer = $this->addSellableItemRequestTransfersFromArray(
259
            $sellableItemsRequestTransfer,
260
            $batchRequestData,
261
        );
262
        $sellableItemsResponseTransfer = $this->createSellableItemsResponseTransfer();
263
264
        $sellable = $this->createSellable($availabilityRepositoryMock);
265
266
        //Act
267
        $sellableItemsResponseTransfer = $sellable->areProductConcretesSellableForStore(
268
            $sellableItemsRequestTransfer,
269
            $sellableItemsResponseTransfer,
270
        );
271
272
        //Assert
273
        $sellableItemsResponseTransferMap = $this->getSellableItemResponseTransfersMapBySku($sellableItemsResponseTransfer);
274
275
        $this->assertSame(2, count($sellableItemsResponseTransfer->getSellableItemResponses()));
276
        $this->assertTrue($sellableItemsResponseTransferMap[static::SKU_PRODUCT]->getIsSellable());
277
278
        $this->assertTrue($sellableItemsResponseTransferMap[static::SKU_PRODUCT_SECOND]->getIsSellable());
279
    }
280
281
    /**
282
     * @return array
283
     */
284
    public function reservedItemsAndExistingStockDataProvider(): array
285
    {
286
        return [
287
            'int stock' => [new Decimal(10)],
288
            'float stock' => [new Decimal(9.8)],
289
            'float stock high precision' => [new Decimal(1.4444444444444)],
290
        ];
291
    }
292
293
    /**
294
     * @return void
295
     */
296
    public function testAreProductsSellableForStoreReturnsCriteriaInResponse(): void
297
    {
298
        // Arrange
299
        $storeTransfer = $this->createStoreTransfer();
300
        $batchRequestData = [
301
            [
302
                'sku' => static::SKU_PRODUCT,
303
                'quantity' => 1,
304
            ],
305
        ];
306
307
        $sellableItemsRequestTransfer = $this->createSellableItemsRequestTransfer($storeTransfer);
308
        $sellableItemsRequestTransfer = $this->addSellableItemRequestTransfersFromArray(
309
            $sellableItemsRequestTransfer,
310
            $batchRequestData,
311
        );
312
313
        $productAvailabilityCriteriaTransfer = (new ProductAvailabilityCriteriaTransfer())
314
            ->setProductOfferReference(static::PRODUCT_OFFER_REFERENCE);
315
316
        $sellableItemsRequestTransfer
317
            ->getSellableItemRequests()
318
            ->offsetGet(0)
319
            ->setProductAvailabilityCriteria($productAvailabilityCriteriaTransfer);
320
321
        $productConcreteAvailabilityTransfer = (new ProductConcreteAvailabilityTransfer())
322
            ->setSku(static::SKU_PRODUCT)
323
            ->setAvailability(1)
324
            ->setIsNeverOutOfStock(true);
325
326
        $availabilityStrategyPlugins = [$this->createAvailabilityStrategyPluginMock($productConcreteAvailabilityTransfer)];
327
        $sellable = $this->createSellable(null, null, null, $availabilityStrategyPlugins);
328
329
        // Act
330
        $sellableItemsResponseTransfer = $sellable->areProductsSellableForStore(
331
            $sellableItemsRequestTransfer,
332
        );
333
334
        // Assert
335
        $this->assertSame(
336
            $productAvailabilityCriteriaTransfer,
337
            $sellableItemsResponseTransfer->getSellableItemResponses()->offsetGet(0)->getProductAvailabilityCriteria(),
338
        );
339
    }
340
341
    /**
342
     * @param \Generated\Shared\Transfer\SellableItemsResponseTransfer $sellableItemsResponseTransfer
343
     *
344
     * @return array<\Generated\Shared\Transfer\SellableItemResponseTransfer>
345
     */
346
    protected function getSellableItemResponseTransfersMapBySku(SellableItemsResponseTransfer $sellableItemsResponseTransfer): array
347
    {
348
        $sellableItemsResponseTransferMap = [];
349
350
        foreach ($sellableItemsResponseTransfer->getSellableItemResponses() as $sellableItemResponseTransfer) {
351
            $sellableItemsResponseTransferMap[$sellableItemResponseTransfer->getSku()] = $sellableItemResponseTransfer;
352
        }
353
354
        return $sellableItemsResponseTransferMap;
355
    }
356
357
    /**
358
     * @return \Generated\Shared\Transfer\SellableItemsResponseTransfer
359
     */
360
    protected function createSellableItemsResponseTransfer(): SellableItemsResponseTransfer
361
    {
362
        return new SellableItemsResponseTransfer();
363
    }
364
365
    /**
366
     * @param \Generated\Shared\Transfer\StoreTransfer $storeTransfer
367
     *
368
     * @return \Generated\Shared\Transfer\SellableItemsRequestTransfer
369
     */
370
    protected function createSellableItemsRequestTransfer(StoreTransfer $storeTransfer): SellableItemsRequestTransfer
371
    {
372
        $sellableItemsRequestTransfer = new SellableItemsRequestTransfer();
373
        $sellableItemsRequestTransfer->setStore($storeTransfer);
374
375
        return $sellableItemsRequestTransfer;
376
    }
377
378
    /**
379
     * @param \Generated\Shared\Transfer\SellableItemsRequestTransfer $sellableItemsRequestTransfer
380
     * @param array<mixed> $batchRequestData
381
     *
382
     * @return \Generated\Shared\Transfer\SellableItemsRequestTransfer
383
     */
384
    protected function addSellableItemRequestTransfersFromArray(
385
        SellableItemsRequestTransfer $sellableItemsRequestTransfer,
386
        array $batchRequestData
387
    ): SellableItemsRequestTransfer {
388
        foreach ($batchRequestData as $sellableItemRequest) {
389
            $sellableItemRequestTransfer = new SellableItemRequestTransfer();
390
            $sellableItemRequestTransfer->fromArray($sellableItemRequest, true);
391
            $sellableItemsRequestTransfer->addSellableItemRequest($sellableItemRequestTransfer);
392
        }
393
394
        return $sellableItemsRequestTransfer;
395
    }
396
397
    /**
398
     * @param \Spryker\Zed\Availability\Persistence\AvailabilityRepositoryInterface|null $availabilityRepositoryMock
399
     * @param \Spryker\Zed\Availability\Business\Model\AvailabilityHandlerInterface|null $availabilityHandlerMock
400
     * @param \Spryker\Zed\Availability\Dependency\Facade\AvailabilityToStoreFacadeInterface|null $storeFacade
401
     * @param list<\Spryker\Zed\AvailabilityExtension\Dependency\Plugin\AvailabilityStrategyPluginInterface> $availabilityStrategyPlugins
402
     *
403
     * @return \Spryker\Zed\Availability\Business\Model\SellableInterface
404
     */
405
    protected function createSellable(
406
        ?AvailabilityRepositoryInterface $availabilityRepositoryMock = null,
407
        ?AvailabilityHandlerInterface $availabilityHandlerMock = null,
408
        ?AvailabilityToStoreFacadeInterface $storeFacade = null,
409
        array $availabilityStrategyPlugins = []
410
    ): SellableInterface {
411
        if ($availabilityRepositoryMock === null) {
412
            $availabilityRepositoryMock = $this->createAvailabilityRepositoryMock();
413
        }
414
415
        if ($availabilityHandlerMock === null) {
416
            $availabilityHandlerMock = $this->createAvailabilityHandlerMock();
417
        }
418
419
        if ($storeFacade === null) {
420
            $storeFacade = $this->createStoreFacade();
421
            $storeFacade->method('getCurrentStore')
422
                ->willReturn($this->createStoreTransfer());
423
424
            $storeFacade->method('getStoreByName')
425
                ->willReturn($this->createStoreTransfer());
426
        }
427
428
        return new Sellable(
429
            $availabilityRepositoryMock,
430
            $availabilityHandlerMock,
431
            $storeFacade,
432
            $availabilityStrategyPlugins,
433
            [],
434
        );
435
    }
436
437
    /**
438
     * @param \Generated\Shared\Transfer\ProductConcreteAvailabilityTransfer $productConcreteAvailabilityTransfer
439
     *
440
     * @return \SprykerTest\Zed\Availability\Business\Model\MockObject|\Spryker\Zed\AvailabilityExtension\Dependency\Plugin\AvailabilityStrategyPluginInterface
441
     */
442
    protected function createAvailabilityStrategyPluginMock(
443
        ProductConcreteAvailabilityTransfer $productConcreteAvailabilityTransfer
444
    ): AvailabilityStrategyPluginInterface {
445
        $availabilityStrategyPluginMock = $this->getMockBuilder(AvailabilityStrategyPluginInterface::class)->getMock();
446
        $availabilityStrategyPluginMock->method('isApplicable')->willReturn(true);
447
        $availabilityStrategyPluginMock->method('findProductConcreteAvailabilityForStore')->willReturn($productConcreteAvailabilityTransfer);
448
449
        return $availabilityStrategyPluginMock;
450
    }
451
452
    /**
453
     * @return \Generated\Shared\Transfer\StoreTransfer
454
     */
455
    protected function createStoreTransfer(): StoreTransfer
456
    {
457
        return (new StoreTransfer())->setName('DE')->setIdStore(1);
458
    }
459
460
    /**
461
     * @return \PHPUnit\Framework\MockObject\MockObject|\Spryker\Zed\Availability\Business\Model\AvailabilityHandlerInterface
462
     */
463
    protected function createAvailabilityHandlerMock(): AvailabilityHandlerInterface
464
    {
465
        return $this->getMockBuilder(AvailabilityHandlerInterface::class)->getMock();
466
    }
467
468
    /**
469
     * @return \PHPUnit\Framework\MockObject\MockObject|\Spryker\Zed\Availability\Dependency\Facade\AvailabilityToStoreFacadeInterface
470
     */
471
    protected function createStoreFacade(): AvailabilityToStoreFacadeInterface
472
    {
473
        return $this->getMockBuilder(AvailabilityToStoreFacadeInterface::class)->getMock();
474
    }
475
476
    /**
477
     * @return \Spryker\Zed\Availability\Persistence\AvailabilityRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject
478
     */
479
    protected function createAvailabilityRepositoryMock(): AvailabilityRepositoryInterface
480
    {
481
        return $this->getMockBuilder(AvailabilityRepositoryInterface::class)->getMock();
482
    }
483
}
484