Completed
Pull Request — master (#275)
by Alejandro
03:03 queued 43s
created

ProcessVisitsCommandTest::setUp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 0
dl 0
loc 14
rs 9.9666
c 0
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace ShlinkioTest\Shlink\CLI\Command\Visit;
5
6
use PHPUnit\Framework\TestCase;
7
use Prophecy\Argument;
8
use Prophecy\Prophecy\ObjectProphecy;
9
use Shlinkio\Shlink\CLI\Command\Visit\ProcessVisitsCommand;
10
use Shlinkio\Shlink\Common\Exception\WrongIpException;
11
use Shlinkio\Shlink\Common\IpGeolocation\IpApiLocationResolver;
12
use Shlinkio\Shlink\Common\Util\IpAddress;
13
use Shlinkio\Shlink\Core\Entity\ShortUrl;
14
use Shlinkio\Shlink\Core\Entity\Visit;
15
use Shlinkio\Shlink\Core\Entity\VisitLocation;
16
use Shlinkio\Shlink\Core\Exception\IpCannotBeLocatedException;
17
use Shlinkio\Shlink\Core\Model\Visitor;
18
use Shlinkio\Shlink\Core\Service\VisitService;
19
use Symfony\Component\Console\Application;
20
use Symfony\Component\Console\Output\OutputInterface;
21
use Symfony\Component\Console\Tester\CommandTester;
22
use Throwable;
23
use Zend\I18n\Translator\Translator;
24
use function array_shift;
25
26
class ProcessVisitsCommandTest extends TestCase
27
{
28
    /**
29
     * @var CommandTester
30
     */
31
    private $commandTester;
32
    /**
33
     * @var ObjectProphecy
34
     */
35
    private $visitService;
36
    /**
37
     * @var ObjectProphecy
38
     */
39
    private $ipResolver;
40
41
    public function setUp()
42
    {
43
        $this->visitService = $this->prophesize(VisitService::class);
44
        $this->ipResolver = $this->prophesize(IpApiLocationResolver::class);
45
46
        $command = new ProcessVisitsCommand(
47
            $this->visitService->reveal(),
48
            $this->ipResolver->reveal(),
49
            Translator::factory([])
50
        );
51
        $app = new Application();
52
        $app->add($command);
53
54
        $this->commandTester = new CommandTester($command);
55
    }
56
57
    /**
58
     * @test
59
     */
60
    public function allPendingVisitsAreProcessed()
61
    {
62
        $visit = new Visit(new ShortUrl(''), new Visitor('', '', '1.2.3.4'));
63
        $location = new VisitLocation([]);
64
65
        $locateVisits = $this->visitService->locateVisits(Argument::cetera())->will(
66
            function (array $args) use ($visit, $location) {
67
                $firstCallback = array_shift($args);
68
                $firstCallback($visit);
69
70
                $secondCallback = array_shift($args);
71
                $secondCallback($location, $visit);
72
            }
73
        );
74
        $resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willReturn([]);
75
76
        $this->commandTester->execute([
77
            'command' => 'visit:process',
78
        ]);
79
        $output = $this->commandTester->getDisplay();
80
81
        $this->assertContains('Processing IP 1.2.3.0', $output);
82
        $locateVisits->shouldHaveBeenCalledOnce();
83
        $resolveIpLocation->shouldHaveBeenCalledOnce();
84
    }
85
86
    /**
87
     * @test
88
     * @dataProvider provideIgnoredAddresses
89
     */
90
    public function localhostAndEmptyAddressesAreIgnored(?string $address, string $message)
91
    {
92
        $visit = new Visit(new ShortUrl(''), new Visitor('', '', $address));
93
        $location = new VisitLocation([]);
94
95
        $locateVisits = $this->visitService->locateVisits(Argument::cetera())->will(
96
            function (array $args) use ($visit, $location) {
97
                $firstCallback = array_shift($args);
98
                $firstCallback($visit);
99
100
                $secondCallback = array_shift($args);
101
                $secondCallback($location, $visit);
102
            }
103
        );
104
        $resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willReturn([]);
105
106
        try {
107
            $this->commandTester->execute([
108
                'command' => 'visit:process',
109
            ], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]);
110
        } catch (Throwable $e) {
111
            $output = $this->commandTester->getDisplay();
112
113
            $this->assertInstanceOf(IpCannotBeLocatedException::class, $e);
114
115
            $this->assertContains($message, $output);
116
            $locateVisits->shouldHaveBeenCalledOnce();
117
            $resolveIpLocation->shouldNotHaveBeenCalled();
118
        }
119
    }
120
121
    public function provideIgnoredAddresses(): array
122
    {
123
        return [
124
            ['', 'Ignored visit with no IP address'],
125
            [null, 'Ignored visit with no IP address'],
126
            [IpAddress::LOCALHOST, 'Ignored localhost address'],
127
        ];
128
    }
129
130
    /**
131
     * @test
132
     */
133
    public function errorWhileLocatingIpIsDisplayed()
134
    {
135
        $visit = new Visit(new ShortUrl(''), new Visitor('', '', '1.2.3.4'));
136
        $location = new VisitLocation([]);
137
138
        $locateVisits = $this->visitService->locateVisits(Argument::cetera())->will(
139
            function (array $args) use ($visit, $location) {
140
                $firstCallback = array_shift($args);
141
                $firstCallback($visit);
142
143
                $secondCallback = array_shift($args);
144
                $secondCallback($location, $visit);
145
            }
146
        );
147
        $resolveIpLocation = $this->ipResolver->resolveIpLocation(Argument::any())->willThrow(WrongIpException::class);
148
149
        try {
150
            $this->commandTester->execute([
151
                'command' => 'visit:process',
152
            ], ['verbosity' => OutputInterface::VERBOSITY_VERBOSE]);
153
        } catch (Throwable $e) {
154
            $output = $this->commandTester->getDisplay();
155
156
            $this->assertInstanceOf(IpCannotBeLocatedException::class, $e);
157
158
            $this->assertContains('An error occurred while locating IP. Skipped', $output);
159
            $locateVisits->shouldHaveBeenCalledOnce();
160
            $resolveIpLocation->shouldHaveBeenCalledOnce();
161
        }
162
    }
163
}
164