Skip to content

Commit 9369a03

Browse files
committed
ACP2E-1747: Auto Coupon incorrectly marked as used for failed card payment
1 parent a047c76 commit 9369a03

File tree

6 files changed

+230
-3
lines changed

6 files changed

+230
-3
lines changed

app/code/Magento/MessageQueue/Model/CheckIsAvailableMessagesInQueue.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace Magento\MessageQueue\Model;
99

10+
use Magento\Framework\MessageQueue\CountableQueueInterface;
1011
use Magento\Framework\MessageQueue\QueueRepository;
1112

1213
/**
@@ -40,6 +41,9 @@ public function __construct(QueueRepository $queueRepository)
4041
public function execute($connectionName, $queueName)
4142
{
4243
$queue = $this->queueRepository->get($connectionName, $queueName);
44+
if ($queue instanceof CountableQueueInterface) {
45+
return $queue->count() > 0;
46+
}
4347
$message = $queue->dequeue();
4448
if ($message) {
4549
$queue->reject($message);
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\MessageQueue\Test\Unit\Model;
9+
10+
use Magento\Framework\MessageQueue\CountableQueueInterface;
11+
use Magento\Framework\MessageQueue\EnvelopeInterface;
12+
use Magento\Framework\MessageQueue\QueueInterface;
13+
use Magento\Framework\MessageQueue\QueueRepository;
14+
use Magento\MessageQueue\Model\CheckIsAvailableMessagesInQueue;
15+
use PHPUnit\Framework\MockObject\MockObject;
16+
use PHPUnit\Framework\TestCase;
17+
18+
/**
19+
* Test for CheckIsAvailableMessagesInQueue
20+
*/
21+
class CheckIsAvailableMessagesInQueueTest extends TestCase
22+
{
23+
/**
24+
* @var QueueRepository|MockObject
25+
*/
26+
private $queueRepository;
27+
28+
/**
29+
* @var CheckIsAvailableMessagesInQueue
30+
*/
31+
private $model;
32+
33+
/**
34+
* @inheritdoc
35+
*/
36+
protected function setUp(): void
37+
{
38+
parent::setUp();
39+
$this->queueRepository = $this->createMock(QueueRepository::class);
40+
$this->model = new CheckIsAvailableMessagesInQueue(
41+
$this->queueRepository
42+
);
43+
}
44+
45+
public function testExecuteNotCountableAndNotEmptyQueue(): void
46+
{
47+
$connectionName = 'test';
48+
$queueName = 'test';
49+
50+
$queue = $this->getMockForAbstractClass(QueueInterface::class);
51+
$message = $this->getMockForAbstractClass(EnvelopeInterface::class);
52+
$this->queueRepository->expects($this->once())
53+
->method('get')
54+
->with($connectionName, $queueName)
55+
->willReturn($queue);
56+
$queue->expects($this->once())
57+
->method('dequeue')
58+
->willReturn($message);
59+
$queue->expects($this->once())
60+
->method('reject')
61+
->willReturn($message);
62+
$this->assertTrue($this->model->execute($connectionName, $queueName));
63+
}
64+
65+
public function testExecuteNotCountableAndEmptyQueue(): void
66+
{
67+
$connectionName = 'test';
68+
$queueName = 'test';
69+
70+
$queue = $this->getMockForAbstractClass(QueueInterface::class);
71+
$this->queueRepository->expects($this->once())
72+
->method('get')
73+
->with($connectionName, $queueName)
74+
->willReturn($queue);
75+
$queue->expects($this->once())
76+
->method('dequeue')
77+
->willReturn(null);
78+
$this->assertFalse($this->model->execute($connectionName, $queueName));
79+
}
80+
81+
public function testExecuteCountableAndNotEmptyQueue(): void
82+
{
83+
$connectionName = 'test';
84+
$queueName = 'test';
85+
86+
$queue = $this->getMockForAbstractClass(CountableQueueInterface::class);
87+
$this->queueRepository->expects($this->once())
88+
->method('get')
89+
->with($connectionName, $queueName)
90+
->willReturn($queue);
91+
$queue->expects($this->once())
92+
->method('count')
93+
->willReturn(1);
94+
$queue->expects($this->never())
95+
->method('dequeue');
96+
$this->assertTrue($this->model->execute($connectionName, $queueName));
97+
}
98+
99+
public function testExecuteCountableAndEmptyQueue(): void
100+
{
101+
$connectionName = 'test';
102+
$queueName = 'test';
103+
104+
$queue = $this->getMockForAbstractClass(CountableQueueInterface::class);
105+
$this->queueRepository->expects($this->once())
106+
->method('get')
107+
->with($connectionName, $queueName)
108+
->willReturn($queue);
109+
$queue->expects($this->once())
110+
->method('count')
111+
->willReturn(0);
112+
$queue->expects($this->never())
113+
->method('dequeue');
114+
$this->assertFalse($this->model->execute($connectionName, $queueName));
115+
}
116+
}

