1
|
|
|
<?php |
2
|
|
|
namespace ShlinkioTest\Shlink\Core\Service; |
3
|
|
|
|
4
|
|
|
use Doctrine\Common\Cache\ArrayCache; |
5
|
|
|
use Doctrine\Common\Cache\Cache; |
6
|
|
|
use Doctrine\Common\Persistence\ObjectRepository; |
7
|
|
|
use Doctrine\DBAL\Connection; |
8
|
|
|
use Doctrine\ORM\EntityManagerInterface; |
9
|
|
|
use Doctrine\ORM\ORMException; |
10
|
|
|
use GuzzleHttp\ClientInterface; |
11
|
|
|
use GuzzleHttp\Exception\ClientException; |
12
|
|
|
use GuzzleHttp\Psr7\Request; |
13
|
|
|
use PHPUnit\Framework\TestCase; |
14
|
|
|
use Prophecy\Argument; |
15
|
|
|
use Prophecy\Prophecy\ObjectProphecy; |
16
|
|
|
use Shlinkio\Shlink\Core\Entity\ShortUrl; |
17
|
|
|
use Shlinkio\Shlink\Core\Service\UrlShortener; |
18
|
|
|
use Zend\Diactoros\Uri; |
19
|
|
|
|
20
|
|
|
class UrlShortenerTest extends TestCase |
21
|
|
|
{ |
22
|
|
|
/** |
23
|
|
|
* @var UrlShortener |
24
|
|
|
*/ |
25
|
|
|
protected $urlShortener; |
26
|
|
|
/** |
27
|
|
|
* @var ObjectProphecy |
28
|
|
|
*/ |
29
|
|
|
protected $em; |
30
|
|
|
/** |
31
|
|
|
* @var ObjectProphecy |
32
|
|
|
*/ |
33
|
|
|
protected $httpClient; |
34
|
|
|
/** |
35
|
|
|
* @var Cache |
36
|
|
|
*/ |
37
|
|
|
protected $cache; |
38
|
|
|
|
39
|
|
|
public function setUp() |
40
|
|
|
{ |
41
|
|
|
$this->httpClient = $this->prophesize(ClientInterface::class); |
42
|
|
|
|
43
|
|
|
$this->em = $this->prophesize(EntityManagerInterface::class); |
44
|
|
|
$conn = $this->prophesize(Connection::class); |
45
|
|
|
$conn->isTransactionActive()->willReturn(false); |
46
|
|
|
$this->em->getConnection()->willReturn($conn->reveal()); |
47
|
|
|
$this->em->flush()->willReturn(null); |
48
|
|
|
$this->em->commit()->willReturn(null); |
49
|
|
|
$this->em->beginTransaction()->willReturn(null); |
50
|
|
|
$this->em->persist(Argument::any())->will(function ($arguments) { |
51
|
|
|
/** @var ShortUrl $shortUrl */ |
52
|
|
|
$shortUrl = $arguments[0]; |
53
|
|
|
$shortUrl->setId(10); |
54
|
|
|
}); |
55
|
|
|
$repo = $this->prophesize(ObjectRepository::class); |
56
|
|
|
$repo->findOneBy(Argument::any())->willReturn(null); |
57
|
|
|
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); |
58
|
|
|
|
59
|
|
|
$this->cache = new ArrayCache(); |
60
|
|
|
|
61
|
|
|
$this->setUrlShortener(false); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @param bool $isUrlValidationExists |
66
|
|
|
*/ |
67
|
|
|
public function setUrlShortener($isUrlValidationExists) |
68
|
|
|
{ |
69
|
|
|
$this->urlShortener = new UrlShortener( |
70
|
|
|
$this->httpClient->reveal(), |
71
|
|
|
$this->em->reveal(), |
72
|
|
|
$this->cache, |
73
|
|
|
UrlShortener::DEFAULT_CHARS, |
74
|
|
|
$isUrlValidationExists |
75
|
|
|
); |
76
|
|
|
} |
77
|
|
|
|
78
|
|
|
/** |
79
|
|
|
* @test |
80
|
|
|
*/ |
81
|
|
|
public function urlIsProperlyShortened() |
82
|
|
|
{ |
83
|
|
|
// 10 -> 12C1c |
|
|
|
|
84
|
|
|
$shortCode = $this->urlShortener->urlToShortCode(new Uri('http://foobar.com/12345/hello?foo=bar')); |
85
|
|
|
$this->assertEquals('12C1c', $shortCode); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* @test |
90
|
|
|
* @expectedException \Shlinkio\Shlink\Common\Exception\RuntimeException |
91
|
|
|
*/ |
92
|
|
|
public function exceptionIsThrownWhenOrmThrowsException() |
93
|
|
|
{ |
94
|
|
|
$conn = $this->prophesize(Connection::class); |
95
|
|
|
$conn->isTransactionActive()->willReturn(true); |
96
|
|
|
$this->em->getConnection()->willReturn($conn->reveal()); |
97
|
|
|
$this->em->rollback()->shouldBeCalledTimes(1); |
98
|
|
|
$this->em->close()->shouldBeCalledTimes(1); |
99
|
|
|
|
100
|
|
|
$this->em->flush()->willThrow(new ORMException()); |
101
|
|
|
$this->urlShortener->urlToShortCode(new Uri('http://foobar.com/12345/hello?foo=bar')); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* @test |
106
|
|
|
* @expectedException \Shlinkio\Shlink\Core\Exception\InvalidUrlException |
107
|
|
|
*/ |
108
|
|
|
public function exceptionIsThrownWhenUrlDoesNotExist() |
109
|
|
|
{ |
110
|
|
|
$this->setUrlShortener(true); |
111
|
|
|
|
112
|
|
|
$this->httpClient->request(Argument::cetera())->willThrow( |
113
|
|
|
new ClientException('', $this->prophesize(Request::class)->reveal()) |
114
|
|
|
); |
115
|
|
|
$this->urlShortener->urlToShortCode(new Uri('http://foobar.com/12345/hello?foo=bar')); |
116
|
|
|
} |
117
|
|
|
|
118
|
|
|
/** |
119
|
|
|
* @test |
120
|
|
|
*/ |
121
|
|
|
public function whenShortUrlExistsItsShortcodeIsReturned() |
122
|
|
|
{ |
123
|
|
|
$shortUrl = new ShortUrl(); |
124
|
|
|
$shortUrl->setShortCode('expected_shortcode'); |
125
|
|
|
$repo = $this->prophesize(ObjectRepository::class); |
126
|
|
|
$repo->findOneBy(Argument::any())->willReturn($shortUrl); |
127
|
|
|
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); |
128
|
|
|
|
129
|
|
|
$shortCode = $this->urlShortener->urlToShortCode(new Uri('http://foobar.com/12345/hello?foo=bar')); |
130
|
|
|
$this->assertEquals($shortUrl->getShortCode(), $shortCode); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @test |
135
|
|
|
*/ |
136
|
|
|
public function shortCodeIsProperlyParsed() |
137
|
|
|
{ |
138
|
|
|
// 12C1c -> 10 |
|
|
|
|
139
|
|
|
$shortCode = '12C1c'; |
140
|
|
|
$shortUrl = new ShortUrl(); |
141
|
|
|
$shortUrl->setShortCode($shortCode) |
142
|
|
|
->setOriginalUrl('expected_url'); |
143
|
|
|
|
144
|
|
|
$repo = $this->prophesize(ObjectRepository::class); |
145
|
|
|
$repo->findOneBy(['shortCode' => $shortCode])->willReturn($shortUrl); |
146
|
|
|
$this->em->getRepository(ShortUrl::class)->willReturn($repo->reveal()); |
147
|
|
|
|
148
|
|
|
$this->assertFalse($this->cache->contains($shortCode . '_longUrl')); |
149
|
|
|
$url = $this->urlShortener->shortCodeToUrl($shortCode); |
150
|
|
|
$this->assertEquals($shortUrl->getOriginalUrl(), $url); |
151
|
|
|
$this->assertTrue($this->cache->contains($shortCode . '_longUrl')); |
152
|
|
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* @test |
156
|
|
|
* @expectedException \Shlinkio\Shlink\Core\Exception\InvalidShortCodeException |
157
|
|
|
*/ |
158
|
|
|
public function invalidCharSetThrowsException() |
159
|
|
|
{ |
160
|
|
|
$this->urlShortener->shortCodeToUrl('&/('); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* @test |
165
|
|
|
*/ |
166
|
|
|
public function cachedShortCodeDoesNotHitDatabase() |
167
|
|
|
{ |
168
|
|
|
$shortCode = '12C1c'; |
169
|
|
|
$expectedUrl = 'expected_url'; |
170
|
|
|
$this->cache->save($shortCode . '_longUrl', $expectedUrl); |
171
|
|
|
$this->em->getRepository(ShortUrl::class)->willReturn(null)->shouldBeCalledTimes(0); |
172
|
|
|
|
173
|
|
|
$url = $this->urlShortener->shortCodeToUrl($shortCode); |
174
|
|
|
$this->assertEquals($expectedUrl, $url); |
175
|
|
|
} |
176
|
|
|
} |
177
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.