]> git.immae.eu Git - github/wallabag/wallabag.git/blob - tests/Wallabag/ImportBundle/Import/PocketImportTest.php
Use namespaced PHPUnit classes
[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 $entry = new Entry($this->user);
230
231 $this->contentProxy
232 ->expects($this->once())
233 ->method('updateEntry')
234 ->willReturn($entry);
235
236 $pocketImport->setClient($client);
237 $pocketImport->authorize('wunderbar_code');
238
239 $res = $pocketImport->import();
240
241 $this->assertTrue($res);
242 $this->assertSame(['skipped' => 1, 'imported' => 1, 'queued' => 0], $pocketImport->getSummary());
243 }
244
245 /**
246 * Will sample results from https://getpocket.com/developer/docs/v3/retrieve.
247 */
248 public function testImportAndMarkAllAsRead()
249 {
250 $client = new Client();
251
252 $mock = new Mock([
253 new Response(200, ['Content-Type' => 'application/json'], Stream::factory(json_encode(['access_token' => 'wunderbar_token']))),
254 new Response(200, ['Content-Type' => 'application/json'], Stream::factory('
255 {
256 "status": 1,
257 "list": {
258 "229279689": {
259 "item_id": "229279689",
260 "resolved_id": "229279689",
261 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
262 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
263 "favorite": "1",
264 "status": "1",
265 "time_added": "1473020899",
266 "time_updated": "1473020899",
267 "time_read": "0",
268 "time_favorited": "0",
269 "sort_id": 0,
270 "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.",
271 "is_article": "1",
272 "has_video": "1",
273 "has_image": "1",
274 "word_count": "3197"
275 },
276 "229279690": {
277 "item_id": "229279689",
278 "resolved_id": "229279689",
279 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview/2",
280 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
281 "favorite": "1",
282 "status": "0",
283 "time_added": "1473020899",
284 "time_updated": "1473020899",
285 "time_read": "0",
286 "time_favorited": "0",
287 "sort_id": 1,
288 "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.",
289 "is_article": "1",
290 "has_video": "0",
291 "has_image": "0",
292 "word_count": "3197"
293 }
294 }
295 }
296 ')),
297 ]);
298
299 $client->getEmitter()->attach($mock);
300
301 $pocketImport = $this->getPocketImport('ConsumerKey', 2);
302
303 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
304 ->disableOriginalConstructor()
305 ->getMock();
306
307 $entryRepo->expects($this->exactly(2))
308 ->method('findByUrlAndUserId')
309 ->will($this->onConsecutiveCalls(false, false));
310
311 $this->em
312 ->expects($this->exactly(2))
313 ->method('getRepository')
314 ->willReturn($entryRepo);
315
316 // check that every entry persisted are archived
317 $this->em
318 ->expects($this->any())
319 ->method('persist')
320 ->with($this->callback(function ($persistedEntry) {
321 return $persistedEntry->isArchived();
322 }));
323
324 $entry = new Entry($this->user);
325
326 $this->contentProxy
327 ->expects($this->exactly(2))
328 ->method('updateEntry')
329 ->willReturn($entry);
330
331 $pocketImport->setClient($client);
332 $pocketImport->authorize('wunderbar_code');
333
334 $res = $pocketImport->setMarkAsRead(true)->import();
335
336 $this->assertTrue($res);
337 $this->assertSame(['skipped' => 0, 'imported' => 2, 'queued' => 0], $pocketImport->getSummary());
338 }
339
340 /**
341 * Will sample results from https://getpocket.com/developer/docs/v3/retrieve.
342 */
343 public function testImportWithRabbit()
344 {
345 $client = new Client();
346
347 $body = <<<'JSON'
348 {
349 "item_id": "229279689",
350 "resolved_id": "229279689",
351 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
352 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
353 "favorite": "1",
354 "status": "1",
355 "time_added": "1473020899",
356 "time_updated": "1473020899",
357 "time_read": "0",
358 "time_favorited": "0",
359 "sort_id": 0,
360 "resolved_title": "The Massive Ryder Cup Preview",
361 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
362 "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.",
363 "is_article": "1",
364 "has_video": "0",
365 "has_image": "0",
366 "word_count": "3197"
367 }
368 JSON;
369
370 $mock = new Mock([
371 new Response(200, ['Content-Type' => 'application/json'], Stream::factory(json_encode(['access_token' => 'wunderbar_token']))),
372 new Response(200, ['Content-Type' => 'application/json'], Stream::factory('
373 {
374 "status": 1,
375 "list": {
376 "229279690": ' . $body . '
377 }
378 }
379 ')),
380 ]);
381
382 $client->getEmitter()->attach($mock);
383
384 $pocketImport = $this->getPocketImport();
385
386 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
387 ->disableOriginalConstructor()
388 ->getMock();
389
390 $entryRepo->expects($this->never())
391 ->method('findByUrlAndUserId');
392
393 $this->em
394 ->expects($this->never())
395 ->method('getRepository');
396
397 $entry = new Entry($this->user);
398
399 $this->contentProxy
400 ->expects($this->never())
401 ->method('updateEntry');
402
403 $producer = $this->getMockBuilder('OldSound\RabbitMqBundle\RabbitMq\Producer')
404 ->disableOriginalConstructor()
405 ->getMock();
406
407 $bodyAsArray = json_decode($body, true);
408 // because with just use `new User()` so it doesn't have an id
409 $bodyAsArray['userId'] = null;
410
411 $producer
412 ->expects($this->once())
413 ->method('publish')
414 ->with(json_encode($bodyAsArray));
415
416 $pocketImport->setClient($client);
417 $pocketImport->setProducer($producer);
418 $pocketImport->authorize('wunderbar_code');
419
420 $res = $pocketImport->setMarkAsRead(true)->import();
421
422 $this->assertTrue($res);
423 $this->assertSame(['skipped' => 0, 'imported' => 0, 'queued' => 1], $pocketImport->getSummary());
424 }
425
426 /**
427 * Will sample results from https://getpocket.com/developer/docs/v3/retrieve.
428 */
429 public function testImportWithRedis()
430 {
431 $client = new Client();
432
433 $body = <<<'JSON'
434 {
435 "item_id": "229279689",
436 "resolved_id": "229279689",
437 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
438 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
439 "favorite": "1",
440 "status": "1",
441 "time_added": "1473020899",
442 "time_updated": "1473020899",
443 "time_read": "0",
444 "time_favorited": "0",
445 "sort_id": 0,
446 "resolved_title": "The Massive Ryder Cup Preview",
447 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
448 "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.",
449 "is_article": "1",
450 "has_video": "0",
451 "has_image": "0",
452 "word_count": "3197"
453 }
454 JSON;
455
456 $mock = new Mock([
457 new Response(200, ['Content-Type' => 'application/json'], Stream::factory(json_encode(['access_token' => 'wunderbar_token']))),
458 new Response(200, ['Content-Type' => 'application/json'], Stream::factory('
459 {
460 "status": 1,
461 "list": {
462 "229279690": ' . $body . '
463 }
464 }
465 ')),
466 ]);
467
468 $client->getEmitter()->attach($mock);
469
470 $pocketImport = $this->getPocketImport();
471
472 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
473 ->disableOriginalConstructor()
474 ->getMock();
475
476 $entryRepo->expects($this->never())
477 ->method('findByUrlAndUserId');
478
479 $this->em
480 ->expects($this->never())
481 ->method('getRepository');
482
483 $entry = new Entry($this->user);
484
485 $this->contentProxy
486 ->expects($this->never())
487 ->method('updateEntry');
488
489 $factory = new RedisMockFactory();
490 $redisMock = $factory->getAdapter('Predis\Client', true);
491
492 $queue = new RedisQueue($redisMock, 'pocket');
493 $producer = new Producer($queue);
494
495 $pocketImport->setClient($client);
496 $pocketImport->setProducer($producer);
497 $pocketImport->authorize('wunderbar_code');
498
499 $res = $pocketImport->setMarkAsRead(true)->import();
500
501 $this->assertTrue($res);
502 $this->assertSame(['skipped' => 0, 'imported' => 0, 'queued' => 1], $pocketImport->getSummary());
503
504 $this->assertNotEmpty($redisMock->lpop('pocket'));
505 }
506
507 public function testImportBadResponse()
508 {
509 $client = new Client();
510
511 $mock = new Mock([
512 new Response(200, ['Content-Type' => 'application/json'], Stream::factory(json_encode(['access_token' => 'wunderbar_token']))),
513 new Response(403),
514 ]);
515
516 $client->getEmitter()->attach($mock);
517
518 $pocketImport = $this->getPocketImport();
519 $pocketImport->setClient($client);
520 $pocketImport->authorize('wunderbar_code');
521
522 $res = $pocketImport->import();
523
524 $this->assertFalse($res);
525
526 $records = $this->logHandler->getRecords();
527 $this->assertContains('PocketImport: Failed to import', $records[0]['message']);
528 $this->assertSame('ERROR', $records[0]['level_name']);
529 }
530
531 public function testImportWithExceptionFromGraby()
532 {
533 $client = new Client();
534
535 $mock = new Mock([
536 new Response(200, ['Content-Type' => 'application/json'], Stream::factory(json_encode(['access_token' => 'wunderbar_token']))),
537 new Response(200, ['Content-Type' => 'application/json'], Stream::factory('
538 {
539 "status": 1,
540 "list": {
541 "229279689": {
542 "status": "1",
543 "favorite": "1",
544 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview"
545 }
546 }
547 }
548 ')),
549 ]);
550
551 $client->getEmitter()->attach($mock);
552
553 $pocketImport = $this->getPocketImport('ConsumerKey', 1);
554
555 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
556 ->disableOriginalConstructor()
557 ->getMock();
558
559 $entryRepo->expects($this->once())
560 ->method('findByUrlAndUserId')
561 ->will($this->onConsecutiveCalls(false, true));
562
563 $this->em
564 ->expects($this->once())
565 ->method('getRepository')
566 ->willReturn($entryRepo);
567
568 $entry = new Entry($this->user);
569
570 $this->contentProxy
571 ->expects($this->once())
572 ->method('updateEntry')
573 ->will($this->throwException(new \Exception()));
574
575 $pocketImport->setClient($client);
576 $pocketImport->authorize('wunderbar_code');
577
578 $res = $pocketImport->import();
579
580 $this->assertTrue($res);
581 $this->assertSame(['skipped' => 0, 'imported' => 1, 'queued' => 0], $pocketImport->getSummary());
582 }
583
584 private function getPocketImport($consumerKey = 'ConsumerKey', $dispatched = 0)
585 {
586 $this->user = new User();
587
588 $config = new Config($this->user);
589 $config->setPocketConsumerKey('xxx');
590
591 $this->user->setConfig($config);
592
593 $this->contentProxy = $this->getMockBuilder('Wallabag\CoreBundle\Helper\ContentProxy')
594 ->disableOriginalConstructor()
595 ->getMock();
596
597 $this->tagsAssigner = $this->getMockBuilder('Wallabag\CoreBundle\Helper\TagsAssigner')
598 ->disableOriginalConstructor()
599 ->getMock();
600
601 $this->em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
602 ->disableOriginalConstructor()
603 ->getMock();
604
605 $this->uow = $this->getMockBuilder('Doctrine\ORM\UnitOfWork')
606 ->disableOriginalConstructor()
607 ->getMock();
608
609 $this->em
610 ->expects($this->any())
611 ->method('getUnitOfWork')
612 ->willReturn($this->uow);
613
614 $this->uow
615 ->expects($this->any())
616 ->method('getScheduledEntityInsertions')
617 ->willReturn([]);
618
619 $dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcher')
620 ->disableOriginalConstructor()
621 ->getMock();
622
623 $dispatcher
624 ->expects($this->exactly($dispatched))
625 ->method('dispatch');
626
627 $pocket = new PocketImport($this->em, $this->contentProxy, $this->tagsAssigner, $dispatcher);
628 $pocket->setUser($this->user);
629
630 $this->logHandler = new TestHandler();
631 $logger = new Logger('test', [$this->logHandler]);
632 $pocket->setLogger($logger);
633
634 return $pocket;
635 }
636 }