Completed
Pull Request — master (#281)
by Grégoire
01:45
created

testGetIteratorWithNoPrefetchCount()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 30
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 30
rs 8.8571
cc 1
eloc 20
nc 1
nop 0
1
<?php
2
3
/*
4
 * This file is part of the Sonata Project package.
5
 *
6
 * (c) Thomas Rabaix <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Sonata\NotificationBundle\Tests\Backend;
13
14
use Enqueue\AmqpLib\AmqpConnectionFactory;
15
use Interop\Amqp\AmqpBind;
16
use Interop\Amqp\AmqpConsumer;
17
use Interop\Amqp\AmqpContext;
18
use Interop\Amqp\AmqpQueue;
19
use Interop\Amqp\AmqpTopic;
20
use Interop\Amqp\Impl\AmqpBind as ImplAmqpBind;
21
use Interop\Amqp\Impl\AmqpQueue as ImplAmqpQueue;
22
use Interop\Amqp\Impl\AmqpTopic as ImplAmqpTopic;
23
use PhpAmqpLib\Channel\AMQPChannel;
24
use PHPUnit\Framework\TestCase;
25
use Sonata\NotificationBundle\Backend\AMQPBackend;
26
use Sonata\NotificationBundle\Backend\AMQPBackendDispatcher;
27
use Sonata\NotificationBundle\Iterator\AMQPMessageIterator;
28
use Sonata\NotificationBundle\Tests\Mock\AmqpConnectionFactoryStub;
29
30
class AMQPBackendTest extends TestCase
31
{
32
    const EXCHANGE = 'exchange';
33
    const QUEUE = 'foo';
34
    const KEY = 'message.type.foo';
35
    const DEAD_LETTER_EXCHANGE = 'dlx';
36
    const DEAD_LETTER_ROUTING_KEY = 'message.type.dl';
37
    const TTL = 60000;
38
    const PREFETCH_COUNT = 1;
39
40
    protected function setUp()
41
    {
42
        if (!class_exists(AmqpConnectionFactory::class)) {
43
            $this->markTestSkipped('enqueue/amqp-lib library is not installed');
44
        }
45
46
        AmqpConnectionFactoryStub::$context = null;
47
        AmqpConnectionFactoryStub::$config = null;
48
    }
49
50
    public function testInitializeWithNoDeadLetterExchangeAndNoDeadLetterRoutingKey()
51
    {
52
        $backend = $this->buildBackend();
53
54
        $queue = new ImplAmqpQueue(self::QUEUE);
55
        $topic = new ImplAmqpTopic(self::EXCHANGE);
56
57
        $contextMock = $this->createMock(AmqpContext::class);
58
        $contextMock->expects($this->once())
59
            ->method('createQueue')
60
            ->with($this->identicalTo(self::QUEUE))
61
            ->willReturn($queue);
62
63
        $contextMock->expects($this->once())
64
            ->method('declareQueue')
65
            ->with($this->identicalTo($queue))
66
            ->willReturnCallback(function (AmqpQueue $queue) {
67
                $this->assertTrue((bool) ($queue->getFlags() & AmqpQueue::FLAG_DURABLE));
68
                $this->assertSame([], $queue->getArguments());
69
            });
70
71
        $contextMock->expects($this->once())
72
            ->method('createTopic')
73
            ->with($this->identicalTo(self::EXCHANGE))
74
            ->willReturn($topic);
75
76
        $contextMock->expects($this->once())
77
            ->method('declareTopic')
78
            ->with($this->identicalTo($topic))
79
            ->willReturnCallback(function (AmqpTopic $topic) {
80
                $this->assertTrue((bool) ($topic->getFlags() & AmqpTopic::FLAG_DURABLE));
81
                $this->assertSame(AmqpTopic::TYPE_DIRECT, $topic->getType());
82
                $this->assertSame([], $topic->getArguments());
83
            });
84
85
        $contextMock->expects($this->once())
86
            ->method('bind')
87
            ->with($this->isInstanceOf(AmqpBind::class))
88
            ->willReturnCallback(function (ImplAmqpBind $bind) use ($queue, $topic) {
89
                $this->assertSame($queue, $bind->getTarget());
90
                $this->assertSame($topic, $bind->getSource());
91
                $this->assertSame(self::KEY, $bind->getRoutingKey());
92
            });
93
94
        AmqpConnectionFactoryStub::$context = $contextMock;
95
96
        $backend->initialize();
97
    }
98
99
    public function testInitializeWithDeadLetterExchangeAndNoDeadLetterRoutingKey()
100
    {
101
        $backend = $this->buildBackend(false, self::DEAD_LETTER_EXCHANGE);
102
103
        $queue = new ImplAmqpQueue(self::QUEUE);
104
        $topic = new ImplAmqpTopic(self::EXCHANGE);
105
        $deadLetterTopic = new ImplAmqpTopic(self::DEAD_LETTER_EXCHANGE);
106
107
        $contextMock = $this->createMock(AmqpContext::class);
108
        $contextMock->expects($this->at(0))
109
            ->method('createQueue')
110
            ->with($this->identicalTo(self::QUEUE))
111
            ->willReturn($queue);
112
113
        $contextMock->expects($this->at(1))
114
            ->method('declareQueue')
115
            ->with($this->identicalTo($queue))
116
            ->willReturnCallback(function (AmqpQueue $queue) {
117
                $this->assertTrue((bool) ($queue->getFlags() & AmqpQueue::FLAG_DURABLE));
118
                $this->assertSame(['x-dead-letter-exchange' => self::DEAD_LETTER_EXCHANGE], $queue->getArguments());
119
            });
120
121
        $contextMock->expects($this->at(2))
122
            ->method('createTopic')
123
            ->with($this->identicalTo(self::EXCHANGE))
124
            ->willReturn($topic);
125
126
        $contextMock->expects($this->at(3))
127
            ->method('declareTopic')
128
            ->with($this->identicalTo($topic));
129
130
        $contextMock->expects($this->at(4))
131
            ->method('bind')
132
            ->with($this->isInstanceOf(AmqpBind::class));
133
134
        $contextMock->expects($this->at(5))
135
            ->method('createTopic')
136
            ->with($this->identicalTo(self::DEAD_LETTER_EXCHANGE))
137
            ->willReturn($deadLetterTopic);
138
139
        $contextMock->expects($this->at(6))
140
            ->method('declareTopic')
141
            ->with($this->identicalTo($deadLetterTopic))
142
            ->willReturnCallback(function (AmqpTopic $topic) {
143
                $this->assertTrue((bool) ($topic->getFlags() & AmqpTopic::FLAG_DURABLE));
144
                $this->assertSame(AmqpTopic::TYPE_DIRECT, $topic->getType());
145
                $this->assertSame([], $topic->getArguments());
146
            });
147
148
        AmqpConnectionFactoryStub::$context = $contextMock;
149
150
        $backend->initialize();
151
    }
152
153
    public function testInitializeWithDeadLetterExchangeAndDeadLetterRoutingKey()
154
    {
155
        $backend = $this->buildBackend(false, self::DEAD_LETTER_EXCHANGE, self::DEAD_LETTER_ROUTING_KEY);
156
157
        $queue = new ImplAmqpQueue(self::QUEUE);
158
        $topic = new ImplAmqpTopic(self::EXCHANGE);
159
        $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...
160
161
        $contextMock = $this->createMock(AmqpContext::class);
162
        $contextMock->expects($this->at(0))
163
            ->method('createQueue')
164
            ->with($this->identicalTo(self::QUEUE))
165
            ->willReturn($queue);
166
167
        $contextMock->expects($this->at(1))
168
            ->method('declareQueue')
169
            ->with($this->identicalTo($queue))
170
            ->willReturnCallback(function (AmqpQueue $queue) {
171
                $this->assertTrue((bool) ($queue->getFlags() & AmqpQueue::FLAG_DURABLE));
172
                $this->assertSame(
173
                    [
174
                        'x-dead-letter-exchange' => self::DEAD_LETTER_EXCHANGE,
175
                        'x-dead-letter-routing-key' => self::DEAD_LETTER_ROUTING_KEY,
176
                    ],
177
                    $queue->getArguments()
178
                );
179
            });
180
181
        $contextMock->expects($this->at(2))
182
            ->method('createTopic')
183
            ->with($this->identicalTo(self::EXCHANGE))
184
            ->willReturn($topic);
185
186
        $contextMock->expects($this->at(3))
187
            ->method('declareTopic')
188
            ->with($this->identicalTo($topic));
189
190
        $contextMock->expects($this->at(4))
191
            ->method('bind')
192
            ->with($this->isInstanceOf(AmqpBind::class));
193
194
        AmqpConnectionFactoryStub::$context = $contextMock;
195
196
        $backend->initialize();
197
    }
198
199
    public function testInitializeWithTTL()
200
    {
201
        $backend = $this->buildBackend(false, null, null, self::TTL);
202
203
        $queue = new ImplAmqpQueue(self::QUEUE);
204
        $topic = new ImplAmqpTopic(self::EXCHANGE);
205
206
        $contextMock = $this->createMock(AmqpContext::class);
207
        $contextMock->expects($this->once())
208
            ->method('createQueue')
209
            ->with($this->identicalTo(self::QUEUE))
210
            ->willReturn($queue);
211
212
        $contextMock->expects($this->once())
213
            ->method('declareQueue')
214
            ->with($this->identicalTo($queue))
215
            ->willReturnCallback(function (AmqpQueue $queue) {
216
                $this->assertTrue((bool) ($queue->getFlags() & AmqpQueue::FLAG_DURABLE));
217
                $this->assertSame(['x-message-ttl' => self::TTL], $queue->getArguments());
218
            });
219
220
        $contextMock->expects($this->once())
221
            ->method('createTopic')
222
            ->with($this->identicalTo(self::EXCHANGE))
223
            ->willReturn($topic);
224
225
        $contextMock->expects($this->once())
226
            ->method('declareTopic')
227
            ->with($this->identicalTo($topic));
228
229
        $contextMock->expects($this->once())
230
            ->method('bind')
231
            ->with($this->isInstanceOf(AmqpBind::class));
232
233
        AmqpConnectionFactoryStub::$context = $contextMock;
234
235
        $backend->initialize();
236
    }
237
238
    public function testGetIteratorWithNoPrefetchCount()
239
    {
240
        $backend = $this->buildBackend();
241
242
        $queue = new ImplAmqpQueue('aQueue');
243
244
        $consumerMock = $this->createMock(AmqpConsumer::class);
245
        $consumerMock->expects($this->once())
246
            ->method('getQueue')
247
            ->willReturn($queue);
248
249
        $contextMock = $this->createMock(AmqpContext::class);
250
        $contextMock->expects($this->never())
251
            ->method('setQos');
252
253
        $contextMock->expects($this->once())
254
            ->method('createQueue')
255
            ->willReturn($queue);
256
257
        $contextMock->expects($this->once())
258
            ->method('createConsumer')
259
            ->with($this->identicalTo($queue))
260
            ->willReturn($consumerMock);
261
262
        AmqpConnectionFactoryStub::$context = $contextMock;
263
264
        $iterator = $backend->getIterator();
265
266
        $this->assertInstanceOf(AMQPMessageIterator::class, $iterator);
267
    }
268
269
    public function testGetIteratorWithPrefetchCount()
270
    {
271
        $backend = $this->buildBackend(false, null, null, null, self::PREFETCH_COUNT);
272
273
        $queue = new ImplAmqpQueue('aQueue');
274
        $consumerMock = $this->createMock(AmqpConsumer::class);
275
        $consumerMock->expects($this->once())
276
            ->method('getQueue')
277
            ->willReturn($queue);
278
279
        $contextMock = $this->createMock(AmqpContext::class);
280
        $contextMock->expects($this->once())
281
            ->method('setQos')
282
            ->with($this->isNull(), $this->identicalTo(self::PREFETCH_COUNT), $this->isFalse());
283
284
        $contextMock->expects($this->once())
285
            ->method('createQueue')
286
            ->willReturn($queue);
287
288
        $contextMock->expects($this->once())
289
            ->method('createConsumer')
290
            ->with($this->identicalTo($queue))
291
            ->willReturn($consumerMock);
292
293
        AmqpConnectionFactoryStub::$context = $contextMock;
294
295
        $iterator = $backend->getIterator();
296
297
        $this->assertInstanceOf(AMQPMessageIterator::class, $iterator);
298
    }
299
300
    /**
301
     * @return AMQPBackend
302
     */
303
    protected function buildBackend($recover = false, $deadLetterExchange = null, $deadLetterRoutingKey = null, $ttl = null, $prefetchCount = null)
304
    {
305
        $backend = new AMQPBackend(
306
            self::EXCHANGE,
307
            self::QUEUE,
308
            $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...
309
            self::KEY,
310
            $deadLetterExchange,
311
            $deadLetterRoutingKey,
312
            $ttl,
313
            $prefetchCount
314
        );
315
316
        $settings = [
317
            'host' => 'foo',
318
            'port' => 'port',
319
            'user' => 'user',
320
            'pass' => 'pass',
321
            'vhost' => '/',
322
            'factory_class' => AmqpConnectionFactoryStub::class,
323
        ];
324
325
        $queues = [
326
            ['queue' => self::QUEUE, 'routing_key' => self::KEY],
327
        ];
328
329
        $dispatcherMock = $this->getMockBuilder(AMQPBackendDispatcher::class)
330
            ->setConstructorArgs([$settings, $queues, 'default', [['type' => self::KEY, 'backend' => $backend]]])
331
            ->setMethods(['getChannel'])
332
            ->getMock();
333
334
        $dispatcherMock
335
            ->expects($this->any())
336
            ->method('getChannel')
337
            ->willReturn($this->createMock(AMQPChannel::class))
338
        ;
339
340
        $backend->setDispatcher($dispatcherMock);
341
342
        return $backend;
343
    }
344
}
345