3 declare(strict_types
=1);
5 namespace Shaarli\Front\Controller\Admin
;
7 use PHPUnit\Framework\TestCase
;
8 use Shaarli\Bookmark\Bookmark
;
9 use Shaarli\Config\ConfigManager
;
10 use Shaarli\Front\Exception\WrongTokenException
;
11 use Shaarli\Http\HttpAccess
;
12 use Shaarli\Security\SessionManager
;
13 use Shaarli\Thumbnailer
;
14 use Slim\Http\Request
;
15 use Slim\Http\Response
;
18 class PostBookmarkControllerTest
extends TestCase
20 use FrontAdminControllerMockHelper
;
22 /** @var PostBookmarkController */
23 protected $controller;
25 public function setUp(): void
27 $this->createContainer();
29 $this->container
->httpAccess
= $this->createMock(HttpAccess
::class);
30 $this->controller
= new PostBookmarkController($this->container
);
34 * Test displaying add link page
36 public function testAddShaare(): void
38 $assignedVariables = [];
39 $this->assignTemplateVars($assignedVariables);
41 $request = $this->createMock(Request
::class);
42 $response = new Response();
44 $result = $this->controller
->addShaare($request, $response);
46 static::assertSame(200, $result->getStatusCode());
47 static::assertSame('addlink', (string) $result->getBody());
49 static::assertSame('Shaare a new link - Shaarli', $assignedVariables['pagetitle']);
53 * Test displaying bookmark create form
54 * Ensure that every step of the standard workflow works properly.
56 public function testDisplayCreateFormWithUrl(): void
58 $this->container
->environment
= [
59 'HTTP_REFERER' => $referer = 'http://shaarli/subfolder/controller/?searchtag=abc'
62 $assignedVariables = [];
63 $this->assignTemplateVars($assignedVariables);
65 $url = 'http://url.tld/other?part=3&utm_ad=pay#hash';
66 $expectedUrl = str_replace('&utm_ad=pay', '', $url);
67 $remoteTitle = 'Remote Title';
68 $remoteDesc = 'Sometimes the meta description is relevant.';
69 $remoteTags = 'abc def';
71 $request = $this->createMock(Request
::class);
72 $request->method('getParam')->willReturnCallback(function (string $key) use ($url): ?string {
73 return $key === 'post' ? $url : null;
75 $response = new Response();
77 $this->container
->httpAccess
78 ->expects(static::once())
79 ->method('getCurlDownloadCallback')
81 function (&$charset, &$title, &$description, &$tags) use (
86 return function () use (
95 $charset = 'ISO-8859-1';
96 $title = $remoteTitle;
97 $description = $remoteDesc;
103 $this->container
->httpAccess
104 ->expects(static::once())
105 ->method('getHttpResponse')
106 ->with($expectedUrl, 30, 4194304)
107 ->willReturnCallback(function($url, $timeout, $maxBytes, $callback): void {
112 $this->container
->bookmarkService
113 ->expects(static::once())
114 ->method('bookmarksCountPerTag')
115 ->willReturn($tags = ['tag1' => 2, 'tag2' => 1])
118 // Make sure that PluginManager hook is triggered
119 $this->container
->pluginManager
120 ->expects(static::at(0))
121 ->method('executeHooks')
122 ->willReturnCallback(function (string $hook, array $data) use ($remoteTitle, $remoteDesc): array {
123 static::assertSame('render_editlink', $hook);
124 static::assertSame($remoteTitle, $data['link']['title']);
125 static::assertSame($remoteDesc, $data['link']['description']);
131 $result = $this->controller
->displayCreateForm($request, $response);
133 static::assertSame(200, $result->getStatusCode());
134 static::assertSame('editlink', (string) $result->getBody());
136 static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']);
138 static::assertSame($expectedUrl, $assignedVariables['link']['url']);
139 static::assertSame($remoteTitle, $assignedVariables['link']['title']);
140 static::assertSame($remoteDesc, $assignedVariables['link']['description']);
141 static::assertSame($remoteTags, $assignedVariables['link']['tags']);
142 static::assertFalse($assignedVariables['link']['private']);
144 static::assertTrue($assignedVariables['link_is_new']);
145 static::assertSame($referer, $assignedVariables['http_referer']);
146 static::assertSame($tags, $assignedVariables['tags']);
147 static::assertArrayHasKey('source', $assignedVariables);
148 static::assertArrayHasKey('default_private_links', $assignedVariables);
152 * Test displaying bookmark create form
153 * Ensure all available query parameters are handled properly.
155 public function testDisplayCreateFormWithFullParameters(): void
157 $assignedVariables = [];
158 $this->assignTemplateVars($assignedVariables);
161 'post' => 'http://url.tld/other?part=3&utm_ad=pay#hash',
162 'title' => 'Provided Title',
163 'description' => 'Provided description.',
168 $expectedUrl = str_replace('&utm_ad=pay', '', $parameters['post']);
170 $request = $this->createMock(Request
::class);
173 ->willReturnCallback(function (string $key) use ($parameters): ?string {
174 return $parameters[$key] ?? null;
176 $response = new Response();
178 $result = $this->controller
->displayCreateForm($request, $response);
180 static::assertSame(200, $result->getStatusCode());
181 static::assertSame('editlink', (string) $result->getBody());
183 static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']);
185 static::assertSame($expectedUrl, $assignedVariables['link']['url']);
186 static::assertSame($parameters['title'], $assignedVariables['link']['title']);
187 static::assertSame($parameters['description'], $assignedVariables['link']['description']);
188 static::assertSame($parameters['tags'], $assignedVariables['link']['tags']);
189 static::assertTrue($assignedVariables['link']['private']);
190 static::assertTrue($assignedVariables['link_is_new']);
191 static::assertSame($parameters['source'], $assignedVariables['source']);
195 * Test displaying bookmark create form
196 * Without any parameter.
198 public function testDisplayCreateFormEmpty(): void
200 $assignedVariables = [];
201 $this->assignTemplateVars($assignedVariables);
203 $request = $this->createMock(Request
::class);
204 $response = new Response();
206 $this->container
->httpAccess
->expects(static::never())->method('getHttpResponse');
207 $this->container
->httpAccess
->expects(static::never())->method('getCurlDownloadCallback');
209 $result = $this->controller
->displayCreateForm($request, $response);
211 static::assertSame(200, $result->getStatusCode());
212 static::assertSame('editlink', (string) $result->getBody());
213 static::assertSame('', $assignedVariables['link']['url']);
214 static::assertSame('Note: ', $assignedVariables['link']['title']);
215 static::assertSame('', $assignedVariables['link']['description']);
216 static::assertSame('', $assignedVariables['link']['tags']);
217 static::assertFalse($assignedVariables['link']['private']);
218 static::assertTrue($assignedVariables['link_is_new']);
222 * Test displaying bookmark create form
223 * URL not using HTTP protocol: do not try to retrieve the title
225 public function testDisplayCreateFormNotHttp(): void
227 $assignedVariables = [];
228 $this->assignTemplateVars($assignedVariables);
230 $url = 'magnet://kubuntu.torrent';
231 $request = $this->createMock(Request
::class);
234 ->willReturnCallback(function (string $key) use ($url): ?string {
235 return $key === 'post' ? $url : null;
237 $response = new Response();
239 $this->container
->httpAccess
->expects(static::never())->method('getHttpResponse');
240 $this->container
->httpAccess
->expects(static::never())->method('getCurlDownloadCallback');
242 $result = $this->controller
->displayCreateForm($request, $response);
244 static::assertSame(200, $result->getStatusCode());
245 static::assertSame('editlink', (string) $result->getBody());
246 static::assertSame($url, $assignedVariables['link']['url']);
247 static::assertTrue($assignedVariables['link_is_new']);
251 * Test displaying bookmark create form
252 * When markdown formatter is enabled, the no markdown tag should be added to existing tags.
254 public function testDisplayCreateFormWithMarkdownEnabled(): void
256 $assignedVariables = [];
257 $this->assignTemplateVars($assignedVariables);
259 $this->container
->conf
= $this->createMock(ConfigManager
::class);
260 $this->container
->conf
261 ->expects(static::atLeastOnce())
262 ->method('get')->willReturnCallback(function (string $key): ?string {
263 if ($key === 'formatter') {
271 $request = $this->createMock(Request
::class);
272 $response = new Response();
274 $result = $this->controller
->displayCreateForm($request, $response);
276 static::assertSame(200, $result->getStatusCode());
277 static::assertSame('editlink', (string) $result->getBody());
278 static::assertSame(['nomarkdown' => 1], $assignedVariables['tags']);
282 * Test displaying bookmark create form
283 * When an existing URL is submitted, we want to edit the existing link.
285 public function testDisplayCreateFormWithExistingUrl(): void
287 $assignedVariables = [];
288 $this->assignTemplateVars($assignedVariables);
290 $url = 'http://url.tld/other?part=3&utm_ad=pay#hash';
291 $expectedUrl = str_replace('&utm_ad=pay', '', $url);
293 $request = $this->createMock(Request
::class);
296 ->willReturnCallback(function (string $key) use ($url): ?string {
297 return $key === 'post' ? $url : null;
299 $response = new Response();
301 $this->container
->httpAccess
->expects(static::never())->method('getHttpResponse');
302 $this->container
->httpAccess
->expects(static::never())->method('getCurlDownloadCallback');
304 $this->container
->bookmarkService
305 ->expects(static::once())
306 ->method('findByUrl')
311 ->setUrl($expectedUrl)
312 ->setTitle($title = 'Bookmark Title')
313 ->setDescription($description = 'Bookmark description.')
314 ->setTags($tags = ['abc', 'def'])
316 ->setCreated($createdAt = new \
DateTime('2020-06-10 18:45:44'))
320 $result = $this->controller
->displayCreateForm($request, $response);
322 static::assertSame(200, $result->getStatusCode());
323 static::assertSame('editlink', (string) $result->getBody());
325 static::assertSame('Edit Shaare - Shaarli', $assignedVariables['pagetitle']);
326 static::assertFalse($assignedVariables['link_is_new']);
328 static::assertSame($id, $assignedVariables['link']['id']);
329 static::assertSame($expectedUrl, $assignedVariables['link']['url']);
330 static::assertSame($title, $assignedVariables['link']['title']);
331 static::assertSame($description, $assignedVariables['link']['description']);
332 static::assertSame(implode(' ', $tags), $assignedVariables['link']['tags']);
333 static::assertTrue($assignedVariables['link']['private']);
334 static::assertSame($createdAt, $assignedVariables['link']['created']);
338 * Test displaying bookmark edit form
339 * When an existing ID is provided, ensure that default workflow works properly.
341 public function testDisplayEditFormDefault(): void
343 $assignedVariables = [];
344 $this->assignTemplateVars($assignedVariables);
348 $request = $this->createMock(Request
::class);
349 $response = new Response();
351 $this->container
->httpAccess
->expects(static::never())->method('getHttpResponse');
352 $this->container
->httpAccess
->expects(static::never())->method('getCurlDownloadCallback');
354 $this->container
->bookmarkService
355 ->expects(static::once())
361 ->setUrl($url = 'http://domain.tld')
362 ->setTitle($title = 'Bookmark Title')
363 ->setDescription($description = 'Bookmark description.')
364 ->setTags($tags = ['abc', 'def'])
366 ->setCreated($createdAt = new \
DateTime('2020-06-10 18:45:44'))
370 $result = $this->controller
->displayEditForm($request, $response, ['id' => (string) $id]);
372 static::assertSame(200, $result->getStatusCode());
373 static::assertSame('editlink', (string) $result->getBody());
375 static::assertSame('Edit Shaare - Shaarli', $assignedVariables['pagetitle']);
376 static::assertFalse($assignedVariables['link_is_new']);
378 static::assertSame($id, $assignedVariables['link']['id']);
379 static::assertSame($url, $assignedVariables['link']['url']);
380 static::assertSame($title, $assignedVariables['link']['title']);
381 static::assertSame($description, $assignedVariables['link']['description']);
382 static::assertSame(implode(' ', $tags), $assignedVariables['link']['tags']);
383 static::assertTrue($assignedVariables['link']['private']);
384 static::assertSame($createdAt, $assignedVariables['link']['created']);
388 * Test save a new bookmark
390 public function testSaveBookmark(): void
394 'lf_url' => 'http://url.tld/other?part=3#hash',
395 'lf_title' => 'Provided Title',
396 'lf_description' => 'Provided description.',
397 'lf_tags' => 'abc def',
399 'returnurl' => 'http://shaarli.tld/subfolder/add-shaare'
402 $request = $this->createMock(Request
::class);
405 ->willReturnCallback(function (string $key) use ($parameters): ?string {
406 return $parameters[$key] ?? null;
409 $request->method('getUri')->willReturnCallback(function (): Uri
{
410 $uri = $this->createMock(Uri
::class);
411 $uri->method('getBasePath')->willReturn('/subfolder');
415 $response = new Response();
417 $checkBookmark = function (Bookmark
$bookmark) use ($parameters) {
418 static::assertSame($parameters['lf_url'], $bookmark->getUrl());
419 static::assertSame($parameters['lf_title'], $bookmark->getTitle());
420 static::assertSame($parameters['lf_description'], $bookmark->getDescription());
421 static::assertSame($parameters['lf_tags'], $bookmark->getTagsString());
422 static::assertTrue($bookmark->isPrivate());
425 $this->container
->bookmarkService
426 ->expects(static::once())
428 ->willReturnCallback(function (Bookmark
$bookmark, bool $save) use ($checkBookmark, $id): void {
429 static::assertFalse($save);
431 $checkBookmark($bookmark);
433 $bookmark->setId($id);
436 $this->container
->bookmarkService
437 ->expects(static::once())
439 ->willReturnCallback(function (Bookmark
$bookmark, bool $save) use ($checkBookmark, $id): void {
440 static::assertTrue($save);
442 $checkBookmark($bookmark);
444 static::assertSame($id, $bookmark->getId());
448 // Make sure that PluginManager hook is triggered
449 $this->container
->pluginManager
450 ->expects(static::at(0))
451 ->method('executeHooks')
452 ->willReturnCallback(function (string $hook, array $data) use ($parameters, $id): array {
453 static::assertSame('save_link', $hook);
455 static::assertSame($id, $data['id']);
456 static::assertSame($parameters['lf_url'], $data['url']);
457 static::assertSame($parameters['lf_title'], $data['title']);
458 static::assertSame($parameters['lf_description'], $data['description']);
459 static::assertSame($parameters['lf_tags'], $data['tags']);
460 static::assertTrue($data['private']);
466 $result = $this->controller
->save($request, $response);
468 static::assertSame(302, $result->getStatusCode());
469 static::assertRegExp('@/subfolder/#\w{6}@', $result->getHeader('location')[0]);
474 * Test save an existing bookmark
476 public function testSaveExistingBookmark(): void
480 'lf_id' => (string) $id,
481 'lf_url' => 'http://url.tld/other?part=3#hash',
482 'lf_title' => 'Provided Title',
483 'lf_description' => 'Provided description.',
484 'lf_tags' => 'abc def',
486 'returnurl' => 'http://shaarli.tld/subfolder/?page=2'
489 $request = $this->createMock(Request
::class);
492 ->willReturnCallback(function (string $key) use ($parameters): ?string {
493 return $parameters[$key] ?? null;
496 $request->method('getUri')->willReturnCallback(function (): Uri
{
497 $uri = $this->createMock(Uri
::class);
498 $uri->method('getBasePath')->willReturn('/subfolder');
502 $response = new Response();
504 $checkBookmark = function (Bookmark
$bookmark) use ($parameters, $id) {
505 static::assertSame($id, $bookmark->getId());
506 static::assertSame($parameters['lf_url'], $bookmark->getUrl());
507 static::assertSame($parameters['lf_title'], $bookmark->getTitle());
508 static::assertSame($parameters['lf_description'], $bookmark->getDescription());
509 static::assertSame($parameters['lf_tags'], $bookmark->getTagsString());
510 static::assertTrue($bookmark->isPrivate());
513 $this->container
->bookmarkService
->expects(static::atLeastOnce())->method('exists')->willReturn(true);
514 $this->container
->bookmarkService
515 ->expects(static::once())
517 ->willReturn((new Bookmark())->setId($id)->setUrl('http://other.url'))
519 $this->container
->bookmarkService
520 ->expects(static::once())
522 ->willReturnCallback(function (Bookmark
$bookmark, bool $save) use ($checkBookmark, $id): void {
523 static::assertFalse($save);
525 $checkBookmark($bookmark);
528 $this->container
->bookmarkService
529 ->expects(static::once())
531 ->willReturnCallback(function (Bookmark
$bookmark, bool $save) use ($checkBookmark, $id): void {
532 static::assertTrue($save);
534 $checkBookmark($bookmark);
536 static::assertSame($id, $bookmark->getId());
540 // Make sure that PluginManager hook is triggered
541 $this->container
->pluginManager
542 ->expects(static::at(0))
543 ->method('executeHooks')
544 ->willReturnCallback(function (string $hook, array $data) use ($parameters, $id): array {
545 static::assertSame('save_link', $hook);
547 static::assertSame($id, $data['id']);
548 static::assertSame($parameters['lf_url'], $data['url']);
549 static::assertSame($parameters['lf_title'], $data['title']);
550 static::assertSame($parameters['lf_description'], $data['description']);
551 static::assertSame($parameters['lf_tags'], $data['tags']);
552 static::assertTrue($data['private']);
558 $result = $this->controller
->save($request, $response);
560 static::assertSame(302, $result->getStatusCode());
561 static::assertRegExp('@/subfolder/\?page=2#\w{6}@', $result->getHeader('location')[0]);
565 * Test save a bookmark - try to retrieve the thumbnail
567 public function testSaveBookmarkWithThumbnail(): void
569 $parameters = ['lf_url' => 'http://url.tld/other?part=3#hash'];
571 $request = $this->createMock(Request
::class);
574 ->willReturnCallback(function (string $key) use ($parameters): ?string {
575 return $parameters[$key] ?? null;
578 $request->method('getUri')->willReturnCallback(function (): Uri
{
579 $uri = $this->createMock(Uri
::class);
580 $uri->method('getBasePath')->willReturn('/subfolder');
584 $response = new Response();
586 $this->container
->conf
= $this->createMock(ConfigManager
::class);
587 $this->container
->conf
->method('get')->willReturnCallback(function (string $key, $default) {
588 return $key === 'thumbnails.mode' ? Thumbnailer
::MODE_ALL
: $default;
591 $this->container
->thumbnailer
= $this->createMock(Thumbnailer
::class);
592 $this->container
->thumbnailer
593 ->expects(static::once())
595 ->with($parameters['lf_url'])
596 ->willReturn($thumb = 'http://thumb.url')
599 $this->container
->bookmarkService
600 ->expects(static::once())
602 ->willReturnCallback(function (Bookmark
$bookmark, bool $save) use ($thumb): void {
603 static::assertSame($thumb, $bookmark->getThumbnail());
607 $result = $this->controller
->save($request, $response);
609 static::assertSame(302, $result->getStatusCode());
613 * Change the password with a wrong existing password
615 public function testSaveBookmarkFromBookmarklet(): void
617 $parameters = ['source' => 'bookmarklet'];
619 $request = $this->createMock(Request
::class);
622 ->willReturnCallback(function (string $key) use ($parameters): ?string {
623 return $parameters[$key] ?? null;
626 $response = new Response();
628 $result = $this->controller
->save($request, $response);
630 static::assertSame(200, $result->getStatusCode());
631 static::assertSame('<script>self.close();</script>', (string) $result->getBody());
635 * Change the password with a wrong existing password
637 public function testSaveBookmarkWrongToken(): void
639 $this->container
->sessionManager
= $this->createMock(SessionManager
::class);
640 $this->container
->sessionManager
->method('checkToken')->willReturn(false);
642 $this->container
->bookmarkService
->expects(static::never())->method('addOrSet');
643 $this->container
->bookmarkService
->expects(static::never())->method('set');
645 $request = $this->createMock(Request
::class);
646 $response = new Response();
648 $this->expectException(WrongTokenException
::class);
650 $this->controller
->save($request, $response);