aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php8
-rw-r--r--tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php95
-rw-r--r--tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php129
3 files changed, 202 insertions, 30 deletions
diff --git a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
index 74ec34b1..4aa60e90 100644
--- a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
+++ b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
@@ -345,7 +345,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
345 'tags' => 'google', 345 'tags' => 'google',
346 'title' => 'New title for my article', 346 'title' => 'New title for my article',
347 'content' => 'my content', 347 'content' => 'my content',
348 'language' => 'de_DE', 348 'language' => 'de',
349 'published_at' => '2016-09-08T11:55:58+0200', 349 'published_at' => '2016-09-08T11:55:58+0200',
350 'authors' => 'bob,helen', 350 'authors' => 'bob,helen',
351 ]); 351 ]);
@@ -362,7 +362,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
362 $this->assertEquals(1, $content['user_id']); 362 $this->assertEquals(1, $content['user_id']);
363 $this->assertCount(2, $content['tags']); 363 $this->assertCount(2, $content['tags']);
364 $this->assertSame('my content', $content['content']); 364 $this->assertSame('my content', $content['content']);
365 $this->assertSame('de_DE', $content['language']); 365 $this->assertSame('de', $content['language']);
366 $this->assertSame('2016-09-08T11:55:58+0200', $content['published_at']); 366 $this->assertSame('2016-09-08T11:55:58+0200', $content['published_at']);
367 $this->assertCount(2, $content['published_by']); 367 $this->assertCount(2, $content['published_by']);
368 $this->assertContains('bob', $content['published_by']); 368 $this->assertContains('bob', $content['published_by']);
@@ -477,7 +477,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
477 'tags' => 'new tag '.uniqid(), 477 'tags' => 'new tag '.uniqid(),
478 'starred' => '1', 478 'starred' => '1',
479 'archive' => '0', 479 'archive' => '0',
480 'language' => 'de_DE', 480 'language' => 'de_AT',
481 'preview_picture' => 'http://preview.io/picture.jpg', 481 'preview_picture' => 'http://preview.io/picture.jpg',
482 'authors' => 'bob,sponge', 482 'authors' => 'bob,sponge',
483 'content' => 'awesome', 483 'content' => 'awesome',
@@ -492,7 +492,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
492 $this->assertEquals('New awesome title', $content['title']); 492 $this->assertEquals('New awesome title', $content['title']);
493 $this->assertGreaterThan($nbTags, count($content['tags'])); 493 $this->assertGreaterThan($nbTags, count($content['tags']));
494 $this->assertEquals(1, $content['user_id']); 494 $this->assertEquals(1, $content['user_id']);
495 $this->assertEquals('de_DE', $content['language']); 495 $this->assertEquals('de_AT', $content['language']);
496 $this->assertEquals('http://preview.io/picture.jpg', $content['preview_picture']); 496 $this->assertEquals('http://preview.io/picture.jpg', $content['preview_picture']);
497 $this->assertContains('sponge', $content['published_by']); 497 $this->assertContains('sponge', $content['published_by']);
498 $this->assertContains('bob', $content['published_by']); 498 $this->assertContains('bob', $content['published_by']);
diff --git a/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php b/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php
index cc7b3672..3babbaca 100644
--- a/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php
+++ b/tests/Wallabag/CoreBundle/Controller/EntryControllerTest.php
@@ -158,6 +158,7 @@ class EntryControllerTest extends WallabagCoreTestCase
158 $this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content); 158 $this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content);
159 $this->assertEquals($this->url, $content->getUrl()); 159 $this->assertEquals($this->url, $content->getUrl());
160 $this->assertContains('Google', $content->getTitle()); 160 $this->assertContains('Google', $content->getTitle());
161 $this->assertEquals('fr', $content->getLanguage());
161 $this->assertEquals('2015-03-28 15:37:39', $content->getPublishedAt()->format('Y-m-d H:i:s')); 162 $this->assertEquals('2015-03-28 15:37:39', $content->getPublishedAt()->format('Y-m-d H:i:s'));
162 $this->assertEquals('Morgane Tual', $author[0]); 163 $this->assertEquals('Morgane Tual', $author[0]);
163 $this->assertArrayHasKey('x-varnish1', $content->getHeaders()); 164 $this->assertArrayHasKey('x-varnish1', $content->getHeaders());
@@ -190,6 +191,7 @@ class EntryControllerTest extends WallabagCoreTestCase
190 191
191 $authors = $content->getPublishedBy(); 192 $authors = $content->getPublishedBy();
192 $this->assertEquals('2017-04-05 19:26:13', $content->getPublishedAt()->format('Y-m-d H:i:s')); 193 $this->assertEquals('2017-04-05 19:26:13', $content->getPublishedAt()->format('Y-m-d H:i:s'));
194 $this->assertEquals('fr', $content->getLanguage());
193 $this->assertEquals('Raphaël Balenieri, correspondant à Pékin', $authors[0]); 195 $this->assertEquals('Raphaël Balenieri, correspondant à Pékin', $authors[0]);
194 $this->assertEquals('Frédéric Autran, correspondant à New York', $authors[1]); 196 $this->assertEquals('Frédéric Autran, correspondant à New York', $authors[1]);
195 } 197 }
@@ -254,15 +256,6 @@ class EntryControllerTest extends WallabagCoreTestCase
254 256
255 $this->assertEquals(302, $client->getResponse()->getStatusCode()); 257 $this->assertEquals(302, $client->getResponse()->getStatusCode());
256 $this->assertContains('/view/', $client->getResponse()->getTargetUrl()); 258 $this->assertContains('/view/', $client->getResponse()->getTargetUrl());
257
258 $em = $client->getContainer()
259 ->get('doctrine.orm.entity_manager');
260 $entry = $em
261 ->getRepository('WallabagCoreBundle:Entry')
262 ->findOneByUrl(urldecode($url));
263
264 $em->remove($entry);
265 $em->flush();
266 } 259 }
267 260
268 /** 261 /**
@@ -297,6 +290,7 @@ class EntryControllerTest extends WallabagCoreTestCase
297 290
298 $this->assertCount(2, $tags); 291 $this->assertCount(2, $tags);
299 $this->assertContains('wallabag', $tags); 292 $this->assertContains('wallabag', $tags);
293 $this->assertEquals('en', $entry->getLanguage());
300 294
301 $em->remove($entry); 295 $em->remove($entry);
302 $em->flush(); 296 $em->flush();
@@ -392,8 +386,6 @@ class EntryControllerTest extends WallabagCoreTestCase
392 } 386 }
393 387
394 /** 388 /**
395 * @depends testPostNewOk
396 *
397 * This test will require an internet connection. 389 * This test will require an internet connection.
398 */ 390 */
399 public function testReload() 391 public function testReload()
@@ -420,9 +412,6 @@ class EntryControllerTest extends WallabagCoreTestCase
420 $this->assertNotEmpty($entry->getContent()); 412 $this->assertNotEmpty($entry->getContent());
421 } 413 }
422 414
423 /**
424 * @depends testPostNewOk
425 */
426 public function testReloadWithFetchingFailed() 415 public function testReloadWithFetchingFailed()
427 { 416 {
428 $this->logInAs('admin'); 417 $this->logInAs('admin');
@@ -1254,4 +1243,82 @@ class EntryControllerTest extends WallabagCoreTestCase
1254 1243
1255 $this->assertCount(1, $crawler->filter('div[class=entry]')); 1244 $this->assertCount(1, $crawler->filter('div[class=entry]'));
1256 } 1245 }
1246
1247 public function dataForLanguage()
1248 {
1249 return [
1250 'ru' => [
1251 'https://www.pravda.ru/world/09-06-2017/1337283-qatar-0/',
1252 'ru',
1253 ],
1254 'fr-FR' => [
1255 'http://www.zataz.com/90-des-dossiers-medicaux-des-coreens-du-sud-vendus-a-des-entreprises-privees/',
1256 'fr_FR',
1257 ],
1258 'de' => [
1259 'http://www.bild.de/politik/ausland/theresa-may/wahlbeben-grossbritannien-analyse-52108924.bild.html',
1260 'de',
1261 ],
1262 'it' => [
1263 'http://www.ansa.it/sito/notizie/mondo/europa/2017/06/08/voto-gb-seggi-aperti-misure-sicurezza-rafforzate_0cb71f7f-e23b-4d5f-95ca-bc12296419f0.html',
1264 'it',
1265 ],
1266 'zh_CN' => [
1267 'http://www.hao123.com/shequ?__noscript__-=1',
1268 'zh_CN',
1269 ],
1270 'de_AT' => [
1271 'https://buy.garmin.com/de-AT/AT/catalog/product/compareResult.ep?compareProduct=112885&compareProduct=36728',
1272 'de_AT',
1273 ],
1274 'ru_RU' => [
1275 'http://netler.ru/ikt/windows-error-reporting.htm',
1276 'ru_RU',
1277 ],
1278 'pt_BR' => [
1279 'http://precodoscombustiveis.com.br/postos/cidade/4121/pr/maringa',
1280 'pt_BR',
1281 ],
1282 'fucked_list_of_languages' => [
1283 'http://geocatalog.webservice-energy.org/geonetwork/srv/eng/main.home',
1284 '',
1285 ],
1286 'es-ES' => [
1287 'http://www.muylinux.com/2015/04/17/odf-reino-unido-microsoft-google',
1288 'es_ES',
1289 ],
1290 ];
1291 }
1292
1293 /**
1294 * @dataProvider dataForLanguage
1295 */
1296 public function testLanguageValidation($url, $expectedLanguage)
1297 {
1298 $this->logInAs('admin');
1299 $client = $this->getClient();
1300
1301 $crawler = $client->request('GET', '/new');
1302
1303 $this->assertEquals(200, $client->getResponse()->getStatusCode());
1304
1305 $form = $crawler->filter('form[name=entry]')->form();
1306
1307 $data = [
1308 'entry[url]' => $url,
1309 ];
1310
1311 $client->submit($form, $data);
1312
1313 $this->assertEquals(302, $client->getResponse()->getStatusCode());
1314
1315 $content = $client->getContainer()
1316 ->get('doctrine.orm.entity_manager')
1317 ->getRepository('WallabagCoreBundle:Entry')
1318 ->findByUrlAndUserId($url, $this->getLoggedInUserId());
1319
1320 $this->assertInstanceOf('Wallabag\CoreBundle\Entity\Entry', $content);
1321 $this->assertEquals($url, $content->getUrl());
1322 $this->assertEquals($expectedLanguage, $content->getLanguage());
1323 }
1257} 1324}
diff --git a/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php b/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php
index a3570125..95dd75ba 100644
--- a/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php
+++ b/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php
@@ -11,6 +11,9 @@ use Wallabag\CoreBundle\Entity\Tag;
11use Wallabag\UserBundle\Entity\User; 11use Wallabag\UserBundle\Entity\User;
12use Wallabag\CoreBundle\Helper\RuleBasedTagger; 12use Wallabag\CoreBundle\Helper\RuleBasedTagger;
13use Graby\Graby; 13use Graby\Graby;
14use Symfony\Component\Validator\Validator\RecursiveValidator;
15use Symfony\Component\Validator\ConstraintViolationList;
16use Symfony\Component\Validator\ConstraintViolation;
14 17
15class ContentProxyTest extends \PHPUnit_Framework_TestCase 18class ContentProxyTest extends \PHPUnit_Framework_TestCase
16{ 19{
@@ -37,7 +40,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
37 'language' => '', 40 'language' => '',
38 ]); 41 ]);
39 42
40 $proxy = new ContentProxy($graby, $tagger, $this->getLogger(), $this->fetchingErrorMessage); 43 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
41 $entry = new Entry(new User()); 44 $entry = new Entry(new User());
42 $proxy->updateEntry($entry, 'http://user@:80'); 45 $proxy->updateEntry($entry, 'http://user@:80');
43 46
@@ -72,7 +75,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
72 'language' => '', 75 'language' => '',
73 ]); 76 ]);
74 77
75 $proxy = new ContentProxy($graby, $tagger, $this->getLogger(), $this->fetchingErrorMessage); 78 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
76 $entry = new Entry(new User()); 79 $entry = new Entry(new User());
77 $proxy->updateEntry($entry, 'http://0.0.0.0'); 80 $proxy->updateEntry($entry, 'http://0.0.0.0');
78 81
@@ -112,7 +115,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
112 ], 115 ],
113 ]); 116 ]);
114 117
115 $proxy = new ContentProxy($graby, $tagger, $this->getLogger(), $this->fetchingErrorMessage); 118 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
116 $entry = new Entry(new User()); 119 $entry = new Entry(new User());
117 $proxy->updateEntry($entry, 'http://domain.io'); 120 $proxy->updateEntry($entry, 'http://domain.io');
118 121
@@ -154,7 +157,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
154 ], 157 ],
155 ]); 158 ]);
156 159
157 $proxy = new ContentProxy($graby, $tagger, $this->getLogger(), $this->fetchingErrorMessage); 160 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
158 $entry = new Entry(new User()); 161 $entry = new Entry(new User());
159 $proxy->updateEntry($entry, 'http://0.0.0.0'); 162 $proxy->updateEntry($entry, 'http://0.0.0.0');
160 163
@@ -192,18 +195,112 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
192 'open_graph' => [ 195 'open_graph' => [
193 'og_title' => 'my OG title', 196 'og_title' => 'my OG title',
194 'og_description' => 'OG desc', 197 'og_description' => 'OG desc',
195 'og_image' => false, 198 'og_image' => null,
196 ], 199 ],
197 ]); 200 ]);
198 201
199 $proxy = new ContentProxy($graby, $tagger, $this->getLogger(), $this->fetchingErrorMessage); 202 $proxy = new ContentProxy($graby, $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
200 $entry = new Entry(new User()); 203 $entry = new Entry(new User());
201 $proxy->updateEntry($entry, 'http://0.0.0.0'); 204 $proxy->updateEntry($entry, 'http://0.0.0.0');
202 205
203 $this->assertEquals('http://1.1.1.1', $entry->getUrl()); 206 $this->assertEquals('http://1.1.1.1', $entry->getUrl());
204 $this->assertEquals('this is my title', $entry->getTitle()); 207 $this->assertEquals('this is my title', $entry->getTitle());
205 $this->assertContains('this is my content', $entry->getContent()); 208 $this->assertContains('this is my content', $entry->getContent());
206 $this->assertNull($entry->getPreviewPicture()); 209 $this->assertEmpty($entry->getPreviewPicture());
210 $this->assertEquals('text/html', $entry->getMimetype());
211 $this->assertEquals('fr', $entry->getLanguage());
212 $this->assertEquals('200', $entry->getHttpStatus());
213 $this->assertEquals(4.0, $entry->getReadingTime());
214 $this->assertEquals('1.1.1.1', $entry->getDomainName());
215 }
216
217 public function testWithContentAndBadLanguage()
218 {
219 $tagger = $this->getTaggerMock();
220 $tagger->expects($this->once())
221 ->method('tag');
222
223 $validator = $this->getValidator();
224 $validator->expects($this->exactly(2))
225 ->method('validate')
226 ->will($this->onConsecutiveCalls(
227 new ConstraintViolationList([new ConstraintViolation('oops', 'oops', [], 'oops', 'language', 'dontexist')]),
228 new ConstraintViolationList()
229 ));
230
231 $graby = $this->getMockBuilder('Graby\Graby')
232 ->setMethods(['fetchContent'])
233 ->disableOriginalConstructor()
234 ->getMock();
235
236 $graby->expects($this->any())
237 ->method('fetchContent')
238 ->willReturn([
239 'html' => str_repeat('this is my content', 325),
240 'title' => 'this is my title',
241 'url' => 'http://1.1.1.1',
242 'content_type' => 'text/html',
243 'language' => 'dontexist',
244 'status' => '200',
245 ]);
246
247 $proxy = new ContentProxy($graby, $tagger, $validator, $this->getLogger(), $this->fetchingErrorMessage);
248 $entry = new Entry(new User());
249 $proxy->updateEntry($entry, 'http://0.0.0.0');
250
251 $this->assertEquals('http://1.1.1.1', $entry->getUrl());
252 $this->assertEquals('this is my title', $entry->getTitle());
253 $this->assertContains('this is my content', $entry->getContent());
254 $this->assertEquals('text/html', $entry->getMimetype());
255 $this->assertEmpty($entry->getLanguage());
256 $this->assertEquals('200', $entry->getHttpStatus());
257 $this->assertEquals(4.0, $entry->getReadingTime());
258 $this->assertEquals('1.1.1.1', $entry->getDomainName());
259 }
260
261 public function testWithContentAndBadOgImage()
262 {
263 $tagger = $this->getTaggerMock();
264 $tagger->expects($this->once())
265 ->method('tag');
266
267 $validator = $this->getValidator();
268 $validator->expects($this->exactly(2))
269 ->method('validate')
270 ->will($this->onConsecutiveCalls(
271 new ConstraintViolationList(),
272 new ConstraintViolationList([new ConstraintViolation('oops', 'oops', [], 'oops', 'url', 'https://')])
273 ));
274
275 $graby = $this->getMockBuilder('Graby\Graby')
276 ->setMethods(['fetchContent'])
277 ->disableOriginalConstructor()
278 ->getMock();
279
280 $graby->expects($this->any())
281 ->method('fetchContent')
282 ->willReturn([
283 'html' => str_repeat('this is my content', 325),
284 'title' => 'this is my title',
285 'url' => 'http://1.1.1.1',
286 'content_type' => 'text/html',
287 'language' => 'fr',
288 'status' => '200',
289 'open_graph' => [
290 'og_title' => 'my OG title',
291 'og_description' => 'OG desc',
292 'og_image' => 'https://',
293 ],
294 ]);
295
296 $proxy = new ContentProxy($graby, $tagger, $validator, $this->getLogger(), $this->fetchingErrorMessage);
297 $entry = new Entry(new User());
298 $proxy->updateEntry($entry, 'http://0.0.0.0');
299
300 $this->assertEquals('http://1.1.1.1', $entry->getUrl());
301 $this->assertEquals('this is my title', $entry->getTitle());
302 $this->assertContains('this is my content', $entry->getContent());
303 $this->assertEmpty($entry->getPreviewPicture());
207 $this->assertEquals('text/html', $entry->getMimetype()); 304 $this->assertEquals('text/html', $entry->getMimetype());
208 $this->assertEquals('fr', $entry->getLanguage()); 305 $this->assertEquals('fr', $entry->getLanguage());
209 $this->assertEquals('200', $entry->getHttpStatus()); 306 $this->assertEquals('200', $entry->getHttpStatus());
@@ -217,7 +314,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
217 $tagger->expects($this->once()) 314 $tagger->expects($this->once())
218 ->method('tag'); 315 ->method('tag');
219 316
220 $proxy = new ContentProxy((new Graby()), $tagger, $this->getLogger(), $this->fetchingErrorMessage); 317 $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
221 $entry = new Entry(new User()); 318 $entry = new Entry(new User());
222 $proxy->updateEntry( 319 $proxy->updateEntry(
223 $entry, 320 $entry,
@@ -259,7 +356,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
259 $logHandler = new TestHandler(); 356 $logHandler = new TestHandler();
260 $logger = new Logger('test', [$logHandler]); 357 $logger = new Logger('test', [$logHandler]);
261 358
262 $proxy = new ContentProxy((new Graby()), $tagger, $logger, $this->fetchingErrorMessage); 359 $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $logger, $this->fetchingErrorMessage);
263 $entry = new Entry(new User()); 360 $entry = new Entry(new User());
264 $proxy->updateEntry( 361 $proxy->updateEntry(
265 $entry, 362 $entry,
@@ -294,7 +391,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
294 $handler = new TestHandler(); 391 $handler = new TestHandler();
295 $logger->pushHandler($handler); 392 $logger->pushHandler($handler);
296 393
297 $proxy = new ContentProxy((new Graby()), $tagger, $logger, $this->fetchingErrorMessage); 394 $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $logger, $this->fetchingErrorMessage);
298 $entry = new Entry(new User()); 395 $entry = new Entry(new User());
299 $proxy->updateEntry( 396 $proxy->updateEntry(
300 $entry, 397 $entry,
@@ -331,7 +428,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
331 ->method('tag') 428 ->method('tag')
332 ->will($this->throwException(new \Exception())); 429 ->will($this->throwException(new \Exception()));
333 430
334 $proxy = new ContentProxy((new Graby()), $tagger, $this->getLogger(), $this->fetchingErrorMessage); 431 $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
335 $entry = new Entry(new User()); 432 $entry = new Entry(new User());
336 $proxy->updateEntry( 433 $proxy->updateEntry(
337 $entry, 434 $entry,
@@ -371,7 +468,7 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
371 $tagger->expects($this->once()) 468 $tagger->expects($this->once())
372 ->method('tag'); 469 ->method('tag');
373 470
374 $proxy = new ContentProxy((new Graby()), $tagger, $this->getLogger(), $this->fetchingErrorMessage); 471 $proxy = new ContentProxy((new Graby()), $tagger, $this->getValidator(), $this->getLogger(), $this->fetchingErrorMessage);
375 $entry = new Entry(new User()); 472 $entry = new Entry(new User());
376 $proxy->updateEntry( 473 $proxy->updateEntry(
377 $entry, 474 $entry,
@@ -413,4 +510,12 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
413 { 510 {
414 return new NullLogger(); 511 return new NullLogger();
415 } 512 }
513
514 private function getValidator()
515 {
516 return $this->getMockBuilder(RecursiveValidator::class)
517 ->setMethods(['validate'])
518 ->disableOriginalConstructor()
519 ->getMock();
520 }
416} 521}