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