VisitRepositoryTest   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 200
Duplicated Lines 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 14
eloc 110
dl 0
loc 200
rs 10
c 4
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A findVisitsByShortCodeReturnsProperData() 0 20 1
A setUp() 0 3 1
A findVisitsReturnsProperVisits() 0 35 4
A provideBlockSize() 0 3 1
A findVisitsByTagReturnsProperData() 0 26 1
A createShortUrlsAndVisits() 0 37 4
A countVisitsByTagReturnsProperData() 0 23 1
A countVisitsByShortCodeReturnsProperData() 0 16 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ShlinkioTest\Shlink\Core\Repository;
6
7
use Cake\Chronos\Chronos;
8
use Doctrine\Common\Collections\ArrayCollection;
9
use Shlinkio\Shlink\Common\Util\DateRange;
10
use Shlinkio\Shlink\Core\Entity\Domain;
11
use Shlinkio\Shlink\Core\Entity\ShortUrl;
12
use Shlinkio\Shlink\Core\Entity\Tag;
13
use Shlinkio\Shlink\Core\Entity\Visit;
14
use Shlinkio\Shlink\Core\Entity\VisitLocation;
15
use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
16
use Shlinkio\Shlink\Core\Model\Visitor;
17
use Shlinkio\Shlink\Core\Repository\VisitRepository;
18
use Shlinkio\Shlink\IpGeolocation\Model\Location;
19
use Shlinkio\Shlink\TestUtils\DbTest\DatabaseTestCase;
20
21
use function Functional\map;
22
use function range;
23
use function sprintf;
24
25
class VisitRepositoryTest extends DatabaseTestCase
26
{
27
    protected const ENTITIES_TO_EMPTY = [
28
        VisitLocation::class,
29
        Visit::class,
30
        ShortUrl::class,
31
        Domain::class,
32
        Tag::class,
33
    ];
34
35
    private VisitRepository $repo;
36
37
    protected function setUp(): void
38
    {
39
        $this->repo = $this->getEntityManager()->getRepository(Visit::class);
40
    }
41
42
    /**
43
     * @test
44
     * @dataProvider provideBlockSize
45
     */
46
    public function findVisitsReturnsProperVisits(int $blockSize): void
47
    {
48
        $shortUrl = new ShortUrl('');
49
        $this->getEntityManager()->persist($shortUrl);
50
        $countIterable = function (iterable $results): int {
51
            $resultsCount = 0;
52
            foreach ($results as $value) {
53
                $resultsCount++;
54
            }
55
56
            return $resultsCount;
57
        };
58
59
        for ($i = 0; $i < 6; $i++) {
60
            $visit = new Visit($shortUrl, Visitor::emptyInstance());
61
62
            if ($i >= 2) {
63
                $location = new VisitLocation(Location::emptyInstance());
64
                $this->getEntityManager()->persist($location);
65
                $visit->locate($location);
66
            }
67
68
            $this->getEntityManager()->persist($visit);
69
        }
70
        $this->getEntityManager()->flush();
71
72
        $withEmptyLocation = $this->repo->findVisitsWithEmptyLocation($blockSize);
73
        $unlocated = $this->repo->findUnlocatedVisits($blockSize);
74
        $all = $this->repo->findAllVisits($blockSize);
75
76
        // Important! assertCount will not work here, as this iterable object loads data dynamically and the count
77
        // is 0 if not iterated
78
        self::assertEquals(2, $countIterable($unlocated));
79
        self::assertEquals(4, $countIterable($withEmptyLocation));
80
        self::assertEquals(6, $countIterable($all));
81
    }
82
83
    public function provideBlockSize(): iterable
84
    {
85
        return map(range(1, 10), fn (int $value) => [$value]);
86
    }
87
88
    /** @test */
89
    public function findVisitsByShortCodeReturnsProperData(): void
90
    {
91
        [$shortCode, $domain] = $this->createShortUrlsAndVisits();
92
93
        self::assertCount(0, $this->repo->findVisitsByShortCode('invalid'));
94
        self::assertCount(6, $this->repo->findVisitsByShortCode($shortCode));
95
        self::assertCount(3, $this->repo->findVisitsByShortCode($shortCode, $domain));
96
        self::assertCount(2, $this->repo->findVisitsByShortCode($shortCode, null, new DateRange(
97
            Chronos::parse('2016-01-02'),
98
            Chronos::parse('2016-01-03'),
99
        )));
100
        self::assertCount(4, $this->repo->findVisitsByShortCode($shortCode, null, new DateRange(
101
            Chronos::parse('2016-01-03'),
102
        )));
103
        self::assertCount(1, $this->repo->findVisitsByShortCode($shortCode, $domain, new DateRange(
104
            Chronos::parse('2016-01-03'),
105
        )));
106
        self::assertCount(3, $this->repo->findVisitsByShortCode($shortCode, null, null, 3, 2));
107
        self::assertCount(2, $this->repo->findVisitsByShortCode($shortCode, null, null, 5, 4));
108
        self::assertCount(1, $this->repo->findVisitsByShortCode($shortCode, $domain, null, 3, 2));
109
    }
110
111
    /** @test */
112
    public function countVisitsByShortCodeReturnsProperData(): void
113
    {
114
        [$shortCode, $domain] = $this->createShortUrlsAndVisits();
115
116
        self::assertEquals(0, $this->repo->countVisitsByShortCode('invalid'));
117
        self::assertEquals(6, $this->repo->countVisitsByShortCode($shortCode));
118
        self::assertEquals(3, $this->repo->countVisitsByShortCode($shortCode, $domain));
119
        self::assertEquals(2, $this->repo->countVisitsByShortCode($shortCode, null, new DateRange(
120
            Chronos::parse('2016-01-02'),
121
            Chronos::parse('2016-01-03'),
122
        )));
123
        self::assertEquals(4, $this->repo->countVisitsByShortCode($shortCode, null, new DateRange(
124
            Chronos::parse('2016-01-03'),
125
        )));
126
        self::assertEquals(1, $this->repo->countVisitsByShortCode($shortCode, $domain, new DateRange(
127
            Chronos::parse('2016-01-03'),
128
        )));
129
    }
130
131
    /** @test */
132
    public function findVisitsByTagReturnsProperData(): void
133
    {
134
        $foo = new Tag('foo');
135
        $this->getEntityManager()->persist($foo);
136
137
        /** @var ShortUrl $shortUrl */
138
        [,, $shortUrl] = $this->createShortUrlsAndVisits(false);
139
        /** @var ShortUrl $shortUrl2 */
140
        [,, $shortUrl2] = $this->createShortUrlsAndVisits(false);
141
        /** @var ShortUrl $shortUrl3 */
142
        [,, $shortUrl3] = $this->createShortUrlsAndVisits(false);
143
144
        $shortUrl->setTags(new ArrayCollection([$foo]));
145
        $shortUrl2->setTags(new ArrayCollection([$foo]));
146
        $shortUrl3->setTags(new ArrayCollection([$foo]));
147
148
        $this->getEntityManager()->flush();
149
150
        self::assertCount(0, $this->repo->findVisitsByTag('invalid'));
151
        self::assertCount(18, $this->repo->findVisitsByTag((string) $foo));
152
        self::assertCount(6, $this->repo->findVisitsByTag((string) $foo, new DateRange(
153
            Chronos::parse('2016-01-02'),
154
            Chronos::parse('2016-01-03'),
155
        )));
156
        self::assertCount(12, $this->repo->findVisitsByTag((string) $foo, new DateRange(
157
            Chronos::parse('2016-01-03'),
158
        )));
159
    }
160
161
    /** @test */
162
    public function countVisitsByTagReturnsProperData(): void
163
    {
164
        $foo = new Tag('foo');
165
        $this->getEntityManager()->persist($foo);
166
167
        /** @var ShortUrl $shortUrl */
168
        [,, $shortUrl] = $this->createShortUrlsAndVisits(false);
169
        /** @var ShortUrl $shortUrl2 */
170
        [,, $shortUrl2] = $this->createShortUrlsAndVisits(false);
171
172
        $shortUrl->setTags(new ArrayCollection([$foo]));
173
        $shortUrl2->setTags(new ArrayCollection([$foo]));
174
175
        $this->getEntityManager()->flush();
176
177
        self::assertEquals(0, $this->repo->countVisitsByTag('invalid'));
178
        self::assertEquals(12, $this->repo->countVisitsByTag((string) $foo));
179
        self::assertEquals(4, $this->repo->countVisitsByTag((string) $foo, new DateRange(
180
            Chronos::parse('2016-01-02'),
181
            Chronos::parse('2016-01-03'),
182
        )));
183
        self::assertEquals(8, $this->repo->countVisitsByTag((string) $foo, new DateRange(
184
            Chronos::parse('2016-01-03'),
185
        )));
186
    }
187
188
    private function createShortUrlsAndVisits(bool $withDomain = true): array
189
    {
190
        $shortUrl = new ShortUrl('');
191
        $domain = 'example.com';
192
        $shortCode = $shortUrl->getShortCode();
193
        $this->getEntityManager()->persist($shortUrl);
194
195
        for ($i = 0; $i < 6; $i++) {
196
            $visit = new Visit(
197
                $shortUrl,
198
                Visitor::emptyInstance(),
199
                true,
200
                Chronos::parse(sprintf('2016-01-0%s', $i + 1)),
201
            );
202
            $this->getEntityManager()->persist($visit);
203
        }
204
205
        if ($withDomain) {
206
            $shortUrlWithDomain = new ShortUrl('', ShortUrlMeta::fromRawData([
207
                'customSlug' => $shortCode,
208
                'domain' => $domain,
209
            ]));
210
            $this->getEntityManager()->persist($shortUrlWithDomain);
211
212
            for ($i = 0; $i < 3; $i++) {
213
                $visit = new Visit(
214
                    $shortUrlWithDomain,
215
                    Visitor::emptyInstance(),
216
                    true,
217
                    Chronos::parse(sprintf('2016-01-0%s', $i + 1)),
218
                );
219
                $this->getEntityManager()->persist($visit);
220
            }
221
            $this->getEntityManager()->flush();
222
        }
223
224
        return [$shortCode, $domain, $shortUrl];
225
    }
226
}
227