app/code/Magento/MysqlMq/Model/Driver/Queue.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
*/
66
namespace Magento\MysqlMq\Model\Driver;
77

8+
use Magento\Framework\App\ObjectManager;
9+
use Magento\Framework\MessageQueue\CountableQueueInterface;
810
use Magento\Framework\MessageQueue\EnvelopeInterface;
9-
use Magento\Framework\MessageQueue\QueueInterface;
1011
use Magento\MysqlMq\Model\QueueManagement;
1112
use Magento\Framework\MessageQueue\EnvelopeFactory;
13+
use Magento\MysqlMq\Model\ResourceModel\Queue as QueueResourceModel;
1214
use Psr\Log\LoggerInterface;
1315

1416
/**
1517
* Queue based on MessageQueue protocol
1618
*/
17-
class Queue implements QueueInterface
19+
class Queue implements CountableQueueInterface
1820
{
1921
/**
2022
* @var QueueManagement
@@ -46,6 +48,11 @@ class Queue implements QueueInterface
4648
*/
4749
private $logger;
4850

51+
/**
52+
* @var QueueResourceModel
53+
*/
54+
private $queueResourceModel;
55+
4956
/**
5057
* Queue constructor.
5158
*
@@ -55,21 +62,25 @@ class Queue implements QueueInterface
5562
* @param string $queueName
5663
* @param int $interval
5764
* @param int $maxNumberOfTrials
65+
* @param QueueResourceModel|null $queueResourceModel
5866
*/
5967
public function __construct(
6068
QueueManagement $queueManagement,
6169
EnvelopeFactory $envelopeFactory,
6270
LoggerInterface $logger,
6371
$queueName,
6472
$interval = 5,
65-
$maxNumberOfTrials = 3
73+
$maxNumberOfTrials = 3,
74+
?QueueResourceModel $queueResourceModel = null
6675
) {
6776
$this->queueManagement = $queueManagement;
6877
$this->envelopeFactory = $envelopeFactory;
6978
$this->queueName = $queueName;
7079
$this->interval = $interval;
7180
$this->maxNumberOfTrials = $maxNumberOfTrials;
7281
$this->logger = $logger;
82+
$this->queueResourceModel = $queueResourceModel
83+
?? ObjectManager::getInstance()->get(QueueResourceModel::class);
7384
}
7485

7586
/**
@@ -151,4 +162,12 @@ public function push(EnvelopeInterface $envelope)
151162
[$this->queueName]
152163
);
153164
}
165+
166+
/**
167+
* @inheritDoc
168+
*/
169+
public function count(): int
170+
{
171+
return $this->queueResourceModel->getMessagesCount($this->queueName);
172+
}
154173
}

app/code/Magento/MysqlMq/Model/ResourceModel/Queue.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
*/
66
namespace Magento\MysqlMq\Model\ResourceModel;
77

8+
use Magento\Framework\DB\Select;
9+
use Magento\Framework\DB\Sql\Expression;
810
use Magento\MysqlMq\Model\QueueManagement;
911

