AMQPBackendTest   A
last analyzed

Complexity

Total Complexity 9

Size/Duplication

Total Lines 302
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 9
lcom 1
cbo 8
dl 0
loc 302
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 9 2
A testInitializeWithNoDeadLetterExchangeAndNoDeadLetterRoutingKey() 0 48 1
A testInitializeWithDeadLetterExchangeAndNoDeadLetterRoutingKey() 0 53 1
A testInitializeWithDeadLetterExchangeAndDeadLetterRoutingKey() 0 45 1
A testInitializeWithTTL() 0 38 1
A testGetIteratorWithNoPrefetchCount() 0 27 1
A testGetIteratorWithPrefetchCount() 0 27 1
A buildBackend() 0 37 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\NotificationBundle\Tests\Backend;
15
16
use Enqueue\AmqpLib\AmqpConnectionFactory;
17
use Enqueue\AmqpLib\AmqpConsumer;
18
use Enqueue\AmqpLib\AmqpContext;
19
use Interop\Amqp\AmqpBind;
20
use Interop\Amqp\AmqpQueue;
21
use Interop\Amqp\AmqpTopic;
22
use Interop\Amqp\Impl\AmqpBind as ImplAmqpBind;
23
use Interop\Amqp\Impl\AmqpQueue as ImplAmqpQueue;
24
use Interop\Amqp\Impl\AmqpTopic as ImplAmqpTopic;
25
use PHPUnit\Framework\TestCase;
26
use Sonata\NotificationBundle\Backend\AMQPBackend;
27
use Sonata\NotificationBundle\Backend\AMQPBackendDispatcher;
28
use Sonata\NotificationBundle\Iterator\AMQPMessageIterator;
29
use Sonata\NotificationBundle\Tests\Mock\AmqpConnectionFactoryStub;
30
31
class AMQPBackendTest extends TestCase
32
{
33
    public const EXCHANGE = 'exchange';
34
    public const QUEUE = 'foo';
35
    public const KEY = 'message.type.foo';
36
    public const DEAD_LETTER_EXCHANGE = 'dlx';
37
    public const DEAD_LETTER_ROUTING_KEY = 'message.type.dl';
38
    public const TTL = 60000;
39
    public const PREFETCH_COUNT = 1;
40
41
    protected function setUp(): void
42
    {
43
        if (!class_exists(AmqpConnectionFactory::class)) {
44
            $this->markTestSkipped('enqueue/amqp-lib library is not installed');
45
        }
46
47
        AmqpConnectionFactoryStub::$context = null;
48
        AmqpConnectionFactoryStub::$config = null;
49
    }
50
51
    public function testInitializeWithNoDeadLetterExchangeAndNoDeadLetterRoutingKey(): void
52
    {
53
        $backend = $this->buildBackend();
54
55
        $queue = new ImplAmqpQueue(self::QUEUE);
56
        $topic = new ImplAmqpTopic(self::EXCHANGE);
57
58
        $contextMock = $this->createMock(AmqpContext::class);
59
        $contextMock->expects($this->once())
60
            ->method('createQueue')
61
            ->with($this->identicalTo(self::QUEUE))
62
            ->willReturn($queue);
63
64
        $contextMock->expects($this->once())
65
            ->method('declareQueue')
66
            ->with($this->identicalTo($queue))
67
            ->willReturnCallback(function (AmqpQueue $queue): void {
68
                $this->assertTrue((bool) ($queue->getFlags() & AmqpQueue::FLAG_DURABLE));
69
                $this->assertSame([], $queue->getArguments());
70
            });
71
72
        $contextMock->expects($this->once())
73
            ->method('createTopic')
74
            ->with($this->identicalTo(self::EXCHANGE))
75
            ->willReturn($topic);
76
77
        $contextMock->expects($this->once())
78
            ->method('declareTopic')
79
            ->with($this->identicalTo($topic))
80
            ->willReturnCallback(function (AmqpTopic $topic): void {
81
                $this->assertTrue((bool) ($topic->getFlags() & AmqpTopic::FLAG_DURABLE));
82
                $this->assertSame(AmqpTopic::TYPE_DIRECT, $topic->getType());
83
                $this->assertSame([], $topic->getArguments());
84
            });
85
86
        $contextMock->expects($this->once())
87
            ->method('bind')
88
            ->with($this->isInstanceOf(AmqpBind::class))
89
            ->willReturnCallback(function (ImplAmqpBind $bind) use ($queue, $topic): void {
90
                $this->assertSame($queue, $bind->getTarget());
91
                $this->assertSame($topic, $bind->getSource());
92
                $this->assertSame(self::KEY, $bind->getRoutingKey());
93
            });
94
95
        AmqpConnectionFactoryStub::$context = $contextMock;
96
97
        $backend->initialize();
98
    }
99
100
    public function testInitializeWithDeadLetterExchangeAndNoDeadLetterRoutingKey(): void
101
    {
102
        $backend = $this->buildBackend(false, self::DEAD_LETTER_EXCHANGE);
103
104
        $queue = new ImplAmqpQueue(self::QUEUE);
105
        $topic = new ImplAmqpTopic(self::EXCHANGE);
106
        $deadLetterTopic = new ImplAmqpTopic(self::DEAD_LETTER_EXCHANGE);
107
108
        $contextMock = $this->createMock(AmqpContext::class);
109
        $contextMock->expects($this->at(0))
110
            ->method('createQueue')
111
            ->with($this->identicalTo(self::QUEUE))
112
            ->willReturn($queue);
113
114
        $contextMock->expects($this->at(1))
115
            ->method('declareQueue')
116
            ->with($this->identicalTo($queue))
117
            ->willReturnCallback(function (AmqpQueue $queue): void {
118
                $this->assertTrue((bool) ($queue->getFlags() & AmqpQueue::FLAG_DURABLE));
119
                $this->assertSame(['x-dead-letter-exchange' => self::DEAD_LETTER_EXCHANGE], $queue->getArguments());
120
            });
121
122
        $contextMock->expects($this->at(2))
123
            ->method('createTopic')
124
            ->with($this->identicalTo(self::EXCHANGE))
125
            ->willReturn($topic);
126
127
        $contextMock->expects($this->at(3))
128
            ->method('declareTopic')
129
            ->with($this->identicalTo($topic));
130
131
        $contextMock->expects($this->at(4))
132
            ->method('bind')
133
            ->with($this->isInstanceOf(AmqpBind::class));
134
135
        $contextMock->expects($this->at(5))
136
            ->method('createTopic')
137
            ->with($this->identicalTo(self::DEAD_LETTER_EXCHANGE))
138
            ->willReturn($deadLetterTopic);
139
140
        $contextMock->expects($this->at(6))
141
            ->method('declareTopic')
142
            ->with($this->identicalTo($deadLetterTopic))
143
            ->willReturnCallback(function (AmqpTopic $topic): void {
144
                $this->assertTrue((bool) ($topic->getFlags() & AmqpTopic::FLAG_DURABLE));
145
                $this->assertSame(AmqpTopic::TYPE_DIRECT, $topic->getType());
146
                $this->assertSame([], $topic->getArguments());
147
            });
148
149
        AmqpConnectionFactoryStub::$context = $contextMock;
150
151
        $backend->initialize();
152
    }
153
154
    public function testInitializeWithDeadLetterExchangeAndDeadLetterRoutingKey(): void
155
    {
156
        $backend = $this->buildBackend(false, self::DEAD_LETTER_EXCHANGE, self::DEAD_LETTER_ROUTING_KEY);
157
158
        $queue = new ImplAmqpQueue(self::QUEUE);
159
        $topic = new ImplAmqpTopic(self::EXCHANGE);
160
        $deadLetterTopic = new ImplAmqpTopic(self::DEAD_LETTER_EXCHANGE);
0 ignored issues
show
Unused Code introduced by
$deadLetterTopic is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
161
162
        $contextMock = $this->createMock(AmqpContext::class);
163
        $contextMock->expects($this->at(0))
164
            ->method('createQueue')
165
            ->with($this->identicalTo(self::QUEUE))
166
            ->willReturn($queue);
167
168
        $contextMock->expects($this->at(1))
169
            ->method('declareQueue')
170
            ->with($this->identicalTo($queue))
171
            ->willReturnCallback(function (AmqpQueue $queue): void {
172
                $this->assertTrue((bool) ($queue->getFlags() & AmqpQueue::FLAG_DURABLE));
173
                $this->assertSame(
174
                    [
175
                        'x-dead-letter-exchange' => self::DEAD_LETTER_EXCHANGE,
176
                        'x-dead-letter-routing-key' => self::DEAD_LETTER_ROUTING_KEY,
177
                    ],
178
                    $queue->getArguments()
179
                );
180
            });
181
182
        $contextMock->expects($this->at(2))
183
            ->method('createTopic')
184
            ->with($this->identicalTo(self::EXCHANGE))
185
            ->willReturn($topic);
186
187
        $contextMock->expects($this->at(3))
188
            ->method('declareTopic')
189
            ->with($this->identicalTo($topic));
190
191
        $contextMock->expects($this->at(4))
192
            ->method('bind')
193
            ->with($this->isInstanceOf(AmqpBind::class));
194
195
        AmqpConnectionFactoryStub::$context = $contextMock;
196
197
        $backend->initialize();
198
    }
199
200
    public function testInitializeWithTTL(): void
201
    {
202
        $backend = $this->buildBackend(false, null, null, self::TTL);
203
204
        $queue = new ImplAmqpQueue(self::QUEUE);
205
        $topic = new ImplAmqpTopic(self::EXCHANGE);
206
207
        $contextMock = $this->createMock(AmqpContext::class);
208
        $contextMock->expects($this->once())
209
            ->method('createQueue')
210
            ->with($this->identicalTo(self::QUEUE))
211
            ->willReturn($queue);
212
213
        $contextMock->expects($this->once())
214
            ->method('declareQueue')
215
            ->with($this->identicalTo($queue))
216
            ->willReturnCallback(function (AmqpQueue $queue): void {
217
                $this->assertTrue((bool) ($queue->getFlags() & AmqpQueue::FLAG_DURABLE));
218
                $this->assertSame(['x-message-ttl' => self::TTL], $queue->getArguments());
219
            });
220
221
        $contextMock->expects($this->once())
222
            ->method('createTopic')
223
            ->with($this->identicalTo(self::EXCHANGE))
224
            ->willReturn($topic);
225
226
        $contextMock->expects($this->once())
227
            ->method('declareTopic')
228
            ->with($this->identicalTo($topic));
229
230
        $contextMock->expects($this->once())
231
            ->method('bind')
232
            ->with($this->isInstanceOf(AmqpBind::class));
233
234
        AmqpConnectionFactoryStub::$context = $contextMock;
235
236
        $backend->initialize();
237
    }
238
239
    public function testGetIteratorWithNoPrefetchCount(): void
240
    {
241
        $backend = $this->buildBackend();
242
243
        $queue = new ImplAmqpQueue('aQueue');
244
245
        $consumerMock = $this->createMock(AmqpConsumer::class);
246
247
        $contextMock = $this->createMock(AmqpContext::class);
248
        $contextMock->expects($this->never())
249
            ->method('setQos');
250
251
        $contextMock->expects($this->once())
252
            ->method('createQueue')
253
            ->willReturn($queue);
254
255
        $contextMock->expects($this->once())
256
            ->method('createConsumer')
257
            ->with($this->identicalTo($queue))
258
            ->willReturn($consumerMock);
259
260
        AmqpConnectionFactoryStub::$context = $contextMock;
261
262
        $iterator = $backend->getIterator();
263
264
        $this->assertInstanceOf(AMQPMessageIterator::class, $iterator);
265
    }
266
267
    public function testGetIteratorWithPrefetchCount(): void
268
    {
269
        $backend = $this->buildBackend(false, null, null, null, self::PREFETCH_COUNT);
270
271
        $queue = new ImplAmqpQueue('aQueue');
272
        $consumerMock = $this->createMock(AmqpConsumer::class);
273
274
        $contextMock = $this->createMock(AmqpContext::class);
275
        $contextMock->expects($this->once())
276
            ->method('setQos')
277
            ->with($this->isNull(), $this->identicalTo(self::PREFETCH_COUNT), $this->isFalse());
278
279
        $contextMock->expects($this->once())
280
            ->method('createQueue')
281
            ->willReturn($queue);
282
283
        $contextMock->expects($this->once())
284
            ->method('createConsumer')
285
            ->with($this->identicalTo($queue))
286
            ->willReturn($consumerMock);
287
288
        AmqpConnectionFactoryStub::$context = $contextMock;
289
290
        $iterator = $backend->getIterator();
291
292
        $this->assertInstanceOf(AMQPMessageIterator::class, $iterator);
293
    }
294
295
    protected function buildBackend($recover = false, $deadLetterExchange = null, $deadLetterRoutingKey = null, $ttl = null, $prefetchCount = null): AMQPBackend
296
    {
297
        $backend = new AMQPBackend(
298
            self::EXCHANGE,
299
            self::QUEUE,
300
            $recover,
0 ignored issues
show
Documentation introduced by
$recover is of type boolean, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
301
            self::KEY,
302
            $deadLetterExchange,
303
            $deadLetterRoutingKey,
304
            $ttl,
305
            $prefetchCount
306
        );
307
308
        $settings = [
309
            'host' => 'foo',
310
            'port' => 'port',
311
            'user' => 'user',
312
            'pass' => 'pass',
313
            'vhost' => '/',
314
            'factory_class' => AmqpConnectionFactoryStub::class,
315
        ];
316
317
        $queues = [
318
            ['queue' => self::QUEUE, 'routing_key' => self::KEY],
319
        ];
320
321
        $dispatcherMock = new AMQPBackendDispatcher(
322
            $settings,
323
            $queues,
324
            'default',
325
            [['type' => self::KEY, 'backend' => $backend]]
326
        );
327
328
        $backend->setDispatcher($dispatcherMock);
329
330
        return $backend;
331
    }
332
}
333