aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php180
-rw-r--r--tests/Wallabag/CoreBundle/Command/InstallCommandTest.php8
-rw-r--r--tests/Wallabag/CoreBundle/Controller/DeveloperControllerTest.php9
-rw-r--r--tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php53
-rw-r--r--tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php6
-rw-r--r--tests/Wallabag/CoreBundle/Controller/SecurityControllerTest.php15
-rw-r--r--tests/Wallabag/CoreBundle/Controller/TagControllerTest.php31
-rw-r--r--tests/Wallabag/CoreBundle/EventListener/LocaleListenerTest.php6
-rw-r--r--tests/Wallabag/ImportBundle/Command/ImportCommandTest.php86
-rw-r--r--tests/Wallabag/ImportBundle/Command/RedisWorkerCommandTest.php74
-rw-r--r--tests/Wallabag/ImportBundle/Consumer/AMQPEntryConsumerTest.php225
-rw-r--r--tests/Wallabag/ImportBundle/Consumer/RedisEntryConsumerTest.php225
-rw-r--r--tests/Wallabag/ImportBundle/Controller/ImportControllerTest.php2
-rw-r--r--tests/Wallabag/ImportBundle/Controller/PocketControllerTest.php30
-rw-r--r--tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php197
-rw-r--r--tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php74
-rw-r--r--tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php76
-rw-r--r--tests/Wallabag/ImportBundle/Import/PocketImportTest.php242
-rw-r--r--tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php233
-rw-r--r--tests/Wallabag/ImportBundle/Import/WallabagV1ImportTest.php87
-rw-r--r--tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php83
-rw-r--r--tests/Wallabag/ImportBundle/fixtures/readability-read.json25
-rw-r--r--tests/Wallabag/ImportBundle/fixtures/readability.json176
-rw-r--r--tests/Wallabag/ImportBundle/fixtures/wallabag-v2-read.json4
-rw-r--r--tests/Wallabag/ImportBundle/fixtures/wallabag-v2.json48
25 files changed, 2134 insertions, 61 deletions
diff --git a/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php
index c39cc357..ee5b2ab7 100644
--- a/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php
+++ b/tests/Wallabag/ApiBundle/Controller/WallabagRestControllerTest.php
@@ -3,6 +3,7 @@
3namespace Tests\Wallabag\ApiBundle\Controller; 3namespace Tests\Wallabag\ApiBundle\Controller;
4 4
5use Tests\Wallabag\ApiBundle\WallabagApiTestCase; 5use Tests\Wallabag\ApiBundle\WallabagApiTestCase;
6use Wallabag\CoreBundle\Entity\Tag;
6 7
7class WallabagRestControllerTest extends WallabagApiTestCase 8class WallabagRestControllerTest extends WallabagApiTestCase
8{ 9{
@@ -121,6 +122,73 @@ class WallabagRestControllerTest extends WallabagApiTestCase
121 ); 122 );
122 } 123 }
123 124
125 public function testGetTaggedEntries()
126 {
127 $this->client->request('GET', '/api/entries', ['tags' => 'foo,bar']);
128
129 $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
130
131 $content = json_decode($this->client->getResponse()->getContent(), true);
132
133 $this->assertGreaterThanOrEqual(1, count($content));
134 $this->assertNotEmpty($content['_embedded']['items']);
135 $this->assertGreaterThanOrEqual(1, $content['total']);
136 $this->assertEquals(1, $content['page']);
137 $this->assertGreaterThanOrEqual(1, $content['pages']);
138
139 $this->assertTrue(
140 $this->client->getResponse()->headers->contains(
141 'Content-Type',
142 'application/json'
143 )
144 );
145 }
146
147 public function testGetDatedEntries()
148 {
149 $this->client->request('GET', '/api/entries', ['since' => 1]);
150
151 $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
152
153 $content = json_decode($this->client->getResponse()->getContent(), true);
154
155 $this->assertGreaterThanOrEqual(1, count($content));
156 $this->assertNotEmpty($content['_embedded']['items']);
157 $this->assertGreaterThanOrEqual(1, $content['total']);
158 $this->assertEquals(1, $content['page']);
159 $this->assertGreaterThanOrEqual(1, $content['pages']);
160
161 $this->assertTrue(
162 $this->client->getResponse()->headers->contains(
163 'Content-Type',
164 'application/json'
165 )
166 );
167 }
168
169 public function testGetDatedSupEntries()
170 {
171 $future = new \DateTime(date('Y-m-d H:i:s'));
172 $this->client->request('GET', '/api/entries', ['since' => $future->getTimestamp() + 1000]);
173
174 $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
175
176 $content = json_decode($this->client->getResponse()->getContent(), true);
177
178 $this->assertGreaterThanOrEqual(1, count($content));
179 $this->assertEmpty($content['_embedded']['items']);
180 $this->assertEquals(0, $content['total']);
181 $this->assertEquals(1, $content['page']);
182 $this->assertEquals(1, $content['pages']);
183
184 $this->assertTrue(
185 $this->client->getResponse()->headers->contains(
186 'Content-Type',
187 'application/json'
188 )
189 );
190 }
191
124 public function testDeleteEntry() 192 public function testDeleteEntry()
125 { 193 {
126 $entry = $this->client->getContainer() 194 $entry = $this->client->getContainer()
@@ -292,7 +360,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase
292 $entry = $this->client->getContainer() 360 $entry = $this->client->getContainer()
293 ->get('doctrine.orm.entity_manager') 361 ->get('doctrine.orm.entity_manager')
294 ->getRepository('WallabagCoreBundle:Entry') 362 ->getRepository('WallabagCoreBundle:Entry')
295 ->findOneWithTags(1); 363 ->findOneWithTags($this->user->getId());
296 364
297 $entry = $entry[0]; 365 $entry = $entry[0];
298 366
@@ -354,7 +422,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase
354 $entry = $this->client->getContainer() 422 $entry = $this->client->getContainer()
355 ->get('doctrine.orm.entity_manager') 423 ->get('doctrine.orm.entity_manager')
356 ->getRepository('WallabagCoreBundle:Entry') 424 ->getRepository('WallabagCoreBundle:Entry')
357 ->findOneWithTags(1); 425 ->findOneWithTags($this->user->getId());
358 $entry = $entry[0]; 426 $entry = $entry[0];
359 427
360 if (!$entry) { 428 if (!$entry) {
@@ -405,7 +473,7 @@ class WallabagRestControllerTest extends WallabagApiTestCase
405 $this->assertEquals($tag['label'], $content['label']); 473 $this->assertEquals($tag['label'], $content['label']);
406 $this->assertEquals($tag['slug'], $content['slug']); 474 $this->assertEquals($tag['slug'], $content['slug']);
407 475
408 $entries = $entry = $this->client->getContainer() 476 $entries = $this->client->getContainer()
409 ->get('doctrine.orm.entity_manager') 477 ->get('doctrine.orm.entity_manager')
410 ->getRepository('WallabagCoreBundle:Entry') 478 ->getRepository('WallabagCoreBundle:Entry')
411 ->findAllByTagId($this->user->getId(), $tag['id']); 479 ->findAllByTagId($this->user->getId(), $tag['id']);
@@ -413,6 +481,112 @@ class WallabagRestControllerTest extends WallabagApiTestCase
413 $this->assertCount(0, $entries); 481 $this->assertCount(0, $entries);
414 } 482 }
415 483
484 public function testDeleteTagByLabel()
485 {
486 $em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
487 $entry = $this->client->getContainer()
488 ->get('doctrine.orm.entity_manager')
489 ->getRepository('WallabagCoreBundle:Entry')
490 ->findOneWithTags($this->user->getId());
491
492 $entry = $entry[0];
493
494 $tag = new Tag();
495 $tag->setLabel('Awesome tag for test');
496 $em->persist($tag);
497
498 $entry->addTag($tag);
499
500 $em->persist($entry);
501 $em->flush();
502
503 $this->client->request('DELETE', '/api/tag/label.json', ['tag' => $tag->getLabel()]);
504
505 $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
506
507 $content = json_decode($this->client->getResponse()->getContent(), true);
508
509 $this->assertArrayHasKey('label', $content);
510 $this->assertEquals($tag->getLabel(), $content['label']);
511 $this->assertEquals($tag->getSlug(), $content['slug']);
512
513 $entries = $this->client->getContainer()
514 ->get('doctrine.orm.entity_manager')
515 ->getRepository('WallabagCoreBundle:Entry')
516 ->findAllByTagId($this->user->getId(), $tag->getId());
517
518 $this->assertCount(0, $entries);
519 }
520
521 public function testDeleteTagByLabelNotFound()
522 {
523 $this->client->request('DELETE', '/api/tag/label.json', ['tag' => 'does not exist']);
524
525 $this->assertEquals(404, $this->client->getResponse()->getStatusCode());
526 }
527
528 public function testDeleteTagsByLabel()
529 {
530 $em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
531 $entry = $this->client->getContainer()
532 ->get('doctrine.orm.entity_manager')
533 ->getRepository('WallabagCoreBundle:Entry')
534 ->findOneWithTags($this->user->getId());
535
536 $entry = $entry[0];
537
538 $tag = new Tag();
539 $tag->setLabel('Awesome tag for tagsLabel');
540 $em->persist($tag);
541
542 $tag2 = new Tag();
543 $tag2->setLabel('Awesome tag for tagsLabel 2');
544 $em->persist($tag2);
545
546 $entry->addTag($tag);
547 $entry->addTag($tag2);
548
549 $em->persist($entry);
550 $em->flush();
551
552 $this->client->request('DELETE', '/api/tags/label.json', ['tags' => $tag->getLabel().','.$tag2->getLabel()]);
553
554 $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
555
556 $content = json_decode($this->client->getResponse()->getContent(), true);
557
558 $this->assertCount(2, $content);
559
560 $this->assertArrayHasKey('label', $content[0]);
561 $this->assertEquals($tag->getLabel(), $content[0]['label']);
562 $this->assertEquals($tag->getSlug(), $content[0]['slug']);
563
564 $this->assertArrayHasKey('label', $content[1]);
565 $this->assertEquals($tag2->getLabel(), $content[1]['label']);
566 $this->assertEquals($tag2->getSlug(), $content[1]['slug']);
567
568 $entries = $this->client->getContainer()
569 ->get('doctrine.orm.entity_manager')
570 ->getRepository('WallabagCoreBundle:Entry')
571 ->findAllByTagId($this->user->getId(), $tag->getId());
572
573 $this->assertCount(0, $entries);
574
575 $entries = $this->client->getContainer()
576 ->get('doctrine.orm.entity_manager')
577 ->getRepository('WallabagCoreBundle:Entry')
578 ->findAllByTagId($this->user->getId(), $tag2->getId());
579
580 $this->assertCount(0, $entries);
581 }
582
583 public function testDeleteTagsByLabelNotFound()
584 {
585 $this->client->request('DELETE', '/api/tags/label.json', ['tags' => 'does not exist']);
586
587 $this->assertEquals(404, $this->client->getResponse()->getStatusCode());
588 }
589
416 public function testGetVersion() 590 public function testGetVersion()
417 { 591 {
418 $this->client->request('GET', '/api/version'); 592 $this->client->request('GET', '/api/version');
diff --git a/tests/Wallabag/CoreBundle/Command/InstallCommandTest.php b/tests/Wallabag/CoreBundle/Command/InstallCommandTest.php
index 83f5bf24..1bfd41d5 100644
--- a/tests/Wallabag/CoreBundle/Command/InstallCommandTest.php
+++ b/tests/Wallabag/CoreBundle/Command/InstallCommandTest.php
@@ -33,7 +33,7 @@ class InstallCommandTest extends WallabagCoreTestCase
33 } 33 }
34 34
35 /** 35 /**
36 * Ensure next tests will have a clean database 36 * Ensure next tests will have a clean database.
37 */ 37 */
38 public static function tearDownAfterClass() 38 public static function tearDownAfterClass()
39 { 39 {
@@ -87,7 +87,6 @@ class InstallCommandTest extends WallabagCoreTestCase
87 $this->assertContains('Setting up database.', $tester->getDisplay()); 87 $this->assertContains('Setting up database.', $tester->getDisplay());
88 $this->assertContains('Administration setup.', $tester->getDisplay()); 88 $this->assertContains('Administration setup.', $tester->getDisplay());
89 $this->assertContains('Config setup.', $tester->getDisplay()); 89 $this->assertContains('Config setup.', $tester->getDisplay());
90 $this->assertContains('Installing assets.', $tester->getDisplay());
91 } 90 }
92 91
93 public function testRunInstallCommandWithReset() 92 public function testRunInstallCommandWithReset()
@@ -119,7 +118,6 @@ class InstallCommandTest extends WallabagCoreTestCase
119 $this->assertContains('Droping database, creating database and schema, clearing the cache', $tester->getDisplay()); 118 $this->assertContains('Droping database, creating database and schema, clearing the cache', $tester->getDisplay());
120 $this->assertContains('Administration setup.', $tester->getDisplay()); 119 $this->assertContains('Administration setup.', $tester->getDisplay());
121 $this->assertContains('Config setup.', $tester->getDisplay()); 120 $this->assertContains('Config setup.', $tester->getDisplay());
122 $this->assertContains('Installing assets.', $tester->getDisplay());
123 121
124 // we force to reset everything 122 // we force to reset everything
125 $this->assertContains('Droping database, creating database and schema, clearing the cache', $tester->getDisplay()); 123 $this->assertContains('Droping database, creating database and schema, clearing the cache', $tester->getDisplay());
@@ -170,7 +168,6 @@ class InstallCommandTest extends WallabagCoreTestCase
170 $this->assertContains('Setting up database.', $tester->getDisplay()); 168 $this->assertContains('Setting up database.', $tester->getDisplay());
171 $this->assertContains('Administration setup.', $tester->getDisplay()); 169 $this->assertContains('Administration setup.', $tester->getDisplay());
172 $this->assertContains('Config setup.', $tester->getDisplay()); 170 $this->assertContains('Config setup.', $tester->getDisplay());
173 $this->assertContains('Installing assets.', $tester->getDisplay());
174 171
175 // the current database doesn't already exist 172 // the current database doesn't already exist
176 $this->assertContains('Creating database and schema, clearing the cache', $tester->getDisplay()); 173 $this->assertContains('Creating database and schema, clearing the cache', $tester->getDisplay());
@@ -208,7 +205,6 @@ class InstallCommandTest extends WallabagCoreTestCase
208 $this->assertContains('Setting up database.', $tester->getDisplay()); 205 $this->assertContains('Setting up database.', $tester->getDisplay());
209 $this->assertContains('Administration setup.', $tester->getDisplay()); 206 $this->assertContains('Administration setup.', $tester->getDisplay());
210 $this->assertContains('Config setup.', $tester->getDisplay()); 207 $this->assertContains('Config setup.', $tester->getDisplay());
211 $this->assertContains('Installing assets.', $tester->getDisplay());
212 208
213 $this->assertContains('Droping schema and creating schema', $tester->getDisplay()); 209 $this->assertContains('Droping schema and creating schema', $tester->getDisplay());
214 } 210 }
@@ -263,7 +259,6 @@ class InstallCommandTest extends WallabagCoreTestCase
263 $this->assertContains('Setting up database.', $tester->getDisplay()); 259 $this->assertContains('Setting up database.', $tester->getDisplay());
264 $this->assertContains('Administration setup.', $tester->getDisplay()); 260 $this->assertContains('Administration setup.', $tester->getDisplay());
265 $this->assertContains('Config setup.', $tester->getDisplay()); 261 $this->assertContains('Config setup.', $tester->getDisplay());
266 $this->assertContains('Installing assets.', $tester->getDisplay());
267 262
268 $this->assertContains('Creating schema', $tester->getDisplay()); 263 $this->assertContains('Creating schema', $tester->getDisplay());
269 } 264 }
@@ -296,6 +291,5 @@ class InstallCommandTest extends WallabagCoreTestCase
296 $this->assertContains('Setting up database.', $tester->getDisplay()); 291 $this->assertContains('Setting up database.', $tester->getDisplay());
297 $this->assertContains('Administration setup.', $tester->getDisplay()); 292 $this->assertContains('Administration setup.', $tester->getDisplay());
298 $this->assertContains('Config setup.', $tester->getDisplay()); 293 $this->assertContains('Config setup.', $tester->getDisplay());
299 $this->assertContains('Installing assets.', $tester->getDisplay());
300 } 294 }
301} 295}
diff --git a/tests/Wallabag/CoreBundle/Controller/DeveloperControllerTest.php b/tests/Wallabag/CoreBundle/Controller/DeveloperControllerTest.php
index 79452ace..97ed0d58 100644
--- a/tests/Wallabag/CoreBundle/Controller/DeveloperControllerTest.php
+++ b/tests/Wallabag/CoreBundle/Controller/DeveloperControllerTest.php
@@ -18,12 +18,19 @@ class DeveloperControllerTest extends WallabagCoreTestCase
18 18
19 $form = $crawler->filter('button[type=submit]')->form(); 19 $form = $crawler->filter('button[type=submit]')->form();
20 20
21 $client->submit($form); 21 $data = [
22 'client[name]' => 'My app',
23 ];
24
25 $crawler = $client->submit($form, $data);
22 26
23 $this->assertEquals(200, $client->getResponse()->getStatusCode()); 27 $this->assertEquals(200, $client->getResponse()->getStatusCode());
24 28
25 $newNbClients = $em->getRepository('WallabagApiBundle:Client')->findAll(); 29 $newNbClients = $em->getRepository('WallabagApiBundle:Client')->findAll();
26 $this->assertGreaterThan(count($nbClients), count($newNbClients)); 30 $this->assertGreaterThan(count($nbClients), count($newNbClients));
31
32 $this->assertGreaterThan(1, $alert = $crawler->filter('.settings ul li strong')->extract(['_text']));
33 $this->assertContains('My app', $alert[0]);
27 } 34 }
28 35
29 public function testListingClient() 36 public function testListingClient()
diff --git a/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php b/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php
index 5c739c78..a74c17d9 100644
--- a/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php
+++ b/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php
@@ -236,6 +236,16 @@ class EntryControllerTest extends WallabagCoreTestCase
236 $this->assertEquals(200, $client->getResponse()->getStatusCode()); 236 $this->assertEquals(200, $client->getResponse()->getStatusCode());
237 } 237 }
238 238
239 public function testUntagged()
240 {
241 $this->logInAs('admin');
242 $client = $this->getClient();
243
244 $client->request('GET', '/untagged/list');
245
246 $this->assertEquals(200, $client->getResponse()->getStatusCode());
247 }
248
239 public function testStarred() 249 public function testStarred()
240 { 250 {
241 $this->logInAs('admin'); 251 $this->logInAs('admin');
@@ -698,4 +708,47 @@ class EntryControllerTest extends WallabagCoreTestCase
698 $crawler = $client->submit($form, $data); 708 $crawler = $client->submit($form, $data);
699 $this->assertCount(2, $crawler->filter('div[class=entry]')); 709 $this->assertCount(2, $crawler->filter('div[class=entry]'));
700 } 710 }
711
712 public function testCache()
713 {
714 $this->logInAs('admin');
715 $client = $this->getClient();
716
717 $content = $client->getContainer()
718 ->get('doctrine.orm.entity_manager')
719 ->getRepository('WallabagCoreBundle:Entry')
720 ->findOneByUser($this->getLoggedInUserId());
721
722 // no uuid
723 $client->request('GET', '/share/'.$content->getUuid());
724 $this->assertEquals(404, $client->getResponse()->getStatusCode());
725
726 // generating the uuid
727 $client->request('GET', '/share/'.$content->getId());
728 $this->assertEquals(302, $client->getResponse()->getStatusCode());
729
730 // follow link with uuid
731 $crawler = $client->followRedirect();
732 $this->assertEquals(200, $client->getResponse()->getStatusCode());
733 $this->assertContains('max-age=25200', $client->getResponse()->headers->get('cache-control'));
734 $this->assertContains('public', $client->getResponse()->headers->get('cache-control'));
735 $this->assertContains('s-maxage=25200', $client->getResponse()->headers->get('cache-control'));
736 $this->assertNotContains('no-cache', $client->getResponse()->headers->get('cache-control'));
737
738 // sharing is now disabled
739 $client->getContainer()->get('craue_config')->set('share_public', 0);
740 $client->request('GET', '/share/'.$content->getUuid());
741 $this->assertEquals(404, $client->getResponse()->getStatusCode());
742
743 $client->request('GET', '/view/'.$content->getId());
744 $this->assertContains('no-cache', $client->getResponse()->headers->get('cache-control'));
745
746 // removing the share
747 $client->request('GET', '/share/delete/'.$content->getId());
748 $this->assertEquals(302, $client->getResponse()->getStatusCode());
749
750 // share is now disable
751 $client->request('GET', '/share/'.$content->getUuid());
752 $this->assertEquals(404, $client->getResponse()->getStatusCode());
753 }
701} 754}
diff --git a/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php b/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php
index b22156c3..47b86117 100644
--- a/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php
+++ b/tests/Wallabag/CoreBundle/Controller/ExportControllerTest.php
@@ -168,7 +168,7 @@ class ExportControllerTest extends WallabagCoreTestCase
168 $this->assertGreaterThan(1, $csv); 168 $this->assertGreaterThan(1, $csv);
169 // +1 for title line 169 // +1 for title line
170 $this->assertEquals(count($contentInDB) + 1, count($csv)); 170 $this->assertEquals(count($contentInDB) + 1, count($csv));
171 $this->assertEquals('Title;URL;Content;Tags;"MIME Type";Language', $csv[0]); 171 $this->assertEquals('Title;URL;Content;Tags;"MIME Type";Language;"Creation date"', $csv[0]);
172 } 172 }
173 173
174 public function testJsonExport() 174 public function testJsonExport()
@@ -210,6 +210,8 @@ class ExportControllerTest extends WallabagCoreTestCase
210 $this->assertArrayHasKey('reading_time', $content[0]); 210 $this->assertArrayHasKey('reading_time', $content[0]);
211 $this->assertArrayHasKey('domain_name', $content[0]); 211 $this->assertArrayHasKey('domain_name', $content[0]);
212 $this->assertArrayHasKey('tags', $content[0]); 212 $this->assertArrayHasKey('tags', $content[0]);
213 $this->assertArrayHasKey('created_at', $content[0]);
214 $this->assertArrayHasKey('updated_at', $content[0]);
213 } 215 }
214 216
215 public function testXmlExport() 217 public function testXmlExport()
@@ -247,5 +249,7 @@ class ExportControllerTest extends WallabagCoreTestCase
247 $this->assertNotEmpty('url', (string) $content->entry[0]->url); 249 $this->assertNotEmpty('url', (string) $content->entry[0]->url);
248 $this->assertNotEmpty('content', (string) $content->entry[0]->content); 250 $this->assertNotEmpty('content', (string) $content->entry[0]->content);
249 $this->assertNotEmpty('domain_name', (string) $content->entry[0]->domain_name); 251 $this->assertNotEmpty('domain_name', (string) $content->entry[0]->domain_name);
252 $this->assertNotEmpty('created_at', (string) $content->entry[0]->created_at);
253 $this->assertNotEmpty('updated_at', (string) $content->entry[0]->updated_at);
250 } 254 }
251} 255}
diff --git a/tests/Wallabag/CoreBundle/Controller/SecurityControllerTest.php b/tests/Wallabag/CoreBundle/Controller/SecurityControllerTest.php
index 03355f5a..08f4676e 100644
--- a/tests/Wallabag/CoreBundle/Controller/SecurityControllerTest.php
+++ b/tests/Wallabag/CoreBundle/Controller/SecurityControllerTest.php
@@ -69,4 +69,19 @@ class SecurityControllerTest extends WallabagCoreTestCase
69 $this->assertTrue($user->isTrustedComputer('ABCDEF')); 69 $this->assertTrue($user->isTrustedComputer('ABCDEF'));
70 $this->assertFalse($user->isTrustedComputer('FEDCBA')); 70 $this->assertFalse($user->isTrustedComputer('FEDCBA'));
71 } 71 }
72
73 public function testEnabledRegistration()
74 {
75 $client = $this->getClient();
76
77 if (!$client->getContainer()->getParameter('fosuser_registration')) {
78 $this->markTestSkipped('fosuser_registration is not enabled.');
79
80 return;
81 }
82
83 $client->followRedirects();
84 $crawler = $client->request('GET', '/register');
85 $this->assertContains('registration.submit', $crawler->filter('body')->extract(['_text'])[0]);
86 }
72} 87}
diff --git a/tests/Wallabag/CoreBundle/Controller/TagControllerTest.php b/tests/Wallabag/CoreBundle/Controller/TagControllerTest.php
index 58450e5f..71652760 100644
--- a/tests/Wallabag/CoreBundle/Controller/TagControllerTest.php
+++ b/tests/Wallabag/CoreBundle/Controller/TagControllerTest.php
@@ -131,4 +131,35 @@ class TagControllerTest extends WallabagCoreTestCase
131 131
132 $this->assertEquals(404, $client->getResponse()->getStatusCode()); 132 $this->assertEquals(404, $client->getResponse()->getStatusCode());
133 } 133 }
134
135 public function testShowEntriesForTagAction()
136 {
137 $this->logInAs('admin');
138 $client = $this->getClient();
139
140 $entry = $client->getContainer()
141 ->get('doctrine.orm.entity_manager')
142 ->getRepository('WallabagCoreBundle:Entry')
143 ->findOneByUsernameAndNotArchived('admin');
144
145 $tag = $client->getContainer()
146 ->get('doctrine.orm.entity_manager')
147 ->getRepository('WallabagCoreBundle:Tag')
148 ->findOneByEntryAndTagLabel($entry, 'foo');
149
150 $crawler = $client->request('GET', '/tag/list/'.$tag->getSlug());
151
152 $this->assertEquals(200, $client->getResponse()->getStatusCode());
153 $this->assertCount(2, $crawler->filter('div[class=entry]'));
154
155 $tag = $client->getContainer()
156 ->get('doctrine.orm.entity_manager')
157 ->getRepository('WallabagCoreBundle:Tag')
158 ->findOneByLabel('baz');
159
160 $crawler = $client->request('GET', '/tag/list/'.$tag->getSlug());
161
162 $this->assertEquals(200, $client->getResponse()->getStatusCode());
163 $this->assertCount(0, $crawler->filter('div[class=entry]'));
164 }
134} 165}
diff --git a/tests/Wallabag/CoreBundle/EventListener/LocaleListenerTest.php b/tests/Wallabag/CoreBundle/EventListener/LocaleListenerTest.php
index 2a7f9390..078bb69a 100644
--- a/tests/Wallabag/CoreBundle/EventListener/LocaleListenerTest.php
+++ b/tests/Wallabag/CoreBundle/EventListener/LocaleListenerTest.php
@@ -15,7 +15,11 @@ class LocaleListenerTest extends \PHPUnit_Framework_TestCase
15{ 15{
16 private function getEvent(Request $request) 16 private function getEvent(Request $request)
17 { 17 {
18 return new GetResponseEvent($this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'), $request, HttpKernelInterface::MASTER_REQUEST); 18 $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')
19 ->disableOriginalConstructor()
20 ->getMock();
21
22 return new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST);
19 } 23 }
20 24
21 public function testWithoutSession() 25 public function testWithoutSession()
diff --git a/tests/Wallabag/ImportBundle/Command/ImportCommandTest.php b/tests/Wallabag/ImportBundle/Command/ImportCommandTest.php
new file mode 100644
index 00000000..eb7fce79
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Command/ImportCommandTest.php
@@ -0,0 +1,86 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Command;
4
5use Symfony\Bundle\FrameworkBundle\Console\Application;
6use Symfony\Component\Console\Tester\CommandTester;
7use Wallabag\ImportBundle\Command\ImportCommand;
8use Tests\Wallabag\CoreBundle\WallabagCoreTestCase;
9use M6Web\Component\RedisMock\RedisMockFactory;
10
11class ImportCommandTest extends WallabagCoreTestCase
12{
13 /**
14 * @expectedException Symfony\Component\Console\Exception\RuntimeException
15 * @expectedExceptionMessage Not enough arguments
16 */
17 public function testRunImportCommandWithoutArguments()
18 {
19 $application = new Application($this->getClient()->getKernel());
20 $application->add(new ImportCommand());
21
22 $command = $application->find('wallabag:import');
23
24 $tester = new CommandTester($command);
25 $tester->execute([
26 'command' => $command->getName(),
27 ]);
28 }
29
30 /**
31 * @expectedException Symfony\Component\Config\Definition\Exception\Exception
32 * @expectedExceptionMessage not found
33 */
34 public function testRunImportCommandWithoutFilepath()
35 {
36 $application = new Application($this->getClient()->getKernel());
37 $application->add(new ImportCommand());
38
39 $command = $application->find('wallabag:import');
40
41 $tester = new CommandTester($command);
42 $tester->execute([
43 'command' => $command->getName(),
44 'userId' => 1,
45 'filepath' => 1,
46 ]);
47 }
48
49 /**
50 * @expectedException Symfony\Component\Config\Definition\Exception\Exception
51 * @expectedExceptionMessage User with id
52 */
53 public function testRunImportCommandWithoutUserId()
54 {
55 $application = new Application($this->getClient()->getKernel());
56 $application->add(new ImportCommand());
57
58 $command = $application->find('wallabag:import');
59
60 $tester = new CommandTester($command);
61 $tester->execute([
62 'command' => $command->getName(),
63 'userId' => 0,
64 'filepath' => './',
65 ]);
66 }
67
68 public function testRunImportCommand()
69 {
70 $application = new Application($this->getClient()->getKernel());
71 $application->add(new ImportCommand());
72
73 $command = $application->find('wallabag:import');
74
75 $tester = new CommandTester($command);
76 $tester->execute([
77 'command' => $command->getName(),
78 'userId' => 1,
79 'filepath' => $application->getKernel()->getContainer()->getParameter('kernel.root_dir').'/../tests/Wallabag/ImportBundle/fixtures/wallabag-v2-read.json',
80 '--importer' => 'v2',
81 ]);
82
83 $this->assertContains('imported', $tester->getDisplay());
84 $this->assertContains('already saved', $tester->getDisplay());
85 }
86}
diff --git a/tests/Wallabag/ImportBundle/Command/RedisWorkerCommandTest.php b/tests/Wallabag/ImportBundle/Command/RedisWorkerCommandTest.php
new file mode 100644
index 00000000..74952847
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Command/RedisWorkerCommandTest.php
@@ -0,0 +1,74 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Command;
4
5use Symfony\Bundle\FrameworkBundle\Console\Application;
6use Symfony\Component\Console\Tester\CommandTester;
7use Wallabag\ImportBundle\Command\RedisWorkerCommand;
8use Tests\Wallabag\CoreBundle\WallabagCoreTestCase;
9use M6Web\Component\RedisMock\RedisMockFactory;
10
11class RedisWorkerCommandTest extends WallabagCoreTestCase
12{
13 /**
14 * @expectedException Symfony\Component\Console\Exception\RuntimeException
15 * @expectedExceptionMessage Not enough arguments (missing: "serviceName")
16 */
17 public function testRunRedisWorkerCommandWithoutArguments()
18 {
19 $application = new Application($this->getClient()->getKernel());
20 $application->add(new RedisWorkerCommand());
21
22 $command = $application->find('wallabag:import:redis-worker');
23
24 $tester = new CommandTester($command);
25 $tester->execute([
26 'command' => $command->getName(),
27 ]);
28 }
29
30 /**
31 * @expectedException Symfony\Component\Config\Definition\Exception\Exception
32 * @expectedExceptionMessage No queue or consumer found for service name
33 */
34 public function testRunRedisWorkerCommandWithBadService()
35 {
36 $application = new Application($this->getClient()->getKernel());
37 $application->add(new RedisWorkerCommand());
38
39 $command = $application->find('wallabag:import:redis-worker');
40
41 $tester = new CommandTester($command);
42 $tester->execute([
43 'command' => $command->getName(),
44 'serviceName' => 'YOMONSERVICE',
45 ]);
46 }
47
48 public function testRunRedisWorkerCommand()
49 {
50 $application = new Application($this->getClient()->getKernel());
51 $application->add(new RedisWorkerCommand());
52
53 $factory = new RedisMockFactory();
54 $redisMock = $factory->getAdapter('Predis\Client', true);
55
56 $application->getKernel()->getContainer()->set('wallabag_core.redis.client', $redisMock);
57
58 // put a fake message in the queue so the worker will stop after reading that message
59 // instead of waiting for others
60 $redisMock->lpush('wallabag.import.readability', '{}');
61
62 $command = $application->find('wallabag:import:redis-worker');
63
64 $tester = new CommandTester($command);
65 $tester->execute([
66 'command' => $command->getName(),
67 'serviceName' => 'readability',
68 '--maxIterations' => 1,
69 ]);
70
71 $this->assertContains('Worker started at', $tester->getDisplay());
72 $this->assertContains('Waiting for message', $tester->getDisplay());
73 }
74}
diff --git a/tests/Wallabag/ImportBundle/Consumer/AMQPEntryConsumerTest.php b/tests/Wallabag/ImportBundle/Consumer/AMQPEntryConsumerTest.php
new file mode 100644
index 00000000..a3263771
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Consumer/AMQPEntryConsumerTest.php
@@ -0,0 +1,225 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Consumer\AMQP;
4
5use Wallabag\ImportBundle\Consumer\AMQPEntryConsumer;
6use PhpAmqpLib\Message\AMQPMessage;
7use Wallabag\UserBundle\Entity\User;
8use Wallabag\CoreBundle\Entity\Entry;
9
10class AMQPEntryConsumerTest extends \PHPUnit_Framework_TestCase
11{
12 public function testMessageOk()
13 {
14 $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
15 ->disableOriginalConstructor()
16 ->getMock();
17
18 $em
19 ->expects($this->once())
20 ->method('flush');
21
22 $em
23 ->expects($this->exactly(2))
24 ->method('clear');
25
26 $body = <<<'JSON'
27{
28 "item_id": "1402935436",
29 "resolved_id": "1402935436",
30 "given_url": "http://mashable.com/2016/09/04/leslie-jones-back-on-twitter-after-hack/?utm_campaign=Mash-Prod-RSS-Feedburner-All-Partial&utm_cid=Mash-Prod-RSS-Feedburner-All-Partial",
31 "given_title": "Leslie Jones is back on Twitter and her comeback tweet rules",
32 "favorite": "0",
33 "status": "0",
34 "time_added": "1473020899",
35 "time_updated": "1473020899",
36 "time_read": "0",
37 "time_favorited": "0",
38 "sort_id": 0,
39 "resolved_title": "Leslie Jones is back on Twitter and her comeback tweet rules",
40 "resolved_url": "http://mashable.com/2016/09/04/leslie-jones-back-on-twitter-after-hack/?utm_campaign=Mash-Prod-RSS-Feedburner-All-Partial&utm_cid=Mash-Prod-RSS-Feedburner-All-Partial",
41 "excerpt": "Leslie Jones is back to communicating with her adoring public on Twitter after cowardly hacker-trolls drove her away, probably to compensate for their own failings. It all started with a mic drop ...",
42 "is_article": "1",
43 "is_index": "0",
44 "has_video": "0",
45 "has_image": "1",
46 "word_count": "200",
47 "tags": {
48 "ifttt": {
49 "item_id": "1402935436",
50 "tag": "ifttt"
51 },
52 "mashable": {
53 "item_id": "1402935436",
54 "tag": "mashable"
55 }
56 },
57 "authors": {
58 "2484273": {
59 "item_id": "1402935436",
60 "author_id": "2484273",
61 "name": "Adam Rosenberg",
62 "url": "http://mashable.com/author/adam-rosenberg/"
63 }
64 },
65 "image": {
66 "item_id": "1402935436",
67 "src": "http://i.amz.mshcdn.com/i-V5cS6_sDqFABaVR0hVSBJqG_w=/950x534/https%3A%2F%2Fblueprint-api-production.s3.amazonaws.com%2Fuploads%2Fcard%2Fimage%2F199899%2Fleslie_jones_war_dogs.jpg",
68 "width": "0",
69 "height": "0"
70 },
71 "images": {
72 "1": {
73 "item_id": "1402935436",
74 "image_id": "1",
75 "src": "http://i.amz.mshcdn.com/i-V5cS6_sDqFABaVR0hVSBJqG_w=/950x534/https%3A%2F%2Fblueprint-api-production.s3.amazonaws.com%2Fuploads%2Fcard%2Fimage%2F199899%2Fleslie_jones_war_dogs.jpg",
76 "width": "0",
77 "height": "0",
78 "credit": "Image: Steve Eichner/NameFace/Sipa USA",
79 "caption": ""
80 }
81 },
82 "userId": 1
83}
84JSON;
85
86 $user = new User();
87 $entry = new Entry($user);
88
89 $userRepository = $this->getMockBuilder('Wallabag\UserBundle\Repository\UserRepository')
90 ->disableOriginalConstructor()
91 ->getMock();
92
93 $userRepository
94 ->expects($this->once())
95 ->method('find')
96 // userId from the body json above
97 ->with(1)
98 ->willReturn($user);
99
100 $import = $this->getMockBuilder('Wallabag\ImportBundle\Import\AbstractImport')
101 ->disableOriginalConstructor()
102 ->getMock();
103
104 $import
105 ->expects($this->once())
106 ->method('setUser')
107 ->with($user);
108
109 $import
110 ->expects($this->once())
111 ->method('parseEntry')
112 ->with(json_decode($body, true))
113 ->willReturn($entry);
114
115 $consumer = new AMQPEntryConsumer(
116 $em,
117 $userRepository,
118 $import
119 );
120
121 $message = new AMQPMessage($body);
122
123 $consumer->execute($message);
124 }
125
126 public function testMessageWithBadUser()
127 {
128 $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
129 ->disableOriginalConstructor()
130 ->getMock();
131
132 $em
133 ->expects($this->never())
134 ->method('flush');
135
136 $em
137 ->expects($this->never())
138 ->method('clear');
139
140 $body = '{ "userId": 123 }';
141
142 $user = new User();
143 $entry = new Entry($user);
144
145 $userRepository = $this->getMockBuilder('Wallabag\UserBundle\Repository\UserRepository')
146 ->disableOriginalConstructor()
147 ->getMock();
148
149 $userRepository
150 ->expects($this->once())
151 ->method('find')
152 // userId from the body json above
153 ->with(123)
154 ->willReturn(null);
155
156 $import = $this->getMockBuilder('Wallabag\ImportBundle\Import\AbstractImport')
157 ->disableOriginalConstructor()
158 ->getMock();
159
160 $consumer = new AMQPEntryConsumer(
161 $em,
162 $userRepository,
163 $import
164 );
165
166 $message = new AMQPMessage($body);
167
168 $consumer->execute($message);
169 }
170
171 public function testMessageWithEntryProcessed()
172 {
173 $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
174 ->disableOriginalConstructor()
175 ->getMock();
176
177 $em
178 ->expects($this->never())
179 ->method('flush');
180
181 $em
182 ->expects($this->never())
183 ->method('clear');
184
185 $body = '{ "userId": 123 }';
186
187 $user = new User();
188
189 $userRepository = $this->getMockBuilder('Wallabag\UserBundle\Repository\UserRepository')
190 ->disableOriginalConstructor()
191 ->getMock();
192
193 $userRepository
194 ->expects($this->once())
195 ->method('find')
196 // userId from the body json above
197 ->with(123)
198 ->willReturn($user);
199
200 $import = $this->getMockBuilder('Wallabag\ImportBundle\Import\AbstractImport')
201 ->disableOriginalConstructor()
202 ->getMock();
203
204 $import
205 ->expects($this->once())
206 ->method('setUser')
207 ->with($user);
208
209 $import
210 ->expects($this->once())
211 ->method('parseEntry')
212 ->with(json_decode($body, true))
213 ->willReturn(null);
214
215 $consumer = new AMQPEntryConsumer(
216 $em,
217 $userRepository,
218 $import
219 );
220
221 $message = new AMQPMessage($body);
222
223 $consumer->execute($message);
224 }
225}
diff --git a/tests/Wallabag/ImportBundle/Consumer/RedisEntryConsumerTest.php b/tests/Wallabag/ImportBundle/Consumer/RedisEntryConsumerTest.php
new file mode 100644
index 00000000..5e8ee41d
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Consumer/RedisEntryConsumerTest.php
@@ -0,0 +1,225 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Consumer\AMQP;
4
5use Wallabag\ImportBundle\Consumer\RedisEntryConsumer;
6use Wallabag\UserBundle\Entity\User;
7use Wallabag\CoreBundle\Entity\Entry;
8
9class RedisEntryConsumerTest extends \PHPUnit_Framework_TestCase
10{
11 public function testMessageOk()
12 {
13 $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
14 ->disableOriginalConstructor()
15 ->getMock();
16
17 $em
18 ->expects($this->once())
19 ->method('flush');
20
21 $em
22 ->expects($this->exactly(2))
23 ->method('clear');
24
25 $body = <<<'JSON'
26{
27 "item_id": "1402935436",
28 "resolved_id": "1402935436",
29 "given_url": "http://mashable.com/2016/09/04/leslie-jones-back-on-twitter-after-hack/?utm_campaign=Mash-Prod-RSS-Feedburner-All-Partial&utm_cid=Mash-Prod-RSS-Feedburner-All-Partial",
30 "given_title": "Leslie Jones is back on Twitter and her comeback tweet rules",
31 "favorite": "0",
32 "status": "0",
33 "time_added": "1473020899",
34 "time_updated": "1473020899",
35 "time_read": "0",
36 "time_favorited": "0",
37 "sort_id": 0,
38 "resolved_title": "Leslie Jones is back on Twitter and her comeback tweet rules",
39 "resolved_url": "http://mashable.com/2016/09/04/leslie-jones-back-on-twitter-after-hack/?utm_campaign=Mash-Prod-RSS-Feedburner-All-Partial&utm_cid=Mash-Prod-RSS-Feedburner-All-Partial",
40 "excerpt": "Leslie Jones is back to communicating with her adoring public on Twitter after cowardly hacker-trolls drove her away, probably to compensate for their own failings. It all started with a mic drop ...",
41 "is_article": "1",
42 "is_index": "0",
43 "has_video": "0",
44 "has_image": "1",
45 "word_count": "200",
46 "tags": {
47 "ifttt": {
48 "item_id": "1402935436",
49 "tag": "ifttt"
50 },
51 "mashable": {
52 "item_id": "1402935436",
53 "tag": "mashable"
54 }
55 },
56 "authors": {
57 "2484273": {
58 "item_id": "1402935436",
59 "author_id": "2484273",
60 "name": "Adam Rosenberg",
61 "url": "http://mashable.com/author/adam-rosenberg/"
62 }
63 },
64 "image": {
65 "item_id": "1402935436",
66 "src": "http://i.amz.mshcdn.com/i-V5cS6_sDqFABaVR0hVSBJqG_w=/950x534/https%3A%2F%2Fblueprint-api-production.s3.amazonaws.com%2Fuploads%2Fcard%2Fimage%2F199899%2Fleslie_jones_war_dogs.jpg",
67 "width": "0",
68 "height": "0"
69 },
70 "images": {
71 "1": {
72 "item_id": "1402935436",
73 "image_id": "1",
74 "src": "http://i.amz.mshcdn.com/i-V5cS6_sDqFABaVR0hVSBJqG_w=/950x534/https%3A%2F%2Fblueprint-api-production.s3.amazonaws.com%2Fuploads%2Fcard%2Fimage%2F199899%2Fleslie_jones_war_dogs.jpg",
75 "width": "0",
76 "height": "0",
77 "credit": "Image: Steve Eichner/NameFace/Sipa USA",
78 "caption": ""
79 }
80 },
81 "userId": 1
82}
83JSON;
84
85 $user = new User();
86 $entry = new Entry($user);
87
88 $userRepository = $this->getMockBuilder('Wallabag\UserBundle\Repository\UserRepository')
89 ->disableOriginalConstructor()
90 ->getMock();
91
92 $userRepository
93 ->expects($this->once())
94 ->method('find')
95 // userId from the body json above
96 ->with(1)
97 ->willReturn($user);
98
99 $import = $this->getMockBuilder('Wallabag\ImportBundle\Import\AbstractImport')
100 ->disableOriginalConstructor()
101 ->getMock();
102
103 $import
104 ->expects($this->once())
105 ->method('setUser')
106 ->with($user);
107
108 $import
109 ->expects($this->once())
110 ->method('parseEntry')
111 ->with(json_decode($body, true))
112 ->willReturn($entry);
113
114 $consumer = new RedisEntryConsumer(
115 $em,
116 $userRepository,
117 $import
118 );
119
120 $res = $consumer->manage($body);
121
122 $this->assertTrue($res);
123 }
124
125 public function testMessageWithBadUser()
126 {
127 $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
128 ->disableOriginalConstructor()
129 ->getMock();
130
131 $em
132 ->expects($this->never())
133 ->method('flush');
134
135 $em
136 ->expects($this->never())
137 ->method('clear');
138
139 $body = '{ "userId": 123 }';
140
141 $user = new User();
142 $entry = new Entry($user);
143
144 $userRepository = $this->getMockBuilder('Wallabag\UserBundle\Repository\UserRepository')
145 ->disableOriginalConstructor()
146 ->getMock();
147
148 $userRepository
149 ->expects($this->once())
150 ->method('find')
151 // userId from the body json above
152 ->with(123)
153 ->willReturn(null);
154
155 $import = $this->getMockBuilder('Wallabag\ImportBundle\Import\AbstractImport')
156 ->disableOriginalConstructor()
157 ->getMock();
158
159 $consumer = new RedisEntryConsumer(
160 $em,
161 $userRepository,
162 $import
163 );
164
165 $res = $consumer->manage($body);
166
167 $this->assertFalse($res);
168 }
169
170 public function testMessageWithEntryProcessed()
171 {
172 $em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
173 ->disableOriginalConstructor()
174 ->getMock();
175
176 $em
177 ->expects($this->never())
178 ->method('flush');
179
180 $em
181 ->expects($this->never())
182 ->method('clear');
183
184 $body = '{ "userId": 123 }';
185
186 $user = new User();
187
188 $userRepository = $this->getMockBuilder('Wallabag\UserBundle\Repository\UserRepository')
189 ->disableOriginalConstructor()
190 ->getMock();
191
192 $userRepository
193 ->expects($this->once())
194 ->method('find')
195 // userId from the body json above
196 ->with(123)
197 ->willReturn($user);
198
199 $import = $this->getMockBuilder('Wallabag\ImportBundle\Import\AbstractImport')
200 ->disableOriginalConstructor()
201 ->getMock();
202
203 $import
204 ->expects($this->once())
205 ->method('setUser')
206 ->with($user);
207
208 $import
209 ->expects($this->once())
210 ->method('parseEntry')
211 ->with(json_decode($body, true))
212 ->willReturn(null);
213
214 $consumer = new RedisEntryConsumer(
215 $em,
216 $userRepository,
217 $import
218 );
219
220 $res = $consumer->manage($body);
221
222 $this->assertFalse($res);
223 $this->assertFalse($consumer->isStopJob($body));
224 }
225}
diff --git a/tests/Wallabag/ImportBundle/Controller/ImportControllerTest.php b/tests/Wallabag/ImportBundle/Controller/ImportControllerTest.php
index 96b5300b..d869cdf9 100644
--- a/tests/Wallabag/ImportBundle/Controller/ImportControllerTest.php
+++ b/tests/Wallabag/ImportBundle/Controller/ImportControllerTest.php
@@ -24,6 +24,6 @@ class ImportControllerTest extends WallabagCoreTestCase
24 $crawler = $client->request('GET', '/import/'); 24 $crawler = $client->request('GET', '/import/');
25 25
26 $this->assertEquals(200, $client->getResponse()->getStatusCode()); 26 $this->assertEquals(200, $client->getResponse()->getStatusCode());
27 $this->assertEquals(3, $crawler->filter('blockquote')->count()); 27 $this->assertEquals(4, $crawler->filter('blockquote')->count());
28 } 28 }
29} 29}
diff --git a/tests/Wallabag/ImportBundle/Controller/PocketControllerTest.php b/tests/Wallabag/ImportBundle/Controller/PocketControllerTest.php
index e0e61df8..35673261 100644
--- a/tests/Wallabag/ImportBundle/Controller/PocketControllerTest.php
+++ b/tests/Wallabag/ImportBundle/Controller/PocketControllerTest.php
@@ -17,6 +17,36 @@ class PocketControllerTest extends WallabagCoreTestCase
17 $this->assertEquals(1, $crawler->filter('button[type=submit]')->count()); 17 $this->assertEquals(1, $crawler->filter('button[type=submit]')->count());
18 } 18 }
19 19
20 public function testImportPocketWithRabbitEnabled()
21 {
22 $this->logInAs('admin');
23 $client = $this->getClient();
24
25 $client->getContainer()->get('craue_config')->set('import_with_rabbitmq', 1);
26
27 $crawler = $client->request('GET', '/import/pocket');
28
29 $this->assertEquals(200, $client->getResponse()->getStatusCode());
30 $this->assertEquals(1, $crawler->filter('button[type=submit]')->count());
31
32 $client->getContainer()->get('craue_config')->set('import_with_rabbitmq', 0);
33 }
34
35 public function testImportPocketWithRedisEnabled()
36 {
37 $this->logInAs('admin');
38 $client = $this->getClient();
39
40 $client->getContainer()->get('craue_config')->set('import_with_redis', 1);
41
42 $crawler = $client->request('GET', '/import/pocket');
43
44 $this->assertEquals(200, $client->getResponse()->getStatusCode());
45 $this->assertEquals(1, $crawler->filter('button[type=submit]')->count());
46
47 $client->getContainer()->get('craue_config')->set('import_with_redis', 0);
48 }
49
20 public function testImportPocketAuthBadToken() 50 public function testImportPocketAuthBadToken()
21 { 51 {
22 $this->logInAs('admin'); 52 $this->logInAs('admin');
diff --git a/tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php b/tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php
new file mode 100644
index 00000000..7b88d891
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php
@@ -0,0 +1,197 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Controller;
4
5use Tests\Wallabag\CoreBundle\WallabagCoreTestCase;
6use Symfony\Component\HttpFoundation\File\UploadedFile;
7
8class ReadabilityControllerTest extends WallabagCoreTestCase
9{
10 public function testImportReadability()
11 {
12 $this->logInAs('admin');
13 $client = $this->getClient();
14
15 $crawler = $client->request('GET', '/import/readability');
16
17 $this->assertEquals(200, $client->getResponse()->getStatusCode());
18 $this->assertEquals(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
19 $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
20 }
21
22 public function testImportReadabilityWithRabbitEnabled()
23 {
24 $this->logInAs('admin');
25 $client = $this->getClient();
26
27 $client->getContainer()->get('craue_config')->set('import_with_rabbitmq', 1);
28
29 $crawler = $client->request('GET', '/import/readability');
30
31 $this->assertEquals(200, $client->getResponse()->getStatusCode());
32 $this->assertEquals(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
33 $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
34
35 $client->getContainer()->get('craue_config')->set('import_with_rabbitmq', 0);
36 }
37
38 public function testImportReadabilityBadFile()
39 {
40 $this->logInAs('admin');
41 $client = $this->getClient();
42
43 $crawler = $client->request('GET', '/import/readability');
44 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
45
46 $data = [
47 'upload_import_file[file]' => '',
48 ];
49
50 $client->submit($form, $data);
51
52 $this->assertEquals(200, $client->getResponse()->getStatusCode());
53 }
54
55 public function testImportReadabilityWithRedisEnabled()
56 {
57 $this->logInAs('admin');
58 $client = $this->getClient();
59
60 $client->getContainer()->get('craue_config')->set('import_with_redis', 1);
61
62 $crawler = $client->request('GET', '/import/readability');
63
64 $this->assertEquals(200, $client->getResponse()->getStatusCode());
65 $this->assertEquals(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
66 $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
67
68 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
69
70 $file = new UploadedFile(__DIR__.'/../fixtures/readability.json', 'readability.json');
71
72 $data = [
73 'upload_import_file[file]' => $file,
74 ];
75
76 $client->submit($form, $data);
77
78 $this->assertEquals(302, $client->getResponse()->getStatusCode());
79
80 $crawler = $client->followRedirect();
81
82 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
83 $this->assertContains('flashes.import.notice.summary', $body[0]);
84
85 $this->assertNotEmpty($client->getContainer()->get('wallabag_core.redis.client')->lpop('wallabag.import.readability'));
86
87 $client->getContainer()->get('craue_config')->set('import_with_redis', 0);
88 }
89
90 public function testImportReadabilityWithFile()
91 {
92 $this->logInAs('admin');
93 $client = $this->getClient();
94
95 $crawler = $client->request('GET', '/import/readability');
96 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
97
98 $file = new UploadedFile(__DIR__.'/../fixtures/readability.json', 'readability.json');
99
100 $data = [
101 'upload_import_file[file]' => $file,
102 ];
103
104 $client->submit($form, $data);
105
106 $this->assertEquals(302, $client->getResponse()->getStatusCode());
107
108 $crawler = $client->followRedirect();
109
110 $content = $client->getContainer()
111 ->get('doctrine.orm.entity_manager')
112 ->getRepository('WallabagCoreBundle:Entry')
113 ->findByUrlAndUserId(
114 'https://venngage.com/blog/hashtags-are-worthless/',
115 $this->getLoggedInUserId()
116 );
117
118 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
119 $this->assertContains('flashes.import.notice.summary', $body[0]);
120
121 $this->assertNotEmpty($content->getMimetype());
122 $this->assertNotEmpty($content->getPreviewPicture());
123 $this->assertNotEmpty($content->getLanguage());
124 $this->assertEquals(0, count($content->getTags()));
125 $this->assertInstanceOf(\DateTime::class, $content->getCreatedAt());
126 $this->assertEquals('2016-08-25', $content->getCreatedAt()->format('Y-m-d'));
127 }
128
129 public function testImportReadabilityWithFileAndMarkAllAsRead()
130 {
131 $this->logInAs('admin');
132 $client = $this->getClient();
133
134 $crawler = $client->request('GET', '/import/readability');
135 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
136
137 $file = new UploadedFile(__DIR__.'/../fixtures/readability-read.json', 'readability-read.json');
138
139 $data = [
140 'upload_import_file[file]' => $file,
141 'upload_import_file[mark_as_read]' => 1,
142 ];
143
144 $client->submit($form, $data);
145
146 $this->assertEquals(302, $client->getResponse()->getStatusCode());
147
148 $crawler = $client->followRedirect();
149
150 $content1 = $client->getContainer()
151 ->get('doctrine.orm.entity_manager')
152 ->getRepository('WallabagCoreBundle:Entry')
153 ->findByUrlAndUserId(
154 'https://blog.travis-ci.com/2016-07-28-what-we-learned-from-analyzing-2-million-travis-builds/',
155 $this->getLoggedInUserId()
156 );
157
158 $this->assertTrue($content1->isArchived());
159
160 $content2 = $client->getContainer()
161 ->get('doctrine.orm.entity_manager')
162 ->getRepository('WallabagCoreBundle:Entry')
163 ->findByUrlAndUserId(
164 'https://facebook.github.io/graphql/',
165 $this->getLoggedInUserId()
166 );
167
168 $this->assertTrue($content2->isArchived());
169
170 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
171 $this->assertContains('flashes.import.notice.summary', $body[0]);
172 }
173
174 public function testImportReadabilityWithEmptyFile()
175 {
176 $this->logInAs('admin');
177 $client = $this->getClient();
178
179 $crawler = $client->request('GET', '/import/readability');
180 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
181
182 $file = new UploadedFile(__DIR__.'/../fixtures/test.txt', 'test.txt');
183
184 $data = [
185 'upload_import_file[file]' => $file,
186 ];
187
188 $client->submit($form, $data);
189
190 $this->assertEquals(302, $client->getResponse()->getStatusCode());
191
192 $crawler = $client->followRedirect();
193
194 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
195 $this->assertContains('flashes.import.notice.failed', $body[0]);
196 }
197}
diff --git a/tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php b/tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php
index c1025b41..98e85d45 100644
--- a/tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php
+++ b/tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php
@@ -19,6 +19,74 @@ class WallabagV1ControllerTest extends WallabagCoreTestCase
19 $this->assertEquals(1, $crawler->filter('input[type=file]')->count()); 19 $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
20 } 20 }
21 21
22 public function testImportWallabagWithRabbitEnabled()
23 {
24 $this->logInAs('admin');
25 $client = $this->getClient();
26
27 $client->getContainer()->get('craue_config')->set('import_with_rabbitmq', 1);
28
29 $crawler = $client->request('GET', '/import/wallabag-v1');
30
31 $this->assertEquals(200, $client->getResponse()->getStatusCode());
32 $this->assertEquals(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
33 $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
34
35 $client->getContainer()->get('craue_config')->set('import_with_rabbitmq', 0);
36 }
37
38 public function testImportWallabagBadFile()
39 {
40 $this->logInAs('admin');
41 $client = $this->getClient();
42
43 $crawler = $client->request('GET', '/import/wallabag-v1');
44 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
45
46 $data = [
47 'upload_import_file[file]' => '',
48 ];
49
50 $client->submit($form, $data);
51
52 $this->assertEquals(200, $client->getResponse()->getStatusCode());
53 }
54
55 public function testImportWallabagWithRedisEnabled()
56 {
57 $this->logInAs('admin');
58 $client = $this->getClient();
59
60 $client->getContainer()->get('craue_config')->set('import_with_redis', 1);
61
62 $crawler = $client->request('GET', '/import/wallabag-v1');
63
64 $this->assertEquals(200, $client->getResponse()->getStatusCode());
65 $this->assertEquals(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
66 $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
67
68 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
69
70 $file = new UploadedFile(__DIR__.'/../fixtures/wallabag-v1.json', 'wallabag-v1.json');
71
72 $data = [
73 'upload_import_file[file]' => $file,
74 ];
75
76 $client->submit($form, $data);
77
78 $this->assertEquals(302, $client->getResponse()->getStatusCode());
79
80 $crawler = $client->followRedirect();
81
82 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
83 $this->assertContains('flashes.import.notice.summary', $body[0]);
84
85 $this->assertNotEmpty($client->getContainer()->get('wallabag_core.redis.client')->lpop('wallabag.import.wallabag_v1'));
86
87 $client->getContainer()->get('craue_config')->set('import_with_redis', 0);
88 }
89
22 public function testImportWallabagWithFile() 90 public function testImportWallabagWithFile()
23 { 91 {
24 $this->logInAs('admin'); 92 $this->logInAs('admin');
@@ -56,6 +124,12 @@ class WallabagV1ControllerTest extends WallabagCoreTestCase
56 124
57 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text'])); 125 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
58 $this->assertContains('flashes.import.notice.summary', $body[0]); 126 $this->assertContains('flashes.import.notice.summary', $body[0]);
127
128 $this->assertEmpty($content->getMimetype());
129 $this->assertEmpty($content->getPreviewPicture());
130 $this->assertEmpty($content->getLanguage());
131 $this->assertEquals(1, count($content->getTags()));
132 $this->assertInstanceOf(\DateTime::class, $content->getCreatedAt());
59 } 133 }
60 134
61 public function testImportWallabagWithFileAndMarkAllAsRead() 135 public function testImportWallabagWithFileAndMarkAllAsRead()
diff --git a/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php b/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php
index d8d2c8bf..74d61f9a 100644
--- a/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php
+++ b/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php
@@ -19,6 +19,74 @@ class WallabagV2ControllerTest extends WallabagCoreTestCase
19 $this->assertEquals(1, $crawler->filter('input[type=file]')->count()); 19 $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
20 } 20 }
21 21
22 public function testImportWallabagWithRabbitEnabled()
23 {
24 $this->logInAs('admin');
25 $client = $this->getClient();
26
27 $client->getContainer()->get('craue_config')->set('import_with_rabbitmq', 1);
28
29 $crawler = $client->request('GET', '/import/wallabag-v2');
30
31 $this->assertEquals(200, $client->getResponse()->getStatusCode());
32 $this->assertEquals(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
33 $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
34
35 $client->getContainer()->get('craue_config')->set('import_with_rabbitmq', 0);
36 }
37
38 public function testImportWallabagBadFile()
39 {
40 $this->logInAs('admin');
41 $client = $this->getClient();
42
43 $crawler = $client->request('GET', '/import/wallabag-v2');
44 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
45
46 $data = [
47 'upload_import_file[file]' => '',
48 ];
49
50 $client->submit($form, $data);
51
52 $this->assertEquals(200, $client->getResponse()->getStatusCode());
53 }
54
55 public function testImportWallabagWithRedisEnabled()
56 {
57 $this->logInAs('admin');
58 $client = $this->getClient();
59
60 $client->getContainer()->get('craue_config')->set('import_with_redis', 1);
61
62 $crawler = $client->request('GET', '/import/wallabag-v2');
63
64 $this->assertEquals(200, $client->getResponse()->getStatusCode());
65 $this->assertEquals(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
66 $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
67
68 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
69
70 $file = new UploadedFile(__DIR__.'/../fixtures/wallabag-v2.json', 'wallabag-v2.json');
71
72 $data = [
73 'upload_import_file[file]' => $file,
74 ];
75
76 $client->submit($form, $data);
77
78 $this->assertEquals(302, $client->getResponse()->getStatusCode());
79
80 $crawler = $client->followRedirect();
81
82 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
83 $this->assertContains('flashes.import.notice.summary', $body[0]);
84
85 $this->assertNotEmpty($client->getContainer()->get('wallabag_core.redis.client')->lpop('wallabag.import.wallabag_v2'));
86
87 $client->getContainer()->get('craue_config')->set('import_with_redis', 0);
88 }
89
22 public function testImportWallabagWithFile() 90 public function testImportWallabagWithFile()
23 { 91 {
24 $this->logInAs('admin'); 92 $this->logInAs('admin');
@@ -50,9 +118,9 @@ class WallabagV2ControllerTest extends WallabagCoreTestCase
50 $this->getLoggedInUserId() 118 $this->getLoggedInUserId()
51 ); 119 );
52 120
53 $this->assertEmpty($content->getMimetype()); 121 $this->assertNotEmpty($content->getMimetype());
54 $this->assertEmpty($content->getPreviewPicture()); 122 $this->assertNotEmpty($content->getPreviewPicture());
55 $this->assertEmpty($content->getLanguage()); 123 $this->assertNotEmpty($content->getLanguage());
56 $this->assertEquals(0, count($content->getTags())); 124 $this->assertEquals(0, count($content->getTags()));
57 125
58 $content = $client->getContainer() 126 $content = $client->getContainer()
@@ -67,6 +135,8 @@ class WallabagV2ControllerTest extends WallabagCoreTestCase
67 $this->assertNotEmpty($content->getPreviewPicture()); 135 $this->assertNotEmpty($content->getPreviewPicture());
68 $this->assertNotEmpty($content->getLanguage()); 136 $this->assertNotEmpty($content->getLanguage());
69 $this->assertEquals(2, count($content->getTags())); 137 $this->assertEquals(2, count($content->getTags()));
138 $this->assertInstanceOf(\DateTime::class, $content->getCreatedAt());
139 $this->assertEquals('2016-09-08', $content->getCreatedAt()->format('Y-m-d'));
70 } 140 }
71 141
72 public function testImportWallabagWithEmptyFile() 142 public function testImportWallabagWithEmptyFile()
diff --git a/tests/Wallabag/ImportBundle/Import/PocketImportTest.php b/tests/Wallabag/ImportBundle/Import/PocketImportTest.php
index 8534e1c8..952521a2 100644
--- a/tests/Wallabag/ImportBundle/Import/PocketImportTest.php
+++ b/tests/Wallabag/ImportBundle/Import/PocketImportTest.php
@@ -4,21 +4,17 @@ namespace Tests\Wallabag\ImportBundle\Import;
4 4
5use Wallabag\UserBundle\Entity\User; 5use Wallabag\UserBundle\Entity\User;
6use Wallabag\CoreBundle\Entity\Entry; 6use Wallabag\CoreBundle\Entity\Entry;
7use Wallabag\CoreBundle\Entity\Config;
7use Wallabag\ImportBundle\Import\PocketImport; 8use Wallabag\ImportBundle\Import\PocketImport;
8use GuzzleHttp\Client; 9use GuzzleHttp\Client;
9use GuzzleHttp\Subscriber\Mock; 10use GuzzleHttp\Subscriber\Mock;
10use GuzzleHttp\Message\Response; 11use GuzzleHttp\Message\Response;
11use GuzzleHttp\Stream\Stream; 12use GuzzleHttp\Stream\Stream;
13use Wallabag\ImportBundle\Redis\Producer;
12use Monolog\Logger; 14use Monolog\Logger;
13use Monolog\Handler\TestHandler; 15use Monolog\Handler\TestHandler;
14 16use Simpleue\Queue\RedisQueue;
15class PocketImportMock extends PocketImport 17use M6Web\Component\RedisMock\RedisMockFactory;
16{
17 public function getAccessToken()
18 {
19 return $this->accessToken;
20 }
21}
22 18
23class PocketImportTest extends \PHPUnit_Framework_TestCase 19class PocketImportTest extends \PHPUnit_Framework_TestCase
24{ 20{
@@ -32,45 +28,24 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
32 { 28 {
33 $this->user = new User(); 29 $this->user = new User();
34 30
35 $this->tokenStorage = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface') 31 $config = new Config($this->user);
36 ->disableOriginalConstructor() 32 $config->setPocketConsumerKey('xxx');
37 ->getMock();
38 33
39 $token = $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface') 34 $this->user->setConfig($config);
40 ->disableOriginalConstructor()
41 ->getMock();
42 35
43 $this->contentProxy = $this->getMockBuilder('Wallabag\CoreBundle\Helper\ContentProxy') 36 $this->contentProxy = $this->getMockBuilder('Wallabag\CoreBundle\Helper\ContentProxy')
44 ->disableOriginalConstructor() 37 ->disableOriginalConstructor()
45 ->getMock(); 38 ->getMock();
46 39
47 $token->expects($this->once())
48 ->method('getUser')
49 ->willReturn($this->user);
50
51 $this->tokenStorage->expects($this->once())
52 ->method('getToken')
53 ->willReturn($token);
54
55 $this->em = $this->getMockBuilder('Doctrine\ORM\EntityManager') 40 $this->em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
56 ->disableOriginalConstructor() 41 ->disableOriginalConstructor()
57 ->getMock(); 42 ->getMock();
58 43
59 $config = $this->getMockBuilder('Craue\ConfigBundle\Util\Config') 44 $pocket = new PocketImport(
60 ->disableOriginalConstructor()
61 ->getMock();
62
63 $config->expects($this->any())
64 ->method('get')
65 ->with('pocket_consumer_key')
66 ->willReturn($consumerKey);
67
68 $pocket = new PocketImportMock(
69 $this->tokenStorage,
70 $this->em, 45 $this->em,
71 $this->contentProxy, 46 $this->contentProxy
72 $config
73 ); 47 );
48 $pocket->setUser($this->user);
74 49
75 $this->logHandler = new TestHandler(); 50 $this->logHandler = new TestHandler();
76 $logger = new Logger('test', [$this->logHandler]); 51 $logger = new Logger('test', [$this->logHandler]);
@@ -189,10 +164,16 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
189 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland", 164 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
190 "favorite": "1", 165 "favorite": "1",
191 "status": "1", 166 "status": "1",
167 "time_added": "1473020899",
168 "time_updated": "1473020899",
169 "time_read": "0",
170 "time_favorited": "0",
171 "sort_id": 0,
192 "resolved_title": "The Massive Ryder Cup Preview", 172 "resolved_title": "The Massive Ryder Cup Preview",
193 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview", 173 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
194 "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.", 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.",
195 "is_article": "1", 175 "is_article": "1",
176 "is_index": "0",
196 "has_video": "1", 177 "has_video": "1",
197 "has_image": "1", 178 "has_image": "1",
198 "word_count": "3197", 179 "word_count": "3197",
@@ -236,10 +217,16 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
236 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland", 217 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
237 "favorite": "1", 218 "favorite": "1",
238 "status": "1", 219 "status": "1",
220 "time_added": "1473020899",
221 "time_updated": "1473020899",
222 "time_read": "0",
223 "time_favorited": "0",
224 "sort_id": 1,
239 "resolved_title": "The Massive Ryder Cup Preview", 225 "resolved_title": "The Massive Ryder Cup Preview",
240 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview", 226 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
241 "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.", 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.",
242 "is_article": "1", 228 "is_article": "1",
229 "is_index": "0",
243 "has_video": "0", 230 "has_video": "0",
244 "has_image": "0", 231 "has_image": "0",
245 "word_count": "3197" 232 "word_count": "3197"
@@ -279,7 +266,7 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
279 $res = $pocketImport->import(); 266 $res = $pocketImport->import();
280 267
281 $this->assertTrue($res); 268 $this->assertTrue($res);
282 $this->assertEquals(['skipped' => 1, 'imported' => 1], $pocketImport->getSummary()); 269 $this->assertEquals(['skipped' => 1, 'imported' => 1, 'queued' => 0], $pocketImport->getSummary());
283 } 270 }
284 271
285 /** 272 /**
@@ -302,6 +289,11 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
302 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland", 289 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
303 "favorite": "1", 290 "favorite": "1",
304 "status": "1", 291 "status": "1",
292 "time_added": "1473020899",
293 "time_updated": "1473020899",
294 "time_read": "0",
295 "time_favorited": "0",
296 "sort_id": 0,
305 "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.", 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.",
306 "is_article": "1", 298 "is_article": "1",
307 "has_video": "1", 299 "has_video": "1",
@@ -315,6 +307,11 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
315 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland", 307 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
316 "favorite": "1", 308 "favorite": "1",
317 "status": "0", 309 "status": "0",
310 "time_added": "1473020899",
311 "time_updated": "1473020899",
312 "time_read": "0",
313 "time_favorited": "0",
314 "sort_id": 1,
318 "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.", 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.",
319 "is_article": "1", 316 "is_article": "1",
320 "has_video": "0", 317 "has_video": "0",
@@ -364,7 +361,174 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
364 $res = $pocketImport->setMarkAsRead(true)->import(); 361 $res = $pocketImport->setMarkAsRead(true)->import();
365 362
366 $this->assertTrue($res); 363 $this->assertTrue($res);
367 $this->assertEquals(['skipped' => 0, 'imported' => 2], $pocketImport->getSummary()); 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}
395JSON;
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}
481JSON;
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'));
368 } 532 }
369 533
370 public function testImportBadResponse() 534 public function testImportBadResponse()
@@ -402,6 +566,8 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
402 "status": 1, 566 "status": 1,
403 "list": { 567 "list": {
404 "229279689": { 568 "229279689": {
569 "status": "1",
570 "favorite": "1",
405 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview" 571 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview"
406 } 572 }
407 } 573 }
@@ -439,6 +605,6 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
439 $res = $pocketImport->import(); 605 $res = $pocketImport->import();
440 606
441 $this->assertTrue($res); 607 $this->assertTrue($res);
442 $this->assertEquals(['skipped' => 1, 'imported' => 0], $pocketImport->getSummary()); 608 $this->assertEquals(['skipped' => 0, 'imported' => 1, 'queued' => 0], $pocketImport->getSummary());
443 } 609 }
444} 610}
diff --git a/tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php b/tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php
new file mode 100644
index 00000000..d98cd486
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php
@@ -0,0 +1,233 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Import;
4
5use Wallabag\ImportBundle\Import\ReadabilityImport;
6use Wallabag\UserBundle\Entity\User;
7use Wallabag\CoreBundle\Entity\Entry;
8use Wallabag\ImportBundle\Redis\Producer;
9use Monolog\Logger;
10use Monolog\Handler\TestHandler;
11use Simpleue\Queue\RedisQueue;
12use M6Web\Component\RedisMock\RedisMockFactory;
13
14class ReadabilityImportTest extends \PHPUnit_Framework_TestCase
15{
16 protected $user;
17 protected $em;
18 protected $logHandler;
19 protected $contentProxy;
20
21 private function getReadabilityImport($unsetUser = false)
22 {
23 $this->user = new User();
24
25 $this->em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
26 ->disableOriginalConstructor()
27 ->getMock();
28
29 $this->contentProxy = $this->getMockBuilder('Wallabag\CoreBundle\Helper\ContentProxy')
30 ->disableOriginalConstructor()
31 ->getMock();
32
33 $wallabag = new ReadabilityImport($this->em, $this->contentProxy);
34
35 $this->logHandler = new TestHandler();
36 $logger = new Logger('test', [$this->logHandler]);
37 $wallabag->setLogger($logger);
38
39 if (false === $unsetUser) {
40 $wallabag->setUser($this->user);
41 }
42
43 return $wallabag;
44 }
45
46 public function testInit()
47 {
48 $readabilityImport = $this->getReadabilityImport();
49
50 $this->assertEquals('Readability', $readabilityImport->getName());
51 $this->assertNotEmpty($readabilityImport->getUrl());
52 $this->assertEquals('import.readability.description', $readabilityImport->getDescription());
53 }
54
55 public function testImport()
56 {
57 $readabilityImport = $this->getReadabilityImport();
58 $readabilityImport->setFilepath(__DIR__.'/../fixtures/readability.json');
59
60 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
61 ->disableOriginalConstructor()
62 ->getMock();
63
64 $entryRepo->expects($this->exactly(24))
65 ->method('findByUrlAndUserId')
66 ->willReturn(false);
67
68 $this->em
69 ->expects($this->any())
70 ->method('getRepository')
71 ->willReturn($entryRepo);
72
73 $entry = $this->getMockBuilder('Wallabag\CoreBundle\Entity\Entry')
74 ->disableOriginalConstructor()
75 ->getMock();
76
77 $this->contentProxy
78 ->expects($this->exactly(24))
79 ->method('updateEntry')
80 ->willReturn($entry);
81
82 $res = $readabilityImport->import();
83
84 $this->assertTrue($res);
85 $this->assertEquals(['skipped' => 0, 'imported' => 24, 'queued' => 0], $readabilityImport->getSummary());
86 }
87
88 public function testImportAndMarkAllAsRead()
89 {
90 $readabilityImport = $this->getReadabilityImport();
91 $readabilityImport->setFilepath(__DIR__.'/../fixtures/readability-read.json');
92
93 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
94 ->disableOriginalConstructor()
95 ->getMock();
96
97 $entryRepo->expects($this->exactly(2))
98 ->method('findByUrlAndUserId')
99 ->will($this->onConsecutiveCalls(false, true));
100
101 $this->em
102 ->expects($this->any())
103 ->method('getRepository')
104 ->willReturn($entryRepo);
105
106 $this->contentProxy
107 ->expects($this->exactly(1))
108 ->method('updateEntry')
109 ->willReturn(new Entry($this->user));
110
111 // check that every entry persisted are archived
112 $this->em
113 ->expects($this->any())
114 ->method('persist')
115 ->with($this->callback(function ($persistedEntry) {
116 return $persistedEntry->isArchived();
117 }));
118
119 $res = $readabilityImport->setMarkAsRead(true)->import();
120
121 $this->assertTrue($res);
122
123 $this->assertEquals(['skipped' => 1, 'imported' => 1, 'queued' => 0], $readabilityImport->getSummary());
124 }
125
126 public function testImportWithRabbit()
127 {
128 $readabilityImport = $this->getReadabilityImport();
129 $readabilityImport->setFilepath(__DIR__.'/../fixtures/readability.json');
130
131 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
132 ->disableOriginalConstructor()
133 ->getMock();
134
135 $entryRepo->expects($this->never())
136 ->method('findByUrlAndUserId');
137
138 $this->em
139 ->expects($this->never())
140 ->method('getRepository');
141
142 $entry = $this->getMockBuilder('Wallabag\CoreBundle\Entity\Entry')
143 ->disableOriginalConstructor()
144 ->getMock();
145
146 $this->contentProxy
147 ->expects($this->never())
148 ->method('updateEntry');
149
150 $producer = $this->getMockBuilder('OldSound\RabbitMqBundle\RabbitMq\Producer')
151 ->disableOriginalConstructor()
152 ->getMock();
153
154 $producer
155 ->expects($this->exactly(24))
156 ->method('publish');
157
158 $readabilityImport->setProducer($producer);
159
160 $res = $readabilityImport->setMarkAsRead(true)->import();
161
162 $this->assertTrue($res);
163 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 24], $readabilityImport->getSummary());
164 }
165
166 public function testImportWithRedis()
167 {
168 $readabilityImport = $this->getReadabilityImport();
169 $readabilityImport->setFilepath(__DIR__.'/../fixtures/readability.json');
170
171 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
172 ->disableOriginalConstructor()
173 ->getMock();
174
175 $entryRepo->expects($this->never())
176 ->method('findByUrlAndUserId');
177
178 $this->em
179 ->expects($this->never())
180 ->method('getRepository');
181
182 $entry = $this->getMockBuilder('Wallabag\CoreBundle\Entity\Entry')
183 ->disableOriginalConstructor()
184 ->getMock();
185
186 $this->contentProxy
187 ->expects($this->never())
188 ->method('updateEntry');
189
190 $factory = new RedisMockFactory();
191 $redisMock = $factory->getAdapter('Predis\Client', true);
192
193 $queue = new RedisQueue($redisMock, 'readability');
194 $producer = new Producer($queue);
195
196 $readabilityImport->setProducer($producer);
197
198 $res = $readabilityImport->setMarkAsRead(true)->import();
199
200 $this->assertTrue($res);
201 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 24], $readabilityImport->getSummary());
202
203 $this->assertNotEmpty($redisMock->lpop('readability'));
204 }
205
206 public function testImportBadFile()
207 {
208 $readabilityImport = $this->getReadabilityImport();
209 $readabilityImport->setFilepath(__DIR__.'/../fixtures/wallabag-v1.jsonx');
210
211 $res = $readabilityImport->import();
212
213 $this->assertFalse($res);
214
215 $records = $this->logHandler->getRecords();
216 $this->assertContains('ReadabilityImport: unable to read file', $records[0]['message']);
217 $this->assertEquals('ERROR', $records[0]['level_name']);
218 }
219
220 public function testImportUserNotDefined()
221 {
222 $readabilityImport = $this->getReadabilityImport(true);
223 $readabilityImport->setFilepath(__DIR__.'/../fixtures/readability.json');
224
225 $res = $readabilityImport->import();
226
227 $this->assertFalse($res);
228
229 $records = $this->logHandler->getRecords();
230 $this->assertContains('ReadabilityImport: user is not defined', $records[0]['message']);
231 $this->assertEquals('ERROR', $records[0]['level_name']);
232 }
233}
diff --git a/tests/Wallabag/ImportBundle/Import/WallabagV1ImportTest.php b/tests/Wallabag/ImportBundle/Import/WallabagV1ImportTest.php
index bdc47dac..5ab4ad00 100644
--- a/tests/Wallabag/ImportBundle/Import/WallabagV1ImportTest.php
+++ b/tests/Wallabag/ImportBundle/Import/WallabagV1ImportTest.php
@@ -5,8 +5,11 @@ namespace Tests\Wallabag\ImportBundle\Import;
5use Wallabag\ImportBundle\Import\WallabagV1Import; 5use Wallabag\ImportBundle\Import\WallabagV1Import;
6use Wallabag\UserBundle\Entity\User; 6use Wallabag\UserBundle\Entity\User;
7use Wallabag\CoreBundle\Entity\Entry; 7use Wallabag\CoreBundle\Entity\Entry;
8use Wallabag\ImportBundle\Redis\Producer;
8use Monolog\Logger; 9use Monolog\Logger;
9use Monolog\Handler\TestHandler; 10use Monolog\Handler\TestHandler;
11use Simpleue\Queue\RedisQueue;
12use M6Web\Component\RedisMock\RedisMockFactory;
10 13
11class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase 14class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase
12{ 15{
@@ -79,7 +82,7 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase
79 $res = $wallabagV1Import->import(); 82 $res = $wallabagV1Import->import();
80 83
81 $this->assertTrue($res); 84 $this->assertTrue($res);
82 $this->assertEquals(['skipped' => 1, 'imported' => 3], $wallabagV1Import->getSummary()); 85 $this->assertEquals(['skipped' => 1, 'imported' => 3, 'queued' => 0], $wallabagV1Import->getSummary());
83 } 86 }
84 87
85 public function testImportAndMarkAllAsRead() 88 public function testImportAndMarkAllAsRead()
@@ -117,7 +120,87 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase
117 120
118 $this->assertTrue($res); 121 $this->assertTrue($res);
119 122
120 $this->assertEquals(['skipped' => 0, 'imported' => 3], $wallabagV1Import->getSummary()); 123 $this->assertEquals(['skipped' => 0, 'imported' => 3, 'queued' => 0], $wallabagV1Import->getSummary());
124 }
125
126 public function testImportWithRabbit()
127 {
128 $wallabagV1Import = $this->getWallabagV1Import();
129 $wallabagV1Import->setFilepath(__DIR__.'/../fixtures/wallabag-v1.json');
130
131 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
132 ->disableOriginalConstructor()
133 ->getMock();
134
135 $entryRepo->expects($this->never())
136 ->method('findByUrlAndUserId');
137
138 $this->em
139 ->expects($this->never())
140 ->method('getRepository');
141
142 $entry = $this->getMockBuilder('Wallabag\CoreBundle\Entity\Entry')
143 ->disableOriginalConstructor()
144 ->getMock();
145
146 $this->contentProxy
147 ->expects($this->never())
148 ->method('updateEntry');
149
150 $producer = $this->getMockBuilder('OldSound\RabbitMqBundle\RabbitMq\Producer')
151 ->disableOriginalConstructor()
152 ->getMock();
153
154 $producer
155 ->expects($this->exactly(4))
156 ->method('publish');
157
158 $wallabagV1Import->setProducer($producer);
159
160 $res = $wallabagV1Import->setMarkAsRead(true)->import();
161
162 $this->assertTrue($res);
163 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 4], $wallabagV1Import->getSummary());
164 }
165
166 public function testImportWithRedis()
167 {
168 $wallabagV1Import = $this->getWallabagV1Import();
169 $wallabagV1Import->setFilepath(__DIR__.'/../fixtures/wallabag-v1.json');
170
171 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
172 ->disableOriginalConstructor()
173 ->getMock();
174
175 $entryRepo->expects($this->never())
176 ->method('findByUrlAndUserId');
177
178 $this->em
179 ->expects($this->never())
180 ->method('getRepository');
181
182 $entry = $this->getMockBuilder('Wallabag\CoreBundle\Entity\Entry')
183 ->disableOriginalConstructor()
184 ->getMock();
185
186 $this->contentProxy
187 ->expects($this->never())
188 ->method('updateEntry');
189
190 $factory = new RedisMockFactory();
191 $redisMock = $factory->getAdapter('Predis\Client', true);
192
193 $queue = new RedisQueue($redisMock, 'wallabag_v1');
194 $producer = new Producer($queue);
195
196 $wallabagV1Import->setProducer($producer);
197
198 $res = $wallabagV1Import->setMarkAsRead(true)->import();
199
200 $this->assertTrue($res);
201 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 4], $wallabagV1Import->getSummary());
202
203 $this->assertNotEmpty($redisMock->lpop('wallabag_v1'));
121 } 204 }
122 205
123 public function testImportBadFile() 206 public function testImportBadFile()
diff --git a/tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php b/tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php
index 4a45e0f0..12bd6bdd 100644
--- a/tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php
+++ b/tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php
@@ -5,8 +5,11 @@ namespace Tests\Wallabag\ImportBundle\Import;
5use Wallabag\ImportBundle\Import\WallabagV2Import; 5use Wallabag\ImportBundle\Import\WallabagV2Import;
6use Wallabag\UserBundle\Entity\User; 6use Wallabag\UserBundle\Entity\User;
7use Wallabag\CoreBundle\Entity\Entry; 7use Wallabag\CoreBundle\Entity\Entry;
8use Wallabag\ImportBundle\Redis\Producer;
8use Monolog\Logger; 9use Monolog\Logger;
9use Monolog\Handler\TestHandler; 10use Monolog\Handler\TestHandler;
11use Simpleue\Queue\RedisQueue;
12use M6Web\Component\RedisMock\RedisMockFactory;
10 13
11class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase 14class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase
12{ 15{
@@ -75,7 +78,7 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase
75 $res = $wallabagV2Import->import(); 78 $res = $wallabagV2Import->import();
76 79
77 $this->assertTrue($res); 80 $this->assertTrue($res);
78 $this->assertEquals(['skipped' => 22, 'imported' => 2], $wallabagV2Import->getSummary()); 81 $this->assertEquals(['skipped' => 22, 'imported' => 2, 'queued' => 0], $wallabagV2Import->getSummary());
79 } 82 }
80 83
81 public function testImportAndMarkAllAsRead() 84 public function testImportAndMarkAllAsRead()
@@ -113,7 +116,79 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase
113 116
114 $this->assertTrue($res); 117 $this->assertTrue($res);
115 118
116 $this->assertEquals(['skipped' => 0, 'imported' => 2], $wallabagV2Import->getSummary()); 119 $this->assertEquals(['skipped' => 0, 'imported' => 2, 'queued' => 0], $wallabagV2Import->getSummary());
120 }
121
122 public function testImportWithRabbit()
123 {
124 $wallabagV2Import = $this->getWallabagV2Import();
125 $wallabagV2Import->setFilepath(__DIR__.'/../fixtures/wallabag-v2.json');
126
127 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
128 ->disableOriginalConstructor()
129 ->getMock();
130
131 $entryRepo->expects($this->never())
132 ->method('findByUrlAndUserId');
133
134 $this->em
135 ->expects($this->never())
136 ->method('getRepository');
137
138 $this->contentProxy
139 ->expects($this->never())
140 ->method('updateEntry');
141
142 $producer = $this->getMockBuilder('OldSound\RabbitMqBundle\RabbitMq\Producer')
143 ->disableOriginalConstructor()
144 ->getMock();
145
146 $producer
147 ->expects($this->exactly(24))
148 ->method('publish');
149
150 $wallabagV2Import->setProducer($producer);
151
152 $res = $wallabagV2Import->setMarkAsRead(true)->import();
153
154 $this->assertTrue($res);
155 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 24], $wallabagV2Import->getSummary());
156 }
157
158 public function testImportWithRedis()
159 {
160 $wallabagV2Import = $this->getWallabagV2Import();
161 $wallabagV2Import->setFilepath(__DIR__.'/../fixtures/wallabag-v2.json');
162
163 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
164 ->disableOriginalConstructor()
165 ->getMock();
166
167 $entryRepo->expects($this->never())
168 ->method('findByUrlAndUserId');
169
170 $this->em
171 ->expects($this->never())
172 ->method('getRepository');
173
174 $this->contentProxy
175 ->expects($this->never())
176 ->method('updateEntry');
177
178 $factory = new RedisMockFactory();
179 $redisMock = $factory->getAdapter('Predis\Client', true);
180
181 $queue = new RedisQueue($redisMock, 'wallabag_v2');
182 $producer = new Producer($queue);
183
184 $wallabagV2Import->setProducer($producer);
185
186 $res = $wallabagV2Import->setMarkAsRead(true)->import();
187
188 $this->assertTrue($res);
189 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 24], $wallabagV2Import->getSummary());
190
191 $this->assertNotEmpty($redisMock->lpop('wallabag_v2'));
117 } 192 }
118 193
119 public function testImportBadFile() 194 public function testImportBadFile()
@@ -152,7 +227,7 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase
152 $res = $wallabagV2Import->import(); 227 $res = $wallabagV2Import->import();
153 228
154 $this->assertFalse($res); 229 $this->assertFalse($res);
155 $this->assertEquals(['skipped' => 0, 'imported' => 0], $wallabagV2Import->getSummary()); 230 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 0], $wallabagV2Import->getSummary());
156 } 231 }
157 232
158 public function testImportWithExceptionFromGraby() 233 public function testImportWithExceptionFromGraby()
@@ -181,6 +256,6 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase
181 $res = $wallabagV2Import->import(); 256 $res = $wallabagV2Import->import();
182 257
183 $this->assertTrue($res); 258 $this->assertTrue($res);
184 $this->assertEquals(['skipped' => 24, 'imported' => 0], $wallabagV2Import->getSummary()); 259 $this->assertEquals(['skipped' => 22, 'imported' => 2, 'queued' => 0], $wallabagV2Import->getSummary());
185 } 260 }
186} 261}
diff --git a/tests/Wallabag/ImportBundle/fixtures/readability-read.json b/tests/Wallabag/ImportBundle/fixtures/readability-read.json
new file mode 100644
index 00000000..c60767dc
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/fixtures/readability-read.json
@@ -0,0 +1,25 @@
1{
2 "bookmarks": [
3 {
4 "article__excerpt": "This is a guest post from Moritz Beller from the Delft University of Technology in The Netherlands. His team produced amazing research on several million Travis CI builds, creating invaluable&hellip;",
5 "favorite": false,
6 "date_archived": "2016-08-02T06:49:30",
7 "article__url": "https://blog.travis-ci.com/2016-07-28-what-we-learned-from-analyzing-2-million-travis-builds/",
8 "date_added": "2016-08-01T05:24:16",
9 "date_favorited": null,
10 "article__title": "Travis",
11 "archive": true
12 },
13 {
14 "article__excerpt": "The GraphQL Type system describes the capabilities of a GraphQL server and is used to determine if a query is valid. The type system also describes the input types of query variables to determine if&hellip;",
15 "favorite": false,
16 "date_archived": "2016-07-19T06:48:31",
17 "article__url": "https://facebook.github.io/graphql/",
18 "date_added": "2016-06-24T17:50:16",
19 "date_favorited": null,
20 "article__title": "GraphQL",
21 "archive": true
22 }
23 ],
24 "recommendations": []
25}
diff --git a/tests/Wallabag/ImportBundle/fixtures/readability.json b/tests/Wallabag/ImportBundle/fixtures/readability.json
new file mode 100644
index 00000000..32f6fa53
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/fixtures/readability.json
@@ -0,0 +1,176 @@
1{
2 "bookmarks": [
3 {
4 "article__excerpt": "When Twitter started it had so much promise to change the way we communicate. But now it has been ruined by the amount of garbage and hate we have to wade through. It&#x2019;s like that polluted&hellip;",
5 "favorite": false,
6 "date_archived": null,
7 "article__url": "https://venngage.com/blog/hashtags-are-worthless/",
8 "date_added": "2016-08-25T12:05:00",
9 "date_favorited": null,
10 "article__title": "We Looked At 167,943 Tweets & Found Out Hashtags Are Worthless",
11 "archive": false
12 },
13 {
14 "article__title": "Réfugiés: l'UE va créer 100 000 places d'accueil dans les Balkans",
15 "article__url": "http://www.liberation.fr/planete/2015/10/26/refugies-l-ue-va-creer-100-000-places-d-accueil-dans-les-balkans_1408867",
16 "archive": false,
17 "date_added": "2016-09-08T11:55:58+0200",
18 "favorite": false
19 },
20 {
21 "article__title": "No title found",
22 "article__url": "http://news.nationalgeographic.com/2016/02/160211-albatrosses-mothers-babies-animals-science/&sf20739758=1",
23 "archive": false,
24 "date_added": "2016-09-08T11:55:58+0200",
25 "favorite": true
26 },
27 {
28 "archive": 0,
29 "date_added": "2016-09-08T11:55:58+0200",
30 "favorite": 0,
31 "article__title": "Échecs",
32 "article__url": "https://fr.wikipedia.org/wiki/Échecs"
33 },
34 {
35 "archive": 0,
36 "date_added": "2016-09-08T11:55:58+0200",
37 "favorite": 0,
38 "article__title": "90% des dossiers médicaux des Coréens du sud vendus à des entreprises privées - ZATAZ",
39 "article__url": "http://www.zataz.com/90-des-dossiers-medicaux-des-coreens-du-sud-vendus-a-des-entreprises-privees/"
40 },
41 {
42 "archive": 0,
43 "date_added": "2016-09-08T11:55:58+0200",
44 "favorite": 0,
45 "article__title": "Mass Surveillance As Art",
46 "article__url": "https://www.nationaljournal.com/s/73311/mass-surveillance-art"
47 },
48 {
49 "archive": 0,
50 "date_added": "2016-09-08T11:55:58+0200",
51 "favorite": 0,
52 "article__title": "What David Cameron did to the pig, his party is now doing to the country",
53 "article__url": "http://www.newstatesman.com/2015/09/what-david-cameron-did-pig-his-party-now-doing-country"
54 },
55 {
56 "archive": 1,
57 "date_added": "2016-09-08T11:55:58+0200",
58 "favorite": 0,
59 "article__title": "CLICK HERE to support 2016 CES Winner, Revolutionary Auto-Tracking Robot",
60 "article__url": "https://www.indiegogo.com/projects/2016-ces-winner-revolutionary-auto-tracking-robot"
61 },
62 {
63 "archive": 0,
64 "date_added": "2016-09-08T11:55:58+0200",
65 "favorite": 1,
66 "article__title": "No title found",
67 "article__url": "http://carnetdevol.shost.ca/wordpress/aide-memoire-sur-les-commandes-associees-a-systemd/"
68 },
69 {
70 "archive": 1,
71 "date_added": "2016-09-08T11:55:58+0200",
72 "favorite": 0,
73 "article__title": "Présentation d'Arduino - Tuto Arduino - Le blog d'Eskimon",
74 "article__url": "http://eskimon.fr/73-arduino-101-presentation"
75 },
76 {
77 "archive": 1,
78 "date_added": "2016-09-08T11:55:58+0200",
79 "favorite": 0,
80 "article__title": "Lenovo ThinkPad X1 Carbon Ultrabook Review",
81 "article__url": "http://www.notebookcheck.net/Lenovo-ThinkPad-X1-Carbon-Ultrabook-Review.138033.0.html"
82 },
83 {
84 "archive": 0,
85 "date_added": "2016-09-08T11:55:58+0200",
86 "favorite": 0,
87 "article__title": "Visitons le Château de Landsberg !",
88 "article__url": "http://autour-du-mont-sainte-odile.overblog.com/2016/01/visitons-le-chateau-de-landsberg.html"
89 },
90 {
91 "archive": 1,
92 "date_added": "2016-09-08T11:55:58+0200",
93 "favorite": 0,
94 "article__title": "Contrer les stéréotypes par les livres : “C'est dès l'enfance qu'ils se construisent”",
95 "article__url": "https://www.actualitte.com/article/monde-edition/contrer-les-stereotypes-par-les-livres-c-est-des-l-enfance-qu-ils-se-construisent/64058"
96 },
97 {
98 "archive": 1,
99 "date_added": "2016-09-08T11:55:58+0200",
100 "favorite": 0,
101 "article__title": "[ROM][6.0.1][Layers][N5] TipsyOS official builds {UBER TCs}",
102 "article__url": "http://forum.xda-developers.com/google-nexus-5/development/rom-tipsyos-official-builds-uber-tcs-t3325989"
103 },
104 {
105 "archive": 0,
106 "date_added": "2016-09-08T11:55:58+0200",
107 "favorite": 0,
108 "article__title": "Top 15 Podcasts All Web Developers Should Follow - Envato Tuts+ Code Article",
109 "article__url": "http://code.tutsplus.com/articles/top-15-podcasts-all-web-developers-should-follow--net-14461"
110 },
111 {
112 "archive": 1,
113 "date_added": "2016-09-08T11:55:58+0200",
114 "favorite": 0,
115 "article__title": "University of Mississippi",
116 "article__url": "http://olemiss.edu"
117 },
118 {
119 "archive": 1,
120 "date_added": "2016-09-08T11:55:58+0200",
121 "favorite": 0,
122 "article__title": "FinnChristiansen.de Jetzt Dank Let’s Encrypt Per HTTPS Erreichbar",
123 "article__url": "https://www.finnchristiansen.de/2015/12/06/finnchristiansen-de-jetzt-dank-lets-encrypt-per-https-erreichbar/"
124 },
125 {
126 "archive": 1,
127 "date_added": "2016-09-08T11:55:58+0200",
128 "favorite": 0,
129 "article__title": "Le développeur et l'ingénierie logicielle",
130 "article__url": "http://wemucs.com/le-developpeur-et-lingenierie-logicielle/"
131 },
132 {
133 "archive": 1,
134 "date_added": "2016-09-08T11:55:58+0200",
135 "favorite": 0,
136 "article__title": "The Role of Methylation in Gene Expression",
137 "article__url": "http://www.nature.com/scitable/topicpage/the-role-of-methylation-in-gene-expression-1070"
138 },
139 {
140 "archive": 1,
141 "date_added": "2016-09-08T11:55:58+0200",
142 "favorite": 0,
143 "article__title": "E-Mail-Adresse kostenlos, FreeMail, De-Mail & Nachrichten",
144 "article__url": "http://web.de"
145 },
146 {
147 "archive": 1,
148 "date_added": "2016-09-08T11:55:58+0200",
149 "favorite": 0,
150 "article__title": "OpenSSH Server on Arch Linux | DominicM test",
151 "article__url": "http://dominicm.com/openssh-server-arch-linux/"
152 },
153 {
154 "archive": 1,
155 "date_added": "2016-09-08T11:55:58+0200",
156 "favorite": 0,
157 "article__title": "Site Moved | Site Help",
158 "article__url": "http://g1.com/help/sitemoved.asp"
159 },
160 {
161 "archive": 1,
162 "date_added": "2016-09-08T11:55:58+0200",
163 "favorite": 0,
164 "article__title": "#Maroc : le stylo anti-pédophiles EAGLE d’AMESYS est moins bien configuré que les faux-lowers Twitter du roi Mohammed VI",
165 "article__url": "https://reflets.info/maroc-le-stylo-anti-pedophiles-eagle-damesys-est-moins-bien-configure-que-les-faux-lowers-twitter-du-roi-mohammed-vi/"
166 },
167 {
168 "archive": 1,
169 "date_added": "2016-09-08T11:55:58+0200",
170 "favorite": 0,
171 "article__title": "Simple Cloud Infrastructure for Developers",
172 "article__url": "https://www.digitalocean.com/"
173 }
174 ],
175 "recommendations": []
176}
diff --git a/tests/Wallabag/ImportBundle/fixtures/wallabag-v2-read.json b/tests/Wallabag/ImportBundle/fixtures/wallabag-v2-read.json
index 3fa0bddf..d8609280 100644
--- a/tests/Wallabag/ImportBundle/fixtures/wallabag-v2-read.json
+++ b/tests/Wallabag/ImportBundle/fixtures/wallabag-v2-read.json
@@ -4,6 +4,8 @@
4 "title": "Wikimedia Foundation removes The Diary of Anne Frank due to copyright law requirements « Wikimedia blog", 4 "title": "Wikimedia Foundation removes The Diary of Anne Frank due to copyright law requirements « Wikimedia blog",
5 "url": "https://blog.wikimedia.org/2016/02/10/anne-frank-diary-removal/", 5 "url": "https://blog.wikimedia.org/2016/02/10/anne-frank-diary-removal/",
6 "is_archived": true, 6 "is_archived": true,
7 "created_at": "2016-09-08T11:55:58+0200",
8 "updated_at": "2016-09-08T11:57:16+0200",
7 "is_starred": false, 9 "is_starred": false,
8 "content": "<p><a href=\"https://commons.wikimedia.org/wiki/File:AnneFrankSchoolPhoto.jpg\" rel=\"attachment wp-att-45105\"><img class=\"alignnone size-full wp-image-45105\" src=\"https://wikimediablog.files.wordpress.com/2016/02/annefrankschoolphoto.jpg?w=316&amp;h=520\" alt=\"AnneFrankSchoolPhoto\" width=\"316\" height=\"520\"/></a><br/><small><i>Anne Frank in 1940. <a href=\"https://commons.wikimedia.org/wiki/File:AnneFrankSchoolPhoto.jpg\">Photo</a> by Collectie Anne Frank Stichting Amsterdam, public domain.</i></small></p>\n<p>Today, in an unfortunate example of the overreach of the United States’ current copyright law, the Wikimedia Foundation removed the Dutch-language text of <a href=\"https://en.wikipedia.org/wiki/The_Diary_of_a_Young_Girl\"><em>The Diary of a Young Girl</em></a>—more commonly known in English as the <em>Diary of Anne Frank—</em>from Wikisource.<sup id=\"one\"><a href=\"https://blog.wikimedia.org/2016/02/10/anne-frank-diary-removal/#cite1\">[1]</a></sup></p>\n<p>We took this action to comply with the United States’ <a href=\"https://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act\">Digital Millennium Copyright Act</a> (DMCA), as we believe the diary is still under US copyright protection under the law as it is currently written. Nevertheless, our removal serves as an excellent example of why the law should be changed to prevent repeated extensions of copyright terms, an issue that has plagued our communities <a href=\"https://en.wikipedia.org/wiki/Wikipedia:Wikipedia_Signpost/2014-02-26/News_and_notes\">for years</a>.</p>\n<h3>What prompted us to remove the diary?</h3>\n<p>The deletion was required because the Foundation is under the jurisdiction of US law and is therefore subject to the DMCA, specifically <a href=\"https://www.law.cornell.edu/uscode/text/17/512\" target=\"_blank\">title 17, chapter 5, section 512 of the United States Code</a>. As we <a href=\"https://meta.wikimedia.org/wiki/Legal/URAA_Statement\" target=\"_blank\">noted</a> in 2013, “The location of the servers, incorporation, and headquarters are just three of many factors that establish US jurisdiction … if infringing content is linked to or embedded in Wikimedia projects, then  the Foundation may still be subject to liability for such use—either as a direct or contributory infringer.</p>\n<p>Based on email discussions sent to the Wikimedia Foundation at legal[at]wikimedia.org, we determined that the Wikimedia Foundation had either “actual knowledge” (i in the statute quoted below) or what is commonly called “red flag knowledge” (ii in the statute quoted below) that the Anne Frank text was hosted on Wikisource and was under copyright. The statute section states that a service provider is only protected by the DMCA when it:</p>\n<p><strong>(i) </strong>does not have actual knowledge that the material or an activity using the material on the system or network is infringing;</p>\n<p><strong>(ii) </strong>in the absence of such actual knowledge, is not aware of facts or circumstances from which infringing activity is apparent; or</p>\n<p>(The rest applies when we get a proper DMCA takedown notice.)</p>\n<p>Of particular concern, the US’ <a href=\"https://en.wikipedia.org/wiki/United_States_Court_of_Appeals_for_the_Ninth_Circuit\">9th Circuit Court of Appeals</a> stated in their ruling for <a href=\"http://law.justia.com/cases/federal/appellate-courts/ca9/09-55902/09-55902-2013-03-14.html\"><em>UMG Recordings, Inc. v. Shelter Capital Partners LLC</em></a> that in circumstances where a hosting provider (like the Wikimedia Foundation) is informed by a third party (like an unrelated user) about infringing copyrighted content, that would likely constitute either actual or red flag knowledge under the DMCA.</p>\n<p>We believe, based on the detail and specificity contained in the emails, that we received that we had actual knowledge sufficient for the DMCA to require us to perform a takedown even in the absence of a demand letter.</p>\n<h3>How is the diary still copyrighted?</h3>\n<p>You may wonder why or how the Anne Frank text is copyrighted at all, as <a href=\"https://en.wikipedia.org/wiki/Anne_Frank\">Anne Frank</a> died in February 1945. With 70 years having passed since her death, the text may have passed into public domain in the Netherlands on January 1, 2016, where it was first published, although <a href=\"http://www.npr.org/sections/thetwo-way/2015/12/31/461606275/mein-kampf-enters-public-domain-arguably-anne-franks-diary-may-too\">there is still some dispute about this</a>.</p>\n<p>However, in the United States, the Anne Frank original text will be under copyright until 2042. This is the result of several factors coming together, and the English-language Wikipedia has actually covered this issue with a multi-part test on its <a href=\"https://en.wikipedia.org/wiki/Wikipedia:Non-U.S._copyrights\">non-US copyrights content guideline.</a></p>\n<p>In short, there are three major laws that together make the diary still copyrighted:</p>\n<ol><li>In general, the U.S. copyright for works published before 1978 is 95 years from date of publication. This came about because copyrights in the U.S. were originally for 28 years, with the ability to then extend that for a second 28 years (making a total of 56). Starting with the <a href=\"https://en.wikipedia.org/wiki/Copyright_Act_of_1976\">1976 Copyright Act</a> and extending to several more acts, the renewal became automatic and was extended. Today, the total term of works published before 1978 is 95 years from date of publication.</li>\n<li>Foreign works of countries that are treaty partners to the United States are covered as if they were US works.</li>\n<li>Even if a country was not a treaty partner under copyright law at the time of a publication, the <a href=\"https://en.wikipedia.org/wiki/Uruguay_Round_Agreements_Act\">1994 Uruguay Round Agreements Act</a> (URAA) restored copyright to works that:\n<ul><li>had been published in a foreign country</li>\n<li>were still under copyright in that country in 1996</li>\n<li>and would have had U.S. copyright but for the fact they were published abroad.</li>\n</ul></li>\n</ol>\n<p>Court challenges to the URAA have all failed, with the most notable (<a href=\"https://en.wikipedia.org/wiki/Golan_v._Holder\"><em>Golan v. Holder</em></a>) resulting in a Supreme Court ruling that upheld the URAA.</p>\n<p>What that means for Anne Frank’s diary is unfortunately simple: no matter how it wound up in the United States and regardless of what formal copyright notices they used, the US grants it copyright until the year 2042, or 95 years after its original publication in 1947.</p>\n<p>Under current copyright law, this remains true regardless of its copyright status anywhere else in the world and regardless of whether it may have been in the public domain in the United States in the past.</p>\n<p><a href=\"https://wikimediafoundation.org/wiki/User:Jrogers_(WMF)\"><em>Jacob Rogers</em></a><em>, Legal Counsel*<br/>Wikimedia Foundation</em></p>\n<p><em>*Special thanks to </em><a href=\"https://wikimediafoundation.org/wiki/User:AMangalick_(WMF)\"><em>Anisha Mangalick</em></a><em>, Legal Fellow, for her assistance in this matter.</em></p>\n<p><a href=\"https://blog.wikimedia.org/2016/02/10/anne-frank-diary-removal/#one\">[1]</a> The diary text was originally located at <a href=\"https://nl.wikisource.org/wiki/Het_Achterhuis_(Anne_Frank)\" rel=\"nofollow\">https://nl.wikisource.org/wiki/Het_Achterhuis_(Anne_Frank)</a>.</p>\n<p><em>This article was edited to clarify that it is not just the location of the Wikimedia Foundation’s servers that determine whether we fall in US jurisdiction.</em></p>\n\t\t\t\t\t\t\t\t\t\t\t", 10 "content": "<p><a href=\"https://commons.wikimedia.org/wiki/File:AnneFrankSchoolPhoto.jpg\" rel=\"attachment wp-att-45105\"><img class=\"alignnone size-full wp-image-45105\" src=\"https://wikimediablog.files.wordpress.com/2016/02/annefrankschoolphoto.jpg?w=316&amp;h=520\" alt=\"AnneFrankSchoolPhoto\" width=\"316\" height=\"520\"/></a><br/><small><i>Anne Frank in 1940. <a href=\"https://commons.wikimedia.org/wiki/File:AnneFrankSchoolPhoto.jpg\">Photo</a> by Collectie Anne Frank Stichting Amsterdam, public domain.</i></small></p>\n<p>Today, in an unfortunate example of the overreach of the United States’ current copyright law, the Wikimedia Foundation removed the Dutch-language text of <a href=\"https://en.wikipedia.org/wiki/The_Diary_of_a_Young_Girl\"><em>The Diary of a Young Girl</em></a>—more commonly known in English as the <em>Diary of Anne Frank—</em>from Wikisource.<sup id=\"one\"><a href=\"https://blog.wikimedia.org/2016/02/10/anne-frank-diary-removal/#cite1\">[1]</a></sup></p>\n<p>We took this action to comply with the United States’ <a href=\"https://en.wikipedia.org/wiki/Digital_Millennium_Copyright_Act\">Digital Millennium Copyright Act</a> (DMCA), as we believe the diary is still under US copyright protection under the law as it is currently written. Nevertheless, our removal serves as an excellent example of why the law should be changed to prevent repeated extensions of copyright terms, an issue that has plagued our communities <a href=\"https://en.wikipedia.org/wiki/Wikipedia:Wikipedia_Signpost/2014-02-26/News_and_notes\">for years</a>.</p>\n<h3>What prompted us to remove the diary?</h3>\n<p>The deletion was required because the Foundation is under the jurisdiction of US law and is therefore subject to the DMCA, specifically <a href=\"https://www.law.cornell.edu/uscode/text/17/512\" target=\"_blank\">title 17, chapter 5, section 512 of the United States Code</a>. As we <a href=\"https://meta.wikimedia.org/wiki/Legal/URAA_Statement\" target=\"_blank\">noted</a> in 2013, “The location of the servers, incorporation, and headquarters are just three of many factors that establish US jurisdiction … if infringing content is linked to or embedded in Wikimedia projects, then  the Foundation may still be subject to liability for such use—either as a direct or contributory infringer.</p>\n<p>Based on email discussions sent to the Wikimedia Foundation at legal[at]wikimedia.org, we determined that the Wikimedia Foundation had either “actual knowledge” (i in the statute quoted below) or what is commonly called “red flag knowledge” (ii in the statute quoted below) that the Anne Frank text was hosted on Wikisource and was under copyright. The statute section states that a service provider is only protected by the DMCA when it:</p>\n<p><strong>(i) </strong>does not have actual knowledge that the material or an activity using the material on the system or network is infringing;</p>\n<p><strong>(ii) </strong>in the absence of such actual knowledge, is not aware of facts or circumstances from which infringing activity is apparent; or</p>\n<p>(The rest applies when we get a proper DMCA takedown notice.)</p>\n<p>Of particular concern, the US’ <a href=\"https://en.wikipedia.org/wiki/United_States_Court_of_Appeals_for_the_Ninth_Circuit\">9th Circuit Court of Appeals</a> stated in their ruling for <a href=\"http://law.justia.com/cases/federal/appellate-courts/ca9/09-55902/09-55902-2013-03-14.html\"><em>UMG Recordings, Inc. v. Shelter Capital Partners LLC</em></a> that in circumstances where a hosting provider (like the Wikimedia Foundation) is informed by a third party (like an unrelated user) about infringing copyrighted content, that would likely constitute either actual or red flag knowledge under the DMCA.</p>\n<p>We believe, based on the detail and specificity contained in the emails, that we received that we had actual knowledge sufficient for the DMCA to require us to perform a takedown even in the absence of a demand letter.</p>\n<h3>How is the diary still copyrighted?</h3>\n<p>You may wonder why or how the Anne Frank text is copyrighted at all, as <a href=\"https://en.wikipedia.org/wiki/Anne_Frank\">Anne Frank</a> died in February 1945. With 70 years having passed since her death, the text may have passed into public domain in the Netherlands on January 1, 2016, where it was first published, although <a href=\"http://www.npr.org/sections/thetwo-way/2015/12/31/461606275/mein-kampf-enters-public-domain-arguably-anne-franks-diary-may-too\">there is still some dispute about this</a>.</p>\n<p>However, in the United States, the Anne Frank original text will be under copyright until 2042. This is the result of several factors coming together, and the English-language Wikipedia has actually covered this issue with a multi-part test on its <a href=\"https://en.wikipedia.org/wiki/Wikipedia:Non-U.S._copyrights\">non-US copyrights content guideline.</a></p>\n<p>In short, there are three major laws that together make the diary still copyrighted:</p>\n<ol><li>In general, the U.S. copyright for works published before 1978 is 95 years from date of publication. This came about because copyrights in the U.S. were originally for 28 years, with the ability to then extend that for a second 28 years (making a total of 56). Starting with the <a href=\"https://en.wikipedia.org/wiki/Copyright_Act_of_1976\">1976 Copyright Act</a> and extending to several more acts, the renewal became automatic and was extended. Today, the total term of works published before 1978 is 95 years from date of publication.</li>\n<li>Foreign works of countries that are treaty partners to the United States are covered as if they were US works.</li>\n<li>Even if a country was not a treaty partner under copyright law at the time of a publication, the <a href=\"https://en.wikipedia.org/wiki/Uruguay_Round_Agreements_Act\">1994 Uruguay Round Agreements Act</a> (URAA) restored copyright to works that:\n<ul><li>had been published in a foreign country</li>\n<li>were still under copyright in that country in 1996</li>\n<li>and would have had U.S. copyright but for the fact they were published abroad.</li>\n</ul></li>\n</ol>\n<p>Court challenges to the URAA have all failed, with the most notable (<a href=\"https://en.wikipedia.org/wiki/Golan_v._Holder\"><em>Golan v. Holder</em></a>) resulting in a Supreme Court ruling that upheld the URAA.</p>\n<p>What that means for Anne Frank’s diary is unfortunately simple: no matter how it wound up in the United States and regardless of what formal copyright notices they used, the US grants it copyright until the year 2042, or 95 years after its original publication in 1947.</p>\n<p>Under current copyright law, this remains true regardless of its copyright status anywhere else in the world and regardless of whether it may have been in the public domain in the United States in the past.</p>\n<p><a href=\"https://wikimediafoundation.org/wiki/User:Jrogers_(WMF)\"><em>Jacob Rogers</em></a><em>, Legal Counsel*<br/>Wikimedia Foundation</em></p>\n<p><em>*Special thanks to </em><a href=\"https://wikimediafoundation.org/wiki/User:AMangalick_(WMF)\"><em>Anisha Mangalick</em></a><em>, Legal Fellow, for her assistance in this matter.</em></p>\n<p><a href=\"https://blog.wikimedia.org/2016/02/10/anne-frank-diary-removal/#one\">[1]</a> The diary text was originally located at <a href=\"https://nl.wikisource.org/wiki/Het_Achterhuis_(Anne_Frank)\" rel=\"nofollow\">https://nl.wikisource.org/wiki/Het_Achterhuis_(Anne_Frank)</a>.</p>\n<p><em>This article was edited to clarify that it is not just the location of the Wikimedia Foundation’s servers that determine whether we fall in US jurisdiction.</em></p>\n\t\t\t\t\t\t\t\t\t\t\t",
9 "mimetype": "text/html", 11 "mimetype": "text/html",
@@ -17,6 +19,8 @@
17 "title": "Tails - Tails 2.0.1 is out", 19 "title": "Tails - Tails 2.0.1 is out",
18 "url": "https://tails.boum.org/news/version_2.0.1/index.en.html", 20 "url": "https://tails.boum.org/news/version_2.0.1/index.en.html",
19 "is_archived": false, 21 "is_archived": false,
22 "created_at": "2016-09-08T11:55:58+0200",
23 "updated_at": "2016-09-08T11:57:16+0200",
20 "is_starred": false, 24 "is_starred": false,
21 "content": "<div id=\"pagebody\" readability=\"39\">\n<p>This release fixes <a href=\"https://tails.boum.org/security/Numerous_security_holes_in_2.0/index.en.html\">numerous security issues</a>. All users must upgrade as soon as possible.</p>\n<div class=\"toc\">\n<ol><li class=\"L1\"><a href=\"https://tails.boum.org/news/version_2.0.1/index.en.html#index1h1\">Changes</a></li>\n<li class=\"L1\"><a href=\"https://tails.boum.org/news/version_2.0.1/index.en.html#index2h1\">Known issues</a></li>\n<li class=\"L1\"><a href=\"https://tails.boum.org/news/version_2.0.1/index.en.html#index3h1\">Download or upgrade</a></li>\n<li class=\"L1\"><a href=\"https://tails.boum.org/news/version_2.0.1/index.en.html#index4h1\">What's coming up?</a></li>\n</ol></div>\n<h2>New features</h2>\n<ul><li readability=\"11\">\n<p>Tails now uses the GNOME Shell desktop environment, in its Classic mode. GNOME Shell provides a modern, simple, and actively developed desktop environment. The Classic mode keeps the traditional Applications, Places menu, and windows list. Accessibility and non-Latin input sources are also better integrated.</p>\n<p>To find your way around, <a href=\"https://tails.boum.org/doc/first_steps/introduction_to_gnome_and_the_tails_desktop/index.en.html\">read our introduction to GNOME and the Tails desktop.</a></p>\n<table class=\"img\"><caption>The desktop and Applications menu</caption>\n<tr><td><img alt=\"Tails 2.0 desktop with applications menu unfolded\" class=\"img\" height=\"384\" src=\"https://tails.boum.org/inc/release_notes/2.0/applications_menu.png\" width=\"512\"/></td>\n</tr></table><table class=\"img\"><caption>The activities overview</caption>\n<tr><td><img alt=\"Tails 2.0 activities overview\" class=\"img\" height=\"384\" src=\"https://tails.boum.org/inc/release_notes/2.0/activities_overview.png\" width=\"512\"/></td>\n</tr></table></li>\n</ul><h2>Upgrades and changes</h2>\n<ul><li readability=\"2\">\n<p>Debian 8 upgrades most included software, for example:</p>\n<ul><li>Many core GNOME utilities from 3.4 to 3.14: Files, Disks, Videos, etc.</li>\n<li>LibreOffice from 3.5 to 4.3</li>\n<li>PiTiVi from 0.15 to 0.93</li>\n<li>Git from 1.7.10 to 2.1.4</li>\n<li>Poedit from 1.5.4 to 1.6.10</li>\n<li>Liferea from 1.8.6 to 1.10</li>\n</ul></li>\n<li readability=\"1\">\n<p>Update Tor Browser to 5.5 (based on Firefox 38.6.0 ESR):</p>\n<ul><li>Add Japanese support.</li>\n</ul></li>\n<li readability=\"2\">\n<p>Remove the Windows camouflage which is currently broken in GNOME Shell. We started working on <a href=\"https://labs.riseup.net/code/issues/10830\">adding it back</a> but <a href=\"https://tails.boum.org/news/windows_camouflage_jessie/index.en.html\">your help is needed</a>!</p>\n</li>\n<li readability=\"1\">\n<p>Change to <code>systemd</code> as init system and use it to:</p>\n<ul><li>Sandbox many services using Linux namespaces and make them harder to exploit.</li>\n<li>Make the launching of Tor and the memory wipe on shutdown more robust.</li>\n<li>Sanitize our code base by replacing many custom scripts.</li>\n</ul></li>\n<li readability=\"1\">\n<p>Update most firmware packages which might improve hardware compatibility.</p>\n</li>\n<li readability=\"1\">\n<p>Notify the user if Tails is running from a non-free virtualization software.</p>\n</li>\n<li readability=\"3\">\n<p>Remove Claws Mail, replaced by <a href=\"https://tails.boum.org/doc/anonymous_internet/icedove/index.en.html\">Icedove</a>, a rebranded version of Mozilla Thunderbird.</p>\n</li>\n</ul><h2>Fixed problems</h2>\n<ul><li readability=\"1\">\n<p>HiDPI displays are better supported. (<a href=\"https://labs.riseup.net/code/issues/8659\">#8659</a>)</p>\n</li>\n<li readability=\"3\">\n<p>Remove the option to open a download with an external application in Tor Browser as this is usually impossible due to the AppArmor confinement. (<a href=\"https://labs.riseup.net/code/issues/9285\">#9285</a>)</p>\n</li>\n<li readability=\"1\">\n<p>Close Vidalia before restarting Tor.</p>\n</li>\n<li readability=\"2\">\n<p>Allow Videos to access the DVD drive. (<a href=\"https://labs.riseup.net/code/issues/10455\">#10455</a>, <a href=\"https://labs.riseup.net/code/issues/9990\">#9990</a>)</p>\n</li>\n<li readability=\"1\">\n<p>Allow configuring printers without administration password. (<a href=\"https://labs.riseup.net/code/issues/8443\">#8443</a>)</p>\n</li>\n</ul>\n<p>See the current list of <a href=\"https://tails.boum.org/support/known_issues/index.en.html\">known issues</a>.</p>\n<p>Go to the <a href=\"https://tails.boum.org/download/index.en.html\">download</a> or <a href=\"https://tails.boum.org/doc/first_steps/upgrade/index.en.html\">upgrade</a> page.</p>\n<p>If your Tails does not boot after an automatic upgrade, please <a href=\"https://tails.boum.org/doc/first_steps/upgrade/index.en.html#manual\">upgrade your Tails manually</a>.</p>\n<p>The next Tails release is <a href=\"https://tails.boum.org/contribute/calendar/\">scheduled</a> for March 08.</p>\n<p>Have a look at our <a href=\"https://labs.riseup.net/code/projects/tails/roadmap\">roadmap</a> to see where we are heading to.</p>\n<p>We need your help and there are many ways to <a href=\"https://tails.boum.org/contribute/index.en.html\">contribute to Tails</a> (<a href=\"https://tails.boum.org/contribute/how/donate/index.en.html\">donating</a> is only one of them). Come <a href=\"https://tails.boum.org/contribute/talk/\">talk to us</a>!</p>\n</div><div id=\"footer\" class=\"pagefooter\" role=\"contentinfo\" readability=\"15\">\n<p>Tags: <a href=\"https://tails.boum.org/tags/announce/\" rel=\"tag\">announce</a></p>\n<p>Pages linking to this one: <a href=\"https://tails.boum.org/inc/stable_i386_release_notes/index.en.html\">inc/stable i386 release notes</a> <a href=\"https://tails.boum.org/security/Numerous_security_holes_in_2.0/index.en.html\">security/Numerous security holes in 2.0</a></p>\n<p>Last edited Sat 13 Feb 2016 02:23:58 PM CET </p>\n</div>", 25 "content": "<div id=\"pagebody\" readability=\"39\">\n<p>This release fixes <a href=\"https://tails.boum.org/security/Numerous_security_holes_in_2.0/index.en.html\">numerous security issues</a>. All users must upgrade as soon as possible.</p>\n<div class=\"toc\">\n<ol><li class=\"L1\"><a href=\"https://tails.boum.org/news/version_2.0.1/index.en.html#index1h1\">Changes</a></li>\n<li class=\"L1\"><a href=\"https://tails.boum.org/news/version_2.0.1/index.en.html#index2h1\">Known issues</a></li>\n<li class=\"L1\"><a href=\"https://tails.boum.org/news/version_2.0.1/index.en.html#index3h1\">Download or upgrade</a></li>\n<li class=\"L1\"><a href=\"https://tails.boum.org/news/version_2.0.1/index.en.html#index4h1\">What's coming up?</a></li>\n</ol></div>\n<h2>New features</h2>\n<ul><li readability=\"11\">\n<p>Tails now uses the GNOME Shell desktop environment, in its Classic mode. GNOME Shell provides a modern, simple, and actively developed desktop environment. The Classic mode keeps the traditional Applications, Places menu, and windows list. Accessibility and non-Latin input sources are also better integrated.</p>\n<p>To find your way around, <a href=\"https://tails.boum.org/doc/first_steps/introduction_to_gnome_and_the_tails_desktop/index.en.html\">read our introduction to GNOME and the Tails desktop.</a></p>\n<table class=\"img\"><caption>The desktop and Applications menu</caption>\n<tr><td><img alt=\"Tails 2.0 desktop with applications menu unfolded\" class=\"img\" height=\"384\" src=\"https://tails.boum.org/inc/release_notes/2.0/applications_menu.png\" width=\"512\"/></td>\n</tr></table><table class=\"img\"><caption>The activities overview</caption>\n<tr><td><img alt=\"Tails 2.0 activities overview\" class=\"img\" height=\"384\" src=\"https://tails.boum.org/inc/release_notes/2.0/activities_overview.png\" width=\"512\"/></td>\n</tr></table></li>\n</ul><h2>Upgrades and changes</h2>\n<ul><li readability=\"2\">\n<p>Debian 8 upgrades most included software, for example:</p>\n<ul><li>Many core GNOME utilities from 3.4 to 3.14: Files, Disks, Videos, etc.</li>\n<li>LibreOffice from 3.5 to 4.3</li>\n<li>PiTiVi from 0.15 to 0.93</li>\n<li>Git from 1.7.10 to 2.1.4</li>\n<li>Poedit from 1.5.4 to 1.6.10</li>\n<li>Liferea from 1.8.6 to 1.10</li>\n</ul></li>\n<li readability=\"1\">\n<p>Update Tor Browser to 5.5 (based on Firefox 38.6.0 ESR):</p>\n<ul><li>Add Japanese support.</li>\n</ul></li>\n<li readability=\"2\">\n<p>Remove the Windows camouflage which is currently broken in GNOME Shell. We started working on <a href=\"https://labs.riseup.net/code/issues/10830\">adding it back</a> but <a href=\"https://tails.boum.org/news/windows_camouflage_jessie/index.en.html\">your help is needed</a>!</p>\n</li>\n<li readability=\"1\">\n<p>Change to <code>systemd</code> as init system and use it to:</p>\n<ul><li>Sandbox many services using Linux namespaces and make them harder to exploit.</li>\n<li>Make the launching of Tor and the memory wipe on shutdown more robust.</li>\n<li>Sanitize our code base by replacing many custom scripts.</li>\n</ul></li>\n<li readability=\"1\">\n<p>Update most firmware packages which might improve hardware compatibility.</p>\n</li>\n<li readability=\"1\">\n<p>Notify the user if Tails is running from a non-free virtualization software.</p>\n</li>\n<li readability=\"3\">\n<p>Remove Claws Mail, replaced by <a href=\"https://tails.boum.org/doc/anonymous_internet/icedove/index.en.html\">Icedove</a>, a rebranded version of Mozilla Thunderbird.</p>\n</li>\n</ul><h2>Fixed problems</h2>\n<ul><li readability=\"1\">\n<p>HiDPI displays are better supported. (<a href=\"https://labs.riseup.net/code/issues/8659\">#8659</a>)</p>\n</li>\n<li readability=\"3\">\n<p>Remove the option to open a download with an external application in Tor Browser as this is usually impossible due to the AppArmor confinement. (<a href=\"https://labs.riseup.net/code/issues/9285\">#9285</a>)</p>\n</li>\n<li readability=\"1\">\n<p>Close Vidalia before restarting Tor.</p>\n</li>\n<li readability=\"2\">\n<p>Allow Videos to access the DVD drive. (<a href=\"https://labs.riseup.net/code/issues/10455\">#10455</a>, <a href=\"https://labs.riseup.net/code/issues/9990\">#9990</a>)</p>\n</li>\n<li readability=\"1\">\n<p>Allow configuring printers without administration password. (<a href=\"https://labs.riseup.net/code/issues/8443\">#8443</a>)</p>\n</li>\n</ul>\n<p>See the current list of <a href=\"https://tails.boum.org/support/known_issues/index.en.html\">known issues</a>.</p>\n<p>Go to the <a href=\"https://tails.boum.org/download/index.en.html\">download</a> or <a href=\"https://tails.boum.org/doc/first_steps/upgrade/index.en.html\">upgrade</a> page.</p>\n<p>If your Tails does not boot after an automatic upgrade, please <a href=\"https://tails.boum.org/doc/first_steps/upgrade/index.en.html#manual\">upgrade your Tails manually</a>.</p>\n<p>The next Tails release is <a href=\"https://tails.boum.org/contribute/calendar/\">scheduled</a> for March 08.</p>\n<p>Have a look at our <a href=\"https://labs.riseup.net/code/projects/tails/roadmap\">roadmap</a> to see where we are heading to.</p>\n<p>We need your help and there are many ways to <a href=\"https://tails.boum.org/contribute/index.en.html\">contribute to Tails</a> (<a href=\"https://tails.boum.org/contribute/how/donate/index.en.html\">donating</a> is only one of them). Come <a href=\"https://tails.boum.org/contribute/talk/\">talk to us</a>!</p>\n</div><div id=\"footer\" class=\"pagefooter\" role=\"contentinfo\" readability=\"15\">\n<p>Tags: <a href=\"https://tails.boum.org/tags/announce/\" rel=\"tag\">announce</a></p>\n<p>Pages linking to this one: <a href=\"https://tails.boum.org/inc/stable_i386_release_notes/index.en.html\">inc/stable i386 release notes</a> <a href=\"https://tails.boum.org/security/Numerous_security_holes_in_2.0/index.en.html\">security/Numerous security holes in 2.0</a></p>\n<p>Last edited Sat 13 Feb 2016 02:23:58 PM CET </p>\n</div>",
22 "mimetype": "text/html", 26 "mimetype": "text/html",
diff --git a/tests/Wallabag/ImportBundle/fixtures/wallabag-v2.json b/tests/Wallabag/ImportBundle/fixtures/wallabag-v2.json
index 37c59668..efa8faf2 100644
--- a/tests/Wallabag/ImportBundle/fixtures/wallabag-v2.json
+++ b/tests/Wallabag/ImportBundle/fixtures/wallabag-v2.json
@@ -4,6 +4,8 @@
4 "title": "Site d'information français d'actualités indépendant et participatif en ligne | Mediapart", 4 "title": "Site d'information français d'actualités indépendant et participatif en ligne | Mediapart",
5 "url": "https://www.mediapart.fr/", 5 "url": "https://www.mediapart.fr/",
6 "is_archived": false, 6 "is_archived": false,
7 "created_at": "2016-09-08T11:55:58+0200",
8 "updated_at": "2016-09-08T11:57:16+0200",
7 "is_starred": false, 9 "is_starred": false,
8 "content": "<div alt=\"li\">Édition <a href=\"https://blogs.mediapart.fr/edition/camedia-0\">CAMédia</a>\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/edition/camedia/article/180116/deux-nouvelles-editions-pour-debattre-dans-le-club-sur-la-laicite-et-sur-la-democratie\">Deux nouvelles éditions pour débattre dans le club sur la laïcité et sur la démocratie</a></h3>\n<p>18 janv. 2016 | Par </p>\n<p>CAMédia après un échange sur « l'éthique du débat » a lancé deux discussions , l'une sur le thème de la laïcité, l'autre ( encore en cours) sur celui de la démocratie. Nous sommes heureux de pouvoir signaler la création de deux nouvelles éditions participatives sur ces thèmes. Nous vous invitons à les lire et à participer à leurs débats.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/lucile-longre/blog/170116/de-limportance-de-rever-eloge-du-merveilleux\">De l'importance de rêver, éloge du merveilleux</a></h3>\n<p>17 janv. 2016 | Par </p>\n<p>Je parlerai ici des rêves comme moteur de vie, de ces rêves qui vous rattachent et vous font espérer à ce qu’il y a de plus humain dans l’homme, même au milieu de la plus noire des détresses.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/barbara-romagnan/blog/180116/fins-dune-toute-puissance\">Fin(s) d'une toute-puissance</a></h3>\n<p>18 janv. 2016 | Par </p>\n<p>En ce début d’année, je recommande la lecture du dernier ouvrage de Guillaume Duval, La France ne sera jamais plus une grande puissance ? Tant mieux !</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/jean-pierre-thibaudat/blog/170116/l-allier-departement-de-destruction-massive-du-tissu-culturel\">L’Allier, département de destruction massive du tissu culturel</a></h3>\n<p>18 janv. 2016 | Par </p>\n<p>Les temps sont durs pour les petites structures, les associations culturelles qui, de bourgades en villages, travaillent au cœur des régions. Leurs subventions sont souvent revues à la baisse. Le département de l’Allier les a carrément supprimées. Pour favoriser « l’événementiel ».</p>\n</div><div alt=\"li\">Édition <a href=\"https://blogs.mediapart.fr/edition/les-invites-de-mediapart\">Les invités de Mediapart</a>\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/edition/les-invites-de-mediapart/article/180116/la-democratie-deja-attaquee-par-la-cooperation-reglementaire-transatlantiqu\">La démocratie déjà attaquée par la coopération réglementaire transatlantique</a></h3>\n<p>18 janv. 2016 | Par </p>\n<p>Lora Verheecke et David Lundy travaillent pour Corporate Europe Observatory, une ONG basée à Bruxelles qui enquête sur le pouvoir des lobbies des grandes entreprises sur la politique de l’Union européenne. Ils révèlent que depuis 25 ans le projet de « coopération réglementaire » mené par l’Union européenne et les États-Unis a été dominé par les grandes entreprises. ET que le TTIP cherche à entériner ce projet.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/jacqueline-derens/blog/180116/2016-une-annee-test-pour-jacob-zuma-et-son-gouvernement\">2016, une année test pour Jacob Zuma et son gouvernement</a></h3>\n<p>18 janv. 2016 | Par </p>\n<p>Les turbulences de l’an passé ont toutes les chances de continuer à troubler le climat politique et social de l’Afrique du Sud en 2016. La situation exige des changements profonds dans la conduite des affaires du pays. Jacob Zuma tout en admettant la nécessité de ces changements, est-il l’homme de la situation ? Son gouvernement répondra-t-il aux attentes des citoyens sud-africains ?</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/marie-cosnay/blog/140116/un-mal-fou-janvier-2016\">Un mal fou (janvier 2016)</a></h3>\n<p>14 janv. 2016 | Par </p>\n<p>J’ai une fringale d’aventure, d’aventures à venir. J’ai la fringale de la fringale des aventures et soudain, rupture. Je n’y arrive plus, tout est bloqué, tout empêché. Faut dire que depuis un an environ, tout est devenu plus compliqué. Ecrire va de moins en moins de soi.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/jean-pierre-veran/blog/170116/redoublement-le-changement-bas-bruit\">Redoublement : le changement à bas bruit ?</a></h3>\n<p>17 janv. 2016 | Par </p>\n<p>S’il est une caractéristique de la forme scolaire française bien établie dans la culture des personnels, des élèves et des parents, c’est bien le redoublement, censé sanctionner des résultats insuffisants pour envisager le passage dans la classe supérieure. Or, en ce domaine, l’évolution est nette.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/michel-de-pracontal/blog/160116/samedi-sciences-196-des-chasseurs-de-mammouths-en-arctique-il-y-45-000-ans\">Samedi-sciences (196): des chasseurs de mammouths en Arctique il y a 45 000 ans</a></h3>\n<p>16 janv. 2016 | Par <a href=\"https://blogs.mediapart.fr/michel-de-pracontal\" class=\"journalist\">Michel de Pracontal</a></p>\n<p>Les restes d’un mammouth retrouvés en Arctique sibérien, datés de 45 000 ans, portent les traces de blessures infligées par des chasseurs humains. Les scientifiques pensaient jusqu’ici que notre espèce ne s’était pas aventurée dans cette région glaciale il y a plus de 30 000 ou 35 0000 ans. En réalité, des hommes ont réussi à survivre en Arctique au moins 10 000 ans plus tôt que l’on croyait.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/alain-zolty/blog/140116/de-la-democratie-du-citoyen-et-de-lethique\">De la démocratie, du citoyen et de l'éthique</a></h3>\n<p>14 janv. 2016 | Par </p>\n<p>Trois ouvrages sont parus au Seuil, qui font état de la nécessité d’intégrer le citoyen dans la gouvernance de la nation. Non pas à titre consultatif mais doté d’un pouvoir délibératif pour constituer une contre-force face aux clans politico-financiers qui dominent la vie publique.</p>\n</div>", 10 "content": "<div alt=\"li\">Édition <a href=\"https://blogs.mediapart.fr/edition/camedia-0\">CAMédia</a>\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/edition/camedia/article/180116/deux-nouvelles-editions-pour-debattre-dans-le-club-sur-la-laicite-et-sur-la-democratie\">Deux nouvelles éditions pour débattre dans le club sur la laïcité et sur la démocratie</a></h3>\n<p>18 janv. 2016 | Par </p>\n<p>CAMédia après un échange sur « l'éthique du débat » a lancé deux discussions , l'une sur le thème de la laïcité, l'autre ( encore en cours) sur celui de la démocratie. Nous sommes heureux de pouvoir signaler la création de deux nouvelles éditions participatives sur ces thèmes. Nous vous invitons à les lire et à participer à leurs débats.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/lucile-longre/blog/170116/de-limportance-de-rever-eloge-du-merveilleux\">De l'importance de rêver, éloge du merveilleux</a></h3>\n<p>17 janv. 2016 | Par </p>\n<p>Je parlerai ici des rêves comme moteur de vie, de ces rêves qui vous rattachent et vous font espérer à ce qu’il y a de plus humain dans l’homme, même au milieu de la plus noire des détresses.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/barbara-romagnan/blog/180116/fins-dune-toute-puissance\">Fin(s) d'une toute-puissance</a></h3>\n<p>18 janv. 2016 | Par </p>\n<p>En ce début d’année, je recommande la lecture du dernier ouvrage de Guillaume Duval, La France ne sera jamais plus une grande puissance ? Tant mieux !</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/jean-pierre-thibaudat/blog/170116/l-allier-departement-de-destruction-massive-du-tissu-culturel\">L’Allier, département de destruction massive du tissu culturel</a></h3>\n<p>18 janv. 2016 | Par </p>\n<p>Les temps sont durs pour les petites structures, les associations culturelles qui, de bourgades en villages, travaillent au cœur des régions. Leurs subventions sont souvent revues à la baisse. Le département de l’Allier les a carrément supprimées. Pour favoriser « l’événementiel ».</p>\n</div><div alt=\"li\">Édition <a href=\"https://blogs.mediapart.fr/edition/les-invites-de-mediapart\">Les invités de Mediapart</a>\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/edition/les-invites-de-mediapart/article/180116/la-democratie-deja-attaquee-par-la-cooperation-reglementaire-transatlantiqu\">La démocratie déjà attaquée par la coopération réglementaire transatlantique</a></h3>\n<p>18 janv. 2016 | Par </p>\n<p>Lora Verheecke et David Lundy travaillent pour Corporate Europe Observatory, une ONG basée à Bruxelles qui enquête sur le pouvoir des lobbies des grandes entreprises sur la politique de l’Union européenne. Ils révèlent que depuis 25 ans le projet de « coopération réglementaire » mené par l’Union européenne et les États-Unis a été dominé par les grandes entreprises. ET que le TTIP cherche à entériner ce projet.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/jacqueline-derens/blog/180116/2016-une-annee-test-pour-jacob-zuma-et-son-gouvernement\">2016, une année test pour Jacob Zuma et son gouvernement</a></h3>\n<p>18 janv. 2016 | Par </p>\n<p>Les turbulences de l’an passé ont toutes les chances de continuer à troubler le climat politique et social de l’Afrique du Sud en 2016. La situation exige des changements profonds dans la conduite des affaires du pays. Jacob Zuma tout en admettant la nécessité de ces changements, est-il l’homme de la situation ? Son gouvernement répondra-t-il aux attentes des citoyens sud-africains ?</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/marie-cosnay/blog/140116/un-mal-fou-janvier-2016\">Un mal fou (janvier 2016)</a></h3>\n<p>14 janv. 2016 | Par </p>\n<p>J’ai une fringale d’aventure, d’aventures à venir. J’ai la fringale de la fringale des aventures et soudain, rupture. Je n’y arrive plus, tout est bloqué, tout empêché. Faut dire que depuis un an environ, tout est devenu plus compliqué. Ecrire va de moins en moins de soi.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/jean-pierre-veran/blog/170116/redoublement-le-changement-bas-bruit\">Redoublement : le changement à bas bruit ?</a></h3>\n<p>17 janv. 2016 | Par </p>\n<p>S’il est une caractéristique de la forme scolaire française bien établie dans la culture des personnels, des élèves et des parents, c’est bien le redoublement, censé sanctionner des résultats insuffisants pour envisager le passage dans la classe supérieure. Or, en ce domaine, l’évolution est nette.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/michel-de-pracontal/blog/160116/samedi-sciences-196-des-chasseurs-de-mammouths-en-arctique-il-y-45-000-ans\">Samedi-sciences (196): des chasseurs de mammouths en Arctique il y a 45 000 ans</a></h3>\n<p>16 janv. 2016 | Par <a href=\"https://blogs.mediapart.fr/michel-de-pracontal\" class=\"journalist\">Michel de Pracontal</a></p>\n<p>Les restes d’un mammouth retrouvés en Arctique sibérien, datés de 45 000 ans, portent les traces de blessures infligées par des chasseurs humains. Les scientifiques pensaient jusqu’ici que notre espèce ne s’était pas aventurée dans cette région glaciale il y a plus de 30 000 ou 35 0000 ans. En réalité, des hommes ont réussi à survivre en Arctique au moins 10 000 ans plus tôt que l’on croyait.</p>\n</div><div alt=\"li\">\n<h3 class=\"title\"><a href=\"https://blogs.mediapart.fr/alain-zolty/blog/140116/de-la-democratie-du-citoyen-et-de-lethique\">De la démocratie, du citoyen et de l'éthique</a></h3>\n<p>14 janv. 2016 | Par </p>\n<p>Trois ouvrages sont parus au Seuil, qui font état de la nécessité d’intégrer le citoyen dans la gouvernance de la nation. Non pas à titre consultatif mais doté d’un pouvoir délibératif pour constituer une contre-force face aux clans politico-financiers qui dominent la vie publique.</p>\n</div>",
9 "mimetype": "text/html", 11 "mimetype": "text/html",
@@ -21,6 +23,8 @@
21 "title": "Réfugiés: l'UE va créer 100 000 places d'accueil dans les Balkans", 23 "title": "Réfugiés: l'UE va créer 100 000 places d'accueil dans les Balkans",
22 "url": "http://www.liberation.fr/planete/2015/10/26/refugies-l-ue-va-creer-100-000-places-d-accueil-dans-les-balkans_1408867", 24 "url": "http://www.liberation.fr/planete/2015/10/26/refugies-l-ue-va-creer-100-000-places-d-accueil-dans-les-balkans_1408867",
23 "is_archived": false, 25 "is_archived": false,
26 "created_at": "2016-09-08T11:55:58+0200",
27 "updated_at": "2016-09-08T11:57:16+0200",
24 "is_starred": false, 28 "is_starred": false,
25 "content": "<p>Pour un sommet sur les réfugiés qui devait se concentrer sur des <em>«mesures opérationnelles immédiates»</em> dans les Balkans, la réunion, dimanche à Bruxelles, de 11 chefs d’Etat et de gouvernement, dont 8 Européens, a été agitée. Dès leur arrivée, Viktor Orbán (Hongrie) et Aléxis Tsípras (Grèce) se sont jeté des anathèmes. Le Premier ministre grec a dénoncé l’attitude <em>«not in my backyard»</em> (pas de ça chez moi) de certains Etats européens, alors que son pays est montré du doigt par d’autres dirigeants, dont Orbán : ils reprochent à la Grèce de ne pas suffisamment contrôler ses frontières avec la Turquie et ne pas montrer assez de zèle dans l’enregistrement des demandeurs d’asile.</p>\n<p>Le sommet, convoqué par la Commission européenne, sur suggestion de l’Allemagne, aura au moins permis à ces 11 Etats – Autriche, Bulgarie, Croatie, Allemagne, Grèce, Hongrie, Roumanie, Slovénie côté européen, et 3 pays «non UE», Albanie, Macédoine et Serbie – de discuter ensemble.</p>\n<h3>400 policiers européens en Slovénie</h3>\n<p>L’objectif, rappelé par Angela Merkel, était de trouver une <em>«réponse coordonnée»</em> à la crise des réfugiés. Quelques mesures ont été annoncées : 100 000 places d’accueil seront créées, dont 50 000 en Grèce, et le reste le long de la route des Balkans. 400 officiers de police de pays européens partiront en Slovénie, actuellement submergée, pour aider au contrôle des frontières. Frontex, l’agence européenne de surveillance des frontières, s’impliquera aux frontières gréco-macédonienne et gréco-albanaise pour des contrôles et identifications.</p>\n<p>Ce sommet est intervenu dans un contexte de fortes tensions, marqué par des fermetures de frontières bloquant les réfugiés dans des zones tampon. Ces obstacles ont été partiellement levés ces derniers jours, les autorités tentant d’organiser un «corridor» informel vers l’Allemagne, qui pourtant durcit sa politique d’accueil et souhaite désormais ralentir le flux. Mais la situation des réfugiés est catastrophique. L’ONG Human Rights Watch craint que des réfugiés ne meurent dans les Balkans. Des groupes de centaines, voire de milliers de personnes, bloqués près des postes-frontières, se retrouvent dans des conditions humanitaires intenables.</p>\n<p>Depuis mi-septembre, 250 000 personnes ont traversé les Balkans. En une semaine, la Slovénie a vu 60 000 réfugiés fouler le sol de son territoire. Dimanche, 15 000 personnes ont transité en Slovénie.</p>\n<h3>Des zones tampon</h3>\n<p>L’enjeu principal du sommet, aux yeux de nombreux Etats de l’Union européenne, était aussi que les pays des Balkans <em>«prennent leur part»</em> face à la crise : qu’ils accueillent et enregistrent davantage de réfugiés. Ces Etats craignent que l’Autriche ou l’Allemagne ne ferment leurs frontières et fassent de leurs pays des <em>«zones tampon»</em>, comme s’en inquiétait Boyko Borissov, Premier ministre bulgare.</p>\n<p><em>« Aujourd’hui, plusieurs Etats du nord de l’Europe veulent que l’on enregistre les migrants puis que l’on détermine leur éligibilité au statut de réfugié,</em> explique Marc Pierini, du think tank Carnegie Europe. <em>La difficulté, c’est que les gens sont en mouvement. Pour le faire, il faut se poser quelque part. La crainte des pays intermédiaires, donc ceux des Balkans, est qu’on enregistre ces personnes sur leur territoire et qu’ils soient contraints de rester sur leur sol. Donc les pays des Balkans ne sont pas désireux d’accueillir ces réfugiés et ces derniers veulent avancer.»</em></p>\n<p>Le sommet a élaboré quelques principes. L’idée générale est de rendre effective la «logique de hotspot» : un enregistrement des demandeurs d’asile à leur point d’entrée dans l’Union européenne, suivi de l’expulsion de ceux qui ne correspondraient pas aux critères de la Convention de Genève, et la répartition des autres, via le mécanisme de relocalisation.</p>\n<p>Dans ce cadre, l’enregistrement des demandeurs d’asile est un élément clé. <em>«Pas d’enregistrement, pas de droit»</em>, a prévenu le président de la Commission européenne, Jean-Claude Juncker, dimanche soir. Les Etats ont tenu à rappeler que les migrants qui refusent de demander l’asile à la frontière peuvent se voir refuser l’entrée dans un pays.</p>\n<p>Et les Etats <em>«décourageront les mouvements de réfugiés»</em> de frontière en frontière. La politique consistant à laisser passer les migrants vers un autre pays est officiellement jugée <em>«inacceptable»</em>.</p>\n<h3>Se jeter dans la gueule du loup</h3>\n<p>Voilà pour la théorie. En pratique, la relocalisation ne devrait concerner que 160 000 réfugiés en deux ans, alors que près de 700 000 personnes sont arrivées en Europe depuis le début de l’année. De plus, les Etats ne jouent pas le jeu. La semaine passée, seules 854 places de relocalisation avaient été proposées.</p>\n<p>Dans ce contexte, il est probable que les Etats des Balkans ne s’impliqueront pas outre mesure dans les solutions proposées, craignant de devoir «garder» les réfugiés alors que l’Union européenne tarde à mettre en œuvre leur répartition.</p>\n<p>Quant aux réfugiés, ils préfèrent traverser les frontières par eux-mêmes, plutôt que de se jeter dans ces «hotspots», considérés comme la gueule du loup.</p>\n<a itemprop=\"name\" href=\"http://www.liberation.fr/auteur/15743-cedric-vallet\">Cédric Vallet</a>", 29 "content": "<p>Pour un sommet sur les réfugiés qui devait se concentrer sur des <em>«mesures opérationnelles immédiates»</em> dans les Balkans, la réunion, dimanche à Bruxelles, de 11 chefs d’Etat et de gouvernement, dont 8 Européens, a été agitée. Dès leur arrivée, Viktor Orbán (Hongrie) et Aléxis Tsípras (Grèce) se sont jeté des anathèmes. Le Premier ministre grec a dénoncé l’attitude <em>«not in my backyard»</em> (pas de ça chez moi) de certains Etats européens, alors que son pays est montré du doigt par d’autres dirigeants, dont Orbán : ils reprochent à la Grèce de ne pas suffisamment contrôler ses frontières avec la Turquie et ne pas montrer assez de zèle dans l’enregistrement des demandeurs d’asile.</p>\n<p>Le sommet, convoqué par la Commission européenne, sur suggestion de l’Allemagne, aura au moins permis à ces 11 Etats – Autriche, Bulgarie, Croatie, Allemagne, Grèce, Hongrie, Roumanie, Slovénie côté européen, et 3 pays «non UE», Albanie, Macédoine et Serbie – de discuter ensemble.</p>\n<h3>400 policiers européens en Slovénie</h3>\n<p>L’objectif, rappelé par Angela Merkel, était de trouver une <em>«réponse coordonnée»</em> à la crise des réfugiés. Quelques mesures ont été annoncées : 100 000 places d’accueil seront créées, dont 50 000 en Grèce, et le reste le long de la route des Balkans. 400 officiers de police de pays européens partiront en Slovénie, actuellement submergée, pour aider au contrôle des frontières. Frontex, l’agence européenne de surveillance des frontières, s’impliquera aux frontières gréco-macédonienne et gréco-albanaise pour des contrôles et identifications.</p>\n<p>Ce sommet est intervenu dans un contexte de fortes tensions, marqué par des fermetures de frontières bloquant les réfugiés dans des zones tampon. Ces obstacles ont été partiellement levés ces derniers jours, les autorités tentant d’organiser un «corridor» informel vers l’Allemagne, qui pourtant durcit sa politique d’accueil et souhaite désormais ralentir le flux. Mais la situation des réfugiés est catastrophique. L’ONG Human Rights Watch craint que des réfugiés ne meurent dans les Balkans. Des groupes de centaines, voire de milliers de personnes, bloqués près des postes-frontières, se retrouvent dans des conditions humanitaires intenables.</p>\n<p>Depuis mi-septembre, 250 000 personnes ont traversé les Balkans. En une semaine, la Slovénie a vu 60 000 réfugiés fouler le sol de son territoire. Dimanche, 15 000 personnes ont transité en Slovénie.</p>\n<h3>Des zones tampon</h3>\n<p>L’enjeu principal du sommet, aux yeux de nombreux Etats de l’Union européenne, était aussi que les pays des Balkans <em>«prennent leur part»</em> face à la crise : qu’ils accueillent et enregistrent davantage de réfugiés. Ces Etats craignent que l’Autriche ou l’Allemagne ne ferment leurs frontières et fassent de leurs pays des <em>«zones tampon»</em>, comme s’en inquiétait Boyko Borissov, Premier ministre bulgare.</p>\n<p><em>« Aujourd’hui, plusieurs Etats du nord de l’Europe veulent que l’on enregistre les migrants puis que l’on détermine leur éligibilité au statut de réfugié,</em> explique Marc Pierini, du think tank Carnegie Europe. <em>La difficulté, c’est que les gens sont en mouvement. Pour le faire, il faut se poser quelque part. La crainte des pays intermédiaires, donc ceux des Balkans, est qu’on enregistre ces personnes sur leur territoire et qu’ils soient contraints de rester sur leur sol. Donc les pays des Balkans ne sont pas désireux d’accueillir ces réfugiés et ces derniers veulent avancer.»</em></p>\n<p>Le sommet a élaboré quelques principes. L’idée générale est de rendre effective la «logique de hotspot» : un enregistrement des demandeurs d’asile à leur point d’entrée dans l’Union européenne, suivi de l’expulsion de ceux qui ne correspondraient pas aux critères de la Convention de Genève, et la répartition des autres, via le mécanisme de relocalisation.</p>\n<p>Dans ce cadre, l’enregistrement des demandeurs d’asile est un élément clé. <em>«Pas d’enregistrement, pas de droit»</em>, a prévenu le président de la Commission européenne, Jean-Claude Juncker, dimanche soir. Les Etats ont tenu à rappeler que les migrants qui refusent de demander l’asile à la frontière peuvent se voir refuser l’entrée dans un pays.</p>\n<p>Et les Etats <em>«décourageront les mouvements de réfugiés»</em> de frontière en frontière. La politique consistant à laisser passer les migrants vers un autre pays est officiellement jugée <em>«inacceptable»</em>.</p>\n<h3>Se jeter dans la gueule du loup</h3>\n<p>Voilà pour la théorie. En pratique, la relocalisation ne devrait concerner que 160 000 réfugiés en deux ans, alors que près de 700 000 personnes sont arrivées en Europe depuis le début de l’année. De plus, les Etats ne jouent pas le jeu. La semaine passée, seules 854 places de relocalisation avaient été proposées.</p>\n<p>Dans ce contexte, il est probable que les Etats des Balkans ne s’impliqueront pas outre mesure dans les solutions proposées, craignant de devoir «garder» les réfugiés alors que l’Union européenne tarde à mettre en œuvre leur répartition.</p>\n<p>Quant aux réfugiés, ils préfèrent traverser les frontières par eux-mêmes, plutôt que de se jeter dans ces «hotspots», considérés comme la gueule du loup.</p>\n<a itemprop=\"name\" href=\"http://www.liberation.fr/auteur/15743-cedric-vallet\">Cédric Vallet</a>",
26 "mimetype": "", 30 "mimetype": "",
@@ -34,6 +38,8 @@
34 "title": "No title found", 38 "title": "No title found",
35 "url": "http://news.nationalgeographic.com/2016/02/160211-albatrosses-mothers-babies-animals-science/&sf20739758=1", 39 "url": "http://news.nationalgeographic.com/2016/02/160211-albatrosses-mothers-babies-animals-science/&sf20739758=1",
36 "is_archived": false, 40 "is_archived": false,
41 "created_at": "2016-09-08T11:55:58+0200",
42 "updated_at": "2016-09-08T11:57:16+0200",
37 "is_starred": true, 43 "is_starred": true,
38 "content": "Oh, what a shame, no content", 44 "content": "Oh, what a shame, no content",
39 "mimetype": "", 45 "mimetype": "",
@@ -44,6 +50,8 @@
44 }, 50 },
45 { 51 {
46 "is_archived": 0, 52 "is_archived": 0,
53 "created_at": "2016-09-08T11:55:58+0200",
54 "updated_at": "2016-09-08T11:57:16+0200",
47 "is_starred": 0, 55 "is_starred": 0,
48 "id": 612, 56 "id": 612,
49 "title": "Échecs", 57 "title": "Échecs",
@@ -58,6 +66,8 @@
58 }, 66 },
59 { 67 {
60 "is_archived": 0, 68 "is_archived": 0,
69 "created_at": "2016-09-08T11:55:58+0200",
70 "updated_at": "2016-09-08T11:57:16+0200",
61 "is_starred": 0, 71 "is_starred": 0,
62 "id": 608, 72 "id": 608,
63 "title": "90% des dossiers médicaux des Coréens du sud vendus à des entreprises privées - ZATAZ", 73 "title": "90% des dossiers médicaux des Coréens du sud vendus à des entreprises privées - ZATAZ",
@@ -73,6 +83,8 @@
73 }, 83 },
74 { 84 {
75 "is_archived": 0, 85 "is_archived": 0,
86 "created_at": "2016-09-08T11:55:58+0200",
87 "updated_at": "2016-09-08T11:57:16+0200",
76 "is_starred": 0, 88 "is_starred": 0,
77 "id": 606, 89 "id": 606,
78 "title": "Mass Surveillance As Art", 90 "title": "Mass Surveillance As Art",
@@ -87,6 +99,8 @@
87 }, 99 },
88 { 100 {
89 "is_archived": 0, 101 "is_archived": 0,
102 "created_at": "2016-09-08T11:55:58+0200",
103 "updated_at": "2016-09-08T11:57:16+0200",
90 "is_starred": 0, 104 "is_starred": 0,
91 "id": 605, 105 "id": 605,
92 "title": "What David Cameron did to the pig, his party is now doing to the country", 106 "title": "What David Cameron did to the pig, his party is now doing to the country",
@@ -102,6 +116,8 @@
102 }, 116 },
103 { 117 {
104 "is_archived": 1, 118 "is_archived": 1,
119 "created_at": "2016-09-08T11:55:58+0200",
120 "updated_at": "2016-09-08T11:57:16+0200",
105 "is_starred": 0, 121 "is_starred": 0,
106 "id": 604, 122 "id": 604,
107 "title": "CLICK HERE to support 2016 CES Winner, Revolutionary Auto-Tracking Robot", 123 "title": "CLICK HERE to support 2016 CES Winner, Revolutionary Auto-Tracking Robot",
@@ -116,6 +132,8 @@
116 }, 132 },
117 { 133 {
118 "is_archived": 0, 134 "is_archived": 0,
135 "created_at": "2016-09-08T11:55:58+0200",
136 "updated_at": "2016-09-08T11:57:16+0200",
119 "is_starred": 1, 137 "is_starred": 1,
120 "id": 603, 138 "id": 603,
121 "title": "No title found", 139 "title": "No title found",
@@ -129,6 +147,8 @@
129 }, 147 },
130 { 148 {
131 "is_archived": 1, 149 "is_archived": 1,
150 "created_at": "2016-09-08T11:55:58+0200",
151 "updated_at": "2016-09-08T11:57:16+0200",
132 "is_starred": 0, 152 "is_starred": 0,
133 "id": 602, 153 "id": 602,
134 "title": "Présentation d'Arduino - Tuto Arduino - Le blog d'Eskimon", 154 "title": "Présentation d'Arduino - Tuto Arduino - Le blog d'Eskimon",
@@ -144,6 +164,8 @@
144 }, 164 },
145 { 165 {
146 "is_archived": 1, 166 "is_archived": 1,
167 "created_at": "2016-09-08T11:55:58+0200",
168 "updated_at": "2016-09-08T11:57:16+0200",
147 "is_starred": 0, 169 "is_starred": 0,
148 "id": 543, 170 "id": 543,
149 "title": "Lenovo ThinkPad X1 Carbon Ultrabook Review", 171 "title": "Lenovo ThinkPad X1 Carbon Ultrabook Review",
@@ -159,6 +181,8 @@
159 }, 181 },
160 { 182 {
161 "is_archived": 0, 183 "is_archived": 0,
184 "created_at": "2016-09-08T11:55:58+0200",
185 "updated_at": "2016-09-08T11:57:16+0200",
162 "is_starred": 0, 186 "is_starred": 0,
163 "id": 541, 187 "id": 541,
164 "title": "Visitons le Château de Landsberg !", 188 "title": "Visitons le Château de Landsberg !",
@@ -174,6 +198,8 @@
174 }, 198 },
175 { 199 {
176 "is_archived": 1, 200 "is_archived": 1,
201 "created_at": "2016-09-08T11:55:58+0200",
202 "updated_at": "2016-09-08T11:57:16+0200",
177 "is_starred": 0, 203 "is_starred": 0,
178 "id": 454, 204 "id": 454,
179 "title": "Contrer les stéréotypes par les livres : “C'est dès l'enfance qu'ils se construisent”", 205 "title": "Contrer les stéréotypes par les livres : “C'est dès l'enfance qu'ils se construisent”",
@@ -189,6 +215,8 @@
189 }, 215 },
190 { 216 {
191 "is_archived": 1, 217 "is_archived": 1,
218 "created_at": "2016-09-08T11:55:58+0200",
219 "updated_at": "2016-09-08T11:57:16+0200",
192 "is_starred": 0, 220 "is_starred": 0,
193 "id": 99, 221 "id": 99,
194 "title": "[ROM][6.0.1][Layers][N5] TipsyOS official builds {UBER TCs}", 222 "title": "[ROM][6.0.1][Layers][N5] TipsyOS official builds {UBER TCs}",
@@ -204,6 +232,8 @@
204 }, 232 },
205 { 233 {
206 "is_archived": 0, 234 "is_archived": 0,
235 "created_at": "2016-09-08T11:55:58+0200",
236 "updated_at": "2016-09-08T11:57:16+0200",
207 "is_starred": 0, 237 "is_starred": 0,
208 "id": 98, 238 "id": 98,
209 "title": "Top 15 Podcasts All Web Developers Should Follow - Envato Tuts+ Code Article", 239 "title": "Top 15 Podcasts All Web Developers Should Follow - Envato Tuts+ Code Article",
@@ -218,6 +248,8 @@
218 }, 248 },
219 { 249 {
220 "is_archived": 1, 250 "is_archived": 1,
251 "created_at": "2016-09-08T11:55:58+0200",
252 "updated_at": "2016-09-08T11:57:16+0200",
221 "is_starred": 0, 253 "is_starred": 0,
222 "id": 97, 254 "id": 97,
223 "title": "University of Mississippi", 255 "title": "University of Mississippi",
@@ -232,6 +264,8 @@
232 }, 264 },
233 { 265 {
234 "is_archived": 1, 266 "is_archived": 1,
267 "created_at": "2016-09-08T11:55:58+0200",
268 "updated_at": "2016-09-08T11:57:16+0200",
235 "is_starred": 0, 269 "is_starred": 0,
236 "id": 96, 270 "id": 96,
237 "title": "FinnChristiansen.de Jetzt Dank Let’s Encrypt Per HTTPS Erreichbar", 271 "title": "FinnChristiansen.de Jetzt Dank Let’s Encrypt Per HTTPS Erreichbar",
@@ -246,6 +280,8 @@
246 }, 280 },
247 { 281 {
248 "is_archived": 1, 282 "is_archived": 1,
283 "created_at": "2016-09-08T11:55:58+0200",
284 "updated_at": "2016-09-08T11:57:16+0200",
249 "is_starred": 0, 285 "is_starred": 0,
250 "id": 82, 286 "id": 82,
251 "title": "Le développeur et l'ingénierie logicielle", 287 "title": "Le développeur et l'ingénierie logicielle",
@@ -259,6 +295,8 @@
259 }, 295 },
260 { 296 {
261 "is_archived": 1, 297 "is_archived": 1,
298 "created_at": "2016-09-08T11:55:58+0200",
299 "updated_at": "2016-09-08T11:57:16+0200",
262 "is_starred": 0, 300 "is_starred": 0,
263 "id": 78, 301 "id": 78,
264 "title": "The Role of Methylation in Gene Expression", 302 "title": "The Role of Methylation in Gene Expression",
@@ -273,6 +311,8 @@
273 }, 311 },
274 { 312 {
275 "is_archived": 1, 313 "is_archived": 1,
314 "created_at": "2016-09-08T11:55:58+0200",
315 "updated_at": "2016-09-08T11:57:16+0200",
276 "is_starred": 0, 316 "is_starred": 0,
277 "id": 53, 317 "id": 53,
278 "title": "E-Mail-Adresse kostenlos, FreeMail, De-Mail & Nachrichten", 318 "title": "E-Mail-Adresse kostenlos, FreeMail, De-Mail & Nachrichten",
@@ -287,6 +327,8 @@
287 }, 327 },
288 { 328 {
289 "is_archived": 1, 329 "is_archived": 1,
330 "created_at": "2016-09-08T11:55:58+0200",
331 "updated_at": "2016-09-08T11:57:16+0200",
290 "is_starred": 0, 332 "is_starred": 0,
291 "id": 48, 333 "id": 48,
292 "title": "OpenSSH Server on Arch Linux | DominicM test", 334 "title": "OpenSSH Server on Arch Linux | DominicM test",
@@ -302,6 +344,8 @@
302 }, 344 },
303 { 345 {
304 "is_archived": 1, 346 "is_archived": 1,
347 "created_at": "2016-09-08T11:55:58+0200",
348 "updated_at": "2016-09-08T11:57:16+0200",
305 "is_starred": 0, 349 "is_starred": 0,
306 "id": 39, 350 "id": 39,
307 "title": "Site Moved | Site Help", 351 "title": "Site Moved | Site Help",
@@ -316,6 +360,8 @@
316 }, 360 },
317 { 361 {
318 "is_archived": 1, 362 "is_archived": 1,
363 "created_at": "2016-09-08T11:55:58+0200",
364 "updated_at": "2016-09-08T11:57:16+0200",
319 "is_starred": 0, 365 "is_starred": 0,
320 "id": 38, 366 "id": 38,
321 "title": "#Maroc : le stylo anti-pédophiles EAGLE d’AMESYS est moins bien configuré que les faux-lowers Twitter du roi Mohammed VI", 367 "title": "#Maroc : le stylo anti-pédophiles EAGLE d’AMESYS est moins bien configuré que les faux-lowers Twitter du roi Mohammed VI",
@@ -331,6 +377,8 @@
331 }, 377 },
332 { 378 {
333 "is_archived": 1, 379 "is_archived": 1,
380 "created_at": "2016-09-08T11:55:58+0200",
381 "updated_at": "2016-09-08T11:57:16+0200",
334 "is_starred": 0, 382 "is_starred": 0,
335 "id": 3, 383 "id": 3,
336 "title": "Simple Cloud Infrastructure for Developers", 384 "title": "Simple Cloud Infrastructure for Developers",