aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/Wallabag/ImportBundle
diff options
context:
space:
mode:
authorPaulino Michelazzo <paulino@michelazzo.com.br>2016-10-18 22:48:23 +0200
committerPaulino Michelazzo <paulino@michelazzo.com.br>2016-10-18 22:48:23 +0200
commit99731f0bb1f6fd2815eeb9af504ce86df927657b (patch)
treeb080efc608d2bbd52b77a4a0067402007f50c5a8 /tests/Wallabag/ImportBundle
parent3a3c6b866b52721431bed22426d9abfcd0d2dfe0 (diff)
parent7180aaed45dce62e40620a9e4b202526ebd6a3bb (diff)
downloadwallabag-99731f0bb1f6fd2815eeb9af504ce86df927657b.tar.gz
wallabag-99731f0bb1f6fd2815eeb9af504ce86df927657b.tar.zst
wallabag-99731f0bb1f6fd2815eeb9af504ce86df927657b.zip
Merge remote-tracking branch 'wallabag/master'
Diffstat (limited to 'tests/Wallabag/ImportBundle')
-rw-r--r--tests/Wallabag/ImportBundle/Command/ImportCommandTest.php85
-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/ChromeControllerTest.php153
-rw-r--r--tests/Wallabag/ImportBundle/Controller/FirefoxControllerTest.php166
-rw-r--r--tests/Wallabag/ImportBundle/Controller/ImportControllerTest.php2
-rw-r--r--tests/Wallabag/ImportBundle/Controller/InstapaperControllerTest.php196
-rw-r--r--tests/Wallabag/ImportBundle/Controller/PocketControllerTest.php31
-rw-r--r--tests/Wallabag/ImportBundle/Controller/ReadabilityControllerTest.php197
-rw-r--r--tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php75
-rw-r--r--tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php77
-rw-r--r--tests/Wallabag/ImportBundle/Import/ChromeImportTest.php233
-rw-r--r--tests/Wallabag/ImportBundle/Import/FirefoxImportTest.php233
-rw-r--r--tests/Wallabag/ImportBundle/Import/InstapaperImportTest.php233
-rw-r--r--tests/Wallabag/ImportBundle/Import/PocketImportTest.php248
-rw-r--r--tests/Wallabag/ImportBundle/Import/ReadabilityImportTest.php233
-rw-r--r--tests/Wallabag/ImportBundle/Import/WallabagV1ImportTest.php101
-rw-r--r--tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php97
-rw-r--r--tests/Wallabag/ImportBundle/fixtures/chrome-bookmarks36
-rw-r--r--tests/Wallabag/ImportBundle/fixtures/firefox-bookmarks.json63
-rw-r--r--tests/Wallabag/ImportBundle/fixtures/instapaper-export.csv4
-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
26 files changed, 3196 insertions, 44 deletions
diff --git a/tests/Wallabag/ImportBundle/Command/ImportCommandTest.php b/tests/Wallabag/ImportBundle/Command/ImportCommandTest.php
new file mode 100644
index 00000000..7be1eb18
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Command/ImportCommandTest.php
@@ -0,0 +1,85 @@
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;
9
10class ImportCommandTest extends WallabagCoreTestCase
11{
12 /**
13 * @expectedException Symfony\Component\Console\Exception\RuntimeException
14 * @expectedExceptionMessage Not enough arguments
15 */
16 public function testRunImportCommandWithoutArguments()
17 {
18 $application = new Application($this->getClient()->getKernel());
19 $application->add(new ImportCommand());
20
21 $command = $application->find('wallabag:import');
22
23 $tester = new CommandTester($command);
24 $tester->execute([
25 'command' => $command->getName(),
26 ]);
27 }
28
29 /**
30 * @expectedException Symfony\Component\Config\Definition\Exception\Exception
31 * @expectedExceptionMessage not found
32 */
33 public function testRunImportCommandWithoutFilepath()
34 {
35 $application = new Application($this->getClient()->getKernel());
36 $application->add(new ImportCommand());
37
38 $command = $application->find('wallabag:import');
39
40 $tester = new CommandTester($command);
41 $tester->execute([
42 'command' => $command->getName(),
43 'userId' => 1,
44 'filepath' => 1,
45 ]);
46 }
47
48 /**
49 * @expectedException Symfony\Component\Config\Definition\Exception\Exception
50 * @expectedExceptionMessage User with id
51 */
52 public function testRunImportCommandWithoutUserId()
53 {
54 $application = new Application($this->getClient()->getKernel());
55 $application->add(new ImportCommand());
56
57 $command = $application->find('wallabag:import');
58
59 $tester = new CommandTester($command);
60 $tester->execute([
61 'command' => $command->getName(),
62 'userId' => 0,
63 'filepath' => './',
64 ]);
65 }
66
67 public function testRunImportCommand()
68 {
69 $application = new Application($this->getClient()->getKernel());
70 $application->add(new ImportCommand());
71
72 $command = $application->find('wallabag:import');
73
74 $tester = new CommandTester($command);
75 $tester->execute([
76 'command' => $command->getName(),
77 'userId' => 1,
78 'filepath' => $application->getKernel()->getContainer()->getParameter('kernel.root_dir').'/../tests/Wallabag/ImportBundle/fixtures/wallabag-v2-read.json',
79 '--importer' => 'v2',
80 ]);
81
82 $this->assertContains('imported', $tester->getDisplay());
83 $this->assertContains('already saved', $tester->getDisplay());
84 }
85}
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/ChromeControllerTest.php b/tests/Wallabag/ImportBundle/Controller/ChromeControllerTest.php
new file mode 100644
index 00000000..c0417bbe
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Controller/ChromeControllerTest.php
@@ -0,0 +1,153 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Controller;
4
5use Tests\Wallabag\CoreBundle\WallabagCoreTestCase;
6use Symfony\Component\HttpFoundation\File\UploadedFile;
7
8class ChromeControllerTest extends WallabagCoreTestCase
9{
10 public function testImportChrome()
11 {
12 $this->logInAs('admin');
13 $client = $this->getClient();
14
15 $crawler = $client->request('GET', '/import/chrome');
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 testImportChromeWithRabbitEnabled()
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/chrome');
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 testImportChromeBadFile()
39 {
40 $this->logInAs('admin');
41 $client = $this->getClient();
42
43 $crawler = $client->request('GET', '/import/chrome');
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 testImportChromeWithRedisEnabled()
56 {
57 $this->checkRedis();
58 $this->logInAs('admin');
59 $client = $this->getClient();
60 $client->getContainer()->get('craue_config')->set('import_with_redis', 1);
61
62 $crawler = $client->request('GET', '/import/chrome');
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/chrome-bookmarks', 'Bookmarks');
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.chrome'));
86
87 $client->getContainer()->get('craue_config')->set('import_with_redis', 0);
88 }
89
90 public function testImportWallabagWithChromeFile()
91 {
92 $this->logInAs('admin');
93 $client = $this->getClient();
94
95 $crawler = $client->request('GET', '/import/chrome');
96 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
97
98 $file = new UploadedFile(__DIR__.'/../fixtures/chrome-bookmarks', 'Bookmarks');
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 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
111 $this->assertContains('flashes.import.notice.summary', $body[0]);
112
113 $content = $client->getContainer()
114 ->get('doctrine.orm.entity_manager')
115 ->getRepository('WallabagCoreBundle:Entry')
116 ->findByUrlAndUserId(
117 'http://www.usinenouvelle.com/article/la-multiplication-des-chefs-de-projet-est-une-catastrophe-manageriale-majeure-affirme-le-sociologue-francois-dupuy.N307730',
118 $this->getLoggedInUserId()
119 );
120
121 $this->assertNotEmpty($content->getPreviewPicture());
122 $this->assertNotEmpty($content->getLanguage());
123 $this->assertEquals(0, count($content->getTags()));
124
125 $createdAt = $content->getCreatedAt();
126 $this->assertEquals('2011', $createdAt->format('Y'));
127 $this->assertEquals('07', $createdAt->format('m'));
128 }
129
130 public function testImportWallabagWithEmptyFile()
131 {
132 $this->logInAs('admin');
133 $client = $this->getClient();
134
135 $crawler = $client->request('GET', '/import/chrome');
136 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
137
138 $file = new UploadedFile(__DIR__.'/../fixtures/test.txt', 'test.txt');
139
140 $data = [
141 'upload_import_file[file]' => $file,
142 ];
143
144 $client->submit($form, $data);
145
146 $this->assertEquals(302, $client->getResponse()->getStatusCode());
147
148 $crawler = $client->followRedirect();
149
150 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
151 $this->assertContains('flashes.import.notice.failed', $body[0]);
152 }
153}
diff --git a/tests/Wallabag/ImportBundle/Controller/FirefoxControllerTest.php b/tests/Wallabag/ImportBundle/Controller/FirefoxControllerTest.php
new file mode 100644
index 00000000..6154ae8d
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Controller/FirefoxControllerTest.php
@@ -0,0 +1,166 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Controller;
4
5use Tests\Wallabag\CoreBundle\WallabagCoreTestCase;
6use Symfony\Component\HttpFoundation\File\UploadedFile;
7
8class FirefoxControllerTest extends WallabagCoreTestCase
9{
10 public function testImportFirefox()
11 {
12 $this->logInAs('admin');
13 $client = $this->getClient();
14
15 $crawler = $client->request('GET', '/import/firefox');
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 testImportFirefoxWithRabbitEnabled()
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/firefox');
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 testImportFirefoxBadFile()
39 {
40 $this->logInAs('admin');
41 $client = $this->getClient();
42
43 $crawler = $client->request('GET', '/import/firefox');
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 testImportFirefoxWithRedisEnabled()
56 {
57 $this->checkRedis();
58 $this->logInAs('admin');
59 $client = $this->getClient();
60 $client->getContainer()->get('craue_config')->set('import_with_redis', 1);
61
62 $crawler = $client->request('GET', '/import/firefox');
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/firefox-bookmarks.json', 'Bookmarks');
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.firefox'));
86
87 $client->getContainer()->get('craue_config')->set('import_with_redis', 0);
88 }
89
90 public function testImportWallabagWithFirefoxFile()
91 {
92 $this->logInAs('admin');
93 $client = $this->getClient();
94
95 $crawler = $client->request('GET', '/import/firefox');
96 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
97
98 $file = new UploadedFile(__DIR__.'/../fixtures/firefox-bookmarks.json', 'Bookmarks');
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 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
111 $this->assertContains('flashes.import.notice.summary', $body[0]);
112
113 $content = $client->getContainer()
114 ->get('doctrine.orm.entity_manager')
115 ->getRepository('WallabagCoreBundle:Entry')
116 ->findByUrlAndUserId(
117 'http://lexpansion.lexpress.fr/high-tech/orange-offre-un-meilleur-reseau-mobile-que-bouygues-et-sfr-free-derriere_1811554.html',
118 $this->getLoggedInUserId()
119 );
120
121 $this->assertNotEmpty($content->getMimetype());
122 $this->assertNotEmpty($content->getPreviewPicture());
123 $this->assertNotEmpty($content->getLanguage());
124 $this->assertEquals(2, count($content->getTags()));
125
126 $content = $client->getContainer()
127 ->get('doctrine.orm.entity_manager')
128 ->getRepository('WallabagCoreBundle:Entry')
129 ->findByUrlAndUserId(
130 'http://stackoverflow.com/questions/15017163/parser-for-exported-bookmarks-html-file-of-google-chrome-and-mozilla-in-java',
131 $this->getLoggedInUserId()
132 );
133
134 $this->assertNotEmpty($content->getMimetype());
135 $this->assertNotEmpty($content->getPreviewPicture());
136 $this->assertEmpty($content->getLanguage());
137
138 $createdAt = $content->getCreatedAt();
139 $this->assertEquals('2013', $createdAt->format('Y'));
140 $this->assertEquals('12', $createdAt->format('m'));
141 }
142
143 public function testImportWallabagWithEmptyFile()
144 {
145 $this->logInAs('admin');
146 $client = $this->getClient();
147
148 $crawler = $client->request('GET', '/import/firefox');
149 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
150
151 $file = new UploadedFile(__DIR__.'/../fixtures/test.txt', 'test.txt');
152
153 $data = [
154 'upload_import_file[file]' => $file,
155 ];
156
157 $client->submit($form, $data);
158
159 $this->assertEquals(302, $client->getResponse()->getStatusCode());
160
161 $crawler = $client->followRedirect();
162
163 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
164 $this->assertContains('flashes.import.notice.failed', $body[0]);
165 }
166}
diff --git a/tests/Wallabag/ImportBundle/Controller/ImportControllerTest.php b/tests/Wallabag/ImportBundle/Controller/ImportControllerTest.php
index 96b5300b..0bc40bdd 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(7, $crawler->filter('blockquote')->count());
28 } 28 }
29} 29}
diff --git a/tests/Wallabag/ImportBundle/Controller/InstapaperControllerTest.php b/tests/Wallabag/ImportBundle/Controller/InstapaperControllerTest.php
new file mode 100644
index 00000000..9df08e75
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Controller/InstapaperControllerTest.php
@@ -0,0 +1,196 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Controller;
4
5use Tests\Wallabag\CoreBundle\WallabagCoreTestCase;
6use Symfony\Component\HttpFoundation\File\UploadedFile;
7
8class InstapaperControllerTest extends WallabagCoreTestCase
9{
10 public function testImportInstapaper()
11 {
12 $this->logInAs('admin');
13 $client = $this->getClient();
14
15 $crawler = $client->request('GET', '/import/instapaper');
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 testImportInstapaperWithRabbitEnabled()
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/instapaper');
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 testImportInstapaperBadFile()
39 {
40 $this->logInAs('admin');
41 $client = $this->getClient();
42
43 $crawler = $client->request('GET', '/import/instapaper');
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 testImportInstapaperWithRedisEnabled()
56 {
57 $this->checkRedis();
58 $this->logInAs('admin');
59 $client = $this->getClient();
60 $client->getContainer()->get('craue_config')->set('import_with_redis', 1);
61
62 $crawler = $client->request('GET', '/import/instapaper');
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/instapaper-export.csv', 'instapaper.csv');
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.instapaper'));
86
87 $client->getContainer()->get('craue_config')->set('import_with_redis', 0);
88 }
89
90 public function testImportInstapaperWithFile()
91 {
92 $this->logInAs('admin');
93 $client = $this->getClient();
94
95 $crawler = $client->request('GET', '/import/instapaper');
96 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
97
98 $file = new UploadedFile(__DIR__.'/../fixtures/instapaper-export.csv', 'instapaper.csv');
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 'http://www.liberation.fr/societe/2012/12/06/baumettes-un-tour-en-cellule_865551',
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 }
127
128 public function testImportInstapaperWithFileAndMarkAllAsRead()
129 {
130 $this->logInAs('admin');
131 $client = $this->getClient();
132
133 $crawler = $client->request('GET', '/import/instapaper');
134 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
135
136 $file = new UploadedFile(__DIR__.'/../fixtures/instapaper-export.csv', 'instapaper-read.csv');
137
138 $data = [
139 'upload_import_file[file]' => $file,
140 'upload_import_file[mark_as_read]' => 1,
141 ];
142
143 $client->submit($form, $data);
144
145 $this->assertEquals(302, $client->getResponse()->getStatusCode());
146
147 $crawler = $client->followRedirect();
148
149 $content1 = $client->getContainer()
150 ->get('doctrine.orm.entity_manager')
151 ->getRepository('WallabagCoreBundle:Entry')
152 ->findByUrlAndUserId(
153 'https://redditblog.com/2016/09/20/amp-and-reactredux/',
154 $this->getLoggedInUserId()
155 );
156
157 $this->assertTrue($content1->isArchived());
158
159 $content2 = $client->getContainer()
160 ->get('doctrine.orm.entity_manager')
161 ->getRepository('WallabagCoreBundle:Entry')
162 ->findByUrlAndUserId(
163 'https://medium.com/@the_minh/why-foursquare-swarm-is-still-my-favourite-social-network-e38228493e6c',
164 $this->getLoggedInUserId()
165 );
166
167 $this->assertTrue($content2->isArchived());
168
169 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
170 $this->assertContains('flashes.import.notice.summary', $body[0]);
171 }
172
173 public function testImportInstapaperWithEmptyFile()
174 {
175 $this->logInAs('admin');
176 $client = $this->getClient();
177
178 $crawler = $client->request('GET', '/import/instapaper');
179 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
180
181 $file = new UploadedFile(__DIR__.'/../fixtures/test.txt', 'test.txt');
182
183 $data = [
184 'upload_import_file[file]' => $file,
185 ];
186
187 $client->submit($form, $data);
188
189 $this->assertEquals(302, $client->getResponse()->getStatusCode());
190
191 $crawler = $client->followRedirect();
192
193 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
194 $this->assertContains('flashes.import.notice.failed', $body[0]);
195 }
196}
diff --git a/tests/Wallabag/ImportBundle/Controller/PocketControllerTest.php b/tests/Wallabag/ImportBundle/Controller/PocketControllerTest.php
index e0e61df8..7d6a300f 100644
--- a/tests/Wallabag/ImportBundle/Controller/PocketControllerTest.php
+++ b/tests/Wallabag/ImportBundle/Controller/PocketControllerTest.php
@@ -17,6 +17,37 @@ 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->checkRedis();
38 $this->logInAs('admin');
39 $client = $this->getClient();
40
41 $client->getContainer()->get('craue_config')->set('import_with_redis', 1);
42
43 $crawler = $client->request('GET', '/import/pocket');
44
45 $this->assertEquals(200, $client->getResponse()->getStatusCode());
46 $this->assertEquals(1, $crawler->filter('button[type=submit]')->count());
47
48 $client->getContainer()->get('craue_config')->set('import_with_redis', 0);
49 }
50
20 public function testImportPocketAuthBadToken() 51 public function testImportPocketAuthBadToken()
21 { 52 {
22 $this->logInAs('admin'); 53 $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..916dd297
--- /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->checkRedis();
58 $this->logInAs('admin');
59 $client = $this->getClient();
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..3497c4b8 100644
--- a/tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php
+++ b/tests/Wallabag/ImportBundle/Controller/WallabagV1ControllerTest.php
@@ -19,6 +19,75 @@ 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->checkRedis();
58 $this->logInAs('admin');
59 $client = $this->getClient();
60
61 $client->getContainer()->get('craue_config')->set('import_with_redis', 1);
62
63 $crawler = $client->request('GET', '/import/wallabag-v1');
64
65 $this->assertEquals(200, $client->getResponse()->getStatusCode());
66 $this->assertEquals(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
67 $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
68
69 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
70
71 $file = new UploadedFile(__DIR__.'/../fixtures/wallabag-v1.json', 'wallabag-v1.json');
72
73 $data = [
74 'upload_import_file[file]' => $file,
75 ];
76
77 $client->submit($form, $data);
78
79 $this->assertEquals(302, $client->getResponse()->getStatusCode());
80
81 $crawler = $client->followRedirect();
82
83 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
84 $this->assertContains('flashes.import.notice.summary', $body[0]);
85
86 $this->assertNotEmpty($client->getContainer()->get('wallabag_core.redis.client')->lpop('wallabag.import.wallabag_v1'));
87
88 $client->getContainer()->get('craue_config')->set('import_with_redis', 0);
89 }
90
22 public function testImportWallabagWithFile() 91 public function testImportWallabagWithFile()
23 { 92 {
24 $this->logInAs('admin'); 93 $this->logInAs('admin');
@@ -56,6 +125,12 @@ class WallabagV1ControllerTest extends WallabagCoreTestCase
56 125
57 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text'])); 126 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
58 $this->assertContains('flashes.import.notice.summary', $body[0]); 127 $this->assertContains('flashes.import.notice.summary', $body[0]);
128
129 $this->assertEmpty($content->getMimetype());
130 $this->assertEmpty($content->getPreviewPicture());
131 $this->assertEmpty($content->getLanguage());
132 $this->assertEquals(1, count($content->getTags()));
133 $this->assertInstanceOf(\DateTime::class, $content->getCreatedAt());
59 } 134 }
60 135
61 public function testImportWallabagWithFileAndMarkAllAsRead() 136 public function testImportWallabagWithFileAndMarkAllAsRead()
diff --git a/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php b/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php
index d8d2c8bf..27d2d52b 100644
--- a/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php
+++ b/tests/Wallabag/ImportBundle/Controller/WallabagV2ControllerTest.php
@@ -19,6 +19,75 @@ 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->checkRedis();
58 $this->logInAs('admin');
59 $client = $this->getClient();
60
61 $client->getContainer()->get('craue_config')->set('import_with_redis', 1);
62
63 $crawler = $client->request('GET', '/import/wallabag-v2');
64
65 $this->assertEquals(200, $client->getResponse()->getStatusCode());
66 $this->assertEquals(1, $crawler->filter('form[name=upload_import_file] > button[type=submit]')->count());
67 $this->assertEquals(1, $crawler->filter('input[type=file]')->count());
68
69 $form = $crawler->filter('form[name=upload_import_file] > button[type=submit]')->form();
70
71 $file = new UploadedFile(__DIR__.'/../fixtures/wallabag-v2.json', 'wallabag-v2.json');
72
73 $data = [
74 'upload_import_file[file]' => $file,
75 ];
76
77 $client->submit($form, $data);
78
79 $this->assertEquals(302, $client->getResponse()->getStatusCode());
80
81 $crawler = $client->followRedirect();
82
83 $this->assertGreaterThan(1, $body = $crawler->filter('body')->extract(['_text']));
84 $this->assertContains('flashes.import.notice.summary', $body[0]);
85
86 $this->assertNotEmpty($client->getContainer()->get('wallabag_core.redis.client')->lpop('wallabag.import.wallabag_v2'));
87
88 $client->getContainer()->get('craue_config')->set('import_with_redis', 0);
89 }
90
22 public function testImportWallabagWithFile() 91 public function testImportWallabagWithFile()
23 { 92 {
24 $this->logInAs('admin'); 93 $this->logInAs('admin');
@@ -50,9 +119,9 @@ class WallabagV2ControllerTest extends WallabagCoreTestCase
50 $this->getLoggedInUserId() 119 $this->getLoggedInUserId()
51 ); 120 );
52 121
53 $this->assertEmpty($content->getMimetype()); 122 $this->assertNotEmpty($content->getMimetype());
54 $this->assertEmpty($content->getPreviewPicture()); 123 $this->assertNotEmpty($content->getPreviewPicture());
55 $this->assertEmpty($content->getLanguage()); 124 $this->assertNotEmpty($content->getLanguage());
56 $this->assertEquals(0, count($content->getTags())); 125 $this->assertEquals(0, count($content->getTags()));
57 126
58 $content = $client->getContainer() 127 $content = $client->getContainer()
@@ -67,6 +136,8 @@ class WallabagV2ControllerTest extends WallabagCoreTestCase
67 $this->assertNotEmpty($content->getPreviewPicture()); 136 $this->assertNotEmpty($content->getPreviewPicture());
68 $this->assertNotEmpty($content->getLanguage()); 137 $this->assertNotEmpty($content->getLanguage());
69 $this->assertEquals(2, count($content->getTags())); 138 $this->assertEquals(2, count($content->getTags()));
139 $this->assertInstanceOf(\DateTime::class, $content->getCreatedAt());
140 $this->assertEquals('2016-09-08', $content->getCreatedAt()->format('Y-m-d'));
70 } 141 }
71 142
72 public function testImportWallabagWithEmptyFile() 143 public function testImportWallabagWithEmptyFile()
diff --git a/tests/Wallabag/ImportBundle/Import/ChromeImportTest.php b/tests/Wallabag/ImportBundle/Import/ChromeImportTest.php
new file mode 100644
index 00000000..1e52615c
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Import/ChromeImportTest.php
@@ -0,0 +1,233 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Import;
4
5use Wallabag\ImportBundle\Import\ChromeImport;
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 ChromeImportTest extends \PHPUnit_Framework_TestCase
15{
16 protected $user;
17 protected $em;
18 protected $logHandler;
19 protected $contentProxy;
20
21 private function getChromeImport($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 ChromeImport($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 $chromeImport = $this->getChromeImport();
49
50 $this->assertEquals('Chrome', $chromeImport->getName());
51 $this->assertNotEmpty($chromeImport->getUrl());
52 $this->assertEquals('import.chrome.description', $chromeImport->getDescription());
53 }
54
55 public function testImport()
56 {
57 $chromeImport = $this->getChromeImport();
58 $chromeImport->setFilepath(__DIR__.'/../fixtures/chrome-bookmarks');
59
60 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
61 ->disableOriginalConstructor()
62 ->getMock();
63
64 $entryRepo->expects($this->exactly(1))
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(1))
79 ->method('updateEntry')
80 ->willReturn($entry);
81
82 $res = $chromeImport->import();
83
84 $this->assertTrue($res);
85 $this->assertEquals(['skipped' => 0, 'imported' => 1, 'queued' => 0], $chromeImport->getSummary());
86 }
87
88 public function testImportAndMarkAllAsRead()
89 {
90 $chromeImport = $this->getChromeImport();
91 $chromeImport->setFilepath(__DIR__.'/../fixtures/chrome-bookmarks');
92
93 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
94 ->disableOriginalConstructor()
95 ->getMock();
96
97 $entryRepo->expects($this->exactly(1))
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 = $chromeImport->setMarkAsRead(true)->import();
120
121 $this->assertTrue($res);
122
123 $this->assertEquals(['skipped' => 0, 'imported' => 1, 'queued' => 0], $chromeImport->getSummary());
124 }
125
126 public function testImportWithRabbit()
127 {
128 $chromeImport = $this->getChromeImport();
129 $chromeImport->setFilepath(__DIR__.'/../fixtures/chrome-bookmarks');
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(1))
156 ->method('publish');
157
158 $chromeImport->setProducer($producer);
159
160 $res = $chromeImport->setMarkAsRead(true)->import();
161
162 $this->assertTrue($res);
163 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 1], $chromeImport->getSummary());
164 }
165
166 public function testImportWithRedis()
167 {
168 $chromeImport = $this->getChromeImport();
169 $chromeImport->setFilepath(__DIR__.'/../fixtures/chrome-bookmarks');
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, 'chrome');
194 $producer = new Producer($queue);
195
196 $chromeImport->setProducer($producer);
197
198 $res = $chromeImport->setMarkAsRead(true)->import();
199
200 $this->assertTrue($res);
201 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 1], $chromeImport->getSummary());
202
203 $this->assertNotEmpty($redisMock->lpop('chrome'));
204 }
205
206 public function testImportBadFile()
207 {
208 $chromeImport = $this->getChromeImport();
209 $chromeImport->setFilepath(__DIR__.'/../fixtures/wallabag-v1.jsonx');
210
211 $res = $chromeImport->import();
212
213 $this->assertFalse($res);
214
215 $records = $this->logHandler->getRecords();
216 $this->assertContains('Wallabag Browser Import: unable to read file', $records[0]['message']);
217 $this->assertEquals('ERROR', $records[0]['level_name']);
218 }
219
220 public function testImportUserNotDefined()
221 {
222 $chromeImport = $this->getChromeImport(true);
223 $chromeImport->setFilepath(__DIR__.'/../fixtures/chrome-bookmarks');
224
225 $res = $chromeImport->import();
226
227 $this->assertFalse($res);
228
229 $records = $this->logHandler->getRecords();
230 $this->assertContains('Wallabag Browser Import: user is not defined', $records[0]['message']);
231 $this->assertEquals('ERROR', $records[0]['level_name']);
232 }
233}
diff --git a/tests/Wallabag/ImportBundle/Import/FirefoxImportTest.php b/tests/Wallabag/ImportBundle/Import/FirefoxImportTest.php
new file mode 100644
index 00000000..007dda6a
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Import/FirefoxImportTest.php
@@ -0,0 +1,233 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Import;
4
5use Wallabag\ImportBundle\Import\FirefoxImport;
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 FirefoxImportTest extends \PHPUnit_Framework_TestCase
15{
16 protected $user;
17 protected $em;
18 protected $logHandler;
19 protected $contentProxy;
20
21 private function getFirefoxImport($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 FirefoxImport($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 $firefoxImport = $this->getFirefoxImport();
49
50 $this->assertEquals('Firefox', $firefoxImport->getName());
51 $this->assertNotEmpty($firefoxImport->getUrl());
52 $this->assertEquals('import.firefox.description', $firefoxImport->getDescription());
53 }
54
55 public function testImport()
56 {
57 $firefoxImport = $this->getFirefoxImport();
58 $firefoxImport->setFilepath(__DIR__.'/../fixtures/firefox-bookmarks.json');
59
60 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
61 ->disableOriginalConstructor()
62 ->getMock();
63
64 $entryRepo->expects($this->exactly(2))
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(2))
79 ->method('updateEntry')
80 ->willReturn($entry);
81
82 $res = $firefoxImport->import();
83
84 $this->assertTrue($res);
85 $this->assertEquals(['skipped' => 0, 'imported' => 2, 'queued' => 0], $firefoxImport->getSummary());
86 }
87
88 public function testImportAndMarkAllAsRead()
89 {
90 $firefoxImport = $this->getFirefoxImport();
91 $firefoxImport->setFilepath(__DIR__.'/../fixtures/firefox-bookmarks.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 = $firefoxImport->setMarkAsRead(true)->import();
120
121 $this->assertTrue($res);
122
123 $this->assertEquals(['skipped' => 1, 'imported' => 1, 'queued' => 0], $firefoxImport->getSummary());
124 }
125
126 public function testImportWithRabbit()
127 {
128 $firefoxImport = $this->getFirefoxImport();
129 $firefoxImport->setFilepath(__DIR__.'/../fixtures/firefox-bookmarks.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(1))
156 ->method('publish');
157
158 $firefoxImport->setProducer($producer);
159
160 $res = $firefoxImport->setMarkAsRead(true)->import();
161
162 $this->assertTrue($res);
163 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 1], $firefoxImport->getSummary());
164 }
165
166 public function testImportWithRedis()
167 {
168 $firefoxImport = $this->getFirefoxImport();
169 $firefoxImport->setFilepath(__DIR__.'/../fixtures/firefox-bookmarks.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, 'firefox');
194 $producer = new Producer($queue);
195
196 $firefoxImport->setProducer($producer);
197
198 $res = $firefoxImport->setMarkAsRead(true)->import();
199
200 $this->assertTrue($res);
201 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 1], $firefoxImport->getSummary());
202
203 $this->assertNotEmpty($redisMock->lpop('firefox'));
204 }
205
206 public function testImportBadFile()
207 {
208 $firefoxImport = $this->getFirefoxImport();
209 $firefoxImport->setFilepath(__DIR__.'/../fixtures/wallabag-v1.jsonx');
210
211 $res = $firefoxImport->import();
212
213 $this->assertFalse($res);
214
215 $records = $this->logHandler->getRecords();
216 $this->assertContains('Wallabag Browser Import: unable to read file', $records[0]['message']);
217 $this->assertEquals('ERROR', $records[0]['level_name']);
218 }
219
220 public function testImportUserNotDefined()
221 {
222 $firefoxImport = $this->getFirefoxImport(true);
223 $firefoxImport->setFilepath(__DIR__.'/../fixtures/firefox-bookmarks.json');
224
225 $res = $firefoxImport->import();
226
227 $this->assertFalse($res);
228
229 $records = $this->logHandler->getRecords();
230 $this->assertContains('Wallabag Browser Import: user is not defined', $records[0]['message']);
231 $this->assertEquals('ERROR', $records[0]['level_name']);
232 }
233}
diff --git a/tests/Wallabag/ImportBundle/Import/InstapaperImportTest.php b/tests/Wallabag/ImportBundle/Import/InstapaperImportTest.php
new file mode 100644
index 00000000..75900bd7
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/Import/InstapaperImportTest.php
@@ -0,0 +1,233 @@
1<?php
2
3namespace Tests\Wallabag\ImportBundle\Import;
4
5use Wallabag\ImportBundle\Import\InstapaperImport;
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 InstapaperImportTest extends \PHPUnit_Framework_TestCase
15{
16 protected $user;
17 protected $em;
18 protected $logHandler;
19 protected $contentProxy;
20
21 private function getInstapaperImport($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 $import = new InstapaperImport($this->em, $this->contentProxy);
34
35 $this->logHandler = new TestHandler();
36 $logger = new Logger('test', [$this->logHandler]);
37 $import->setLogger($logger);
38
39 if (false === $unsetUser) {
40 $import->setUser($this->user);
41 }
42
43 return $import;
44 }
45
46 public function testInit()
47 {
48 $instapaperImport = $this->getInstapaperImport();
49
50 $this->assertEquals('Instapaper', $instapaperImport->getName());
51 $this->assertNotEmpty($instapaperImport->getUrl());
52 $this->assertEquals('import.instapaper.description', $instapaperImport->getDescription());
53 }
54
55 public function testImport()
56 {
57 $instapaperImport = $this->getInstapaperImport();
58 $instapaperImport->setFilepath(__DIR__.'/../fixtures/instapaper-export.csv');
59
60 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
61 ->disableOriginalConstructor()
62 ->getMock();
63
64 $entryRepo->expects($this->exactly(3))
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(3))
79 ->method('updateEntry')
80 ->willReturn($entry);
81
82 $res = $instapaperImport->import();
83
84 $this->assertTrue($res);
85 $this->assertEquals(['skipped' => 0, 'imported' => 3, 'queued' => 0], $instapaperImport->getSummary());
86 }
87
88 public function testImportAndMarkAllAsRead()
89 {
90 $instapaperImport = $this->getInstapaperImport();
91 $instapaperImport->setFilepath(__DIR__.'/../fixtures/instapaper-export.csv');
92
93 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
94 ->disableOriginalConstructor()
95 ->getMock();
96
97 $entryRepo->expects($this->exactly(3))
98 ->method('findByUrlAndUserId')
99 ->will($this->onConsecutiveCalls(false, true, true));
100
101 $this->em
102 ->expects($this->any())
103 ->method('getRepository')
104 ->willReturn($entryRepo);
105
106 $this->contentProxy
107 ->expects($this->once())
108 ->method('updateEntry')
109 ->willReturn(new Entry($this->user));
110
111 // check that every entry persisted are archived
112 $this->em
113 ->expects($this->once())
114 ->method('persist')
115 ->with($this->callback(function ($persistedEntry) {
116 return $persistedEntry->isArchived();
117 }));
118
119 $res = $instapaperImport->setMarkAsRead(true)->import();
120
121 $this->assertTrue($res);
122
123 $this->assertEquals(['skipped' => 2, 'imported' => 1, 'queued' => 0], $instapaperImport->getSummary());
124 }
125
126 public function testImportWithRabbit()
127 {
128 $instapaperImport = $this->getInstapaperImport();
129 $instapaperImport->setFilepath(__DIR__.'/../fixtures/instapaper-export.csv');
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(3))
156 ->method('publish');
157
158 $instapaperImport->setProducer($producer);
159
160 $res = $instapaperImport->setMarkAsRead(true)->import();
161
162 $this->assertTrue($res);
163 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 3], $instapaperImport->getSummary());
164 }
165
166 public function testImportWithRedis()
167 {
168 $instapaperImport = $this->getInstapaperImport();
169 $instapaperImport->setFilepath(__DIR__.'/../fixtures/instapaper-export.csv');
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, 'instapaper');
194 $producer = new Producer($queue);
195
196 $instapaperImport->setProducer($producer);
197
198 $res = $instapaperImport->setMarkAsRead(true)->import();
199
200 $this->assertTrue($res);
201 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 3], $instapaperImport->getSummary());
202
203 $this->assertNotEmpty($redisMock->lpop('instapaper'));
204 }
205
206 public function testImportBadFile()
207 {
208 $instapaperImport = $this->getInstapaperImport();
209 $instapaperImport->setFilepath(__DIR__.'/../fixtures/wallabag-v1.jsonx');
210
211 $res = $instapaperImport->import();
212
213 $this->assertFalse($res);
214
215 $records = $this->logHandler->getRecords();
216 $this->assertContains('InstapaperImport: unable to read file', $records[0]['message']);
217 $this->assertEquals('ERROR', $records[0]['level_name']);
218 }
219
220 public function testImportUserNotDefined()
221 {
222 $instapaperImport = $this->getInstapaperImport(true);
223 $instapaperImport->setFilepath(__DIR__.'/../fixtures/instapaper-export.csv');
224
225 $res = $instapaperImport->import();
226
227 $this->assertFalse($res);
228
229 $records = $this->logHandler->getRecords();
230 $this->assertContains('InstapaperImport: user is not defined', $records[0]['message']);
231 $this->assertEquals('ERROR', $records[0]['level_name']);
232 }
233}
diff --git a/tests/Wallabag/ImportBundle/Import/PocketImportTest.php b/tests/Wallabag/ImportBundle/Import/PocketImportTest.php
index 8534e1c8..9ec7935c 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,38 @@ 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 $this->uow = $this->getMockBuilder('Doctrine\ORM\UnitOfWork')
60 ->disableOriginalConstructor() 45 ->disableOriginalConstructor()
61 ->getMock(); 46 ->getMock();
62 47
63 $config->expects($this->any()) 48 $this->em
64 ->method('get') 49 ->expects($this->any())
65 ->with('pocket_consumer_key') 50 ->method('getUnitOfWork')
66 ->willReturn($consumerKey); 51 ->willReturn($this->uow);
52
53 $this->uow
54 ->expects($this->any())
55 ->method('getScheduledEntityInsertions')
56 ->willReturn([]);
67 57
68 $pocket = new PocketImportMock( 58 $pocket = new PocketImport(
69 $this->tokenStorage,
70 $this->em, 59 $this->em,
71 $this->contentProxy, 60 $this->contentProxy
72 $config
73 ); 61 );
62 $pocket->setUser($this->user);
74 63
75 $this->logHandler = new TestHandler(); 64 $this->logHandler = new TestHandler();
76 $logger = new Logger('test', [$this->logHandler]); 65 $logger = new Logger('test', [$this->logHandler]);
@@ -189,10 +178,16 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
189 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland", 178 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
190 "favorite": "1", 179 "favorite": "1",
191 "status": "1", 180 "status": "1",
181 "time_added": "1473020899",
182 "time_updated": "1473020899",
183 "time_read": "0",
184 "time_favorited": "0",
185 "sort_id": 0,
192 "resolved_title": "The Massive Ryder Cup Preview", 186 "resolved_title": "The Massive Ryder Cup Preview",
193 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview", 187 "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.", 188 "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", 189 "is_article": "1",
190 "is_index": "0",
196 "has_video": "1", 191 "has_video": "1",
197 "has_image": "1", 192 "has_image": "1",
198 "word_count": "3197", 193 "word_count": "3197",
@@ -236,10 +231,16 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
236 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland", 231 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
237 "favorite": "1", 232 "favorite": "1",
238 "status": "1", 233 "status": "1",
234 "time_added": "1473020899",
235 "time_updated": "1473020899",
236 "time_read": "0",
237 "time_favorited": "0",
238 "sort_id": 1,
239 "resolved_title": "The Massive Ryder Cup Preview", 239 "resolved_title": "The Massive Ryder Cup Preview",
240 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview", 240 "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.", 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.",
242 "is_article": "1", 242 "is_article": "1",
243 "is_index": "0",
243 "has_video": "0", 244 "has_video": "0",
244 "has_image": "0", 245 "has_image": "0",
245 "word_count": "3197" 246 "word_count": "3197"
@@ -279,7 +280,7 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
279 $res = $pocketImport->import(); 280 $res = $pocketImport->import();
280 281
281 $this->assertTrue($res); 282 $this->assertTrue($res);
282 $this->assertEquals(['skipped' => 1, 'imported' => 1], $pocketImport->getSummary()); 283 $this->assertEquals(['skipped' => 1, 'imported' => 1, 'queued' => 0], $pocketImport->getSummary());
283 } 284 }
284 285
285 /** 286 /**
@@ -302,6 +303,11 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
302 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland", 303 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
303 "favorite": "1", 304 "favorite": "1",
304 "status": "1", 305 "status": "1",
306 "time_added": "1473020899",
307 "time_updated": "1473020899",
308 "time_read": "0",
309 "time_favorited": "0",
310 "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.", 311 "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", 312 "is_article": "1",
307 "has_video": "1", 313 "has_video": "1",
@@ -315,6 +321,11 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
315 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland", 321 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
316 "favorite": "1", 322 "favorite": "1",
317 "status": "0", 323 "status": "0",
324 "time_added": "1473020899",
325 "time_updated": "1473020899",
326 "time_read": "0",
327 "time_favorited": "0",
328 "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.", 329 "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", 330 "is_article": "1",
320 "has_video": "0", 331 "has_video": "0",
@@ -364,7 +375,174 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
364 $res = $pocketImport->setMarkAsRead(true)->import(); 375 $res = $pocketImport->setMarkAsRead(true)->import();
365 376
366 $this->assertTrue($res); 377 $this->assertTrue($res);
367 $this->assertEquals(['skipped' => 0, 'imported' => 2], $pocketImport->getSummary()); 378 $this->assertEquals(['skipped' => 0, 'imported' => 2, 'queued' => 0], $pocketImport->getSummary());
379 }
380
381 /**
382 * Will sample results from https://getpocket.com/developer/docs/v3/retrieve.
383 */
384 public function testImportWithRabbit()
385 {
386 $client = new Client();
387
388 $body = <<<'JSON'
389{
390 "item_id": "229279689",
391 "resolved_id": "229279689",
392 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
393 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
394 "favorite": "1",
395 "status": "1",
396 "time_added": "1473020899",
397 "time_updated": "1473020899",
398 "time_read": "0",
399 "time_favorited": "0",
400 "sort_id": 0,
401 "resolved_title": "The Massive Ryder Cup Preview",
402 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
403 "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.",
404 "is_article": "1",
405 "has_video": "0",
406 "has_image": "0",
407 "word_count": "3197"
408}
409JSON;
410
411 $mock = new Mock([
412 new Response(200, ['Content-Type' => 'application/json'], Stream::factory(json_encode(['access_token' => 'wunderbar_token']))),
413 new Response(200, ['Content-Type' => 'application/json'], Stream::factory('
414 {
415 "status": 1,
416 "list": {
417 "229279690": '.$body.'
418 }
419 }
420 ')),
421 ]);
422
423 $client->getEmitter()->attach($mock);
424
425 $pocketImport = $this->getPocketImport();
426
427 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
428 ->disableOriginalConstructor()
429 ->getMock();
430
431 $entryRepo->expects($this->never())
432 ->method('findByUrlAndUserId');
433
434 $this->em
435 ->expects($this->never())
436 ->method('getRepository');
437
438 $entry = new Entry($this->user);
439
440 $this->contentProxy
441 ->expects($this->never())
442 ->method('updateEntry');
443
444 $producer = $this->getMockBuilder('OldSound\RabbitMqBundle\RabbitMq\Producer')
445 ->disableOriginalConstructor()
446 ->getMock();
447
448 $bodyAsArray = json_decode($body, true);
449 // because with just use `new User()` so it doesn't have an id
450 $bodyAsArray['userId'] = null;
451
452 $producer
453 ->expects($this->once())
454 ->method('publish')
455 ->with(json_encode($bodyAsArray));
456
457 $pocketImport->setClient($client);
458 $pocketImport->setProducer($producer);
459 $pocketImport->authorize('wunderbar_code');
460
461 $res = $pocketImport->setMarkAsRead(true)->import();
462
463 $this->assertTrue($res);
464 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 1], $pocketImport->getSummary());
465 }
466
467 /**
468 * Will sample results from https://getpocket.com/developer/docs/v3/retrieve.
469 */
470 public function testImportWithRedis()
471 {
472 $client = new Client();
473
474 $body = <<<'JSON'
475{
476 "item_id": "229279689",
477 "resolved_id": "229279689",
478 "given_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
479 "given_title": "The Massive Ryder Cup Preview - The Triangle Blog - Grantland",
480 "favorite": "1",
481 "status": "1",
482 "time_added": "1473020899",
483 "time_updated": "1473020899",
484 "time_read": "0",
485 "time_favorited": "0",
486 "sort_id": 0,
487 "resolved_title": "The Massive Ryder Cup Preview",
488 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview",
489 "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.",
490 "is_article": "1",
491 "has_video": "0",
492 "has_image": "0",
493 "word_count": "3197"
494}
495JSON;
496
497 $mock = new Mock([
498 new Response(200, ['Content-Type' => 'application/json'], Stream::factory(json_encode(['access_token' => 'wunderbar_token']))),
499 new Response(200, ['Content-Type' => 'application/json'], Stream::factory('
500 {
501 "status": 1,
502 "list": {
503 "229279690": '.$body.'
504 }
505 }
506 ')),
507 ]);
508
509 $client->getEmitter()->attach($mock);
510
511 $pocketImport = $this->getPocketImport();
512
513 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
514 ->disableOriginalConstructor()
515 ->getMock();
516
517 $entryRepo->expects($this->never())
518 ->method('findByUrlAndUserId');
519
520 $this->em
521 ->expects($this->never())
522 ->method('getRepository');
523
524 $entry = new Entry($this->user);
525
526 $this->contentProxy
527 ->expects($this->never())
528 ->method('updateEntry');
529
530 $factory = new RedisMockFactory();
531 $redisMock = $factory->getAdapter('Predis\Client', true);
532
533 $queue = new RedisQueue($redisMock, 'pocket');
534 $producer = new Producer($queue);
535
536 $pocketImport->setClient($client);
537 $pocketImport->setProducer($producer);
538 $pocketImport->authorize('wunderbar_code');
539
540 $res = $pocketImport->setMarkAsRead(true)->import();
541
542 $this->assertTrue($res);
543 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 1], $pocketImport->getSummary());
544
545 $this->assertNotEmpty($redisMock->lpop('pocket'));
368 } 546 }
369 547
370 public function testImportBadResponse() 548 public function testImportBadResponse()
@@ -402,6 +580,8 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
402 "status": 1, 580 "status": 1,
403 "list": { 581 "list": {
404 "229279689": { 582 "229279689": {
583 "status": "1",
584 "favorite": "1",
405 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview" 585 "resolved_url": "http://www.grantland.com/blog/the-triangle/post/_/id/38347/ryder-cup-preview"
406 } 586 }
407 } 587 }
@@ -439,6 +619,6 @@ class PocketImportTest extends \PHPUnit_Framework_TestCase
439 $res = $pocketImport->import(); 619 $res = $pocketImport->import();
440 620
441 $this->assertTrue($res); 621 $this->assertTrue($res);
442 $this->assertEquals(['skipped' => 1, 'imported' => 0], $pocketImport->getSummary()); 622 $this->assertEquals(['skipped' => 0, 'imported' => 1, 'queued' => 0], $pocketImport->getSummary());
443 } 623 }
444} 624}
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..82dc4c7e 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{
@@ -23,6 +26,20 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase
23 ->disableOriginalConstructor() 26 ->disableOriginalConstructor()
24 ->getMock(); 27 ->getMock();
25 28
29 $this->uow = $this->getMockBuilder('Doctrine\ORM\UnitOfWork')
30 ->disableOriginalConstructor()
31 ->getMock();
32
33 $this->em
34 ->expects($this->any())
35 ->method('getUnitOfWork')
36 ->willReturn($this->uow);
37
38 $this->uow
39 ->expects($this->any())
40 ->method('getScheduledEntityInsertions')
41 ->willReturn([]);
42
26 $this->contentProxy = $this->getMockBuilder('Wallabag\CoreBundle\Helper\ContentProxy') 43 $this->contentProxy = $this->getMockBuilder('Wallabag\CoreBundle\Helper\ContentProxy')
27 ->disableOriginalConstructor() 44 ->disableOriginalConstructor()
28 ->getMock(); 45 ->getMock();
@@ -79,7 +96,7 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase
79 $res = $wallabagV1Import->import(); 96 $res = $wallabagV1Import->import();
80 97
81 $this->assertTrue($res); 98 $this->assertTrue($res);
82 $this->assertEquals(['skipped' => 1, 'imported' => 3], $wallabagV1Import->getSummary()); 99 $this->assertEquals(['skipped' => 1, 'imported' => 3, 'queued' => 0], $wallabagV1Import->getSummary());
83 } 100 }
84 101
85 public function testImportAndMarkAllAsRead() 102 public function testImportAndMarkAllAsRead()
@@ -117,7 +134,87 @@ class WallabagV1ImportTest extends \PHPUnit_Framework_TestCase
117 134
118 $this->assertTrue($res); 135 $this->assertTrue($res);
119 136
120 $this->assertEquals(['skipped' => 0, 'imported' => 3], $wallabagV1Import->getSummary()); 137 $this->assertEquals(['skipped' => 0, 'imported' => 3, 'queued' => 0], $wallabagV1Import->getSummary());
138 }
139
140 public function testImportWithRabbit()
141 {
142 $wallabagV1Import = $this->getWallabagV1Import();
143 $wallabagV1Import->setFilepath(__DIR__.'/../fixtures/wallabag-v1.json');
144
145 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
146 ->disableOriginalConstructor()
147 ->getMock();
148
149 $entryRepo->expects($this->never())
150 ->method('findByUrlAndUserId');
151
152 $this->em
153 ->expects($this->never())
154 ->method('getRepository');
155
156 $entry = $this->getMockBuilder('Wallabag\CoreBundle\Entity\Entry')
157 ->disableOriginalConstructor()
158 ->getMock();
159
160 $this->contentProxy
161 ->expects($this->never())
162 ->method('updateEntry');
163
164 $producer = $this->getMockBuilder('OldSound\RabbitMqBundle\RabbitMq\Producer')
165 ->disableOriginalConstructor()
166 ->getMock();
167
168 $producer
169 ->expects($this->exactly(4))
170 ->method('publish');
171
172 $wallabagV1Import->setProducer($producer);
173
174 $res = $wallabagV1Import->setMarkAsRead(true)->import();
175
176 $this->assertTrue($res);
177 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 4], $wallabagV1Import->getSummary());
178 }
179
180 public function testImportWithRedis()
181 {
182 $wallabagV1Import = $this->getWallabagV1Import();
183 $wallabagV1Import->setFilepath(__DIR__.'/../fixtures/wallabag-v1.json');
184
185 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
186 ->disableOriginalConstructor()
187 ->getMock();
188
189 $entryRepo->expects($this->never())
190 ->method('findByUrlAndUserId');
191
192 $this->em
193 ->expects($this->never())
194 ->method('getRepository');
195
196 $entry = $this->getMockBuilder('Wallabag\CoreBundle\Entity\Entry')
197 ->disableOriginalConstructor()
198 ->getMock();
199
200 $this->contentProxy
201 ->expects($this->never())
202 ->method('updateEntry');
203
204 $factory = new RedisMockFactory();
205 $redisMock = $factory->getAdapter('Predis\Client', true);
206
207 $queue = new RedisQueue($redisMock, 'wallabag_v1');
208 $producer = new Producer($queue);
209
210 $wallabagV1Import->setProducer($producer);
211
212 $res = $wallabagV1Import->setMarkAsRead(true)->import();
213
214 $this->assertTrue($res);
215 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 4], $wallabagV1Import->getSummary());
216
217 $this->assertNotEmpty($redisMock->lpop('wallabag_v1'));
121 } 218 }
122 219
123 public function testImportBadFile() 220 public function testImportBadFile()
diff --git a/tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php b/tests/Wallabag/ImportBundle/Import/WallabagV2ImportTest.php
index 4a45e0f0..bea89efb 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{
@@ -23,6 +26,20 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase
23 ->disableOriginalConstructor() 26 ->disableOriginalConstructor()
24 ->getMock(); 27 ->getMock();
25 28
29 $this->uow = $this->getMockBuilder('Doctrine\ORM\UnitOfWork')
30 ->disableOriginalConstructor()
31 ->getMock();
32
33 $this->em
34 ->expects($this->any())
35 ->method('getUnitOfWork')
36 ->willReturn($this->uow);
37
38 $this->uow
39 ->expects($this->any())
40 ->method('getScheduledEntityInsertions')
41 ->willReturn([]);
42
26 $this->contentProxy = $this->getMockBuilder('Wallabag\CoreBundle\Helper\ContentProxy') 43 $this->contentProxy = $this->getMockBuilder('Wallabag\CoreBundle\Helper\ContentProxy')
27 ->disableOriginalConstructor() 44 ->disableOriginalConstructor()
28 ->getMock(); 45 ->getMock();
@@ -75,7 +92,7 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase
75 $res = $wallabagV2Import->import(); 92 $res = $wallabagV2Import->import();
76 93
77 $this->assertTrue($res); 94 $this->assertTrue($res);
78 $this->assertEquals(['skipped' => 22, 'imported' => 2], $wallabagV2Import->getSummary()); 95 $this->assertEquals(['skipped' => 22, 'imported' => 2, 'queued' => 0], $wallabagV2Import->getSummary());
79 } 96 }
80 97
81 public function testImportAndMarkAllAsRead() 98 public function testImportAndMarkAllAsRead()
@@ -113,7 +130,79 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase
113 130
114 $this->assertTrue($res); 131 $this->assertTrue($res);
115 132
116 $this->assertEquals(['skipped' => 0, 'imported' => 2], $wallabagV2Import->getSummary()); 133 $this->assertEquals(['skipped' => 0, 'imported' => 2, 'queued' => 0], $wallabagV2Import->getSummary());
134 }
135
136 public function testImportWithRabbit()
137 {
138 $wallabagV2Import = $this->getWallabagV2Import();
139 $wallabagV2Import->setFilepath(__DIR__.'/../fixtures/wallabag-v2.json');
140
141 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
142 ->disableOriginalConstructor()
143 ->getMock();
144
145 $entryRepo->expects($this->never())
146 ->method('findByUrlAndUserId');
147
148 $this->em
149 ->expects($this->never())
150 ->method('getRepository');
151
152 $this->contentProxy
153 ->expects($this->never())
154 ->method('updateEntry');
155
156 $producer = $this->getMockBuilder('OldSound\RabbitMqBundle\RabbitMq\Producer')
157 ->disableOriginalConstructor()
158 ->getMock();
159
160 $producer
161 ->expects($this->exactly(24))
162 ->method('publish');
163
164 $wallabagV2Import->setProducer($producer);
165
166 $res = $wallabagV2Import->setMarkAsRead(true)->import();
167
168 $this->assertTrue($res);
169 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 24], $wallabagV2Import->getSummary());
170 }
171
172 public function testImportWithRedis()
173 {
174 $wallabagV2Import = $this->getWallabagV2Import();
175 $wallabagV2Import->setFilepath(__DIR__.'/../fixtures/wallabag-v2.json');
176
177 $entryRepo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\EntryRepository')
178 ->disableOriginalConstructor()
179 ->getMock();
180
181 $entryRepo->expects($this->never())
182 ->method('findByUrlAndUserId');
183
184 $this->em
185 ->expects($this->never())
186 ->method('getRepository');
187
188 $this->contentProxy
189 ->expects($this->never())
190 ->method('updateEntry');
191
192 $factory = new RedisMockFactory();
193 $redisMock = $factory->getAdapter('Predis\Client', true);
194
195 $queue = new RedisQueue($redisMock, 'wallabag_v2');
196 $producer = new Producer($queue);
197
198 $wallabagV2Import->setProducer($producer);
199
200 $res = $wallabagV2Import->setMarkAsRead(true)->import();
201
202 $this->assertTrue($res);
203 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 24], $wallabagV2Import->getSummary());
204
205 $this->assertNotEmpty($redisMock->lpop('wallabag_v2'));
117 } 206 }
118 207
119 public function testImportBadFile() 208 public function testImportBadFile()
@@ -152,7 +241,7 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase
152 $res = $wallabagV2Import->import(); 241 $res = $wallabagV2Import->import();
153 242
154 $this->assertFalse($res); 243 $this->assertFalse($res);
155 $this->assertEquals(['skipped' => 0, 'imported' => 0], $wallabagV2Import->getSummary()); 244 $this->assertEquals(['skipped' => 0, 'imported' => 0, 'queued' => 0], $wallabagV2Import->getSummary());
156 } 245 }
157 246
158 public function testImportWithExceptionFromGraby() 247 public function testImportWithExceptionFromGraby()
@@ -181,6 +270,6 @@ class WallabagV2ImportTest extends \PHPUnit_Framework_TestCase
181 $res = $wallabagV2Import->import(); 270 $res = $wallabagV2Import->import();
182 271
183 $this->assertTrue($res); 272 $this->assertTrue($res);
184 $this->assertEquals(['skipped' => 24, 'imported' => 0], $wallabagV2Import->getSummary()); 273 $this->assertEquals(['skipped' => 22, 'imported' => 2, 'queued' => 0], $wallabagV2Import->getSummary());
185 } 274 }
186} 275}
diff --git a/tests/Wallabag/ImportBundle/fixtures/chrome-bookmarks b/tests/Wallabag/ImportBundle/fixtures/chrome-bookmarks
new file mode 100644
index 00000000..0478eb41
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/fixtures/chrome-bookmarks
@@ -0,0 +1,36 @@
1{
2 "checksum": "f3aa0e9c0edad632a246f7e98ec64918",
3 "roots": {
4 "bookmark_bar": {
5 "children": [ {
6 "date_added": "13118850929335823",
7 "id": "6",
8 "name": "\"La multiplication des chefs de projet est une catastrophe managériale majeure\", affirme le sociologue François Dupuy - Ressources humaines",
9 "type": "url",
10 "url": "http://www.usinenouvelle.com/article/la-multiplication-des-chefs-de-projet-est-une-catastrophe-manageriale-majeure-affirme-le-sociologue-francois-dupuy.N307730"
11 } ],
12 "date_added": "13118829474385693",
13 "date_modified": "13118850929335823",
14 "id": "1",
15 "name": "Barre de favoris",
16 "type": "folder"
17 },
18 "other": {
19 "children": [ ],
20 "date_added": "13118829474385701",
21 "date_modified": "0",
22 "id": "2",
23 "name": "Autres favoris",
24 "type": "folder"
25 },
26 "synced": {
27 "children": [ ],
28 "date_added": "13118829474385702",
29 "date_modified": "0",
30 "id": "3",
31 "name": "Favoris sur mobile",
32 "type": "folder"
33 }
34 },
35 "version": 1
36}
diff --git a/tests/Wallabag/ImportBundle/fixtures/firefox-bookmarks.json b/tests/Wallabag/ImportBundle/fixtures/firefox-bookmarks.json
new file mode 100644
index 00000000..406b5697
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/fixtures/firefox-bookmarks.json
@@ -0,0 +1,63 @@
1{
2 "guid": "root________",
3 "title": "",
4 "index": 0,
5 "dateAdded": 1388166091504000,
6 "lastModified": 1472897622350000,
7 "id": 1,
8 "type": "text/x-moz-place-container",
9 "root": "placesRoot",
10 "children": [
11 {
12 "guid": "toolbar_____",
13 "title": "Barre personnelle",
14 "index": 1,
15 "dateAdded": 1388166091504000,
16 "lastModified": 1472897622263000,
17 "id": 3,
18 "annos": [
19 {
20 "name": "bookmarkProperties/description",
21 "flags": 0,
22 "expires": 4,
23 "value": "Ajoutez des marque-pages dans ce dossier pour les voir apparaître sur votre barre personnelle"
24 }
25 ],
26 "type": "text/x-moz-place-container",
27 "root": "toolbarFolder",
28 "children": [
29 {
30 "guid": "tard77lzbC5H",
31 "title": "Orange offre un meilleur réseau mobile que Bouygues et SFR, Free derrière - L'Express L'Expansion",
32 "index": 1,
33 "dateAdded": 1388166091644000,
34 "lastModified": 1388166091644000,
35 "tags":"test,tag",
36 "id": 4,
37 "type": "text/x-moz-place",
38 "uri": "http://lexpansion.lexpress.fr/high-tech/orange-offre-un-meilleur-reseau-mobile-que-bouygues-et-sfr-free-derriere_1811554.html"
39 },
40 {
41 "guid": "E385l9vZ_LVn",
42 "title": "Parser for Exported Bookmarks HTML file of Google Chrome and Mozilla in Java",
43 "index": 1,
44 "dateAdded": 1388166091544000,
45 "lastModified": 1388166091545000,
46 "id": 5,
47 "type": "text/x-moz-place",
48 "uri": "http://stackoverflow.com/questions/15017163/parser-for-exported-bookmarks-html-file-of-google-chrome-and-mozilla-in-java"
49 }
50 ]
51 },
52 {
53 "guid": "unfiled_____",
54 "title": "Autres marque-pages",
55 "index": 3,
56 "dateAdded": 1388166091504000,
57 "lastModified": 1388166091542000,
58 "id": 6,
59 "type": "text/x-moz-place-container",
60 "root": "unfiledBookmarksFolder"
61 }
62 ]
63}
diff --git a/tests/Wallabag/ImportBundle/fixtures/instapaper-export.csv b/tests/Wallabag/ImportBundle/fixtures/instapaper-export.csv
new file mode 100644
index 00000000..28a4c8e6
--- /dev/null
+++ b/tests/Wallabag/ImportBundle/fixtures/instapaper-export.csv
@@ -0,0 +1,4 @@
1URL,Title,Selection,Folder
2http://www.liberation.fr/societe/2012/12/06/baumettes-un-tour-en-cellule_865551,Baumettes : un tour en cellule,,Unread
3https://redditblog.com/2016/09/20/amp-and-reactredux/,AMP and React+Redux: Why Not?,,Archive
4https://medium.com/@the_minh/why-foursquare-swarm-is-still-my-favourite-social-network-e38228493e6c,Why Foursquare / Swarm is still my favourite social network,,Starred
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",