Completed
Pull Request — develop (#724)
by Alejandro
04:45
created

NotifyVisitToWebHooksTest   A

Complexity

Total Complexity 6

Size/Duplication

Total Lines 98
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 6
eloc 58
c 0
b 0
f 0
dl 0
loc 98
rs 10

5 Methods

Rating   Name   Duplication   Size   Complexity  
A setUp() 0 5 1
A createListener() 0 9 1
A expectedRequestsArePerformedToWebhooks() 0 42 2
A emptyWebhooksMakeNoFurtherActions() 0 7 1
A invalidVisitDoesNotPerformAnyRequest() 0 18 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ShlinkioTest\Shlink\Core\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;
0 ignored issues
show
Bug introduced by
The type Shlinkio\Shlink\Core\Eve...r\NotifyVisitToWebHooks was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
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
    private ObjectProphecy $httpClient;
32
    private ObjectProphecy $em;
33
    private ObjectProphecy $logger;
34
35
    public function setUp(): void
36
    {
37
        $this->httpClient = $this->prophesize(ClientInterface::class);
38
        $this->em = $this->prophesize(EntityManagerInterface::class);
39
        $this->logger = $this->prophesize(LoggerInterface::class);
40
    }
41
42
    /** @test */
43
    public function emptyWebhooksMakeNoFurtherActions(): void
44
    {
45
        $find = $this->em->find(Visit::class, '1')->willReturn(null);
46
47
        $this->createListener([])(new VisitLocated('1'));
48
49
        $find->shouldNotHaveBeenCalled();
50
    }
51
52
    /** @test */
53
    public function invalidVisitDoesNotPerformAnyRequest(): void
54
    {
55
        $find = $this->em->find(Visit::class, '1')->willReturn(null);
56
        $requestAsync = $this->httpClient->requestAsync(
57
            RequestMethodInterface::METHOD_POST,
58
            Argument::type('string'),
59
            Argument::type('array'),
60
        )->willReturn(new FulfilledPromise(''));
61
        $logWarning = $this->logger->warning(
62
            'Tried to notify webhooks for visit with id "{visitId}", but it does not exist.',
63
            ['visitId' => '1'],
64
        );
65
66
        $this->createListener(['foo', 'bar'])(new VisitLocated('1'));
67
68
        $find->shouldHaveBeenCalledOnce();
69
        $logWarning->shouldHaveBeenCalledOnce();
70
        $requestAsync->shouldNotHaveBeenCalled();
71
    }
72
73
    /** @test */
74
    public function expectedRequestsArePerformedToWebhooks(): void
75
    {
76
        $webhooks = ['foo', 'invalid', 'bar', 'baz'];
77
        $invalidWebhooks = ['invalid', 'baz'];
78
79
        $find = $this->em->find(Visit::class, '1')->willReturn(new Visit(new ShortUrl(''), Visitor::emptyInstance()));
80
        $requestAsync = $this->httpClient->requestAsync(
81
            RequestMethodInterface::METHOD_POST,
82
            Argument::type('string'),
83
            Argument::that(function (array $requestOptions) {
84
                Assert::assertArrayHasKey(RequestOptions::HEADERS, $requestOptions);
85
                Assert::assertArrayHasKey(RequestOptions::JSON, $requestOptions);
86
                Assert::assertArrayHasKey(RequestOptions::TIMEOUT, $requestOptions);
87
                Assert::assertEquals($requestOptions[RequestOptions::TIMEOUT], 10);
88
                Assert::assertEquals($requestOptions[RequestOptions::HEADERS], ['User-Agent' => 'Shlink:v1.2.3']);
89
                Assert::assertArrayHasKey('shortUrl', $requestOptions[RequestOptions::JSON]);
90
                Assert::assertArrayHasKey('visit', $requestOptions[RequestOptions::JSON]);
91
92
                return $requestOptions;
93
            }),
94
        )->will(function (array $args) use ($invalidWebhooks) {
95
            [, $webhook] = $args;
96
            $e = new Exception('');
97
98
            return contains($invalidWebhooks, $webhook) ? new RejectedPromise($e) : new FulfilledPromise('');
99
        });
100
        $logWarning = $this->logger->warning(
101
            'Failed to notify visit with id "{visitId}" to webhook "{webhook}". {e}',
102
            Argument::that(function (array $extra) {
103
                Assert::assertArrayHasKey('webhook', $extra);
104
                Assert::assertArrayHasKey('visitId', $extra);
105
                Assert::assertArrayHasKey('e', $extra);
106
107
                return $extra;
108
            }),
109
        );
110
111
        $this->createListener($webhooks)(new VisitLocated('1'));
112
113
        $find->shouldHaveBeenCalledOnce();
114
        $requestAsync->shouldHaveBeenCalledTimes(count($webhooks));
115
        $logWarning->shouldHaveBeenCalledTimes(count($invalidWebhooks));
116
    }
117
118
    private function createListener(array $webhooks): NotifyVisitToWebHooks
119
    {
120
        return new NotifyVisitToWebHooks(
121
            $this->httpClient->reveal(),
122
            $this->em->reveal(),
123
            $this->logger->reveal(),
124
            $webhooks,
125
            [],
126
            new AppOptions(['name' => 'Shlink', 'version' => '1.2.3']),
127
        );
128
    }
129
}
130