Failed Conditions
Pull Request — master (#262)
by Guilherme
10:25 queued 04:26
created

PhoneVerificationServiceTest   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 443
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 443
rs 9
c 0
b 0
f 0
wmc 35
1
<?php
2
/**
3
 * This file is part of the login-cidadao project or it's bundles.
4
 *
5
 * (c) Guilherme Donato <guilhermednt on github>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace LoginCidadao\PhoneVerificationBundle\Tests\Service;
12
13
use LoginCidadao\PhoneVerificationBundle\PhoneVerificationEvents;
14
use LoginCidadao\PhoneVerificationBundle\Service\PhoneVerificationService;
15
use PHPUnit\Framework\TestCase;
16
17
class PhoneVerificationServiceTest extends TestCase
18
{
19
    private function getPhoneVerification()
20
    {
21
        $phoneVerificationClass = 'LoginCidadao\PhoneVerificationBundle\Model\PhoneVerificationInterface';
22
        $phoneVerification = $this->createMock($phoneVerificationClass);
23
24
        return $phoneVerification;
25
    }
26
27
    private function getServiceOptions()
28
    {
29
        $options = $this->getMockBuilder('LoginCidadao\PhoneVerificationBundle\Service\PhoneVerificationOptions')
30
            ->disableOriginalConstructor()
31
            ->getMock();
32
33
        return $options;
34
    }
35
36
    private function getEntityManager()
37
    {
38
        return $this->getMockBuilder('Doctrine\ORM\EntityManager')
39
            ->disableOriginalConstructor()
40
            ->getMock();
41
    }
42
43
    private function getDispatcher()
44
    {
45
        return $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')
46
            ->disableOriginalConstructor()
47
            ->getMock();
48
    }
49
50
    private function getRepository($class)
51
    {
52
        $repository = $this->getMockBuilder($class)
53
            ->disableOriginalConstructor()
54
            ->getMock();
55
56
        return $repository;
57
    }
58
59
    private function getPhoneVerificationRepository()
60
    {
61
        return $this->getRepository('LoginCidadao\PhoneVerificationBundle\Entity\PhoneVerificationRepository');
62
    }
63
64
    private function getSentVerificationRepository()
65
    {
66
        return $this->getRepository('LoginCidadao\PhoneVerificationBundle\Entity\SentVerificationRepository');
67
    }
68
69
    /**
70
     * @param array $arguments
71
     * @return PhoneVerificationService
72
     */
73
    private function getService(array $arguments = [])
74
    {
75
        if (array_key_exists('em', $arguments)) {
76
            $em = $arguments['em'];
77
        } else {
78
            $em = $this->getEntityManager();
79
        }
80
81
        if (array_key_exists('dispatcher', $arguments)) {
82
            $dispatcher = $arguments['dispatcher'];
83
        } else {
84
            $dispatcher = $this->getDispatcher();
85
        }
86
87
        if (array_key_exists('options', $arguments)) {
88
            $options = $arguments['options'];
89
        } else {
90
            $options = $this->getServiceOptions();
91
            $options->expects($this->any())->method('getLength')->willReturn(6);
92
            $options->expects($this->any())->method('getVerificationTokenLength')->willReturn(10);
93
            $options->expects($this->any())->method('isUseNumbers')->willReturn(true);
94
        }
95
96
        if (array_key_exists('phone_verification_repository', $arguments)) {
97
            $phoneVerificationRepository = $arguments['phone_verification_repository'];
98
        } else {
99
            $phoneVerificationRepository = $this->getPhoneVerificationRepository();
100
        }
101
102
        if (array_key_exists('sent_verification_repository', $arguments)) {
103
            $sentVerificationRepository = $arguments['sent_verification_repository'];
104
        } else {
105
            $sentVerificationRepository = $this->getSentVerificationRepository();
106
        }
107
108
        $em->expects($this->exactly(2))->method('getRepository')->willReturnCallback(
109
            function ($class) use ($phoneVerificationRepository, $sentVerificationRepository) {
110
                switch ($class) {
111
                    case 'LoginCidadaoPhoneVerificationBundle:PhoneVerification':
112
                        return $phoneVerificationRepository;
113
                    case 'LoginCidadaoPhoneVerificationBundle:SentVerification':
114
                        return $sentVerificationRepository;
115
                    default:
116
                        return null;
117
                }
118
            }
119
        );
120
121
        return new PhoneVerificationService($options, $em, $dispatcher);
122
    }
123
124
    public function testGetPhoneVerification()
125
    {
126
        $phoneVerificationClass = 'LoginCidadao\PhoneVerificationBundle\Model\PhoneVerificationInterface';
127
        $phoneVerification = $this->createMock($phoneVerificationClass);
128
129
        $repository = $this->getPhoneVerificationRepository();
130
        $repository->expects($this->once())->method('findOneBy')->willReturn($phoneVerification);
131
132
        $service = $this->getService(['phone_verification_repository' => $repository]);
133
134
        $person = $this->createMock('LoginCidadao\CoreBundle\Model\PersonInterface');
135
        $phone = $this->createMock('libphonenumber\PhoneNumber');
136
137
        $this->assertEquals($phoneVerification, $service->getPhoneVerification($person, $phone));
138
    }
139
140
    public function testCreatePhoneVerification()
141
    {
142
        $phoneVerificationClass = 'LoginCidadao\PhoneVerificationBundle\Model\PhoneVerificationInterface';
143
        $existingPhoneVerification = $this->createMock($phoneVerificationClass);
144
145
        $repository = $this->getPhoneVerificationRepository();
146
        $repository->expects($this->atLeastOnce())->method('findOneBy')->willReturn($existingPhoneVerification);
147
148
        $em = $this->getEntityManager();
149
        $em->expects($this->once())->method('persist');
150
        $em->expects($this->once())->method('remove')->with($existingPhoneVerification);
151
        $em->expects($this->exactly(2))->method('flush');
152
153
        $service = $this->getService(['em' => $em, 'phone_verification_repository' => $repository]);
154
155
        $person = $this->createMock('LoginCidadao\CoreBundle\Model\PersonInterface');
156
        $phone = $this->createMock('libphonenumber\PhoneNumber');
157
158
        $this->assertInstanceOf($phoneVerificationClass, $service->createPhoneVerification($person, $phone));
159
    }
160
161
    public function testGetPendingPhoneVerification()
162
    {
163
        $phoneVerificationClass = 'LoginCidadao\PhoneVerificationBundle\Model\PhoneVerificationInterface';
164
        $phoneVerification = $this->createMock($phoneVerificationClass);
165
166
        $repoClass = 'LoginCidadao\PhoneVerificationBundle\Entity\PhoneVerificationRepository';
167
        $repository = $this->getMockBuilder($repoClass)
168
            ->disableOriginalConstructor()
169
            ->getMock();
170
        $repository->expects($this->once())->method('findOneBy')->willReturn($phoneVerification);
171
        $repository->expects($this->once())->method('findBy')->willReturn($phoneVerification);
172
173
        $service = $this->getService(['phone_verification_repository' => $repository]);
174
175
        $person = $this->createMock('LoginCidadao\CoreBundle\Model\PersonInterface');
176
        $phone = $this->createMock('libphonenumber\PhoneNumber');
177
178
        $this->assertEquals($phoneVerification, $service->getPendingPhoneVerification($person, $phone));
179
        $this->assertEquals($phoneVerification, $service->getAllPendingPhoneVerification($person));
180
    }
181
182
    public function testRemovePhoneVerification()
183
    {
184
        $em = $this->getEntityManager();
185
        $em->expects($this->once())->method('remove');
186
        $em->expects($this->once())->method('flush');
187
188
        $service = $this->getService(compact('em'));
189
190
        $phoneVerificationClass = 'LoginCidadao\PhoneVerificationBundle\Model\PhoneVerificationInterface';
191
        $phoneVerification = $this->createMock($phoneVerificationClass);
192
        $this->assertTrue($service->removePhoneVerification($phoneVerification));
193
    }
194
195
    public function testEnforcePhoneVerification()
196
    {
197
        $em = $this->getEntityManager();
198
        $em->expects($this->once())->method('persist');
199
        $em->expects($this->once())->method('flush');
200
201
        $repository = $this->getPhoneVerificationRepository();
202
        $repository->expects($this->atLeastOnce())->method('findOneBy')->willReturn(null);
203
204
        $service = $this->getService(['em' => $em, 'phone_verification_repository' => $repository]);
205
206
        $person = $this->createMock('LoginCidadao\CoreBundle\Model\PersonInterface');
207
        $phone = $this->createMock('libphonenumber\PhoneNumber');
208
209
        $service->enforcePhoneVerification($person, $phone);
210
    }
211
212
    public function testCheckCaseSensitiveVerificationCode()
213
    {
214
        $options = $this->getServiceOptions();
215
        $options->expects($this->any())->method('isCaseSensitive')->willReturn(true);
216
217
        $service = $this->getService(compact('em', 'options'));
218
219
        $this->assertTrue($service->checkVerificationCode('abc', 'abc'));
220
        $this->assertFalse($service->checkVerificationCode('ABC', 'abc'));
221
    }
222
223
    public function testCheckNotCaseSensitiveVerificationCode()
224
    {
225
        $options = $this->getServiceOptions();
226
        $options->expects($this->any())->method('isCaseSensitive')->willReturn(false);
227
228
        $service = $this->getService(compact('em', 'options'));
229
230
        $this->assertTrue($service->checkVerificationCode('abc', 'abc'));
231
        $this->assertTrue($service->checkVerificationCode('ABC', 'abc'));
232
    }
233
234
    public function testSuccessfulVerify()
235
    {
236
        $phoneVerificationClass = 'LoginCidadao\PhoneVerificationBundle\Model\PhoneVerificationInterface';
237
        $phoneVerification = $this->createMock($phoneVerificationClass);
238
        $phoneVerification->expects($this->once())->method('setVerifiedAt');
239
        $phoneVerification->expects($this->atLeastOnce())->method('getVerificationCode')->willReturn('123');
240
241
        $em = $this->getEntityManager();
242
        $em->expects($this->once())->method('persist');
243
        $em->expects($this->once())->method('flush');
244
245
        $dispatcher = $this->getDispatcher();
246
        $dispatcher->expects($this->once())->method('dispatch');
247
248
        $service = $this->getService(compact('em', 'dispatcher'));
249
        $result = $service->verify($phoneVerification, '123');
250
251
        $this->assertTrue($result);
252
    }
253
254
    public function testUnsuccessfulVerify()
255
    {
256
        $phoneVerificationClass = 'LoginCidadao\PhoneVerificationBundle\Model\PhoneVerificationInterface';
257
        $phoneVerification = $this->createMock($phoneVerificationClass);
258
        $phoneVerification->expects($this->never())->method('setVerifiedAt');
259
        $phoneVerification->expects($this->atLeastOnce())->method('getVerificationCode')->willReturn('321');
260
261
        $em = $this->getEntityManager();
262
        $em->expects($this->never())->method('persist');
263
        $em->expects($this->never())->method('flush');
264
265
        $dispatcher = $this->getDispatcher();
266
        $dispatcher->expects($this->never())->method('dispatch');
267
268
        $service = $this->getService(compact('em', 'dispatcher'));
269
        $result = $service->verify($phoneVerification, '123');
270
271
        $this->assertFalse($result);
272
    }
273
274
    public function testGetPhoneVerificationById()
275
    {
276
        $id = random_int(1, 9999);
277
        $phoneVerificationClass = 'LoginCidadao\PhoneVerificationBundle\Model\PhoneVerificationInterface';
278
        $phoneVerification = $this->createMock($phoneVerificationClass);
279
        $person = $this->createMock('LoginCidadao\CoreBundle\Model\PersonInterface');
280
281
        $repository = $this->getPhoneVerificationRepository();
282
        $repository->expects($this->once())->method('findOneBy')
283
            ->with(['person' => $person, 'id' => $id])
284
            ->willReturn($phoneVerification);
285
286
        $service = $this->getService(['phone_verification_repository' => $repository]);
287
288
        $this->assertEquals($phoneVerification, $service->getPhoneVerificationById($id, $person));
289
    }
290
291
    public function testGetPendingPhoneVerificationById()
292
    {
293
        $id = random_int(1, 9999);
294
        $phoneVerificationClass = 'LoginCidadao\PhoneVerificationBundle\Model\PhoneVerificationInterface';
295
        $phoneVerification = $this->createMock($phoneVerificationClass);
296
        $person = $this->createMock('LoginCidadao\CoreBundle\Model\PersonInterface');
297
298
        $repository = $this->getPhoneVerificationRepository();
299
        $repository->expects($this->once())->method('findOneBy')
300
            ->with(['person' => $person, 'id' => $id, 'verifiedAt' => null])
301
            ->willReturn($phoneVerification);
302
303
        $service = $this->getService(['phone_verification_repository' => $repository]);
304
305
        $this->assertEquals($phoneVerification, $service->getPendingPhoneVerificationById($id, $person));
306
    }
307
308
    public function testSendVerificationCodeSuccess()
309
    {
310
        $phoneVerificationClass = 'LoginCidadao\PhoneVerificationBundle\Model\PhoneVerificationInterface';
311
        $phoneVerification = $this->createMock($phoneVerificationClass);
312
313
        $sentVerification = $this->createMock(
314
            'LoginCidadao\PhoneVerificationBundle\Model\SentVerificationInterface'
315
        );
316
317
        $dispatcher = $this->getDispatcher();
318
        $dispatcher->expects($this->once())->method('dispatch')
319
            ->with(
320
                PhoneVerificationEvents::PHONE_VERIFICATION_REQUESTED,
321
                $this->isInstanceOf('LoginCidadao\PhoneVerificationBundle\Event\SendPhoneVerificationEvent')
322
            )->willReturnCallback(
323
                function ($eventName, $event) use ($sentVerification) {
324
                    $event->setSentVerification($sentVerification);
325
                }
326
            );
327
328
        $service = $this->getService(compact('dispatcher'));
329
        $result = $service->sendVerificationCode($phoneVerification);
330
331
        $this->assertEquals($sentVerification, $result);
332
    }
333
334
    public function testSendVerificationCodeFailure()
335
    {
336
        $this->expectException('LoginCidadao\PhoneVerificationBundle\Exception\VerificationNotSentException');
337
338
        $phoneVerification = $this->getPhoneVerification();
339
340
        $dispatcher = $this->getDispatcher();
341
        $dispatcher->expects($this->once())->method('dispatch')
342
            ->with(
343
                PhoneVerificationEvents::PHONE_VERIFICATION_REQUESTED,
344
                $this->isInstanceOf('LoginCidadao\PhoneVerificationBundle\Event\SendPhoneVerificationEvent')
345
            );
346
347
        $service = $this->getService(compact('dispatcher'));
348
        $service->sendVerificationCode($phoneVerification);
349
    }
350
351
    public function testResendVerificationCodeSuccess()
352
    {
353
        $phoneVerification = $this->getPhoneVerification();
354
355
        $dispatcher = $this->getDispatcher();
356
        $dispatcher->expects($this->once())->method('dispatch')
357
            ->with(
358
                PhoneVerificationEvents::PHONE_VERIFICATION_REQUESTED,
359
                $this->isInstanceOf('LoginCidadao\PhoneVerificationBundle\Event\SendPhoneVerificationEvent')
360
            )->willReturnCallback(
361
                function ($eventName, $event) {
362
                    $sentAt = new \DateTime("-5 minutes");
363
                    $sentVerification = $this->createMock(
364
                        'LoginCidadao\PhoneVerificationBundle\Model\SentVerificationInterface'
365
                    );
366
                    $event->setSentVerification($sentVerification);
367
                }
368
            );
369
370
        $service = $this->getService(compact('dispatcher'));
371
        $service->sendVerificationCode($phoneVerification);
372
    }
373
374
    public function testResendVerificationCodeFailure()
375
    {
376
        $this->expectException('Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException');
377
        $sentAt = new \DateTime();
378
        $sentVerification = $this->createMock('LoginCidadao\PhoneVerificationBundle\Model\SentVerificationInterface');
379
        $sentVerification->expects($this->once())->method('getSentAt')->willReturn($sentAt);
380
381
        $phoneVerification = $this->getPhoneVerification();
382
383
        $repository = $this->getSentVerificationRepository();
384
        $repository->expects($this->once())->method('getLastVerificationSent')->willReturn($sentVerification);
385
386
        $options = $this->getServiceOptions();
387
        $options->expects($this->once())->method('getSmsResendTimeout')->willReturn('+5 minutes');
388
389
        $service = $this->getService(['sent_verification_repository' => $repository, 'options' => $options]);
390
        $service->sendVerificationCode($phoneVerification);
391
    }
392
393
    public function testRegisterVerificationSent()
394
    {
395
        $sentVerification = $this->createMock('LoginCidadao\PhoneVerificationBundle\Model\SentVerificationInterface');
396
397
        $em = $this->getEntityManager();
398
        $em->expects($this->once())->method('persist')->with($sentVerification);
399
        $em->expects($this->once())->method('flush')->with($sentVerification);
400
401
        $service = $this->getService(['em' => $em]);
402
        $service->registerVerificationSent($sentVerification);
403
    }
404
405
    public function testGetNextResendDate()
406
    {
407
        $timeout = \DateInterval::createFromDateString('+ 5 minutes');
408
        $sentAt = new \DateTime("-5 minutes");
409
        $expected = $sentAt->add($timeout);
410
        $sentVerification = $this->createMock('LoginCidadao\PhoneVerificationBundle\Model\SentVerificationInterface');
411
        $sentVerification->expects($this->once())->method('getSentAt')->willReturn($sentAt);
412
413
        $repository = $this->getSentVerificationRepository();
414
        $repository->expects($this->once())->method('getLastVerificationSent')->willReturn($sentVerification);
415
416
        $options = $this->getServiceOptions();
417
        $options->expects($this->once())->method('getSmsResendTimeout')->willReturn('+ 5 minutes');
418
419
        $phoneVerification = $this->getPhoneVerification();
420
421
        $service = $this->getService(['sent_verification_repository' => $repository, 'options' => $options]);
422
        $nextResend = $service->getNextResendDate($phoneVerification);
423
424
        $this->assertEquals($expected, $nextResend);
425
    }
426
427
    public function testVerifyTokenSuccess()
428
    {
429
        $token = 'abc123';
430
431
        $phoneVerification = $this->getPhoneVerification();
432
        $phoneVerification->expects($this->once())->method('isVerified')->willReturn(false);
433
        $phoneVerification->expects($this->once())->method('getVerificationToken')->willReturn($token);
434
435
        $service = $this->getService();
436
        $this->assertTrue($service->verifyToken($phoneVerification, $token));
437
    }
438
439
    public function testVerifyTokenAlreadyVerified()
440
    {
441
        $token = 'abc123';
442
443
        $phoneVerification = $this->getPhoneVerification();
444
        $phoneVerification->expects($this->once())->method('isVerified')->willReturn(true);
445
446
        $service = $this->getService();
447
        $this->assertTrue($service->verifyToken($phoneVerification, $token));
448
    }
449
450
    public function testVerifyTokenInvalidToken()
451
    {
452
        $token = 'abc123';
453
454
        $phoneVerification = $this->getPhoneVerification();
455
        $phoneVerification->expects($this->once())->method('isVerified')->willReturn(false);
456
        $phoneVerification->expects($this->once())->method('getVerificationToken')->willReturn($token);
457
458
        $service = $this->getService();
459
        $this->assertFalse($service->verifyToken($phoneVerification, 'wrong'));
460
    }
461
}
462