Completed
Push — develop ( 34d8b3...fd6151 )
by Alejandro
22s queued 14s
created

NotifyVisitToWebHooksTest::createListener()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 6
nc 1
nop 1
dl 0
loc 9
rs 10
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ShlinkioTest\Shlink\Rest\EventDispatcher;
6
7
use Doctrine\ORM\EntityManagerInterface;
8
use Exception;
9
use Fig\Http\Message\RequestMethodInterface;
10
use GuzzleHttp\ClientInterface;
11
use GuzzleHttp\Promise\FulfilledPromise;
12
use GuzzleHttp\Promise\RejectedPromise;
13
use GuzzleHttp\RequestOptions;
14
use PHPUnit\Framework\Assert;
15
use PHPUnit\Framework\TestCase;
16
use Prophecy\Argument;
17
use Prophecy\Prophecy\ObjectProphecy;
18
use Psr\Log\LoggerInterface;
19
use Shlinkio\Shlink\Core\Entity\ShortUrl;
20
use Shlinkio\Shlink\Core\Entity\Visit;
21
use Shlinkio\Shlink\Core\EventDispatcher\NotifyVisitToWebHooks;
22
use Shlinkio\Shlink\Core\EventDispatcher\VisitLocated;
23
use Shlinkio\Shlink\Core\Model\Visitor;
24
use Shlinkio\Shlink\Core\Options\AppOptions;
25
26
use function count;
27
use function Functional\contains;
28
29
class NotifyVisitToWebHooksTest extends TestCase
30
{
31
    /** @var ObjectProphecy */
32
    private $httpClient;
33
    /** @var ObjectProphecy */
34
    private $em;
35
    /** @var ObjectProphecy */
36
    private $logger;
37
38
    public function setUp(): void
39
    {
40
        $this->httpClient = $this->prophesize(ClientInterface::class);
41
        $this->em = $this->prophesize(EntityManagerInterface::class);
42
        $this->logger = $this->prophesize(LoggerInterface::class);
43
    }
44
45
    /** @test */
46
    public function emptyWebhooksMakeNoFurtherActions(): void
47
    {
48
        $find = $this->em->find(Visit::class, '1')->willReturn(null);
49
50
        $this->createListener([])(new VisitLocated('1'));
51
52
        $find->shouldNotHaveBeenCalled();
53
    }
54
55
    /** @test */
56
    public function invalidVisitDoesNotPerformAnyRequest(): void
57
    {
58
        $find = $this->em->find(Visit::class, '1')->willReturn(null);
59
        $requestAsync = $this->httpClient->requestAsync(
60
            RequestMethodInterface::METHOD_POST,
61
            Argument::type('string'),
62
            Argument::type('array')
63
        )->willReturn(new FulfilledPromise(''));
64
        $logWarning = $this->logger->warning(
65
            'Tried to notify webhooks for visit with id "{visitId}", but it does not exist.',
66
            ['visitId' => '1']
67
        );
68
69
        $this->createListener(['foo', 'bar'])(new VisitLocated('1'));
70
71
        $find->shouldHaveBeenCalledOnce();
72
        $logWarning->shouldHaveBeenCalledOnce();
73
        $requestAsync->shouldNotHaveBeenCalled();
74
    }
75
76
    /** @test */
77
    public function expectedRequestsArePerformedToWebhooks(): void
78
    {
79
        $webhooks = ['foo', 'invalid', 'bar', 'baz'];
80
        $invalidWebhooks = ['invalid', 'baz'];
81
82
        $find = $this->em->find(Visit::class, '1')->willReturn(new Visit(new ShortUrl(''), Visitor::emptyInstance()));
83
        $requestAsync = $this->httpClient->requestAsync(
84
            RequestMethodInterface::METHOD_POST,
85
            Argument::type('string'),
86
            Argument::that(function (array $requestOptions) {
87
                Assert::assertArrayHasKey(RequestOptions::HEADERS, $requestOptions);
88
                Assert::assertArrayHasKey(RequestOptions::JSON, $requestOptions);
89
                Assert::assertArrayHasKey(RequestOptions::TIMEOUT, $requestOptions);
90
                Assert::assertEquals($requestOptions[RequestOptions::TIMEOUT], 10);
91
                Assert::assertEquals($requestOptions[RequestOptions::HEADERS], ['User-Agent' => 'Shlink:v1.2.3']);
92
                Assert::assertArrayHasKey('shortUrl', $requestOptions[RequestOptions::JSON]);
93
                Assert::assertArrayHasKey('visit', $requestOptions[RequestOptions::JSON]);
94
95
                return $requestOptions;
96
            })
97
        )->will(function (array $args) use ($invalidWebhooks) {
98
            [, $webhook] = $args;
99
            $e = new Exception('');
100
101
            return contains($invalidWebhooks, $webhook) ? new RejectedPromise($e) : new FulfilledPromise('');
102
        });
103
        $logWarning = $this->logger->warning(
104
            'Failed to notify visit with id "{visitId}" to webhook "{webhook}". {e}',
105
            Argument::that(function (array $extra) {
106
                Assert::assertArrayHasKey('webhook', $extra);
107
                Assert::assertArrayHasKey('visitId', $extra);
108
                Assert::assertArrayHasKey('e', $extra);
109
110
                return $extra;
111
            })
112
        );
113
114
        $this->createListener($webhooks)(new VisitLocated('1'));
115
116
        $find->shouldHaveBeenCalledOnce();
117
        $requestAsync->shouldHaveBeenCalledTimes(count($webhooks));
118
        $logWarning->shouldHaveBeenCalledTimes(count($invalidWebhooks));
119
    }
120
121
    private function createListener(array $webhooks): NotifyVisitToWebHooks
122
    {
123
        return new NotifyVisitToWebHooks(
124
            $this->httpClient->reveal(),
125
            $this->em->reveal(),
126
            $this->logger->reveal(),
127
            $webhooks,
128
            [],
129
            new AppOptions(['name' => 'Shlink', 'version' => '1.2.3'])
130
        );
131
    }
132
}
133