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