emptyWebhooksMakeNoFurtherActions()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
c 0
b 0
f 0
dl 0
loc 7
rs 10
cc 1
nc 1
nop 0
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\PhpUnit\ProphecyTrait;
18
use Prophecy\Prophecy\ObjectProphecy;
19
use Psr\Log\LoggerInterface;
20
use Shlinkio\Shlink\Core\Entity\ShortUrl;
21
use Shlinkio\Shlink\Core\Entity\Visit;
22
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...
23
use Shlinkio\Shlink\Core\EventDispatcher\VisitLocated;
24
use Shlinkio\Shlink\Core\Model\Visitor;
25
use Shlinkio\Shlink\Core\Options\AppOptions;
26
27
use function count;
28
use function Functional\contains;
29
30
class NotifyVisitToWebHooksTest extends TestCase
31
{
32
    use ProphecyTrait;
33
34
    private ObjectProphecy $httpClient;
35
    private ObjectProphecy $em;
36
    private ObjectProphecy $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