]> git.immae.eu Git - github/wallabag/wallabag.git/blob - tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php
Merge pull request #3515 from wallabag/php-7.2
[github/wallabag/wallabag.git] / tests / Wallabag / CoreBundle / Helper / ContentProxyTest.php
1 <?php
2
3 namespace Tests\Wallabag\CoreBundle\Helper;
4
5 use Graby\Graby;
6 use Monolog\Handler\TestHandler;
7 use Monolog\Logger;
8 use PHPUnit\Framework\TestCase;
9 use Psr\Log\NullLogger;
10 use Symfony\Component\Validator\ConstraintViolation;
11 use Symfony\Component\Validator\ConstraintViolationList;
12 use Symfony\Component\Validator\Validator\RecursiveValidator;
13 use Wallabag\CoreBundle\Entity\Entry;
14 use Wallabag\CoreBundle\Entity\Tag;
15 use Wallabag\CoreBundle\Helper\ContentProxy;
16 use Wallabag\CoreBundle\Helper\RuleBasedTagger;
17 use Wallabag\UserBundle\Entity\User;
18
19 class ContentProxyTest extends TestCase
20 {
21 private $fetchingErrorMessage = 'wallabag can\'t retrieve contents for this article. Please <a href="http://doc.wallabag.org/en/user/errors_during_fetching.html#how-can-i-help-to-fix-that">troubleshoot this issue</a>.';
22
23 public function testWithBadUrl()
24 {
25 $tagger = $this->getTaggerMock();
26 $tagger->expects($this->once())
27 ->method('tag');
28
29 $graby = $this->getMockBuilder('Graby\Graby')
30 ->setMethods(['fetchContent'])
31 ->disableOriginalConstructor()
32 ->getMock();
33
34 $graby->expects($this->any())
35 ->method('fetchContent')
36 ->willReturn([
37 'html' => false,
38 'title' => '',
39 'url' => '',
40 'content_type' => '',
41 'language' => '',
42 ]);
43
44 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
45 $entry = new Entry(new User());
46 $proxy->updateEntry($entry, 'http://user@:80');
47
48 $this->assertSame('http://user@:80', $entry->getUrl());
49 $this->assertEmpty($entry->getTitle());
50 $this->assertSame($this->fetchingErrorMessage, $entry->getContent());
51 $this->assertEmpty($entry->getPreviewPicture());
52 $this->assertEmpty($entry->getMimetype());
53 $this->assertEmpty($entry->getLanguage());
54 $this->assertSame(0.0, $entry->getReadingTime());
55 $this->assertNull($entry->getDomainName());
56 }
57
58 public function testWithEmptyContent()
59 {
60 $tagger = $this->getTaggerMock();
61 $tagger->expects($this->once())
62 ->method('tag');
63
64 $graby = $this->getMockBuilder('Graby\Graby')
65 ->setMethods(['fetchContent'])
66 ->disableOriginalConstructor()
67 ->getMock();
68
69 $graby->expects($this->any())
70 ->method('fetchContent')
71 ->willReturn([
72 'html' => false,
73 'title' => '',
74 'url' => '',
75 'content_type' => '',
76 'language' => '',
77 ]);
78
79 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
80 $entry = new Entry(new User());
81 $proxy->updateEntry($entry, 'http://0.0.0.0');
82
83 $this->assertSame('http://0.0.0.0', $entry->getUrl());
84 $this->assertEmpty($entry->getTitle());
85 $this->assertSame($this->fetchingErrorMessage, $entry->getContent());
86 $this->assertEmpty($entry->getPreviewPicture());
87 $this->assertEmpty($entry->getMimetype());
88 $this->assertEmpty($entry->getLanguage());
89 $this->assertSame(0.0, $entry->getReadingTime());
90 $this->assertSame('0.0.0.0', $entry->getDomainName());
91 }
92
93 public function testWithEmptyContentButOG()
94 {
95 $tagger = $this->getTaggerMock();
96 $tagger->expects($this->once())
97 ->method('tag');
98
99 $graby = $this->getMockBuilder('Graby\Graby')
100 ->setMethods(['fetchContent'])
101 ->disableOriginalConstructor()
102 ->getMock();
103
104 $graby->expects($this->any())
105 ->method('fetchContent')
106 ->willReturn([
107 'html' => false,
108 'title' => '',
109 'url' => '',
110 'content_type' => '',
111 'language' => '',
112 'status' => '',
113 'open_graph' => [
114 'og_title' => 'my title',
115 'og_description' => 'desc',
116 ],
117 ]);
118
119 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
120 $entry = new Entry(new User());
121 $proxy->updateEntry($entry, 'http://domain.io');
122
123 $this->assertSame('http://domain.io', $entry->getUrl());
124 $this->assertSame('my title', $entry->getTitle());
125 $this->assertSame($this->fetchingErrorMessage . '<p><i>But we found a short description: </i></p>desc', $entry->getContent());
126 $this->assertEmpty($entry->getPreviewPicture());
127 $this->assertEmpty($entry->getLanguage());
128 $this->assertEmpty($entry->getHttpStatus());
129 $this->assertEmpty($entry->getMimetype());
130 $this->assertSame(0.0, $entry->getReadingTime());
131 $this->assertSame('domain.io', $entry->getDomainName());
132 }
133
134 public function testWithContent()
135 {
136 $tagger = $this->getTaggerMock();
137 $tagger->expects($this->once())
138 ->method('tag');
139
140 $graby = $this->getMockBuilder('Graby\Graby')
141 ->setMethods(['fetchContent'])
142 ->disableOriginalConstructor()
143 ->getMock();
144
145 $graby->expects($this->any())
146 ->method('fetchContent')
147 ->willReturn([
148 'html' => str_repeat('this is my content', 325),
149 'title' => 'this is my title',
150 'url' => 'http://1.1.1.1',
151 'content_type' => 'text/html',
152 'language' => 'fr',
153 'status' => '200',
154 'open_graph' => [
155 'og_title' => 'my OG title',
156 'og_description' => 'OG desc',
157 'og_image' => 'http://3.3.3.3/cover.jpg',
158 ],
159 ]);
160
161 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
162 $entry = new Entry(new User());
163 $proxy->updateEntry($entry, 'http://0.0.0.0');
164
165 $this->assertSame('http://1.1.1.1', $entry->getUrl());
166 $this->assertSame('this is my title', $entry->getTitle());
167 $this->assertContains('this is my content', $entry->getContent());
168 $this->assertSame('http://3.3.3.3/cover.jpg', $entry->getPreviewPicture());
169 $this->assertSame('text/html', $entry->getMimetype());
170 $this->assertSame('fr', $entry->getLanguage());
171 $this->assertSame('200', $entry->getHttpStatus());
172 $this->assertSame(4.0, $entry->getReadingTime());
173 $this->assertSame('1.1.1.1', $entry->getDomainName());
174 }
175
176 public function testWithContentAndNoOgImage()
177 {
178 $tagger = $this->getTaggerMock();
179 $tagger->expects($this->once())
180 ->method('tag');
181
182 $graby = $this->getMockBuilder('Graby\Graby')
183 ->setMethods(['fetchContent'])
184 ->disableOriginalConstructor()
185 ->getMock();
186
187 $graby->expects($this->any())
188 ->method('fetchContent')
189 ->willReturn([
190 'html' => str_repeat('this is my content', 325),
191 'title' => 'this is my title',
192 'url' => 'http://1.1.1.1',
193 'content_type' => 'text/html',
194 'language' => 'fr',
195 'status' => '200',
196 'open_graph' => [
197 'og_title' => 'my OG title',
198 'og_description' => 'OG desc',
199 'og_image' => null,
200 ],
201 ]);
202
203 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
204 $entry = new Entry(new User());
205 $proxy->updateEntry($entry, 'http://0.0.0.0');
206
207 $this->assertSame('http://1.1.1.1', $entry->getUrl());
208 $this->assertSame('this is my title', $entry->getTitle());
209 $this->assertContains('this is my content', $entry->getContent());
210 $this->assertNull($entry->getPreviewPicture());
211 $this->assertSame('text/html', $entry->getMimetype());
212 $this->assertSame('fr', $entry->getLanguage());
213 $this->assertSame('200', $entry->getHttpStatus());
214 $this->assertSame(4.0, $entry->getReadingTime());
215 $this->assertSame('1.1.1.1', $entry->getDomainName());
216 }
217
218 public function testWithContentAndBadLanguage()
219 {
220 $tagger = $this->getTaggerMock();
221 $tagger->expects($this->once())
222 ->method('tag');
223
224 $validator = $this->getValidator(false);
225 $validator->expects($this->once())
226 ->method('validate')
227 ->willReturn(new ConstraintViolationList([new ConstraintViolation('oops', 'oops', [], 'oops', 'language', 'dontexist')]));
228
229 $graby = $this->getMockBuilder('Graby\Graby')
230 ->setMethods(['fetchContent'])
231 ->disableOriginalConstructor()
232 ->getMock();
233
234 $graby->expects($this->any())
235 ->method('fetchContent')
236 ->willReturn([
237 'html' => str_repeat('this is my content', 325),
238 'title' => 'this is my title',
239 'url' => 'http://1.1.1.1',
240 'content_type' => 'text/html',
241 'language' => 'dontexist',
242 'status' => '200',
243 ]);
244
245 $proxy = new ContentProxy($graby, $tagger, $validator, $this->getLogger(), $this->fetchingErrorMessage);
246 $entry = new Entry(new User());
247 $proxy->updateEntry($entry, 'http://0.0.0.0');
248
249 $this->assertSame('http://1.1.1.1', $entry->getUrl());
250 $this->assertSame('this is my title', $entry->getTitle());
251 $this->assertContains('this is my content', $entry->getContent());
252 $this->assertSame('text/html', $entry->getMimetype());
253 $this->assertNull($entry->getLanguage());
254 $this->assertSame('200', $entry->getHttpStatus());
255 $this->assertSame(4.0, $entry->getReadingTime());
256 $this->assertSame('1.1.1.1', $entry->getDomainName());
257 }
258
259 public function testWithContentAndBadOgImage()
260 {
261 $tagger = $this->getTaggerMock();
262 $tagger->expects($this->once())
263 ->method('tag');
264
265 $validator = $this->getValidator(false);
266 $validator->expects($this->exactly(2))
267 ->method('validate')
268 ->will($this->onConsecutiveCalls(
269 new ConstraintViolationList(),
270 new ConstraintViolationList([new ConstraintViolation('oops', 'oops', [], 'oops', 'url', 'https://')])
271 ));
272
273 $graby = $this->getMockBuilder('Graby\Graby')
274 ->setMethods(['fetchContent'])
275 ->disableOriginalConstructor()
276 ->getMock();
277
278 $graby->expects($this->any())
279 ->method('fetchContent')
280 ->willReturn([
281 'html' => str_repeat('this is my content', 325),
282 'title' => 'this is my title',
283 'url' => 'http://1.1.1.1',
284 'content_type' => 'text/html',
285 'language' => 'fr',
286 'status' => '200',
287 'open_graph' => [
288 'og_title' => 'my OG title',
289 'og_description' => 'OG desc',
290 'og_image' => 'https://',
291 ],
292 ]);
293
294 $proxy = new ContentProxy($graby, $tagger, $validator, $this->getLogger(), $this->fetchingErrorMessage);
295 $entry = new Entry(new User());
296 $proxy->updateEntry($entry, 'http://0.0.0.0');
297
298 $this->assertSame('http://1.1.1.1', $entry->getUrl());
299 $this->assertSame('this is my title', $entry->getTitle());
300 $this->assertContains('this is my content', $entry->getContent());
301 $this->assertNull($entry->getPreviewPicture());
302 $this->assertSame('text/html', $entry->getMimetype());
303 $this->assertSame('fr', $entry->getLanguage());
304 $this->assertSame('200', $entry->getHttpStatus());
305 $this->assertSame(4.0, $entry->getReadingTime());
306 $this->assertSame('1.1.1.1', $entry->getDomainName());
307 }
308
309 public function testWithForcedContent()
310 {
311 $tagger = $this->getTaggerMock();
312 $tagger->expects($this->once())
313 ->method('tag');
314
315 $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage, true);
316 $entry = new Entry(new User());
317 $proxy->updateEntry(
318 $entry,
319 'http://0.0.0.0',
320 [
321 'html' => str_repeat('this is my content', 325),
322 'title' => 'this is my title',
323 'url' => 'http://1.1.1.1',
324 'content_type' => 'text/html',
325 'language' => 'fr',
326 'date' => '1395635872',
327 'authors' => ['Jeremy', 'Nico', 'Thomas'],
328 'all_headers' => [
329 'Cache-Control' => 'no-cache',
330 ],
331 ]
332 );
333
334 $this->assertSame('http://1.1.1.1', $entry->getUrl());
335 $this->assertSame('this is my title', $entry->getTitle());
336 $this->assertContains('this is my content', $entry->getContent());
337 $this->assertSame('text/html', $entry->getMimetype());
338 $this->assertSame('fr', $entry->getLanguage());
339 $this->assertSame(4.0, $entry->getReadingTime());
340 $this->assertSame('1.1.1.1', $entry->getDomainName());
341 $this->assertSame('24/03/2014', $entry->getPublishedAt()->format('d/m/Y'));
342 $this->assertContains('Jeremy', $entry->getPublishedBy());
343 $this->assertContains('Nico', $entry->getPublishedBy());
344 $this->assertContains('Thomas', $entry->getPublishedBy());
345 $this->assertNotNull($entry->getHeaders(), 'Headers are stored, so value is not null');
346 $this->assertContains('no-cache', $entry->getHeaders());
347 }
348
349 public function testWithForcedContentAndDatetime()
350 {
351 $tagger = $this->getTaggerMock();
352 $tagger->expects($this->once())
353 ->method('tag');
354
355 $logHandler = new TestHandler();
356 $logger = new Logger('test', [$logHandler]);
357
358 $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $logger, $this->fetchingErrorMessage);
359 $entry = new Entry(new User());
360 $proxy->updateEntry(
361 $entry,
362 'http://1.1.1.1',
363 [
364 'html' => str_repeat('this is my content', 325),
365 'title' => 'this is my title',
366 'url' => 'http://1.1.1.1',
367 'content_type' => 'text/html',
368 'language' => 'fr',
369 'date' => '2016-09-08T11:55:58+0200',
370 ]
371 );
372
373 $this->assertSame('http://1.1.1.1', $entry->getUrl());
374 $this->assertSame('this is my title', $entry->getTitle());
375 $this->assertContains('this is my content', $entry->getContent());
376 $this->assertSame('text/html', $entry->getMimetype());
377 $this->assertSame('fr', $entry->getLanguage());
378 $this->assertSame(4.0, $entry->getReadingTime());
379 $this->assertSame('1.1.1.1', $entry->getDomainName());
380 $this->assertSame('08/09/2016', $entry->getPublishedAt()->format('d/m/Y'));
381 }
382
383 public function testWithForcedContentAndBadDate()
384 {
385 $tagger = $this->getTaggerMock();
386 $tagger->expects($this->once())
387 ->method('tag');
388
389 $logger = new Logger('foo');
390 $handler = new TestHandler();
391 $logger->pushHandler($handler);
392
393 $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $logger, $this->fetchingErrorMessage);
394 $entry = new Entry(new User());
395 $proxy->updateEntry(
396 $entry,
397 'http://1.1.1.1',
398 [
399 'html' => str_repeat('this is my content', 325),
400 'title' => 'this is my title',
401 'url' => 'http://1.1.1.1',
402 'content_type' => 'text/html',
403 'language' => 'fr',
404 'date' => '01 02 2012',
405 ]
406 );
407
408 $this->assertSame('http://1.1.1.1', $entry->getUrl());
409 $this->assertSame('this is my title', $entry->getTitle());
410 $this->assertContains('this is my content', $entry->getContent());
411 $this->assertSame('text/html', $entry->getMimetype());
412 $this->assertSame('fr', $entry->getLanguage());
413 $this->assertSame(4.0, $entry->getReadingTime());
414 $this->assertSame('1.1.1.1', $entry->getDomainName());
415 $this->assertNull($entry->getPublishedAt());
416
417 $records = $handler->getRecords();
418
419 $this->assertCount(1, $records);
420 $this->assertContains('Error while defining date', $records[0]['message']);
421 }
422
423 public function testTaggerThrowException()
424 {
425 $tagger = $this->getTaggerMock();
426 $tagger->expects($this->once())
427 ->method('tag')
428 ->will($this->throwException(new \Exception()));
429
430 $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
431 $entry = new Entry(new User());
432 $proxy->updateEntry(
433 $entry,
434 'http://1.1.1.1',
435 [
436 'html' => str_repeat('this is my content', 325),
437 'title' => 'this is my title',
438 'url' => 'http://1.1.1.1',
439 'content_type' => 'text/html',
440 'language' => 'fr',
441 ]
442 );
443
444 $this->assertCount(0, $entry->getTags());
445 }
446
447 public function dataForCrazyHtml()
448 {
449 return [
450 'script and comment' => [
451 '<strong>Script inside:</strong> <!--[if gte IE 4]><script>alert(\'lol\');</script><![endif]--><br />',
452 'lol',
453 ],
454 'script' => [
455 '<strong>Script inside:</strong><script>alert(\'lol\');</script>',
456 'script',
457 ],
458 ];
459 }
460
461 /**
462 * @dataProvider dataForCrazyHtml
463 */
464 public function testWithCrazyHtmlContent($html, $escapedString)
465 {
466 $tagger = $this->getTaggerMock();
467 $tagger->expects($this->once())
468 ->method('tag');
469
470 $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
471 $entry = new Entry(new User());
472 $proxy->updateEntry(
473 $entry,
474 'http://1.1.1.1',
475 [
476 'html' => $html,
477 'title' => 'this is my title',
478 'url' => 'http://1.1.1.1',
479 'content_type' => 'text/html',
480 'language' => 'fr',
481 'status' => '200',
482 'open_graph' => [
483 'og_title' => 'my OG title',
484 'og_description' => 'OG desc',
485 'og_image' => 'http://3.3.3.3/cover.jpg',
486 ],
487 ]
488 );
489
490 $this->assertSame('http://1.1.1.1', $entry->getUrl());
491 $this->assertSame('this is my title', $entry->getTitle());
492 $this->assertNotContains($escapedString, $entry->getContent());
493 $this->assertSame('http://3.3.3.3/cover.jpg', $entry->getPreviewPicture());
494 $this->assertSame('text/html', $entry->getMimetype());
495 $this->assertSame('fr', $entry->getLanguage());
496 $this->assertSame('200', $entry->getHttpStatus());
497 $this->assertSame('1.1.1.1', $entry->getDomainName());
498 }
499
500 public function testWithImageAsContent()
501 {
502 $tagger = $this->getTaggerMock();
503 $tagger->expects($this->once())
504 ->method('tag');
505
506 $graby = $this->getMockBuilder('Graby\Graby')
507 ->setMethods(['fetchContent'])
508 ->disableOriginalConstructor()
509 ->getMock();
510
511 $graby->expects($this->any())
512 ->method('fetchContent')
513 ->willReturn([
514 'html' => '<p><img src="http://1.1.1.1/image.jpg" /></p>',
515 'title' => 'this is my title',
516 'url' => 'http://1.1.1.1/image.jpg',
517 'content_type' => 'image/jpeg',
518 'status' => '200',
519 'open_graph' => [],
520 ]);
521
522 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
523 $entry = new Entry(new User());
524 $proxy->updateEntry($entry, 'http://0.0.0.0');
525
526 $this->assertSame('http://1.1.1.1/image.jpg', $entry->getUrl());
527 $this->assertSame('this is my title', $entry->getTitle());
528 $this->assertContains('http://1.1.1.1/image.jpg', $entry->getContent());
529 $this->assertSame('http://1.1.1.1/image.jpg', $entry->getPreviewPicture());
530 $this->assertSame('image/jpeg', $entry->getMimetype());
531 $this->assertSame('200', $entry->getHttpStatus());
532 $this->assertSame('1.1.1.1', $entry->getDomainName());
533 }
534
535 private function getTaggerMock()
536 {
537 return $this->getMockBuilder(RuleBasedTagger::class)
538 ->setMethods(['tag'])
539 ->disableOriginalConstructor()
540 ->getMock();
541 }
542
543 private function getLogger()
544 {
545 return new NullLogger();
546 }
547
548 private function getValidator($withDefaultMock = true)
549 {
550 $mock = $this->getMockBuilder(RecursiveValidator::class)
551 ->setMethods(['validate'])
552 ->disableOriginalConstructor()
553 ->getMock();
554
555 if ($withDefaultMock) {
556 $mock->expects($this->any())
557 ->method('validate')
558 ->willReturn(new ConstraintViolationList());
559 }
560
561 return $mock;
562 }
563 }