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