1012
/**
@@ -240,6 +242,35 @@ public function changeStatus($relationIds, $status)
240242
);
241243
}
242244

245+
/**
246+
* Get number of pending messages in the queue
247+
*
248+
* @param string $queueName
249+
* @return int
250+
*/
251+
public function getMessagesCount(string $queueName): int
252+
{
253+
$connection = $this->getConnection();
254+
$select = $connection->select()
255+
->from(
256+
['queue_message' => $this->getMessageTable()],
257+
)->join(
258+
['queue_message_status' => $this->getMessageStatusTable()],
259+
'queue_message.id = queue_message_status.message_id'
260+
)->join(
261+
['queue' => $this->getQueueTable()],
262+
'queue.id = queue_message_status.queue_id'
263+
)->where(
264+
'queue_message_status.status IN (?)',
265+
[QueueManagement::MESSAGE_STATUS_NEW, QueueManagement::MESSAGE_STATUS_RETRY_REQUIRED]
266+
)->where('queue.name = ?', $queueName);
267+
268+
$select->reset(Select::COLUMNS);
269+
$select->columns(new Expression('COUNT(*)'));
270+
271+
return (int) $connection->fetchOne($select);
272+
}
273+
243274
/**
244275
* Get name of table storing message statuses and associations to queues.
245276
*

dev/tests/integration/testsuite/Magento/MysqlMq/Model/Driver/QueueTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace Magento\MysqlMq\Model\Driver;
77

88
use Magento\MysqlMq\Model\Driver\Queue;
9+
use Magento\MysqlMq\Model\ResourceModel\MessageCollection;
910

1011
/**
1112
* Test for MySQL queue driver class.
@@ -43,6 +44,11 @@ protected function tearDown(): void
4344
/** @var \Magento\Framework\MessageQueue\Config\Data $queueConfig */
4445
$queueConfig = $this->objectManager->get(\Magento\Framework\MessageQueue\Config\Data::class);
4546
$queueConfig->reset();
47+
$messageCollection = $this->objectManager->create(MessageCollection::class);
48+
foreach ($messageCollection as $message) {
49+
$message->delete();
50+
}
51+
parent::tearDown();
4652
}
4753

4854
/**
@@ -65,4 +71,29 @@ public function testPushAndDequeue()
6571
$this->assertArrayHasKey('topic_name', $actualMessageProperties);
6672
$this->assertEquals($topicName, $actualMessageProperties['topic_name']);
6773
}
74+
75+
/**
76+
* @magentoDataFixture Magento/MysqlMq/_files/queues.php
77+
*/
78+
public function testCount()
79+
{
80+
/** @var \Magento\Framework\MessageQueue\EnvelopeFactory $envelopFactory */
81+
$envelopFactory = $this->objectManager->get(\Magento\Framework\MessageQueue\EnvelopeFactory::class);
82+
$messageBody = '{"data": {"body": "Message body"}, "message_id": 1}';
83+
$topicName = 'some.topic';
84+
$envelop1 = $envelopFactory->create(['body' => $messageBody, 'properties' => ['topic_name' => $topicName]]);
85+
$envelop2 = $envelopFactory->create(['body' => $messageBody, 'properties' => ['topic_name' => $topicName]]);
86+
$envelop3 = $envelopFactory->create(['body' => $messageBody, 'properties' => ['topic_name' => $topicName]]);
87+
88+
$this->queue->push($envelop1);
89+
$this->queue->push($envelop2);
90+
$this->queue->push($envelop3);
91+
92+
// Take first message in progress and reject
93+
$this->queue->reject($this->queue->dequeue());
94+
// Take second message in progress
95+
$this->queue->dequeue();
96+
// Assert that only 2 messages are available in queue (message1 and message3)
97+
$this->assertEquals(2, $this->queue->count());
98+
}
6899
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Framework\MessageQueue;
9+
10+
use Countable;
11+
12+
/**
13+
* {@inheritdoc}
14+
*
15+
* Queue driver that implements this interface must implement count() method
16+
* that returns the number of pending messages in the queue
17+
*/
18+
interface CountableQueueInterface extends QueueInterface, Countable
19+
{
20+
/**
21+
* Get number of pending messages in the queue
22+
*
23+
* @return int
24+
*/
25+
public function count(): int;
26+
}

0 commit comments

Comments
 (0)