aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJeremy Benoist <jeremy.benoist@gmail.com>2017-06-30 16:54:26 +0200
committerJeremy Benoist <jeremy.benoist@gmail.com>2017-07-03 13:45:04 +0200
commita05b61159e147776f63baee731b5026796e5f7ae (patch)
tree80dae1ea169f3fb072a7aa2072f24507e10e91ed
parent71e1cbc8eb5928d393b0772055d6b711e90a09b3 (diff)
downloadwallabag-a05b61159e147776f63baee731b5026796e5f7ae.tar.gz
wallabag-a05b61159e147776f63baee731b5026796e5f7ae.tar.zst
wallabag-a05b61159e147776f63baee731b5026796e5f7ae.zip
Fix PATCH method
The PATCH method for the entry should only update what user sent to us and not the whole entry as it was before. Also, sending tags when patching an entry will now remove all current tags & assocatied new ones.
-rw-r--r--src/Wallabag/ApiBundle/Controller/EntryRestController.php211
-rw-r--r--src/Wallabag/CoreBundle/Entity/Entry.php16
-rw-r--r--src/Wallabag/CoreBundle/Helper/ContentProxy.php89
-rw-r--r--tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php68
-rw-r--r--tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php7
5 files changed, 270 insertions, 121 deletions
diff --git a/src/Wallabag/ApiBundle/Controller/EntryRestController.php b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
index ca460c84..a2e913af 100644
--- a/src/Wallabag/ApiBundle/Controller/EntryRestController.php
+++ b/src/Wallabag/ApiBundle/Controller/EntryRestController.php
@@ -299,8 +299,8 @@ class EntryRestController extends WallabagRestController
299 * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."}, 299 * {"name"="url", "dataType"="string", "required"=true, "format"="http://www.test.com/article.html", "description"="Url for the entry."},
300 * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."}, 300 * {"name"="title", "dataType"="string", "required"=false, "description"="Optional, we'll get the title from the page."},
301 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."}, 301 * {"name"="tags", "dataType"="string", "required"=false, "format"="tag1,tag2,tag3", "description"="a comma-separated list of tags."},
302 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"},
303 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"}, 302 * {"name"="archive", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already archived"},
303 * {"name"="starred", "dataType"="integer", "required"=false, "format"="1 or 0", "description"="entry already starred"},
304 * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"}, 304 * {"name"="content", "dataType"="string", "required"=false, "description"="Content of the entry"},
305 * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"}, 305 * {"name"="language", "dataType"="string", "required"=false, "description"="Language of the entry"},
306 * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"}, 306 * {"name"="preview_picture", "dataType"="string", "required"=false, "description"="Preview picture of the entry"},
@@ -328,7 +328,58 @@ class EntryRestController extends WallabagRestController
328 $entry->setUrl($url); 328 $entry->setUrl($url);
329 } 329 }
330 330
331 $this->upsertEntry($entry, $request); 331 $data = $this->retrieveValueFromRequest($request);
332
333 try {
334 $this->get('wallabag_core.content_proxy')->updateEntry(
335 $entry,
336 $entry->getUrl(),
337 [
338 'title' => !empty($data['title']) ? $data['title'] : $entry->getTitle(),
339 'html' => !empty($data['content']) ? $data['content'] : $entry->getContent(),
340 'url' => $entry->getUrl(),
341 'language' => !empty($data['language']) ? $data['language'] : $entry->getLanguage(),
342 'date' => !empty($data['publishedAt']) ? $data['publishedAt'] : $entry->getPublishedAt(),
343 // faking the open graph preview picture
344 'open_graph' => [
345 'og_image' => !empty($data['picture']) ? $data['picture'] : $entry->getPreviewPicture(),
346 ],
347 'authors' => is_string($data['authors']) ? explode(',', $data['authors']) : $entry->getPublishedBy(),
348 ]
349 );
350 } catch (\Exception $e) {
351 $this->get('logger')->error('Error while saving an entry', [
352 'exception' => $e,
353 'entry' => $entry,
354 ]);
355 }
356
357 if (!is_null($data['isArchived'])) {
358 $entry->setArchived((bool) $data['isArchived']);
359 }
360
361 if (!is_null($data['isStarred'])) {
362 $entry->setStarred((bool) $data['isStarred']);
363 }
364
365 if (!empty($data['tags'])) {
366 $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $data['tags']);
367 }
368
369 if (!is_null($data['isPublic'])) {
370 if (true === (bool) $data['isPublic'] && null === $entry->getUid()) {
371 $entry->generateUid();
372 } elseif (false === (bool) $data['isPublic']) {
373 $entry->cleanUid();
374 }
375 }
376
377 $em = $this->getDoctrine()->getManager();
378 $em->persist($entry);
379 $em->flush();
380
381 // entry saved, dispatch event about it!
382 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
332 383
333 return $this->sendResponse($entry); 384 return $this->sendResponse($entry);
334 } 385 }
@@ -361,7 +412,78 @@ class EntryRestController extends WallabagRestController
361 $this->validateAuthentication(); 412 $this->validateAuthentication();
362 $this->validateUserAccess($entry->getUser()->getId()); 413 $this->validateUserAccess($entry->getUser()->getId());
363 414
364 $this->upsertEntry($entry, $request, true); 415 $contentProxy = $this->get('wallabag_core.content_proxy');
416
417 $data = $this->retrieveValueFromRequest($request);
418
419 // this is a special case where user want to manually update the entry content
420 // the ContentProxy will only cleanup the html
421 // and also we force to not re-fetch the content in case of error
422 if (!empty($data['content'])) {
423 try {
424 $contentProxy->updateEntry(
425 $entry,
426 $entry->getUrl(),
427 [
428 'html' => $data['content'],
429 ],
430 true
431 );
432 } catch (\Exception $e) {
433 $this->get('logger')->error('Error while saving an entry', [
434 'exception' => $e,
435 'entry' => $entry,
436 ]);
437 }
438 }
439
440 if (!empty($data['title'])) {
441 $entry->setTitle($data['title']);
442 }
443
444 if (!empty($data['language'])) {
445 $contentProxy->updateLanguage($entry, $data['language']);
446 }
447
448 if (!empty($data['authors']) && is_string($data['authors'])) {
449 $entry->setPublishedBy(explode(',', $data['authors']));
450 }
451
452 if (!empty($data['picture'])) {
453 $contentProxy->updatePreviewPicture($entry, $data['picture']);
454 }
455
456 if (!empty($data['publishedAt'])) {
457 $contentProxy->updatePublishedAt($entry, $data['publishedAt']);
458 }
459
460 if (!is_null($data['isArchived'])) {
461 $entry->setArchived((bool) $data['isArchived']);
462 }
463
464 if (!is_null($data['isStarred'])) {
465 $entry->setStarred((bool) $data['isStarred']);
466 }
467
468 if (!empty($data['tags'])) {
469 $entry->removeAllTags();
470 $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $data['tags']);
471 }
472
473 if (!is_null($data['isPublic'])) {
474 if (true === (bool) $data['isPublic'] && null === $entry->getUid()) {
475 $entry->generateUid();
476 } elseif (false === (bool) $data['isPublic']) {
477 $entry->cleanUid();
478 }
479 }
480
481 $em = $this->getDoctrine()->getManager();
482 $em->persist($entry);
483 $em->flush();
484
485 // entry saved, dispatch event about it!
486 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
365 487
366 return $this->sendResponse($entry); 488 return $this->sendResponse($entry);
367 } 489 }
@@ -634,76 +756,27 @@ class EntryRestController extends WallabagRestController
634 } 756 }
635 757
636 /** 758 /**
637 * Update or Insert a new entry. 759 * Retrieve value from the request.
760 * Used for POST & PATCH on a an entry.
638 * 761 *
639 * @param Entry $entry
640 * @param Request $request 762 * @param Request $request
641 * @param bool $disableContentUpdate If we don't want the content to be update by fetching the url (used when patching instead of posting) 763 *
764 * @return array
642 */ 765 */
643 private function upsertEntry(Entry $entry, Request $request, $disableContentUpdate = false) 766 private function retrieveValueFromRequest(Request $request)
644 { 767 {
645 $title = $request->request->get('title'); 768 return [
646 $tags = $request->request->get('tags', []); 769 'title' => $request->request->get('title'),
647 $isArchived = $request->request->get('archive'); 770 'tags' => $request->request->get('tags', []),
648 $isStarred = $request->request->get('starred'); 771 'isArchived' => $request->request->get('archive'),
649 $isPublic = $request->request->get('public'); 772 'isStarred' => $request->request->get('starred'),
650 $content = $request->request->get('content'); 773 'isPublic' => $request->request->get('public'),
651 $language = $request->request->get('language'); 774 'content' => $request->request->get('content'),
652 $picture = $request->request->get('preview_picture'); 775 'language' => $request->request->get('language'),
653 $publishedAt = $request->request->get('published_at'); 776 'picture' => $request->request->get('preview_picture'),
654 $authors = $request->request->get('authors', ''); 777 'publishedAt' => $request->request->get('published_at'),
655 778 'authors' => $request->request->get('authors', ''),
656 try { 779 ];
657 $this->get('wallabag_core.content_proxy')->updateEntry(
658 $entry,
659 $entry->getUrl(),
660 [
661 'title' => !empty($title) ? $title : $entry->getTitle(),
662 'html' => !empty($content) ? $content : $entry->getContent(),
663 'url' => $entry->getUrl(),
664 'language' => !empty($language) ? $language : $entry->getLanguage(),
665 'date' => !empty($publishedAt) ? $publishedAt : $entry->getPublishedAt(),
666 // faking the open graph preview picture
667 'open_graph' => [
668 'og_image' => !empty($picture) ? $picture : $entry->getPreviewPicture(),
669 ],
670 'authors' => is_string($authors) ? explode(',', $authors) : $entry->getPublishedBy(),
671 ],
672 $disableContentUpdate
673 );
674 } catch (\Exception $e) {
675 $this->get('logger')->error('Error while saving an entry', [
676 'exception' => $e,
677 'entry' => $entry,
678 ]);
679 }
680
681 if (null !== $isArchived) {
682 $entry->setArchived((bool) $isArchived);
683 }
684
685 if (null !== $isStarred) {
686 $entry->setStarred((bool) $isStarred);
687 }
688
689 if (!empty($tags)) {
690 $this->get('wallabag_core.tags_assigner')->assignTagsToEntry($entry, $tags);
691 }
692
693 if (null !== $isPublic) {
694 if (true === (bool) $isPublic && null === $entry->getUid()) {
695 $entry->generateUid();
696 } elseif (false === (bool) $isPublic) {
697 $entry->cleanUid();
698 }
699 }
700
701 $em = $this->getDoctrine()->getManager();
702 $em->persist($entry);
703 $em->flush();
704
705 // entry saved, dispatch event about it!
706 $this->get('event_dispatcher')->dispatch(EntrySavedEvent::NAME, new EntrySavedEvent($entry));
707 } 780 }
708 781
709 /** 782 /**
diff --git a/src/Wallabag/CoreBundle/Entity/Entry.php b/src/Wallabag/CoreBundle/Entity/Entry.php
index 581e8906..cba72d31 100644
--- a/src/Wallabag/CoreBundle/Entity/Entry.php
+++ b/src/Wallabag/CoreBundle/Entity/Entry.php
@@ -593,6 +593,11 @@ class Entry
593 $tag->addEntry($this); 593 $tag->addEntry($this);
594 } 594 }
595 595
596 /**
597 * Remove the given tag from the entry (if the tag is associated).
598 *
599 * @param Tag $tag
600 */
596 public function removeTag(Tag $tag) 601 public function removeTag(Tag $tag)
597 { 602 {
598 if (!$this->tags->contains($tag)) { 603 if (!$this->tags->contains($tag)) {
@@ -604,6 +609,17 @@ class Entry
604 } 609 }
605 610
606 /** 611 /**
612 * Remove all assigned tags from the entry.
613 */
614 public function removeAllTags()
615 {
616 foreach ($this->tags as $tag) {
617 $this->tags->removeElement($tag);
618 $tag->removeEntry($this);
619 }
620 }
621
622 /**
607 * Set previewPicture. 623 * Set previewPicture.
608 * 624 *
609 * @param string $previewPicture 625 * @param string $previewPicture
diff --git a/src/Wallabag/CoreBundle/Helper/ContentProxy.php b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
index ddecd6f4..2a650e97 100644
--- a/src/Wallabag/CoreBundle/Helper/ContentProxy.php
+++ b/src/Wallabag/CoreBundle/Helper/ContentProxy.php
@@ -75,9 +75,17 @@ class ContentProxy
75 */ 75 */
76 private function stockEntry(Entry $entry, array $content) 76 private function stockEntry(Entry $entry, array $content)
77 { 77 {
78 $title = $content['title']; 78 $entry->setUrl($content['url']);
79 if (!$title && !empty($content['open_graph']['og_title'])) { 79
80 $title = $content['open_graph']['og_title']; 80 $domainName = parse_url($entry->getUrl(), PHP_URL_HOST);
81 if (false !== $domainName) {
82 $entry->setDomainName($domainName);
83 }
84
85 if (!empty($content['title'])) {
86 $entry->setTitle($content['title']);
87 } elseif (!empty($content['open_graph']['og_title'])) {
88 $entry->setTitle($content['open_graph']['og_title']);
81 } 89 }
82 90
83 $html = $content['html']; 91 $html = $content['html'];
@@ -90,24 +98,11 @@ class ContentProxy
90 } 98 }
91 } 99 }
92 100
93 $entry->setUrl($content['url']);
94 $entry->setTitle($title);
95 $entry->setContent($html); 101 $entry->setContent($html);
96 $entry->setHttpStatus(isset($content['status']) ? $content['status'] : ''); 102 $entry->setReadingTime(Utils::getReadingTime($html));
97
98 if (!empty($content['date'])) {
99 $date = $content['date'];
100
101 // is it a timestamp?
102 if (filter_var($date, FILTER_VALIDATE_INT) !== false) {
103 $date = '@' . $content['date'];
104 }
105 103
106 try { 104 if (!empty($content['status'])) {
107 $entry->setPublishedAt(new \DateTime($date)); 105 $entry->setHttpStatus($content['status']);
108 } catch (\Exception $e) {
109 $this->logger->warning('Error while defining date', ['e' => $e, 'url' => $content['url'], 'date' => $content['date']]);
110 }
111 } 106 }
112 107
113 if (!empty($content['authors']) && is_array($content['authors'])) { 108 if (!empty($content['authors']) && is_array($content['authors'])) {
@@ -118,15 +113,17 @@ class ContentProxy
118 $entry->setHeaders($content['all_headers']); 113 $entry->setHeaders($content['all_headers']);
119 } 114 }
120 115
121 $this->validateAndSetLanguage( 116 if (!empty($content['date'])) {
122 $entry, 117 $this->updatePublishedAt($entry, $content['date']);
123 isset($content['language']) ? $content['language'] : null 118 }
124 );
125 119
126 $this->validateAndSetPreviewPicture( 120 if (!empty($content['language'])) {
127 $entry, 121 $this->updateLanguage($entry, $content['language']);
128 isset($content['open_graph']['og_image']) ? $content['open_graph']['og_image'] : null 122 }
129 ); 123
124 if (!empty($content['open_graph']['og_image'])) {
125 $this->updatePreviewPicture($entry, $content['open_graph']['og_image']);
126 }
130 127
131 // if content is an image, define it as a preview too 128 // if content is an image, define it as a preview too
132 if (!empty($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) { 129 if (!empty($content['content_type']) && in_array($this->mimeGuesser->guess($content['content_type']), ['jpeg', 'jpg', 'gif', 'png'], true)) {
@@ -136,12 +133,8 @@ class ContentProxy
136 ); 133 );
137 } 134 }
138 135
139 $entry->setMimetype(isset($content['content_type']) ? $content['content_type'] : ''); 136 if (!empty($content['content_type'])) {
140 $entry->setReadingTime(Utils::getReadingTime($html)); 137 $entry->setMimetype($content['content_type']);
141
142 $domainName = parse_url($entry->getUrl(), PHP_URL_HOST);
143 if (false !== $domainName) {
144 $entry->setDomainName($domainName);
145 } 138 }
146 139
147 try { 140 try {
@@ -170,9 +163,9 @@ class ContentProxy
170 * Use a Symfony validator to ensure the language is well formatted. 163 * Use a Symfony validator to ensure the language is well formatted.
171 * 164 *
172 * @param Entry $entry 165 * @param Entry $entry
173 * @param string $value Language to validate 166 * @param string $value Language to validate and save
174 */ 167 */
175 private function validateAndSetLanguage($entry, $value) 168 public function updateLanguage(Entry $entry, $value)
176 { 169 {
177 // some lang are defined as fr-FR, es-ES. 170 // some lang are defined as fr-FR, es-ES.
178 // replacing - by _ might increase language support 171 // replacing - by _ might increase language support
@@ -196,9 +189,9 @@ class ContentProxy
196 * Use a Symfony validator to ensure the preview picture is a real url. 189 * Use a Symfony validator to ensure the preview picture is a real url.
197 * 190 *
198 * @param Entry $entry 191 * @param Entry $entry
199 * @param string $value URL to validate 192 * @param string $value URL to validate and save
200 */ 193 */
201 private function validateAndSetPreviewPicture($entry, $value) 194 public function updatePreviewPicture(Entry $entry, $value)
202 { 195 {
203 $errors = $this->validator->validate( 196 $errors = $this->validator->validate(
204 $value, 197 $value,
@@ -213,4 +206,26 @@ class ContentProxy
213 206
214 $this->logger->warning('PreviewPicture validation failed. ' . (string) $errors); 207 $this->logger->warning('PreviewPicture validation failed. ' . (string) $errors);
215 } 208 }
209
210 /**
211 * Update date.
212 *
213 * @param Entry $entry
214 * @param string $value Date to validate and save
215 */
216 public function updatePublishedAt(Entry $entry, $value)
217 {
218 $date = $value;
219
220 // is it a timestamp?
221 if (filter_var($date, FILTER_VALIDATE_INT) !== false) {
222 $date = '@'.$value;
223 }
224
225 try {
226 $entry->setPublishedAt(new \DateTime($date));
227 } catch (\Exception $e) {
228 $this->logger->warning('Error while defining date', ['e' => $e, 'url' => $entry->getUrl(), 'date' => $value]);
229 }
230 }
216} 231}
diff --git a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
index ae4af4cd..0647bb23 100644
--- a/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
+++ b/tests/Wallabag/ApiBundle/Controller/EntryRestControllerTest.php
@@ -519,10 +519,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
519 $this->markTestSkipped('No content found in db.'); 519 $this->markTestSkipped('No content found in db.');
520 } 520 }
521 521
522 // hydrate the tags relations 522 $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [
523 $nbTags = count($entry->getTags());
524
525 $this->client->request('PATCH', '/api/entries/' . $entry->getId() . '.json', [
526 'title' => 'New awesome title', 523 'title' => 'New awesome title',
527 'tags' => 'new tag ' . uniqid(), 524 'tags' => 'new tag ' . uniqid(),
528 'starred' => '1', 525 'starred' => '1',
@@ -532,6 +529,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
532 'authors' => 'bob,sponge', 529 'authors' => 'bob,sponge',
533 'content' => 'awesome', 530 'content' => 'awesome',
534 'public' => 0, 531 'public' => 0,
532 'published_at' => 1488833381,
535 ]); 533 ]);
536 534
537 $this->assertSame(200, $this->client->getResponse()->getStatusCode()); 535 $this->assertSame(200, $this->client->getResponse()->getStatusCode());
@@ -541,7 +539,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
541 $this->assertSame($entry->getId(), $content['id']); 539 $this->assertSame($entry->getId(), $content['id']);
542 $this->assertSame($entry->getUrl(), $content['url']); 540 $this->assertSame($entry->getUrl(), $content['url']);
543 $this->assertSame('New awesome title', $content['title']); 541 $this->assertSame('New awesome title', $content['title']);
544 $this->assertGreaterThan($nbTags, count($content['tags'])); 542 $this->assertGreaterThanOrEqual(1, count($content['tags']), 'We force only one tag');
545 $this->assertSame(1, $content['user_id']); 543 $this->assertSame(1, $content['user_id']);
546 $this->assertSame('de_AT', $content['language']); 544 $this->assertSame('de_AT', $content['language']);
547 $this->assertSame('http://preview.io/picture.jpg', $content['preview_picture']); 545 $this->assertSame('http://preview.io/picture.jpg', $content['preview_picture']);
@@ -549,6 +547,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
549 $this->assertContains('bob', $content['published_by']); 547 $this->assertContains('bob', $content['published_by']);
550 $this->assertSame('awesome', $content['content']); 548 $this->assertSame('awesome', $content['content']);
551 $this->assertFalse($content['is_public'], 'Entry is no more shared'); 549 $this->assertFalse($content['is_public'], 'Entry is no more shared');
550 $this->assertContains('2017-03-06', $content['published_at']);
552 } 551 }
553 552
554 public function testPatchEntryWithoutQuotes() 553 public function testPatchEntryWithoutQuotes()
@@ -562,8 +561,8 @@ class EntryRestControllerTest extends WallabagApiTestCase
562 $this->markTestSkipped('No content found in db.'); 561 $this->markTestSkipped('No content found in db.');
563 } 562 }
564 563
565 // hydrate the tags relations 564 $previousContent = $entry->getContent();
566 $nbTags = count($entry->getTags()); 565 $previousLanguage = $entry->getLanguage();
567 566
568 $this->client->request('PATCH', '/api/entries/' . $entry->getId() . '.json', [ 567 $this->client->request('PATCH', '/api/entries/' . $entry->getId() . '.json', [
569 'title' => 'New awesome title', 568 'title' => 'New awesome title',
@@ -579,9 +578,11 @@ class EntryRestControllerTest extends WallabagApiTestCase
579 578
580 $this->assertSame($entry->getId(), $content['id']); 579 $this->assertSame($entry->getId(), $content['id']);
581 $this->assertSame($entry->getUrl(), $content['url']); 580 $this->assertSame($entry->getUrl(), $content['url']);
582 $this->assertSame('New awesome title', $content['title']); 581 $this->assertGreaterThanOrEqual(1, count($content['tags']), 'We force only one tag');
583 $this->assertGreaterThan($nbTags, count($content['tags'])); 582 $this->assertGreaterThan($nbTags, count($content['tags']));
584 $this->assertEmpty($content['published_by'], 'Authors were not saved because of an array instead of a string'); 583 $this->assertEmpty($content['published_by'], 'Authors were not saved because of an array instead of a string');
584 $this->assertEquals($previousContent, $content['content'], 'Ensure content has not moved');
585 $this->assertEquals($previousLanguage, $content['language'], 'Ensure language has not moved');
585 } 586 }
586 587
587 public function testGetTagsEntry() 588 public function testGetTagsEntry()
@@ -727,8 +728,10 @@ class EntryRestControllerTest extends WallabagApiTestCase
727 $this->markTestSkipped('No content found in db.'); 728 $this->markTestSkipped('No content found in db.');
728 } 729 }
729 730
730 $this->client->request('PATCH', '/api/entries/' . $entry->getId() . '.json', [ 731 $previousTitle = $entry->getTitle();
731 'title' => $entry->getTitle() . '++', 732
733 $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', [
734 'title' => $entry->getTitle().'++',
732 ]); 735 ]);
733 736
734 $this->assertSame(200, $this->client->getResponse()->getStatusCode()); 737 $this->assertSame(200, $this->client->getResponse()->getStatusCode());
@@ -736,6 +739,7 @@ class EntryRestControllerTest extends WallabagApiTestCase
736 $content = json_decode($this->client->getResponse()->getContent(), true); 739 $content = json_decode($this->client->getResponse()->getContent(), true);
737 740
738 $this->assertSame(1, $content['is_archived']); 741 $this->assertSame(1, $content['is_archived']);
742 $this->assertEquals($previousTitle.'++', $content['title']);
739 } 743 }
740 744
741 public function testSaveIsStarredAfterPatch() 745 public function testSaveIsStarredAfterPatch()
@@ -907,6 +911,17 @@ class EntryRestControllerTest extends WallabagApiTestCase
907 $this->assertCount(4, $tags); 911 $this->assertCount(4, $tags);
908 } 912 }
909 913
914 public function testPostEntriesTagsListActionNoList()
915 {
916 $this->client->request('POST', '/api/entries/tags/lists?list='.json_encode([]));
917
918 $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
919
920 $content = json_decode($this->client->getResponse()->getContent(), true);
921
922 $this->assertEmpty($content);
923 }
924
910 public function testDeleteEntriesTagsListAction() 925 public function testDeleteEntriesTagsListAction()
911 { 926 {
912 $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); 927 $em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
@@ -933,6 +948,17 @@ class EntryRestControllerTest extends WallabagApiTestCase
933 $this->assertCount(0, $entry->getTags()); 948 $this->assertCount(0, $entry->getTags());
934 } 949 }
935 950
951 public function testDeleteEntriesTagsListActionNoList()
952 {
953 $this->client->request('DELETE', '/api/entries/tags/list?list='.json_encode([]));
954
955 $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
956
957 $content = json_decode($this->client->getResponse()->getContent(), true);
958
959 $this->assertEmpty($content);
960 }
961
936 public function testPostEntriesListAction() 962 public function testPostEntriesListAction()
937 { 963 {
938 $list = [ 964 $list = [
@@ -953,6 +979,17 @@ class EntryRestControllerTest extends WallabagApiTestCase
953 $this->assertSame('http://0.0.0.0/entry2', $content[1]['url']); 979 $this->assertSame('http://0.0.0.0/entry2', $content[1]['url']);
954 } 980 }
955 981
982 public function testPostEntriesListActionWithNoUrls()
983 {
984 $this->client->request('POST', '/api/entries/lists?urls='.json_encode([]));
985
986 $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
987
988 $content = json_decode($this->client->getResponse()->getContent(), true);
989
990 $this->assertEmpty($content);
991 }
992
956 public function testDeleteEntriesListAction() 993 public function testDeleteEntriesListAction()
957 { 994 {
958 $em = $this->client->getContainer()->get('doctrine.orm.entity_manager'); 995 $em = $this->client->getContainer()->get('doctrine.orm.entity_manager');
@@ -978,6 +1015,17 @@ class EntryRestControllerTest extends WallabagApiTestCase
978 $this->assertSame('http://0.0.0.0/test-entry-not-exist', $content[1]['url']); 1015 $this->assertSame('http://0.0.0.0/test-entry-not-exist', $content[1]['url']);
979 } 1016 }
980 1017
1018 public function testDeleteEntriesListActionWithNoUrls()
1019 {
1020 $this->client->request('DELETE', '/api/entries/list?urls='.json_encode([]));
1021
1022 $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
1023
1024 $content = json_decode($this->client->getResponse()->getContent(), true);
1025
1026 $this->assertEmpty($content);
1027 }
1028
981 public function testLimitBulkAction() 1029 public function testLimitBulkAction()
982 { 1030 {
983 $list = [ 1031 $list = [
diff --git a/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php b/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php
index c63671c4..f394b947 100644
--- a/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php
+++ b/tests/Wallabag/CoreBundle/Helper/ContentProxyTest.php
@@ -221,12 +221,9 @@ class ContentProxyTest extends \PHPUnit_Framework_TestCase
221 ->method('tag'); 221 ->method('tag');
222 222
223 $validator = $this->getValidator(); 223 $validator = $this->getValidator();
224 $validator->expects($this->exactly(2)) 224 $validator->expects($this->once())
225 ->method('validate') 225 ->method('validate')
226 ->will($this->onConsecutiveCalls( 226 ->willReturn(new ConstraintViolationList([new ConstraintViolation('oops', 'oops', [], 'oops', 'language', 'dontexist')]));
227 new ConstraintViolationList([new ConstraintViolation('oops', 'oops', [], 'oops', 'language', 'dontexist')]),
228 new ConstraintViolationList()
229 ));
230 227
231 $graby = $this->getMockBuilder('Graby\Graby') 228 $graby = $this->getMockBuilder('Graby\Graby')
232 ->setMethods(['fetchContent']) 229 ->setMethods(['fetchContent'])