3 declare(strict_types
=1);
5 namespace Shaarli\Front\Controller
;
7 use PHPUnit\Framework\TestCase
;
8 use Shaarli\Bookmark\Bookmark
;
9 use Shaarli\Bookmark\BookmarkServiceInterface
;
10 use Shaarli\Config\ConfigManager
;
11 use Shaarli\Container\ShaarliContainer
;
12 use Shaarli\Feed\CachedPage
;
13 use Shaarli\Formatter\BookmarkFormatter
;
14 use Shaarli\Formatter\BookmarkRawFormatter
;
15 use Shaarli\Formatter\FormatterFactory
;
16 use Shaarli\Plugin\PluginManager
;
17 use Shaarli\Render\PageBuilder
;
18 use Shaarli\Render\PageCacheManager
;
19 use Shaarli\Security\LoginManager
;
20 use Slim\Http\Request
;
21 use Slim\Http\Response
;
23 class DailyControllerTest
extends TestCase
25 /** @var ShaarliContainer */
28 /** @var DailyController */
29 protected $controller;
31 public function setUp(): void
33 $this->container
= $this->createMock(ShaarliContainer
::class);
34 $this->controller
= new DailyController($this->container
);
35 DailyController
::$DAILY_RSS_NB_DAYS = 2;
38 public function testValidIndexControllerInvokeDefault(): void
40 $this->createValidContainerMockSet();
42 $currentDay = new \
DateTimeImmutable('2020-05-13');
44 $request = $this->createMock(Request
::class);
45 $request->method('getQueryParam')->willReturn($currentDay->format('Ymd'));
46 $response = new Response();
48 // Save RainTPL assigned variables
49 $assignedVariables = [];
50 $this->assignTemplateVars($assignedVariables);
52 // Links dataset: 2 links with thumbnails
53 $this->container
->bookmarkService
54 ->expects(static::once())
56 ->willReturnCallback(function () use ($currentDay): array {
59 $currentDay->format('Ymd'),
64 $this->container
->bookmarkService
65 ->expects(static::once())
67 ->willReturnCallback(function (): array {
71 ->setUrl('http://url.tld')
72 ->setTitle(static::generateContent(50))
73 ->setDescription(static::generateContent(500))
77 ->setUrl('http://url2.tld')
78 ->setTitle(static::generateContent(50))
79 ->setDescription(static::generateContent(500))
83 ->setUrl('http://url3.tld')
84 ->setTitle(static::generateContent(50))
85 ->setDescription(static::generateContent(500))
91 // Make sure that PluginManager hook is triggered
92 $this->container
->pluginManager
93 ->expects(static::at(0))
94 ->method('executeHooks')
95 ->willReturnCallback(function (string $hook, array $data, array $param) use ($currentDay): array {
96 static::assertSame('render_daily', $hook);
98 static::assertArrayHasKey('linksToDisplay', $data);
99 static::assertCount(3, $data['linksToDisplay']);
100 static::assertSame(1, $data['linksToDisplay'][0]['id']);
101 static::assertSame($currentDay->getTimestamp(), $data['day']);
102 static::assertSame('20200510', $data['previousday']);
103 static::assertSame('20200516', $data['nextday']);
105 static::assertArrayHasKey('loggedin', $param);
110 $result = $this->controller
->index($request, $response);
112 static::assertSame(200, $result->getStatusCode());
113 static::assertSame('daily', (string) $result->getBody());
115 'Daily - '. format_date($currentDay, false, true) .' - Shaarli',
116 $assignedVariables['pagetitle']
118 static::assertEquals($currentDay, $assignedVariables['dayDate']);
119 static::assertEquals($currentDay->getTimestamp(), $assignedVariables['day']);
120 static::assertCount(3, $assignedVariables['linksToDisplay']);
122 $link = $assignedVariables['linksToDisplay'][0];
124 static::assertSame(1, $link['id']);
125 static::assertSame('http://url.tld', $link['url']);
126 static::assertNotEmpty($link['title']);
127 static::assertNotEmpty($link['description']);
128 static::assertNotEmpty($link['formatedDescription']);
130 $link = $assignedVariables['linksToDisplay'][1];
132 static::assertSame(2, $link['id']);
133 static::assertSame('http://url2.tld', $link['url']);
134 static::assertNotEmpty($link['title']);
135 static::assertNotEmpty($link['description']);
136 static::assertNotEmpty($link['formatedDescription']);
138 $link = $assignedVariables['linksToDisplay'][2];
140 static::assertSame(3, $link['id']);
141 static::assertSame('http://url3.tld', $link['url']);
142 static::assertNotEmpty($link['title']);
143 static::assertNotEmpty($link['description']);
144 static::assertNotEmpty($link['formatedDescription']);
146 static::assertCount(3, $assignedVariables['cols']);
147 static::assertCount(1, $assignedVariables['cols'][0]);
148 static::assertCount(1, $assignedVariables['cols'][1]);
149 static::assertCount(1, $assignedVariables['cols'][2]);
151 $link = $assignedVariables['cols'][0][0];
153 static::assertSame(1, $link['id']);
154 static::assertSame('http://url.tld', $link['url']);
155 static::assertNotEmpty($link['title']);
156 static::assertNotEmpty($link['description']);
157 static::assertNotEmpty($link['formatedDescription']);
159 $link = $assignedVariables['cols'][1][0];
161 static::assertSame(2, $link['id']);
162 static::assertSame('http://url2.tld', $link['url']);
163 static::assertNotEmpty($link['title']);
164 static::assertNotEmpty($link['description']);
165 static::assertNotEmpty($link['formatedDescription']);
167 $link = $assignedVariables['cols'][2][0];
169 static::assertSame(3, $link['id']);
170 static::assertSame('http://url3.tld', $link['url']);
171 static::assertNotEmpty($link['title']);
172 static::assertNotEmpty($link['description']);
173 static::assertNotEmpty($link['formatedDescription']);
177 * Daily page - test that everything goes fine with no future or past bookmarks
179 public function testValidIndexControllerInvokeNoFutureOrPast(): void
181 $this->createValidContainerMockSet();
183 $currentDay = new \
DateTimeImmutable('2020-05-13');
185 $request = $this->createMock(Request
::class);
186 $response = new Response();
188 // Save RainTPL assigned variables
189 $assignedVariables = [];
190 $this->assignTemplateVars($assignedVariables);
192 // Links dataset: 2 links with thumbnails
193 $this->container
->bookmarkService
194 ->expects(static::once())
196 ->willReturnCallback(function () use ($currentDay): array {
198 $currentDay->format($currentDay->format('Ymd')),
202 $this->container
->bookmarkService
203 ->expects(static::once())
204 ->method('filterDay')
205 ->willReturnCallback(function (): array {
209 ->setUrl('http://url.tld')
210 ->setTitle(static::generateContent(50))
211 ->setDescription(static::generateContent(500))
217 // Make sure that PluginManager hook is triggered
218 $this->container
->pluginManager
219 ->expects(static::at(0))
220 ->method('executeHooks')
221 ->willReturnCallback(function (string $hook, array $data, array $param) use ($currentDay): array {
222 static::assertSame('render_daily', $hook);
224 static::assertArrayHasKey('linksToDisplay', $data);
225 static::assertCount(1, $data['linksToDisplay']);
226 static::assertSame(1, $data['linksToDisplay'][0]['id']);
227 static::assertSame($currentDay->getTimestamp(), $data['day']);
228 static::assertEmpty($data['previousday']);
229 static::assertEmpty($data['nextday']);
231 static::assertArrayHasKey('loggedin', $param);
236 $result = $this->controller
->index($request, $response);
238 static::assertSame(200, $result->getStatusCode());
239 static::assertSame('daily', (string) $result->getBody());
241 'Daily - '. format_date($currentDay, false, true) .' - Shaarli',
242 $assignedVariables['pagetitle']
244 static::assertCount(1, $assignedVariables['linksToDisplay']);
246 $link = $assignedVariables['linksToDisplay'][0];
247 static::assertSame(1, $link['id']);
251 * Daily page - test that height adjustment in columns is working
253 public function testValidIndexControllerInvokeHeightAdjustment(): void
255 $this->createValidContainerMockSet();
257 $currentDay = new \
DateTimeImmutable('2020-05-13');
259 $request = $this->createMock(Request
::class);
260 $response = new Response();
262 // Save RainTPL assigned variables
263 $assignedVariables = [];
264 $this->assignTemplateVars($assignedVariables);
266 // Links dataset: 2 links with thumbnails
267 $this->container
->bookmarkService
268 ->expects(static::once())
270 ->willReturnCallback(function () use ($currentDay): array {
272 $currentDay->format($currentDay->format('Ymd')),
276 $this->container
->bookmarkService
277 ->expects(static::once())
278 ->method('filterDay')
279 ->willReturnCallback(function (): array {
281 (new Bookmark())->setId(1)->setUrl('http://url.tld')->setTitle('title'),
284 ->setUrl('http://url.tld')
285 ->setTitle(static::generateContent(50))
286 ->setDescription(static::generateContent(5000))
288 (new Bookmark())->setId(3)->setUrl('http://url.tld')->setTitle('title'),
289 (new Bookmark())->setId(4)->setUrl('http://url.tld')->setTitle('title'),
290 (new Bookmark())->setId(5)->setUrl('http://url.tld')->setTitle('title'),
291 (new Bookmark())->setId(6)->setUrl('http://url.tld')->setTitle('title'),
292 (new Bookmark())->setId(7)->setUrl('http://url.tld')->setTitle('title'),
297 // Make sure that PluginManager hook is triggered
298 $this->container
->pluginManager
299 ->expects(static::at(0))
300 ->method('executeHooks')
301 ->willReturnCallback(function (string $hook, array $data, array $param): array {
306 $result = $this->controller
->index($request, $response);
308 static::assertSame(200, $result->getStatusCode());
309 static::assertSame('daily', (string) $result->getBody());
310 static::assertCount(7, $assignedVariables['linksToDisplay']);
312 $columnIds = function (array $column): array {
313 return array_map(function (array $item): int {
return $item
['id']; }, $column);
316 static::assertSame([1, 4, 6], $columnIds($assignedVariables['cols'][0]));
317 static::assertSame([2], $columnIds($assignedVariables['cols'][1]));
318 static::assertSame([3, 5, 7], $columnIds($assignedVariables['cols'][2]));
322 * Daily page - no bookmark
324 public function testValidIndexControllerInvokeNoBookmark(): void
326 $this->createValidContainerMockSet();
328 $request = $this->createMock(Request
::class);
329 $response = new Response();
331 // Save RainTPL assigned variables
332 $assignedVariables = [];
333 $this->assignTemplateVars($assignedVariables);
335 // Links dataset: 2 links with thumbnails
336 $this->container
->bookmarkService
337 ->expects(static::once())
339 ->willReturnCallback(function (): array {
343 $this->container
->bookmarkService
344 ->expects(static::once())
345 ->method('filterDay')
346 ->willReturnCallback(function (): array {
351 // Make sure that PluginManager hook is triggered
352 $this->container
->pluginManager
353 ->expects(static::at(0))
354 ->method('executeHooks')
355 ->willReturnCallback(function (string $hook, array $data, array $param): array {
360 $result = $this->controller
->index($request, $response);
362 static::assertSame(200, $result->getStatusCode());
363 static::assertSame('daily', (string) $result->getBody());
364 static::assertCount(0, $assignedVariables['linksToDisplay']);
365 static::assertSame('Today', $assignedVariables['dayDesc']);
366 static::assertEquals((new \
DateTime())->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
367 static::assertEquals((new \
DateTime())->setTime(0, 0), $assignedVariables['dayDate']);
371 * Daily RSS - default behaviour
373 public function testValidRssControllerInvokeDefault(): void
375 $this->createValidContainerMockSet();
378 new \
DateTimeImmutable('2020-05-17'),
379 new \
DateTimeImmutable('2020-05-15'),
380 new \
DateTimeImmutable('2020-05-13'),
383 $request = $this->createMock(Request
::class);
384 $response = new Response();
386 $this->container
->bookmarkService
->expects(static::once())->method('search')->willReturn([
387 (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'),
388 (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
389 (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
390 (new Bookmark())->setId(4)->setCreated($dates[2])->setUrl('http://domain.tld/4'),
393 $this->container
->pageCacheManager
394 ->expects(static::once())
395 ->method('getCachePage')
396 ->willReturnCallback(function (): CachedPage
{
397 $cachedPage = $this->createMock(CachedPage
::class);
398 $cachedPage->expects(static::once())->method('cache')->with('dailyrss');
404 // Save RainTPL assigned variables
405 $assignedVariables = [];
406 $this->assignTemplateVars($assignedVariables);
408 $result = $this->controller
->rss($request, $response);
410 static::assertSame(200, $result->getStatusCode());
411 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
412 static::assertSame('dailyrss', (string) $result->getBody());
413 static::assertSame('Shaarli', $assignedVariables['title']);
414 static::assertSame('http://shaarli', $assignedVariables['index_url']);
415 static::assertSame('http://shaarli/daily-rss', $assignedVariables['page_url']);
416 static::assertFalse($assignedVariables['hide_timestamps']);
417 static::assertCount(2, $assignedVariables['days']);
419 $day = $assignedVariables['days'][$dates[0]->format('Ymd')];
421 static::assertEquals($dates[0], $day['date']);
422 static::assertSame($dates[0]->format(\DateTime
::RSS
), $day['date_rss']);
423 static::assertSame(format_date($dates[0], false), $day['date_human']);
424 static::assertSame('http://shaarli/daily?day='. $dates[0]->format('Ymd'), $day['absolute_url']);
425 static::assertCount(1, $day['links']);
426 static::assertSame(1, $day['links'][0]['id']);
427 static::assertSame('http://domain.tld/1', $day['links'][0]['url']);
428 static::assertEquals($dates[0], $day['links'][0]['created']);
430 $day = $assignedVariables['days'][$dates[1]->format('Ymd')];
432 static::assertEquals($dates[1], $day['date']);
433 static::assertSame($dates[1]->format(\DateTime
::RSS
), $day['date_rss']);
434 static::assertSame(format_date($dates[1], false), $day['date_human']);
435 static::assertSame('http://shaarli/daily?day='. $dates[1]->format('Ymd'), $day['absolute_url']);
436 static::assertCount(2, $day['links']);
438 static::assertSame(2, $day['links'][0]['id']);
439 static::assertSame('http://domain.tld/2', $day['links'][0]['url']);
440 static::assertEquals($dates[1], $day['links'][0]['created']);
441 static::assertSame(3, $day['links'][1]['id']);
442 static::assertSame('http://domain.tld/3', $day['links'][1]['url']);
443 static::assertEquals($dates[1], $day['links'][1]['created']);
447 * Daily RSS - trigger cache rendering
449 public function testValidRssControllerInvokeTriggerCache(): void
451 $this->createValidContainerMockSet();
453 $request = $this->createMock(Request
::class);
454 $response = new Response();
456 $this->container
->pageCacheManager
->method('getCachePage')->willReturnCallback(function (): CachedPage
{
457 $cachedPage = $this->createMock(CachedPage
::class);
458 $cachedPage->method('cachedVersion')->willReturn('this is cache!');
463 $this->container
->bookmarkService
->expects(static::never())->method('search');
465 $result = $this->controller
->rss($request, $response);
467 static::assertSame(200, $result->getStatusCode());
468 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
469 static::assertSame('this is cache!', (string) $result->getBody());
473 * Daily RSS - No bookmark
475 public function testValidRssControllerInvokeNoBookmark(): void
477 $this->createValidContainerMockSet();
479 $request = $this->createMock(Request
::class);
480 $response = new Response();
482 $this->container
->bookmarkService
->expects(static::once())->method('search')->willReturn([]);
484 // Save RainTPL assigned variables
485 $assignedVariables = [];
486 $this->assignTemplateVars($assignedVariables);
488 $result = $this->controller
->rss($request, $response);
490 static::assertSame(200, $result->getStatusCode());
491 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
492 static::assertSame('dailyrss', (string) $result->getBody());
493 static::assertSame('Shaarli', $assignedVariables['title']);
494 static::assertSame('http://shaarli', $assignedVariables['index_url']);
495 static::assertSame('http://shaarli/daily-rss', $assignedVariables['page_url']);
496 static::assertFalse($assignedVariables['hide_timestamps']);
497 static::assertCount(0, $assignedVariables['days']);
500 protected function createValidContainerMockSet(): void
502 $loginManager = $this->createMock(LoginManager
::class);
503 $this->container
->loginManager
= $loginManager;
506 $conf = $this->createMock(ConfigManager
::class);
507 $this->container
->conf
= $conf;
508 $this->container
->conf
->method('get')->willReturnCallback(function (string $parameter, $default) {
513 $pageBuilder = $this->createMock(PageBuilder
::class);
516 ->willReturnCallback(function (string $template): string {
520 $this->container
->pageBuilder
= $pageBuilder;
523 $pluginManager = $this->createMock(PluginManager
::class);
524 $this->container
->pluginManager
= $pluginManager;
527 $bookmarkService = $this->createMock(BookmarkServiceInterface
::class);
528 $this->container
->bookmarkService
= $bookmarkService;
531 $formatterFactory = $this->createMock(FormatterFactory
::class);
533 ->method('getFormatter')
534 ->willReturnCallback(function (): BookmarkFormatter
{
535 return new BookmarkRawFormatter($this->container
->conf
, true);
538 $this->container
->formatterFactory
= $formatterFactory;
541 $pageCacheManager = $this->createMock(PageCacheManager
::class);
542 $this->container
->pageCacheManager
= $pageCacheManager;
545 $this->container
->environment
= [
546 'SERVER_NAME' => 'shaarli',
547 'SERVER_PORT' => '80',
548 'REQUEST_URI' => '/daily-rss',
552 protected function assignTemplateVars(array &$variables): void
554 $this->container
->pageBuilder
555 ->expects(static::atLeastOnce())
557 ->willReturnCallback(function ($key, $value) use (&$variables) {
558 $variables[$key] = $value;
565 protected static function generateContent(int $length): string
567 // bin2hex(random_bytes) generates string twice as long as given parameter
568 $length = (int) ceil($length / 2);
569 return bin2hex(random_bytes($length));