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