]> git.immae.eu Git - github/wallabag/wallabag.git/blob - tests/Wallabag/ImportBundle/Import/PocketImportTest.php
Merge pull request #3959 from wallabag/mig-tag-collation
[github/wallabag/wallabag.git] / tests / Wallabag / ImportBundle / Import / PocketImportTest.php
1 <?php
2
3 namespace Tests\Wallabag\ImportBundle\Import;
4
5 use GuzzleHttp\Psr7\Response;
6 use Http\Mock\Client as HttpMockClient;
7 use M6Web\Component\RedisMock\RedisMockFactory;
8 use Monolog\Handler\TestHandler;
9 use Monolog\Logger;
10 use PHPUnit\Framework\TestCase;
11 use Simpleue\Queue\RedisQueue;
12 use Wallabag\CoreBundle\Entity\Config;
13 use Wallabag\CoreBundle\Entity\Entry;
14 use Wallabag\ImportBundle\Import\PocketImport;
15 use Wallabag\ImportBundle\Redis\Producer;
16 use Wallabag\UserBundle\Entity\User;
17
18 class PocketImportTest extends TestCase
19 {
20 protected $token;
21 protected $user;
22 protected $em;
23 protected $contentProxy;
24 protected $logHandler;
25 protected $tagsAssigner;
26 protected $uow;
27
28 public function testInit()
29 {
30 $pocketImport = $this->getPocketImport();
31
32 $this->assertSame('Pocket', $pocketImport->getName());
33 $this->assertNotEmpty($pocketImport->getUrl());
34 $this->assertSame('import.pocket.description', $pocketImport->getDescription());
35 }
36
37 public function testOAuthRequest()
38 {
39 $httpMockClient = new HttpMockClient();
40 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], json_encode(['code' => 'wunderbar_code'])));
41
42 $pocketImport = $this->getPocketImport();
43 $pocketImport->setClient($httpMockClient);
44
45 $code = $pocketImport->getRequestToken('http://0.0.0.0/redirect');
46
47 $this->assertSame('wunderbar_code', $code);
48 }
49
50 public function testOAuthRequestBadResponse()
51 {
52 $httpMockClient = new HttpMockClient();
53 $httpMockClient->addResponse(new Response(403));
54
55 $pocketImport = $this->getPocketImport();
56 $pocketImport->setClient($httpMockClient);
57
58 $code = $pocketImport->getRequestToken('http://0.0.0.0/redirect');
59
60 $this->assertFalse($code);
61
62 $records = $this->logHandler->getRecords();
63 $this->assertContains('PocketImport: Failed to request token', $records[0]['message']);
64 $this->assertSame('ERROR', $records[0]['level_name']);
65 }
66
67 public function testOAuthAuthorize()
68 {
69 $httpMockClient = new HttpMockClient();
70 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], json_encode(['access_token' => 'wunderbar_token'])));
71
72 $pocketImport = $this->getPocketImport();
73 $pocketImport->setClient($httpMockClient);
74
75 $res = $pocketImport->authorize('wunderbar_code');
76
77 $this->assertTrue($res);
78 $this->assertSame('wunderbar_token', $pocketImport->getAccessToken());
79 }
80
81 public function testOAuthAuthorizeBadResponse()
82 {
83 $httpMockClient = new HttpMockClient();
84 $httpMockClient->addResponse(new Response(403));
85
86 $pocketImport = $this->getPocketImport();
87 $pocketImport->setClient($httpMockClient);
88
89 $res = $pocketImport->authorize('wunderbar_code');
90
91 $this->assertFalse($res);
92
93 $records = $this->logHandler->getRecords();
94 $this->assertContains('PocketImport: Failed to authorize client', $records[0]['message']);
95 $this->assertSame('ERROR', $records[0]['level_name']);
96 }
97
98 /**
99 * Will sample results from https://getpocket.com/developer/docs/v3/retrieve.
100 */
101 public function testImport()
102 {
103 $httpMockClient = new HttpMockClient();
104 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], json_encode(['access_token' => 'wunderbar_token'])));
105 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], <<<'JSON'
106 {
107 "status": 1,
108 "list": {
109 "229279689": {
110 "item_id": "229279689",
111 "resolved_id": "229279689",
112 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
113 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
114 "favorite": "1",
115 "status": "1",
116 "time_added": "1473020899",
117 "time_updated": "1473020899",
118 "time_read": "0",
119 "time_favorited": "0",
120 "sort_id": 0,
121 "resolved_title": "The Massive Ryder Cup Preview",
122 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
123 "excerpt": "The list of things I love about the Ryder Cup is so long that it could fill a (tedious) novel, and golf fans can probably guess most of them.",
124 "is_article": "1",
125 "is_index": "0",
126 "has_video": "1",
127 "has_image": "1",
128 "word_count": "3197",
129 "images": {
130 "1": {
131 "item_id": "229279689",
132 "image_id": "1",
133 "src": "http://a.espncdn.com/combiner/i?img=/photo/2012/0927/grant_g_ryder_cr_640.jpg&w=640&h=360",
134 "width": "0",
135 "height": "0",
136 "credit": "Jamie Squire/Getty Images",
137 "caption": ""
138 }
139 },
140 "videos": {
141 "1": {
142 "item_id": "229279689",
143 "video_id": "1",
144 "src": "http://www.youtube.com/v/Er34PbFkVGk?version=3&hl=en_US&rel=0",
145 "width": "420",
146 "height": "315",
147 "type": "1",
148 "vid": "Er34PbFkVGk"
149 }
150 },
151 "tags": {
152 "grantland": {
153 "item_id": "1147652870",
154 "tag": "grantland"
155 },
156 "Ryder Cup": {
157 "item_id": "1147652870",
158 "tag": "Ryder Cup"
159 }
160 }
161 },
162 "229279690": {
163 "item_id": "229279689",
164 "resolved_id": "229279689",
165 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
166 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
167 "favorite": "1",
168 "status": "1",
169 "time_added": "1473020899",
170 "time_updated": "1473020899",
171 "time_read": "0",
172 "time_favorited": "0",
173 "sort_id": 1,
174 "resolved_title": "The Massive Ryder Cup Preview",
175 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
176 "excerpt": "The list of things I love about the Ryder Cup is so long that it could fill a (tedious) novel, and golf fans can probably guess most of them.",
177 "is_article": "1",
178 "is_index": "0",
179 "has_video": "0",
180 "has_image": "0",
181 "word_count": "3197"
182 }
183 }
184 }
185 JSON
186 ));
187
188 $pocketImport = $this->getPocketImport('ConsumerKey', 1);
189
190 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
191 ->disableOriginalConstructor()
192 ->getMock();
193
194 $entryRepo->expects($this->exactly(2))
195 ->method('findByUrlAndUserId')
196 ->will($this->onConsecutiveCalls(false, true));
197
198 $this->em
199 ->expects($this->exactly(2))
200 ->method('getRepository')
201 ->willReturn($entryRepo);
202
203 $this->em
204 ->expects($this->any())
205 ->method('persist')
206 ->with($this->callback(function ($persistedEntry) {
207 return $persistedEntry->isArchived() && $persistedEntry->isStarred();
208 }));
209
210 $entry = new Entry($this->user);
211
212 $this->contentProxy
213 ->expects($this->once())
214 ->method('updateEntry')
215 ->willReturn($entry);
216
217 $pocketImport->setClient($httpMockClient);
218 $pocketImport->authorize('wunderbar_code');
219
220 $res = $pocketImport->import();
221
222 $this->assertTrue($res);
223 $this->assertSame(['skipped' => 1, 'imported' => 1, 'queued' => 0], $pocketImport->getSummary());
224 }
225
226 /**
227 * Will sample results from https://getpocket.com/developer/docs/v3/retrieve.
228 */
229 public function testImportAndMarkAllAsRead()
230 {
231 $httpMockClient = new HttpMockClient();
232 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], json_encode(['access_token' => 'wunderbar_token'])));
233 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], <<<'JSON'
234 {
235 "status": 1,
236 "list": {
237 "229279689": {
238 "item_id": "229279689",
239 "resolved_id": "229279689",
240 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
241 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
242 "favorite": "1",
243 "status": "1",
244 "time_added": "1473020899",
245 "time_updated": "1473020899",
246 "time_read": "0",
247 "time_favorited": "0",
248 "sort_id": 0,
249 "excerpt": "The list of things I love about the Ryder Cup is so long that it could fill a (tedious) novel, and golf fans can probably guess most of them.",
250 "is_article": "1",
251 "has_video": "1",
252 "has_image": "1",
253 "word_count": "3197"
254 },
255 "229279690": {
256 "item_id": "229279689",
257 "resolved_id": "229279689",
258 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview/2",
259 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
260 "favorite": "1",
261 "status": "0",
262 "time_added": "1473020899",
263 "time_updated": "1473020899",
264 "time_read": "0",
265 "time_favorited": "0",
266 "sort_id": 1,
267 "excerpt": "The list of things I love about the Ryder Cup is so long that it could fill a (tedious) novel, and golf fans can probably guess most of them.",
268 "is_article": "1",
269 "has_video": "0",
270 "has_image": "0",
271 "word_count": "3197"
272 }
273 }
274 }
275 JSON
276 ));
277
278 $pocketImport = $this->getPocketImport('ConsumerKey', 2);
279
280 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
281 ->disableOriginalConstructor()
282 ->getMock();
283
284 $entryRepo->expects($this->exactly(2))
285 ->method('findByUrlAndUserId')
286 ->will($this->onConsecutiveCalls(false, false));
287
288 $this->em
289 ->expects($this->exactly(2))
290 ->method('getRepository')
291 ->willReturn($entryRepo);
292
293 // check that every entry persisted are archived
294 $this->em
295 ->expects($this->any())
296 ->method('persist')
297 ->with($this->callback(function ($persistedEntry) {
298 return $persistedEntry->isArchived();
299 }));
300
301 $entry = new Entry($this->user);
302
303 $this->contentProxy
304 ->expects($this->exactly(2))
305 ->method('updateEntry')
306 ->willReturn($entry);
307
308 $pocketImport->setClient($httpMockClient);
309 $pocketImport->authorize('wunderbar_code');
310
311 $res = $pocketImport->setMarkAsRead(true)->import();
312
313 $this->assertTrue($res);
314 $this->assertSame(['skipped' => 0, 'imported' => 2, 'queued' => 0], $pocketImport->getSummary());
315 }
316
317 /**
318 * Will sample results from https://getpocket.com/developer/docs/v3/retrieve.
319 */
320 public function testImportWithRabbit()
321 {
322 $httpMockClient = new HttpMockClient();
323
324 $body = <<<'JSON'
325 {
326 "item_id": "229279689",
327 "resolved_id": "229279689",
328 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
329 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
330 "favorite": "1",
331 "status": "1",
332 "time_added": "1473020899",
333 "time_updated": "1473020899",
334 "time_read": "0",
335 "time_favorited": "0",
336 "sort_id": 0,
337 "resolved_title": "The Massive Ryder Cup Preview",
338 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
339 "excerpt": "The list of things I love about the Ryder Cup is so long that it could fill a (tedious) novel, and golf fans can probably guess most of them.",
340 "is_article": "1",
341 "has_video": "0",
342 "has_image": "0",
343 "word_count": "3197"
344 }
345 JSON;
346
347 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], json_encode(['access_token' => 'wunderbar_token'])));
348 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], <<<JSON
349 {
350 "status": 1,
351 "list": {
352 "229279690": $body
353 }
354 }
355 JSON
356 ));
357
358 $pocketImport = $this->getPocketImport();
359
360 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
361 ->disableOriginalConstructor()
362 ->getMock();
363
364 $entryRepo->expects($this->never())
365 ->method('findByUrlAndUserId');
366
367 $this->em
368 ->expects($this->never())
369 ->method('getRepository');
370
371 $entry = new Entry($this->user);
372
373 $this->contentProxy
374 ->expects($this->never())
375 ->method('updateEntry');
376
377 $producer = $this->getMockBuilder('OldSound\RabbitMqBundle\RabbitMq\Producer')
378 ->disableOriginalConstructor()
379 ->getMock();
380
381 $bodyAsArray = json_decode($body, true);
382 // because with just use `new User()` so it doesn't have an id
383 $bodyAsArray['userId'] = null;
384
385 $producer
386 ->expects($this->once())
387 ->method('publish')
388 ->with(json_encode($bodyAsArray));
389
390 $pocketImport->setClient($httpMockClient);
391 $pocketImport->setProducer($producer);
392 $pocketImport->authorize('wunderbar_code');
393
394 $res = $pocketImport->setMarkAsRead(true)->import();
395
396 $this->assertTrue($res);
397 $this->assertSame(['skipped' => 0, 'imported' => 0, 'queued' => 1], $pocketImport->getSummary());
398 }
399
400 /**
401 * Will sample results from https://getpocket.com/developer/docs/v3/retrieve.
402 */
403 public function testImportWithRedis()
404 {
405 $httpMockClient = new HttpMockClient();
406
407 $body = <<<'JSON'
408 {
409 "item_id": "229279689",
410 "resolved_id": "229279689",
411 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
412 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
413 "favorite": "1",
414 "status": "1",
415 "time_added": "1473020899",
416 "time_updated": "1473020899",
417 "time_read": "0",
418 "time_favorited": "0",
419 "sort_id": 0,
420 "resolved_title": "The Massive Ryder Cup Preview",
421 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
422 "excerpt": "The list of things I love about the Ryder Cup is so long that it could fill a (tedious) novel, and golf fans can probably guess most of them.",
423 "is_article": "1",
424 "has_video": "0",
425 "has_image": "0",
426 "word_count": "3197"
427 }
428 JSON;
429
430 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], json_encode(['access_token' => 'wunderbar_token'])));
431 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], <<<JSON
432 {
433 "status": 1,
434 "list": {
435 "229279690": $body
436 }
437 }
438 JSON
439 ));
440
441 $pocketImport = $this->getPocketImport();
442
443 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
444 ->disableOriginalConstructor()
445 ->getMock();
446
447 $entryRepo->expects($this->never())
448 ->method('findByUrlAndUserId');
449
450 $this->em
451 ->expects($this->never())
452 ->method('getRepository');
453
454 $entry = new Entry($this->user);
455
456 $this->contentProxy
457 ->expects($this->never())
458 ->method('updateEntry');
459
460 $factory = new RedisMockFactory();
461 $redisMock = $factory->getAdapter('Predis\Client', true);
462
463 $queue = new RedisQueue($redisMock, 'pocket');
464 $producer = new Producer($queue);
465
466 $pocketImport->setClient($httpMockClient);
467 $pocketImport->setProducer($producer);
468 $pocketImport->authorize('wunderbar_code');
469
470 $res = $pocketImport->setMarkAsRead(true)->import();
471
472 $this->assertTrue($res);
473 $this->assertSame(['skipped' => 0, 'imported' => 0, 'queued' => 1], $pocketImport->getSummary());
474
475 $this->assertNotEmpty($redisMock->lpop('pocket'));
476 }
477
478 public function testImportBadResponse()
479 {
480 $httpMockClient = new HttpMockClient();
481
482 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], json_encode(['access_token' => 'wunderbar_token'])));
483 $httpMockClient->addResponse(new Response(403));
484
485 $pocketImport = $this->getPocketImport();
486 $pocketImport->setClient($httpMockClient);
487 $pocketImport->authorize('wunderbar_code');
488
489 $res = $pocketImport->import();
490
491 $this->assertFalse($res);
492
493 $records = $this->logHandler->getRecords();
494 $this->assertContains('PocketImport: Failed to import', $records[0]['message']);
495 $this->assertSame('ERROR', $records[0]['level_name']);
496 }
497
498 public function testImportWithExceptionFromGraby()
499 {
500 $httpMockClient = new HttpMockClient();
501
502 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], json_encode(['access_token' => 'wunderbar_token'])));
503 $httpMockClient->addResponse(new Response(200, ['Content-Type' => 'application/json'], <<<'JSON'
504 {
505 "status": 1,
506 "list": {
507 "229279689": {
508 "status": "1",
509 "favorite": "1",
510 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview"
511 }
512 }
513 }
514
515 JSON
516 ));
517
518 $pocketImport = $this->getPocketImport('ConsumerKey', 1);
519
520 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
521 ->disableOriginalConstructor()
522 ->getMock();
523
524 $entryRepo->expects($this->once())
525 ->method('findByUrlAndUserId')
526 ->will($this->onConsecutiveCalls(false, true));
527
528 $this->em
529 ->expects($this->once())
530 ->method('getRepository')
531 ->willReturn($entryRepo);
532
533 $entry = new Entry($this->user);
534
535 $this->contentProxy
536 ->expects($this->once())
537 ->method('updateEntry')
538 ->will($this->throwException(new \Exception()));
539
540 $pocketImport->setClient($httpMockClient);
541 $pocketImport->authorize('wunderbar_code');
542
543 $res = $pocketImport->import();
544
545 $this->assertTrue($res);
546 $this->assertSame(['skipped' => 0, 'imported' => 1, 'queued' => 0], $pocketImport->getSummary());
547 }
548
549 private function getPocketImport($consumerKey = 'ConsumerKey', $dispatched = 0)
550 {
551 $this->user = new User();
552
553 $config = new Config($this->user);
554 $config->setPocketConsumerKey('xxx');
555
556 $this->user->setConfig($config);
557
558 $this->contentProxy = $this->getMockBuilder('Wallabag\CoreBundle\Helper\ContentProxy')
559 ->disableOriginalConstructor()
560 ->getMock();
561
562 $this->tagsAssigner = $this->getMockBuilder('Wallabag\CoreBundle\Helper\TagsAssigner')
563 ->disableOriginalConstructor()
564 ->getMock();
565
566 $this->em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
567 ->disableOriginalConstructor()
568 ->getMock();
569
570 $this->uow = $this->getMockBuilder('Doctrine\ORM\UnitOfWork')
571 ->disableOriginalConstructor()
572 ->getMock();
573
574 $this->em
575 ->expects($this->any())
576 ->method('getUnitOfWork')
577 ->willReturn($this->uow);
578
579 $this->uow
580 ->expects($this->any())
581 ->method('getScheduledEntityInsertions')
582 ->willReturn([]);
583
584 $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher')
585 ->disableOriginalConstructor()
586 ->getMock();
587
588 $dispatcher
589 ->expects($this->exactly($dispatched))
590 ->method('dispatch');
591
592 $pocket = new PocketImport($this->em, $this->contentProxy, $this->tagsAssigner, $dispatcher);
593 $pocket->setUser($this->user);
594
595 $this->logHandler = new TestHandler();
596 $logger = new Logger('test', [$this->logHandler]);
597 $pocket->setLogger($logger);
598
599 return $pocket;
600 }
601 }