aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/FileUtilsTest.php110
-rw-r--r--tests/HistoryTest.php25
-rw-r--r--tests/LanguagesTest.php4
-rw-r--r--tests/PluginManagerTest.php76
-rw-r--r--tests/RouterTest.php509
-rw-r--r--tests/TestCase.php77
-rw-r--r--tests/ThumbnailerTest.php5
-rw-r--r--tests/TimeZoneTest.php4
-rw-r--r--tests/UtilsTest.php44
-rw-r--r--tests/api/ApiMiddlewareTest.php65
-rw-r--r--tests/api/ApiUtilsTest.php76
-rw-r--r--tests/api/controllers/history/HistoryTest.php6
-rw-r--r--tests/api/controllers/info/InfoTest.php10
-rw-r--r--tests/api/controllers/links/DeleteLinkTest.php19
-rw-r--r--tests/api/controllers/links/GetLinkIdTest.php18
-rw-r--r--tests/api/controllers/links/GetLinksTest.php14
-rw-r--r--tests/api/controllers/links/PostLinkTest.php22
-rw-r--r--tests/api/controllers/links/PutLinkTest.php20
-rw-r--r--tests/api/controllers/tags/DeleteTagTest.php23
-rw-r--r--tests/api/controllers/tags/GetTagNameTest.php16
-rw-r--r--tests/api/controllers/tags/GetTagsTest.php10
-rw-r--r--tests/api/controllers/tags/PutTagTest.php22
-rw-r--r--tests/bookmark/BookmarkArrayTest.php38
-rw-r--r--tests/bookmark/BookmarkFileServiceTest.php373
-rw-r--r--tests/bookmark/BookmarkFilterTest.php74
-rw-r--r--tests/bookmark/BookmarkInitializerTest.php97
-rw-r--r--tests/bookmark/BookmarkTest.php94
-rw-r--r--tests/bookmark/LinkUtilsTest.php351
-rw-r--r--tests/bootstrap.php18
-rw-r--r--tests/config/ConfigJsonTest.php25
-rw-r--r--tests/config/ConfigManagerTest.php26
-rw-r--r--tests/config/ConfigPhpTest.php4
-rw-r--r--tests/config/ConfigPluginTest.php22
-rw-r--r--tests/container/ContainerBuilderTest.php54
-rw-r--r--tests/container/ShaarliTestContainer.php42
-rw-r--r--tests/feed/CachedPageTest.php12
-rw-r--r--tests/feed/FeedBuilderTest.php122
-rw-r--r--tests/formatter/BookmarkDefaultFormatterTest.php123
-rw-r--r--tests/formatter/BookmarkMarkdownExtraFormatterTest.php162
-rw-r--r--tests/formatter/BookmarkMarkdownFormatterTest.php8
-rw-r--r--tests/formatter/BookmarkRawFormatterTest.php4
-rw-r--r--tests/formatter/FormatterFactoryTest.php4
-rw-r--r--tests/front/ShaarliAdminMiddlewareTest.php100
-rw-r--r--tests/front/ShaarliMiddlewareTest.php163
-rw-r--r--tests/front/controller/LoginControllerTest.php178
-rw-r--r--tests/front/controller/ShaarliControllerTest.php116
-rw-r--r--tests/front/controller/admin/ConfigureControllerTest.php252
-rw-r--r--tests/front/controller/admin/ExportControllerTest.php163
-rw-r--r--tests/front/controller/admin/FrontAdminControllerMockHelper.php56
-rw-r--r--tests/front/controller/admin/ImportControllerTest.php148
-rw-r--r--tests/front/controller/admin/LogoutControllerTest.php50
-rw-r--r--tests/front/controller/admin/ManageTagControllerTest.php272
-rw-r--r--tests/front/controller/admin/PasswordControllerTest.php203
-rw-r--r--tests/front/controller/admin/PluginsControllerTest.php205
-rw-r--r--tests/front/controller/admin/ServerControllerTest.php184
-rw-r--r--tests/front/controller/admin/SessionFilterControllerTest.php177
-rw-r--r--tests/front/controller/admin/ShaareAddControllerTest.php97
-rw-r--r--tests/front/controller/admin/ShaareManageControllerTest/ChangeVisibilityBookmarkTest.php418
-rw-r--r--tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php380
-rw-r--r--tests/front/controller/admin/ShaareManageControllerTest/PinBookmarkTest.php145
-rw-r--r--tests/front/controller/admin/ShaareManageControllerTest/SharePrivateTest.php139
-rw-r--r--tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateBatchFormTest.php63
-rw-r--r--tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateFormTest.php367
-rw-r--r--tests/front/controller/admin/ShaarePublishControllerTest/DisplayEditFormTest.php155
-rw-r--r--tests/front/controller/admin/ShaarePublishControllerTest/SaveBookmarkTest.php369
-rw-r--r--tests/front/controller/admin/ShaarliAdminControllerTest.php184
-rw-r--r--tests/front/controller/admin/ThumbnailsControllerTest.php156
-rw-r--r--tests/front/controller/admin/TokenControllerTest.php41
-rw-r--r--tests/front/controller/admin/ToolsControllerTest.php69
-rw-r--r--tests/front/controller/visitor/BookmarkListControllerTest.php532
-rw-r--r--tests/front/controller/visitor/DailyControllerTest.php716
-rw-r--r--tests/front/controller/visitor/ErrorControllerTest.php70
-rw-r--r--tests/front/controller/visitor/ErrorNotFoundControllerTest.php81
-rw-r--r--tests/front/controller/visitor/FeedControllerTest.php151
-rw-r--r--tests/front/controller/visitor/FrontControllerMockHelper.php118
-rw-r--r--tests/front/controller/visitor/InstallControllerTest.php304
-rw-r--r--tests/front/controller/visitor/LoginControllerTest.php404
-rw-r--r--tests/front/controller/visitor/OpenSearchControllerTest.php44
-rw-r--r--tests/front/controller/visitor/PictureWallControllerTest.php123
-rw-r--r--tests/front/controller/visitor/PublicSessionFilterControllerTest.php122
-rw-r--r--tests/front/controller/visitor/ShaarliVisitorControllerTest.php246
-rw-r--r--tests/front/controller/visitor/TagCloudControllerTest.php381
-rw-r--r--tests/front/controller/visitor/TagControllerTest.php215
-rw-r--r--tests/helper/ApplicationUtilsTest.php (renamed from tests/ApplicationUtilsTest.php)86
-rw-r--r--tests/helper/DailyPageHelperTest.php262
-rw-r--r--tests/helper/FileUtilsTest.php197
-rw-r--r--tests/http/HttpUtils/ClientIpIdTest.php2
-rw-r--r--tests/http/HttpUtils/GetHttpUrlTest.php2
-rw-r--r--tests/http/HttpUtils/GetIpAdressFromProxyTest.php2
-rw-r--r--tests/http/HttpUtils/IndexUrlTest.php68
-rw-r--r--tests/http/HttpUtils/IndexUrlTestWithConstant.php51
-rw-r--r--tests/http/HttpUtils/IsHttpsTest.php2
-rw-r--r--tests/http/HttpUtils/PageUrlTest.php2
-rw-r--r--tests/http/HttpUtils/ServerUrlTest.php2
-rw-r--r--tests/http/MetadataRetrieverTest.php154
-rw-r--r--tests/http/UrlTest.php2
-rw-r--r--tests/http/UrlUtils/CleanupUrlTest.php2
-rw-r--r--tests/http/UrlUtils/GetUrlSchemeTest.php2
-rw-r--r--tests/http/UrlUtils/UnparseUrlTest.php2
-rw-r--r--tests/http/UrlUtils/WhitelistProtocolsTest.php2
-rw-r--r--tests/languages/fr/LanguagesFrTest.php6
-rw-r--r--tests/legacy/LegacyControllerTest.php101
-rw-r--r--tests/legacy/LegacyLinkDBTest.php41
-rw-r--r--tests/legacy/LegacyLinkFilterTest.php18
-rw-r--r--tests/legacy/LegacyUpdaterTest.php24
-rw-r--r--tests/netscape/BookmarkExportTest.php78
-rw-r--r--tests/netscape/BookmarkImportTest.php80
-rw-r--r--tests/plugins/PluginAddlinkTest.php14
-rw-r--r--tests/plugins/PluginArchiveorgTest.php37
-rw-r--r--tests/plugins/PluginDefaultColorsTest.php25
-rw-r--r--tests/plugins/PluginIssoTest.php23
-rw-r--r--tests/plugins/PluginPlayvideosTest.php10
-rw-r--r--tests/plugins/PluginPubsubhubbubTest.php10
-rw-r--r--tests/plugins/PluginQrcodeTest.php8
-rw-r--r--tests/plugins/PluginWallabagTest.php4
-rw-r--r--tests/plugins/WallabagInstanceTest.php4
-rw-r--r--tests/plugins/resources/hashtags.md10
-rw-r--r--tests/plugins/resources/hashtags.raw10
-rw-r--r--tests/plugins/resources/markdown.html33
-rw-r--r--tests/plugins/resources/markdown.md34
-rw-r--r--tests/plugins/test/test.php8
-rw-r--r--tests/render/PageCacheManagerTest.php (renamed from tests/feed/CacheTest.php)43
-rw-r--r--tests/render/ThemeUtilsTest.php2
-rw-r--r--tests/security/BanManagerTest.php9
-rw-r--r--tests/security/LoginManagerTest.php81
-rw-r--r--tests/security/SessionManagerTest.php91
-rw-r--r--tests/updater/DummyUpdater.php8
-rw-r--r--tests/updater/UpdaterTest.php79
-rw-r--r--tests/utils/FakeApplicationUtils.php2
-rw-r--r--tests/utils/FakeConfigManager.php10
-rw-r--r--tests/utils/ReferenceHistory.php2
-rw-r--r--tests/utils/ReferenceLinkDB.php10
132 files changed, 10933 insertions, 2007 deletions
diff --git a/tests/FileUtilsTest.php b/tests/FileUtilsTest.php
deleted file mode 100644
index 57719175..00000000
--- a/tests/FileUtilsTest.php
+++ /dev/null
@@ -1,110 +0,0 @@
1<?php
2
3namespace Shaarli;
4
5use Exception;
6
7/**
8 * Class FileUtilsTest
9 *
10 * Test file utility class.
11 */
12class FileUtilsTest extends \PHPUnit\Framework\TestCase
13{
14 /**
15 * @var string Test file path.
16 */
17 protected static $file = 'sandbox/flat.db';
18
19 /**
20 * Delete test file after every test.
21 */
22 public function tearDown()
23 {
24 @unlink(self::$file);
25 }
26
27 /**
28 * Test writeDB, then readDB with different data.
29 */
30 public function testSimpleWriteRead()
31 {
32 $data = ['blue', 'red'];
33 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
34 $this->assertTrue(startsWith(file_get_contents(self::$file), '<?php /*'));
35 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
36
37 $data = 0;
38 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
39 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
40
41 $data = null;
42 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
43 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
44
45 $data = false;
46 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
47 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
48 }
49
50 /**
51 * File not writable: raise an exception.
52 *
53 * @expectedException Shaarli\Exceptions\IOException
54 * @expectedExceptionMessage Error accessing "sandbox/flat.db"
55 */
56 public function testWriteWithoutPermission()
57 {
58 touch(self::$file);
59 chmod(self::$file, 0440);
60 FileUtils::writeFlatDB(self::$file, null);
61 }
62
63 /**
64 * Folder non existent: raise an exception.
65 *
66 * @expectedException Shaarli\Exceptions\IOException
67 * @expectedExceptionMessage Error accessing "nopefolder"
68 */
69 public function testWriteFolderDoesNotExist()
70 {
71 FileUtils::writeFlatDB('nopefolder/file', null);
72 }
73
74 /**
75 * Folder non writable: raise an exception.
76 *
77 * @expectedException Shaarli\Exceptions\IOException
78 * @expectedExceptionMessage Error accessing "sandbox"
79 */
80 public function testWriteFolderPermission()
81 {
82 chmod(dirname(self::$file), 0555);
83 try {
84 FileUtils::writeFlatDB(self::$file, null);
85 } catch (Exception $e) {
86 chmod(dirname(self::$file), 0755);
87 throw $e;
88 }
89 }
90
91 /**
92 * Read non existent file, use default parameter.
93 */
94 public function testReadNotExistentFile()
95 {
96 $this->assertEquals(null, FileUtils::readFlatDB(self::$file));
97 $this->assertEquals(['test'], FileUtils::readFlatDB(self::$file, ['test']));
98 }
99
100 /**
101 * Read non readable file, use default parameter.
102 */
103 public function testReadNotReadable()
104 {
105 touch(self::$file);
106 chmod(self::$file, 0220);
107 $this->assertEquals(null, FileUtils::readFlatDB(self::$file));
108 $this->assertEquals(['test'], FileUtils::readFlatDB(self::$file, ['test']));
109 }
110}
diff --git a/tests/HistoryTest.php b/tests/HistoryTest.php
index 7189c3a9..e810104e 100644
--- a/tests/HistoryTest.php
+++ b/tests/HistoryTest.php
@@ -3,10 +3,9 @@
3namespace Shaarli; 3namespace Shaarli;
4 4
5use DateTime; 5use DateTime;
6use Exception;
7use Shaarli\Bookmark\Bookmark; 6use Shaarli\Bookmark\Bookmark;
8 7
9class HistoryTest extends \PHPUnit\Framework\TestCase 8class HistoryTest extends \Shaarli\TestCase
10{ 9{
11 /** 10 /**
12 * @var string History file path 11 * @var string History file path
@@ -16,7 +15,7 @@ class HistoryTest extends \PHPUnit\Framework\TestCase
16 /** 15 /**
17 * Delete history file. 16 * Delete history file.
18 */ 17 */
19 public function setUp() 18 protected function setUp(): void
20 { 19 {
21 if (file_exists(self::$historyFilePath)) { 20 if (file_exists(self::$historyFilePath)) {
22 unlink(self::$historyFilePath); 21 unlink(self::$historyFilePath);
@@ -44,12 +43,12 @@ class HistoryTest extends \PHPUnit\Framework\TestCase
44 43
45 /** 44 /**
46 * Not writable history file: raise an exception. 45 * Not writable history file: raise an exception.
47 *
48 * @expectedException Exception
49 * @expectedExceptionMessage History file isn't readable or writable
50 */ 46 */
51 public function testConstructNotWritable() 47 public function testConstructNotWritable()
52 { 48 {
49 $this->expectException(\Exception::class);
50 $this->expectExceptionMessage('History file isn\'t readable or writable');
51
53 touch(self::$historyFilePath); 52 touch(self::$historyFilePath);
54 chmod(self::$historyFilePath, 0440); 53 chmod(self::$historyFilePath, 0440);
55 $history = new History(self::$historyFilePath); 54 $history = new History(self::$historyFilePath);
@@ -58,12 +57,12 @@ class HistoryTest extends \PHPUnit\Framework\TestCase
58 57
59 /** 58 /**
60 * Not parsable history file: raise an exception. 59 * Not parsable history file: raise an exception.
61 *
62 * @expectedException Exception
63 * @expectedExceptionMessageRegExp /Could not parse history file/
64 */ 60 */
65 public function testConstructNotParsable() 61 public function testConstructNotParsable()
66 { 62 {
63 $this->expectException(\Exception::class);
64 $this->expectExceptionMessageRegExp('/Could not parse history file/');
65
67 file_put_contents(self::$historyFilePath, 'not parsable'); 66 file_put_contents(self::$historyFilePath, 'not parsable');
68 $history = new History(self::$historyFilePath); 67 $history = new History(self::$historyFilePath);
69 // gzinflate generates a warning 68 // gzinflate generates a warning
@@ -90,14 +89,6 @@ class HistoryTest extends \PHPUnit\Framework\TestCase
90 $this->assertEquals(History::CREATED, $actual['event']); 89 $this->assertEquals(History::CREATED, $actual['event']);
91 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 90 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
92 $this->assertEquals(1, $actual['id']); 91 $this->assertEquals(1, $actual['id']);
93
94 $history = new History(self::$historyFilePath);
95 $bookmark = (new Bookmark())->setId('str');
96 $history->addLink($bookmark);
97 $actual = $history->getHistory()[0];
98 $this->assertEquals(History::CREATED, $actual['event']);
99 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
100 $this->assertEquals('str', $actual['id']);
101 } 92 }
102 93
103// /** 94// /**
diff --git a/tests/LanguagesTest.php b/tests/LanguagesTest.php
index de83f291..ce24c160 100644
--- a/tests/LanguagesTest.php
+++ b/tests/LanguagesTest.php
@@ -7,7 +7,7 @@ use Shaarli\Config\ConfigManager;
7/** 7/**
8 * Class LanguagesTest. 8 * Class LanguagesTest.
9 */ 9 */
10class LanguagesTest extends \PHPUnit\Framework\TestCase 10class LanguagesTest extends \Shaarli\TestCase
11{ 11{
12 /** 12 /**
13 * @var string Config file path (without extension). 13 * @var string Config file path (without extension).
@@ -22,7 +22,7 @@ class LanguagesTest extends \PHPUnit\Framework\TestCase
22 /** 22 /**
23 * 23 *
24 */ 24 */
25 public function setUp() 25 protected function setUp(): void
26 { 26 {
27 $this->conf = new ConfigManager(self::$configFile); 27 $this->conf = new ConfigManager(self::$configFile);
28 } 28 }
diff --git a/tests/PluginManagerTest.php b/tests/PluginManagerTest.php
index 195d959c..efef5e87 100644
--- a/tests/PluginManagerTest.php
+++ b/tests/PluginManagerTest.php
@@ -1,4 +1,5 @@
1<?php 1<?php
2
2namespace Shaarli\Plugin; 3namespace Shaarli\Plugin;
3 4
4use Shaarli\Config\ConfigManager; 5use Shaarli\Config\ConfigManager;
@@ -6,7 +7,7 @@ use Shaarli\Config\ConfigManager;
6/** 7/**
7 * Unit tests for Plugins 8 * Unit tests for Plugins
8 */ 9 */
9class PluginManagerTest extends \PHPUnit\Framework\TestCase 10class PluginManagerTest extends \Shaarli\TestCase
10{ 11{
11 /** 12 /**
12 * Path to tests plugin. 13 * Path to tests plugin.
@@ -25,7 +26,7 @@ class PluginManagerTest extends \PHPUnit\Framework\TestCase
25 */ 26 */
26 protected $pluginManager; 27 protected $pluginManager;
27 28
28 public function setUp() 29 public function setUp(): void
29 { 30 {
30 $conf = new ConfigManager(''); 31 $conf = new ConfigManager('');
31 $this->pluginManager = new PluginManager($conf); 32 $this->pluginManager = new PluginManager($conf);
@@ -33,57 +34,88 @@ class PluginManagerTest extends \PHPUnit\Framework\TestCase
33 34
34 /** 35 /**
35 * Test plugin loading and hook execution. 36 * Test plugin loading and hook execution.
36 *
37 * @return void
38 */ 37 */
39 public function testPlugin() 38 public function testPlugin(): void
40 { 39 {
41 PluginManager::$PLUGINS_PATH = self::$pluginPath; 40 PluginManager::$PLUGINS_PATH = self::$pluginPath;
42 $this->pluginManager->load(array(self::$pluginName)); 41 $this->pluginManager->load(array(self::$pluginName));
43 42
44 $this->assertTrue(function_exists('hook_test_random')); 43 $this->assertTrue(function_exists('hook_test_random'));
45 44
46 $data = array(0 => 'woot'); 45 $data = [0 => 'woot'];
47 $this->pluginManager->executeHooks('random', $data); 46 $this->pluginManager->executeHooks('random', $data);
48 $this->assertEquals('woot', $data[1]);
49 47
50 $data = array(0 => 'woot'); 48 static::assertCount(2, $data);
49 static::assertSame('woot', $data[1]);
50
51 $data = [0 => 'woot'];
51 $this->pluginManager->executeHooks('random', $data, array('target' => 'test')); 52 $this->pluginManager->executeHooks('random', $data, array('target' => 'test'));
52 $this->assertEquals('page test', $data[1]);
53 53
54 $data = array(0 => 'woot'); 54 static::assertCount(2, $data);
55 static::assertSame('page test', $data[1]);
56
57 $data = [0 => 'woot'];
55 $this->pluginManager->executeHooks('random', $data, array('loggedin' => true)); 58 $this->pluginManager->executeHooks('random', $data, array('loggedin' => true));
56 $this->assertEquals('loggedin', $data[1]); 59
60 static::assertCount(2, $data);
61 static::assertEquals('loggedin', $data[1]);
62
63 $data = [0 => 'woot'];
64 $this->pluginManager->executeHooks('random', $data, array('loggedin' => null));
65
66 static::assertCount(3, $data);
67 static::assertEquals('loggedin', $data[1]);
68 static::assertArrayHasKey(2, $data);
69 static::assertNull($data[2]);
70 }
71
72 /**
73 * Test plugin loading and hook execution with an error: raise an incompatibility error.
74 */
75 public function testPluginWithPhpError(): void
76 {
77 PluginManager::$PLUGINS_PATH = self::$pluginPath;
78 $this->pluginManager->load(array(self::$pluginName));
79
80 $this->assertTrue(function_exists('hook_test_error'));
81
82 $data = [];
83 $this->pluginManager->executeHooks('error', $data);
84
85 $this->assertRegExp(
86 '/test \[plugin incompatibility\]: Class [\'"]Unknown[\'"] not found/',
87 $this->pluginManager->getErrors()[0]
88 );
57 } 89 }
58 90
59 /** 91 /**
60 * Test missing plugin loading. 92 * Test missing plugin loading.
61 */ 93 */
62 public function testPluginNotFound() 94 public function testPluginNotFound(): void
63 { 95 {
64 $this->pluginManager->load(array()); 96 $this->pluginManager->load([]);
65 $this->pluginManager->load(array('nope', 'renope')); 97 $this->pluginManager->load(['nope', 'renope']);
66 $this->addToAssertionCount(1); 98 $this->addToAssertionCount(1);
67 } 99 }
68 100
69 /** 101 /**
70 * Test plugin metadata loading. 102 * Test plugin metadata loading.
71 */ 103 */
72 public function testGetPluginsMeta() 104 public function testGetPluginsMeta(): void
73 { 105 {
74 PluginManager::$PLUGINS_PATH = self::$pluginPath; 106 PluginManager::$PLUGINS_PATH = self::$pluginPath;
75 $this->pluginManager->load(array(self::$pluginName)); 107 $this->pluginManager->load([self::$pluginName]);
76 108
77 $expectedParameters = array( 109 $expectedParameters = [
78 'pop' => array( 110 'pop' => [
79 'value' => '', 111 'value' => '',
80 'desc' => 'pop description', 112 'desc' => 'pop description',
81 ), 113 ],
82 'hip' => array( 114 'hip' => [
83 'value' => '', 115 'value' => '',
84 'desc' => '', 116 'desc' => '',
85 ), 117 ],
86 ); 118 ];
87 $meta = $this->pluginManager->getPluginsMeta(); 119 $meta = $this->pluginManager->getPluginsMeta();
88 $this->assertEquals('test plugin', $meta[self::$pluginName]['description']); 120 $this->assertEquals('test plugin', $meta[self::$pluginName]['description']);
89 $this->assertEquals($expectedParameters, $meta[self::$pluginName]['parameters']); 121 $this->assertEquals($expectedParameters, $meta[self::$pluginName]['parameters']);
diff --git a/tests/RouterTest.php b/tests/RouterTest.php
deleted file mode 100644
index 0cd49bb8..00000000
--- a/tests/RouterTest.php
+++ /dev/null
@@ -1,509 +0,0 @@
1<?php
2namespace Shaarli;
3
4/**
5 * Unit tests for Router
6 */
7class RouterTest extends \PHPUnit\Framework\TestCase
8{
9 /**
10 * Test findPage: login page output.
11 * Valid: page should be return.
12 *
13 * @return void
14 */
15 public function testFindPageLoginValid()
16 {
17 $this->assertEquals(
18 Router::$PAGE_LOGIN,
19 Router::findPage('do=login', array(), false)
20 );
21
22 $this->assertEquals(
23 Router::$PAGE_LOGIN,
24 Router::findPage('do=login', array(), 1)
25 );
26
27 $this->assertEquals(
28 Router::$PAGE_LOGIN,
29 Router::findPage('do=login&stuff', array(), false)
30 );
31 }
32
33 /**
34 * Test findPage: login page output.
35 * Invalid: page shouldn't be return.
36 *
37 * @return void
38 */
39 public function testFindPageLoginInvalid()
40 {
41 $this->assertNotEquals(
42 Router::$PAGE_LOGIN,
43 Router::findPage('do=login', array(), true)
44 );
45
46 $this->assertNotEquals(
47 Router::$PAGE_LOGIN,
48 Router::findPage('do=other', array(), false)
49 );
50 }
51
52 /**
53 * Test findPage: picwall page output.
54 * Valid: page should be return.
55 *
56 * @return void
57 */
58 public function testFindPagePicwallValid()
59 {
60 $this->assertEquals(
61 Router::$PAGE_PICWALL,
62 Router::findPage('do=picwall', array(), false)
63 );
64
65 $this->assertEquals(
66 Router::$PAGE_PICWALL,
67 Router::findPage('do=picwall', array(), true)
68 );
69 }
70
71 /**
72 * Test findPage: picwall page output.
73 * Invalid: page shouldn't be return.
74 *
75 * @return void
76 */
77 public function testFindPagePicwallInvalid()
78 {
79 $this->assertEquals(
80 Router::$PAGE_PICWALL,
81 Router::findPage('do=picwall&stuff', array(), false)
82 );
83
84 $this->assertNotEquals(
85 Router::$PAGE_PICWALL,
86 Router::findPage('do=other', array(), false)
87 );
88 }
89
90 /**
91 * Test findPage: tagcloud page output.
92 * Valid: page should be return.
93 *
94 * @return void
95 */
96 public function testFindPageTagcloudValid()
97 {
98 $this->assertEquals(
99 Router::$PAGE_TAGCLOUD,
100 Router::findPage('do=tagcloud', array(), false)
101 );
102
103 $this->assertEquals(
104 Router::$PAGE_TAGCLOUD,
105 Router::findPage('do=tagcloud', array(), true)
106 );
107
108 $this->assertEquals(
109 Router::$PAGE_TAGCLOUD,
110 Router::findPage('do=tagcloud&stuff', array(), false)
111 );
112 }
113
114 /**
115 * Test findPage: tagcloud page output.
116 * Invalid: page shouldn't be return.
117 *
118 * @return void
119 */
120 public function testFindPageTagcloudInvalid()
121 {
122 $this->assertNotEquals(
123 Router::$PAGE_TAGCLOUD,
124 Router::findPage('do=other', array(), false)
125 );
126 }
127
128 /**
129 * Test findPage: linklist page output.
130 * Valid: page should be return.
131 *
132 * @return void
133 */
134 public function testFindPageLinklistValid()
135 {
136 $this->assertEquals(
137 Router::$PAGE_LINKLIST,
138 Router::findPage('', array(), true)
139 );
140
141 $this->assertEquals(
142 Router::$PAGE_LINKLIST,
143 Router::findPage('whatever', array(), true)
144 );
145
146 $this->assertEquals(
147 Router::$PAGE_LINKLIST,
148 Router::findPage('whatever', array(), false)
149 );
150
151 $this->assertEquals(
152 Router::$PAGE_LINKLIST,
153 Router::findPage('do=tools', array(), false)
154 );
155 }
156
157 /**
158 * Test findPage: tools page output.
159 * Valid: page should be return.
160 *
161 * @return void
162 */
163 public function testFindPageToolsValid()
164 {
165 $this->assertEquals(
166 Router::$PAGE_TOOLS,
167 Router::findPage('do=tools', array(), true)
168 );
169
170 $this->assertEquals(
171 Router::$PAGE_TOOLS,
172 Router::findPage('do=tools&stuff', array(), true)
173 );
174 }
175
176 /**
177 * Test findPage: tools page output.
178 * Invalid: page shouldn't be return.
179 *
180 * @return void
181 */
182 public function testFindPageToolsInvalid()
183 {
184 $this->assertNotEquals(
185 Router::$PAGE_TOOLS,
186 Router::findPage('do=tools', array(), 1)
187 );
188
189 $this->assertNotEquals(
190 Router::$PAGE_TOOLS,
191 Router::findPage('do=tools', array(), false)
192 );
193
194 $this->assertNotEquals(
195 Router::$PAGE_TOOLS,
196 Router::findPage('do=other', array(), true)
197 );
198 }
199
200 /**
201 * Test findPage: changepasswd page output.
202 * Valid: page should be return.
203 *
204 * @return void
205 */
206 public function testFindPageChangepasswdValid()
207 {
208 $this->assertEquals(
209 Router::$PAGE_CHANGEPASSWORD,
210 Router::findPage('do=changepasswd', array(), true)
211 );
212 $this->assertEquals(
213 Router::$PAGE_CHANGEPASSWORD,
214 Router::findPage('do=changepasswd&stuff', array(), true)
215 );
216 }
217
218 /**
219 * Test findPage: changepasswd page output.
220 * Invalid: page shouldn't be return.
221 *
222 * @return void
223 */
224 public function testFindPageChangepasswdInvalid()
225 {
226 $this->assertNotEquals(
227 Router::$PAGE_CHANGEPASSWORD,
228 Router::findPage('do=changepasswd', array(), 1)
229 );
230
231 $this->assertNotEquals(
232 Router::$PAGE_CHANGEPASSWORD,
233 Router::findPage('do=changepasswd', array(), false)
234 );
235
236 $this->assertNotEquals(
237 Router::$PAGE_CHANGEPASSWORD,
238 Router::findPage('do=other', array(), true)
239 );
240 }
241 /**
242 * Test findPage: configure page output.
243 * Valid: page should be return.
244 *
245 * @return void
246 */
247 public function testFindPageConfigureValid()
248 {
249 $this->assertEquals(
250 Router::$PAGE_CONFIGURE,
251 Router::findPage('do=configure', array(), true)
252 );
253
254 $this->assertEquals(
255 Router::$PAGE_CONFIGURE,
256 Router::findPage('do=configure&stuff', array(), true)
257 );
258 }
259
260 /**
261 * Test findPage: configure page output.
262 * Invalid: page shouldn't be return.
263 *
264 * @return void
265 */
266 public function testFindPageConfigureInvalid()
267 {
268 $this->assertNotEquals(
269 Router::$PAGE_CONFIGURE,
270 Router::findPage('do=configure', array(), 1)
271 );
272
273 $this->assertNotEquals(
274 Router::$PAGE_CONFIGURE,
275 Router::findPage('do=configure', array(), false)
276 );
277
278 $this->assertNotEquals(
279 Router::$PAGE_CONFIGURE,
280 Router::findPage('do=other', array(), true)
281 );
282 }
283
284 /**
285 * Test findPage: changetag page output.
286 * Valid: page should be return.
287 *
288 * @return void
289 */
290 public function testFindPageChangetagValid()
291 {
292 $this->assertEquals(
293 Router::$PAGE_CHANGETAG,
294 Router::findPage('do=changetag', array(), true)
295 );
296
297 $this->assertEquals(
298 Router::$PAGE_CHANGETAG,
299 Router::findPage('do=changetag&stuff', array(), true)
300 );
301 }
302
303 /**
304 * Test findPage: changetag page output.
305 * Invalid: page shouldn't be return.
306 *
307 * @return void
308 */
309 public function testFindPageChangetagInvalid()
310 {
311 $this->assertNotEquals(
312 Router::$PAGE_CHANGETAG,
313 Router::findPage('do=changetag', array(), 1)
314 );
315
316 $this->assertNotEquals(
317 Router::$PAGE_CHANGETAG,
318 Router::findPage('do=changetag', array(), false)
319 );
320
321 $this->assertNotEquals(
322 Router::$PAGE_CHANGETAG,
323 Router::findPage('do=other', array(), true)
324 );
325 }
326
327 /**
328 * Test findPage: addlink page output.
329 * Valid: page should be return.
330 *
331 * @return void
332 */
333 public function testFindPageAddlinkValid()
334 {
335 $this->assertEquals(
336 Router::$PAGE_ADDLINK,
337 Router::findPage('do=addlink', array(), true)
338 );
339
340 $this->assertEquals(
341 Router::$PAGE_ADDLINK,
342 Router::findPage('do=addlink&stuff', array(), true)
343 );
344 }
345
346 /**
347 * Test findPage: addlink page output.
348 * Invalid: page shouldn't be return.
349 *
350 * @return void
351 */
352 public function testFindPageAddlinkInvalid()
353 {
354 $this->assertNotEquals(
355 Router::$PAGE_ADDLINK,
356 Router::findPage('do=addlink', array(), 1)
357 );
358
359 $this->assertNotEquals(
360 Router::$PAGE_ADDLINK,
361 Router::findPage('do=addlink', array(), false)
362 );
363
364 $this->assertNotEquals(
365 Router::$PAGE_ADDLINK,
366 Router::findPage('do=other', array(), true)
367 );
368 }
369
370 /**
371 * Test findPage: export page output.
372 * Valid: page should be return.
373 *
374 * @return void
375 */
376 public function testFindPageExportValid()
377 {
378 $this->assertEquals(
379 Router::$PAGE_EXPORT,
380 Router::findPage('do=export', array(), true)
381 );
382
383 $this->assertEquals(
384 Router::$PAGE_EXPORT,
385 Router::findPage('do=export&stuff', array(), true)
386 );
387 }
388
389 /**
390 * Test findPage: export page output.
391 * Invalid: page shouldn't be return.
392 *
393 * @return void
394 */
395 public function testFindPageExportInvalid()
396 {
397 $this->assertNotEquals(
398 Router::$PAGE_EXPORT,
399 Router::findPage('do=export', array(), 1)
400 );
401
402 $this->assertNotEquals(
403 Router::$PAGE_EXPORT,
404 Router::findPage('do=export', array(), false)
405 );
406
407 $this->assertNotEquals(
408 Router::$PAGE_EXPORT,
409 Router::findPage('do=other', array(), true)
410 );
411 }
412
413 /**
414 * Test findPage: import page output.
415 * Valid: page should be return.
416 *
417 * @return void
418 */
419 public function testFindPageImportValid()
420 {
421 $this->assertEquals(
422 Router::$PAGE_IMPORT,
423 Router::findPage('do=import', array(), true)
424 );
425
426 $this->assertEquals(
427 Router::$PAGE_IMPORT,
428 Router::findPage('do=import&stuff', array(), true)
429 );
430 }
431
432 /**
433 * Test findPage: import page output.
434 * Invalid: page shouldn't be return.
435 *
436 * @return void
437 */
438 public function testFindPageImportInvalid()
439 {
440 $this->assertNotEquals(
441 Router::$PAGE_IMPORT,
442 Router::findPage('do=import', array(), 1)
443 );
444
445 $this->assertNotEquals(
446 Router::$PAGE_IMPORT,
447 Router::findPage('do=import', array(), false)
448 );
449
450 $this->assertNotEquals(
451 Router::$PAGE_IMPORT,
452 Router::findPage('do=other', array(), true)
453 );
454 }
455
456 /**
457 * Test findPage: editlink page output.
458 * Valid: page should be return.
459 *
460 * @return void
461 */
462 public function testFindPageEditlinkValid()
463 {
464 $this->assertEquals(
465 Router::$PAGE_EDITLINK,
466 Router::findPage('whatever', array('edit_link' => 1), true)
467 );
468
469 $this->assertEquals(
470 Router::$PAGE_EDITLINK,
471 Router::findPage('', array('edit_link' => 1), true)
472 );
473
474
475 $this->assertEquals(
476 Router::$PAGE_EDITLINK,
477 Router::findPage('whatever', array('post' => 1), true)
478 );
479
480 $this->assertEquals(
481 Router::$PAGE_EDITLINK,
482 Router::findPage('whatever', array('post' => 1, 'edit_link' => 1), true)
483 );
484 }
485
486 /**
487 * Test findPage: editlink page output.
488 * Invalid: page shouldn't be return.
489 *
490 * @return void
491 */
492 public function testFindPageEditlinkInvalid()
493 {
494 $this->assertNotEquals(
495 Router::$PAGE_EDITLINK,
496 Router::findPage('whatever', array('edit_link' => 1), false)
497 );
498
499 $this->assertNotEquals(
500 Router::$PAGE_EDITLINK,
501 Router::findPage('whatever', array('edit_link' => 1), 1)
502 );
503
504 $this->assertNotEquals(
505 Router::$PAGE_EDITLINK,
506 Router::findPage('whatever', array(), true)
507 );
508 }
509}
diff --git a/tests/TestCase.php b/tests/TestCase.php
new file mode 100644
index 00000000..781e7aa3
--- /dev/null
+++ b/tests/TestCase.php
@@ -0,0 +1,77 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli;
6
7/**
8 * Helper class extending \PHPUnit\Framework\TestCase.
9 * Used to make Shaarli UT run on multiple versions of PHPUnit.
10 */
11class TestCase extends \PHPUnit\Framework\TestCase
12{
13 /**
14 * expectExceptionMessageRegExp has been removed and replaced by expectExceptionMessageMatches in PHPUnit 9.
15 */
16 public function expectExceptionMessageRegExp(string $regularExpression): void
17 {
18 if (method_exists($this, 'expectExceptionMessageMatches')) {
19 $this->expectExceptionMessageMatches($regularExpression);
20 } else {
21 parent::expectExceptionMessageRegExp($regularExpression);
22 }
23 }
24
25 /**
26 * assertContains is now used for iterable, strings should use assertStringContainsString
27 */
28 public function assertContainsPolyfill($expected, $actual, string $message = ''): void
29 {
30 if (is_string($actual) && method_exists($this, 'assertStringContainsString')) {
31 static::assertStringContainsString($expected, $actual, $message);
32 } else {
33 static::assertContains($expected, $actual, $message);
34 }
35 }
36
37 /**
38 * assertNotContains is now used for iterable, strings should use assertStringNotContainsString
39 */
40 public function assertNotContainsPolyfill($expected, $actual, string $message = ''): void
41 {
42 if (is_string($actual) && method_exists($this, 'assertStringNotContainsString')) {
43 static::assertStringNotContainsString($expected, $actual, $message);
44 } else {
45 static::assertNotContains($expected, $actual, $message);
46 }
47 }
48
49 /**
50 * assertFileNotExists has been renamed in assertFileDoesNotExist
51 */
52 public static function assertFileNotExists(string $filename, string $message = ''): void
53 {
54 if (method_exists(TestCase::class, 'assertFileDoesNotExist')) {
55 static::assertFileDoesNotExist($filename, $message);
56 } else {
57 parent::assertFileNotExists($filename, $message);
58 }
59 }
60
61 /**
62 * assertRegExp has been renamed in assertMatchesRegularExpression
63 */
64 public static function assertRegExp(string $pattern, string $string, string $message = ''): void
65 {
66 if (method_exists(TestCase::class, 'assertMatchesRegularExpression')) {
67 static::assertMatchesRegularExpression($pattern, $string, $message);
68 } else {
69 parent::assertRegExp($pattern, $string, $message);
70 }
71 }
72
73 public function isInTestsContext(): bool
74 {
75 return true;
76 }
77}
diff --git a/tests/ThumbnailerTest.php b/tests/ThumbnailerTest.php
index c01849f7..70519aca 100644
--- a/tests/ThumbnailerTest.php
+++ b/tests/ThumbnailerTest.php
@@ -2,7 +2,6 @@
2 2
3namespace Shaarli; 3namespace Shaarli;
4 4
5use PHPUnit\Framework\TestCase;
6use Shaarli\Config\ConfigManager; 5use Shaarli\Config\ConfigManager;
7use WebThumbnailer\Application\ConfigManager as WTConfigManager; 6use WebThumbnailer\Application\ConfigManager as WTConfigManager;
8 7
@@ -30,7 +29,7 @@ class ThumbnailerTest extends TestCase
30 */ 29 */
31 protected $conf; 30 protected $conf;
32 31
33 public function setUp() 32 protected function setUp(): void
34 { 33 {
35 $this->conf = new ConfigManager('tests/utils/config/configJson'); 34 $this->conf = new ConfigManager('tests/utils/config/configJson');
36 $this->conf->set('thumbnails.mode', Thumbnailer::MODE_ALL); 35 $this->conf->set('thumbnails.mode', Thumbnailer::MODE_ALL);
@@ -43,7 +42,7 @@ class ThumbnailerTest extends TestCase
43 WTConfigManager::addFile('tests/utils/config/wt.json'); 42 WTConfigManager::addFile('tests/utils/config/wt.json');
44 } 43 }
45 44
46 public function tearDown() 45 protected function tearDown(): void
47 { 46 {
48 $this->rrmdirContent('sandbox/'); 47 $this->rrmdirContent('sandbox/');
49 } 48 }
diff --git a/tests/TimeZoneTest.php b/tests/TimeZoneTest.php
index 02bf060f..77862855 100644
--- a/tests/TimeZoneTest.php
+++ b/tests/TimeZoneTest.php
@@ -8,14 +8,14 @@ require_once 'application/TimeZone.php';
8/** 8/**
9 * Unitary tests for timezone utilities 9 * Unitary tests for timezone utilities
10 */ 10 */
11class TimeZoneTest extends PHPUnit\Framework\TestCase 11class TimeZoneTest extends \Shaarli\TestCase
12{ 12{
13 /** 13 /**
14 * @var array of timezones 14 * @var array of timezones
15 */ 15 */
16 protected $installedTimezones; 16 protected $installedTimezones;
17 17
18 public function setUp() 18 protected function setUp(): void
19 { 19 {
20 $this->installedTimezones = [ 20 $this->installedTimezones = [
21 'Antarctica/Syowa', 21 'Antarctica/Syowa',
diff --git a/tests/UtilsTest.php b/tests/UtilsTest.php
index 26d2a6b8..59dca75f 100644
--- a/tests/UtilsTest.php
+++ b/tests/UtilsTest.php
@@ -10,7 +10,7 @@ require_once 'application/Languages.php';
10/** 10/**
11 * Unitary tests for Shaarli utilities 11 * Unitary tests for Shaarli utilities
12 */ 12 */
13class UtilsTest extends PHPUnit\Framework\TestCase 13class UtilsTest extends \Shaarli\TestCase
14{ 14{
15 // Log file 15 // Log file
16 protected static $testLogFile = 'tests.log'; 16 protected static $testLogFile = 'tests.log';
@@ -26,7 +26,7 @@ class UtilsTest extends PHPUnit\Framework\TestCase
26 /** 26 /**
27 * Assign reference data 27 * Assign reference data
28 */ 28 */
29 public static function setUpBeforeClass() 29 public static function setUpBeforeClass(): void
30 { 30 {
31 self::$defaultTimeZone = date_default_timezone_get(); 31 self::$defaultTimeZone = date_default_timezone_get();
32 // Timezone without DST for test consistency 32 // Timezone without DST for test consistency
@@ -36,7 +36,7 @@ class UtilsTest extends PHPUnit\Framework\TestCase
36 /** 36 /**
37 * Reset the timezone 37 * Reset the timezone
38 */ 38 */
39 public static function tearDownAfterClass() 39 public static function tearDownAfterClass(): void
40 { 40 {
41 date_default_timezone_set(self::$defaultTimeZone); 41 date_default_timezone_set(self::$defaultTimeZone);
42 } 42 }
@@ -44,7 +44,7 @@ class UtilsTest extends PHPUnit\Framework\TestCase
44 /** 44 /**
45 * Resets test data before each test 45 * Resets test data before each test
46 */ 46 */
47 protected function setUp() 47 protected function setUp(): void
48 { 48 {
49 if (file_exists(self::$testLogFile)) { 49 if (file_exists(self::$testLogFile)) {
50 unlink(self::$testLogFile); 50 unlink(self::$testLogFile);
@@ -63,41 +63,25 @@ class UtilsTest extends PHPUnit\Framework\TestCase
63 } 63 }
64 64
65 /** 65 /**
66 * Log a message to a file - IPv4 client address 66 * Format a log a message - IPv4 client address
67 */ 67 */
68 public function testLogmIp4() 68 public function testFormatLogIp4()
69 { 69 {
70 $logMessage = 'IPv4 client connected'; 70 $message = 'IPv4 client connected';
71 logm(self::$testLogFile, '127.0.0.1', $logMessage); 71 $log = format_log($message, '127.0.0.1');
72 list($date, $ip, $message) = $this->getLastLogEntry();
73 72
74 $this->assertInstanceOf( 73 static::assertSame('- 127.0.0.1 - IPv4 client connected', $log);
75 'DateTime',
76 DateTime::createFromFormat(self::$dateFormat, $date)
77 );
78 $this->assertTrue(
79 filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false
80 );
81 $this->assertEquals($logMessage, $message);
82 } 74 }
83 75
84 /** 76 /**
85 * Log a message to a file - IPv6 client address 77 * Format a log a message - IPv6 client address
86 */ 78 */
87 public function testLogmIp6() 79 public function testFormatLogIp6()
88 { 80 {
89 $logMessage = 'IPv6 client connected'; 81 $message = 'IPv6 client connected';
90 logm(self::$testLogFile, '2001:db8::ff00:42:8329', $logMessage); 82 $log = format_log($message, '2001:db8::ff00:42:8329');
91 list($date, $ip, $message) = $this->getLastLogEntry();
92 83
93 $this->assertInstanceOf( 84 static::assertSame('- 2001:db8::ff00:42:8329 - IPv6 client connected', $log);
94 'DateTime',
95 DateTime::createFromFormat(self::$dateFormat, $date)
96 );
97 $this->assertTrue(
98 filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false
99 );
100 $this->assertEquals($logMessage, $message);
101 } 85 }
102 86
103 /** 87 /**
diff --git a/tests/api/ApiMiddlewareTest.php b/tests/api/ApiMiddlewareTest.php
index df2fb33a..86700840 100644
--- a/tests/api/ApiMiddlewareTest.php
+++ b/tests/api/ApiMiddlewareTest.php
@@ -18,7 +18,7 @@ use Slim\Http\Response;
18 * 18 *
19 * @package Api 19 * @package Api
20 */ 20 */
21class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase 21class ApiMiddlewareTest extends \Shaarli\TestCase
22{ 22{
23 /** 23 /**
24 * @var string datastore to test write operations 24 * @var string datastore to test write operations
@@ -26,7 +26,7 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
26 protected static $testDatastore = 'sandbox/datastore.php'; 26 protected static $testDatastore = 'sandbox/datastore.php';
27 27
28 /** 28 /**
29 * @var \ConfigManager instance 29 * @var ConfigManager instance
30 */ 30 */
31 protected $conf; 31 protected $conf;
32 32
@@ -43,7 +43,7 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
43 /** 43 /**
44 * Before every test, instantiate a new Api with its config, plugins and bookmarks. 44 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
45 */ 45 */
46 public function setUp() 46 protected function setUp(): void
47 { 47 {
48 $this->conf = new ConfigManager('tests/utils/config/configJson'); 48 $this->conf = new ConfigManager('tests/utils/config/configJson');
49 $this->conf->set('api.secret', 'NapoleonWasALizard'); 49 $this->conf->set('api.secret', 'NapoleonWasALizard');
@@ -61,12 +61,59 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
61 /** 61 /**
62 * After every test, remove the test datastore. 62 * After every test, remove the test datastore.
63 */ 63 */
64 public function tearDown() 64 protected function tearDown(): void
65 { 65 {
66 @unlink(self::$testDatastore); 66 @unlink(self::$testDatastore);
67 } 67 }
68 68
69 /** 69 /**
70 * Invoke the middleware with a valid token
71 */
72 public function testInvokeMiddlewareWithValidToken(): void
73 {
74 $next = function (Request $request, Response $response): Response {
75 return $response;
76 };
77 $mw = new ApiMiddleware($this->container);
78 $env = Environment::mock([
79 'REQUEST_METHOD' => 'GET',
80 'REQUEST_URI' => '/echo',
81 'HTTP_AUTHORIZATION'=> 'Bearer ' . ApiUtilsTest::generateValidJwtToken('NapoleonWasALizard'),
82 ]);
83 $request = Request::createFromEnvironment($env);
84 $response = new Response();
85 /** @var Response $response */
86 $response = $mw($request, $response, $next);
87
88 $this->assertEquals(200, $response->getStatusCode());
89 }
90
91 /**
92 * Invoke the middleware with a valid token
93 * Using specific Apache CGI redirected authorization.
94 */
95 public function testInvokeMiddlewareWithValidTokenFromRedirectedHeader(): void
96 {
97 $next = function (Request $request, Response $response): Response {
98 return $response;
99 };
100
101 $token = 'Bearer ' . ApiUtilsTest::generateValidJwtToken('NapoleonWasALizard');
102 $this->container->environment['REDIRECT_HTTP_AUTHORIZATION'] = $token;
103 $mw = new ApiMiddleware($this->container);
104 $env = Environment::mock([
105 'REQUEST_METHOD' => 'GET',
106 'REQUEST_URI' => '/echo',
107 ]);
108 $request = Request::createFromEnvironment($env);
109 $response = new Response();
110 /** @var Response $response */
111 $response = $mw($request, $response, $next);
112
113 $this->assertEquals(200, $response->getStatusCode());
114 }
115
116 /**
70 * Invoke the middleware with the API disabled: 117 * Invoke the middleware with the API disabled:
71 * should return a 401 error Unauthorized. 118 * should return a 401 error Unauthorized.
72 */ 119 */
@@ -109,7 +156,7 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
109 $this->assertEquals(401, $response->getStatusCode()); 156 $this->assertEquals(401, $response->getStatusCode());
110 $body = json_decode((string) $response->getBody()); 157 $body = json_decode((string) $response->getBody());
111 $this->assertEquals('Not authorized: API is disabled', $body->message); 158 $this->assertEquals('Not authorized: API is disabled', $body->message);
112 $this->assertContains('ApiAuthorizationException', $body->stacktrace); 159 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
113 } 160 }
114 161
115 /** 162 /**
@@ -132,7 +179,7 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
132 $this->assertEquals(401, $response->getStatusCode()); 179 $this->assertEquals(401, $response->getStatusCode());
133 $body = json_decode((string) $response->getBody()); 180 $body = json_decode((string) $response->getBody());
134 $this->assertEquals('Not authorized: JWT token not provided', $body->message); 181 $this->assertEquals('Not authorized: JWT token not provided', $body->message);
135 $this->assertContains('ApiAuthorizationException', $body->stacktrace); 182 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
136 } 183 }
137 184
138 /** 185 /**
@@ -157,7 +204,7 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
157 $this->assertEquals(401, $response->getStatusCode()); 204 $this->assertEquals(401, $response->getStatusCode());
158 $body = json_decode((string) $response->getBody()); 205 $body = json_decode((string) $response->getBody());
159 $this->assertEquals('Not authorized: Token secret must be set in Shaarli\'s administration', $body->message); 206 $this->assertEquals('Not authorized: Token secret must be set in Shaarli\'s administration', $body->message);
160 $this->assertContains('ApiAuthorizationException', $body->stacktrace); 207 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
161 } 208 }
162 209
163 /** 210 /**
@@ -180,7 +227,7 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
180 $this->assertEquals(401, $response->getStatusCode()); 227 $this->assertEquals(401, $response->getStatusCode());
181 $body = json_decode((string) $response->getBody()); 228 $body = json_decode((string) $response->getBody());
182 $this->assertEquals('Not authorized: Invalid JWT header', $body->message); 229 $this->assertEquals('Not authorized: Invalid JWT header', $body->message);
183 $this->assertContains('ApiAuthorizationException', $body->stacktrace); 230 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
184 } 231 }
185 232
186 /** 233 /**
@@ -206,6 +253,6 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
206 $this->assertEquals(401, $response->getStatusCode()); 253 $this->assertEquals(401, $response->getStatusCode());
207 $body = json_decode((string) $response->getBody()); 254 $body = json_decode((string) $response->getBody());
208 $this->assertEquals('Not authorized: Malformed JWT token', $body->message); 255 $this->assertEquals('Not authorized: Malformed JWT token', $body->message);
209 $this->assertContains('ApiAuthorizationException', $body->stacktrace); 256 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
210 } 257 }
211} 258}
diff --git a/tests/api/ApiUtilsTest.php b/tests/api/ApiUtilsTest.php
index 7efec9bb..7a143859 100644
--- a/tests/api/ApiUtilsTest.php
+++ b/tests/api/ApiUtilsTest.php
@@ -8,12 +8,12 @@ use Shaarli\Http\Base64Url;
8/** 8/**
9 * Class ApiUtilsTest 9 * Class ApiUtilsTest
10 */ 10 */
11class ApiUtilsTest extends \PHPUnit\Framework\TestCase 11class ApiUtilsTest extends \Shaarli\TestCase
12{ 12{
13 /** 13 /**
14 * Force the timezone for ISO datetimes. 14 * Force the timezone for ISO datetimes.
15 */ 15 */
16 public static function setUpBeforeClass() 16 public static function setUpBeforeClass(): void
17 { 17 {
18 date_default_timezone_set('UTC'); 18 date_default_timezone_set('UTC');
19 } 19 }
@@ -66,143 +66,143 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
66 66
67 /** 67 /**
68 * Test validateJwtToken() with a malformed JWT token. 68 * Test validateJwtToken() with a malformed JWT token.
69 *
70 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
71 * @expectedExceptionMessage Malformed JWT token
72 */ 69 */
73 public function testValidateJwtTokenMalformed() 70 public function testValidateJwtTokenMalformed()
74 { 71 {
72 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
73 $this->expectExceptionMessage('Malformed JWT token');
74
75 $token = 'ABC.DEF'; 75 $token = 'ABC.DEF';
76 ApiUtils::validateJwtToken($token, 'foo'); 76 ApiUtils::validateJwtToken($token, 'foo');
77 } 77 }
78 78
79 /** 79 /**
80 * Test validateJwtToken() with an empty JWT token. 80 * Test validateJwtToken() with an empty JWT token.
81 *
82 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
83 * @expectedExceptionMessage Malformed JWT token
84 */ 81 */
85 public function testValidateJwtTokenMalformedEmpty() 82 public function testValidateJwtTokenMalformedEmpty()
86 { 83 {
84 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
85 $this->expectExceptionMessage('Malformed JWT token');
86
87 $token = false; 87 $token = false;
88 ApiUtils::validateJwtToken($token, 'foo'); 88 ApiUtils::validateJwtToken($token, 'foo');
89 } 89 }
90 90
91 /** 91 /**
92 * Test validateJwtToken() with a JWT token without header. 92 * Test validateJwtToken() with a JWT token without header.
93 *
94 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
95 * @expectedExceptionMessage Malformed JWT token
96 */ 93 */
97 public function testValidateJwtTokenMalformedEmptyHeader() 94 public function testValidateJwtTokenMalformedEmptyHeader()
98 { 95 {
96 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
97 $this->expectExceptionMessage('Malformed JWT token');
98
99 $token = '.payload.signature'; 99 $token = '.payload.signature';
100 ApiUtils::validateJwtToken($token, 'foo'); 100 ApiUtils::validateJwtToken($token, 'foo');
101 } 101 }
102 102
103 /** 103 /**
104 * Test validateJwtToken() with a JWT token without payload 104 * Test validateJwtToken() with a JWT token without payload
105 *
106 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
107 * @expectedExceptionMessage Malformed JWT token
108 */ 105 */
109 public function testValidateJwtTokenMalformedEmptyPayload() 106 public function testValidateJwtTokenMalformedEmptyPayload()
110 { 107 {
108 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
109 $this->expectExceptionMessage('Malformed JWT token');
110
111 $token = 'header..signature'; 111 $token = 'header..signature';
112 ApiUtils::validateJwtToken($token, 'foo'); 112 ApiUtils::validateJwtToken($token, 'foo');
113 } 113 }
114 114
115 /** 115 /**
116 * Test validateJwtToken() with a JWT token with an empty signature. 116 * Test validateJwtToken() with a JWT token with an empty signature.
117 *
118 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
119 * @expectedExceptionMessage Invalid JWT signature
120 */ 117 */
121 public function testValidateJwtTokenInvalidSignatureEmpty() 118 public function testValidateJwtTokenInvalidSignatureEmpty()
122 { 119 {
120 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
121 $this->expectExceptionMessage('Invalid JWT signature');
122
123 $token = 'header.payload.'; 123 $token = 'header.payload.';
124 ApiUtils::validateJwtToken($token, 'foo'); 124 ApiUtils::validateJwtToken($token, 'foo');
125 } 125 }
126 126
127 /** 127 /**
128 * Test validateJwtToken() with a JWT token with an invalid signature. 128 * Test validateJwtToken() with a JWT token with an invalid signature.
129 *
130 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
131 * @expectedExceptionMessage Invalid JWT signature
132 */ 129 */
133 public function testValidateJwtTokenInvalidSignature() 130 public function testValidateJwtTokenInvalidSignature()
134 { 131 {
132 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
133 $this->expectExceptionMessage('Invalid JWT signature');
134
135 $token = 'header.payload.nope'; 135 $token = 'header.payload.nope';
136 ApiUtils::validateJwtToken($token, 'foo'); 136 ApiUtils::validateJwtToken($token, 'foo');
137 } 137 }
138 138
139 /** 139 /**
140 * Test validateJwtToken() with a JWT token with a signature generated with the wrong API secret. 140 * Test validateJwtToken() with a JWT token with a signature generated with the wrong API secret.
141 *
142 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
143 * @expectedExceptionMessage Invalid JWT signature
144 */ 141 */
145 public function testValidateJwtTokenInvalidSignatureSecret() 142 public function testValidateJwtTokenInvalidSignatureSecret()
146 { 143 {
144 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
145 $this->expectExceptionMessage('Invalid JWT signature');
146
147 ApiUtils::validateJwtToken(self::generateValidJwtToken('foo'), 'bar'); 147 ApiUtils::validateJwtToken(self::generateValidJwtToken('foo'), 'bar');
148 } 148 }
149 149
150 /** 150 /**
151 * Test validateJwtToken() with a JWT token with a an invalid header (not JSON). 151 * Test validateJwtToken() with a JWT token with a an invalid header (not JSON).
152 *
153 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
154 * @expectedExceptionMessage Invalid JWT header
155 */ 152 */
156 public function testValidateJwtTokenInvalidHeader() 153 public function testValidateJwtTokenInvalidHeader()
157 { 154 {
155 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
156 $this->expectExceptionMessage('Invalid JWT header');
157
158 $token = $this->generateCustomJwtToken('notJSON', '{"JSON":1}', 'secret'); 158 $token = $this->generateCustomJwtToken('notJSON', '{"JSON":1}', 'secret');
159 ApiUtils::validateJwtToken($token, 'secret'); 159 ApiUtils::validateJwtToken($token, 'secret');
160 } 160 }
161 161
162 /** 162 /**
163 * Test validateJwtToken() with a JWT token with a an invalid payload (not JSON). 163 * Test validateJwtToken() with a JWT token with a an invalid payload (not JSON).
164 *
165 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
166 * @expectedExceptionMessage Invalid JWT payload
167 */ 164 */
168 public function testValidateJwtTokenInvalidPayload() 165 public function testValidateJwtTokenInvalidPayload()
169 { 166 {
167 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
168 $this->expectExceptionMessage('Invalid JWT payload');
169
170 $token = $this->generateCustomJwtToken('{"JSON":1}', 'notJSON', 'secret'); 170 $token = $this->generateCustomJwtToken('{"JSON":1}', 'notJSON', 'secret');
171 ApiUtils::validateJwtToken($token, 'secret'); 171 ApiUtils::validateJwtToken($token, 'secret');
172 } 172 }
173 173
174 /** 174 /**
175 * Test validateJwtToken() with a JWT token without issued time. 175 * Test validateJwtToken() with a JWT token without issued time.
176 *
177 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
178 * @expectedExceptionMessage Invalid JWT issued time
179 */ 176 */
180 public function testValidateJwtTokenInvalidTimeEmpty() 177 public function testValidateJwtTokenInvalidTimeEmpty()
181 { 178 {
179 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
180 $this->expectExceptionMessage('Invalid JWT issued time');
181
182 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"JSON":1}', 'secret'); 182 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"JSON":1}', 'secret');
183 ApiUtils::validateJwtToken($token, 'secret'); 183 ApiUtils::validateJwtToken($token, 'secret');
184 } 184 }
185 185
186 /** 186 /**
187 * Test validateJwtToken() with an expired JWT token. 187 * Test validateJwtToken() with an expired JWT token.
188 *
189 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
190 * @expectedExceptionMessage Invalid JWT issued time
191 */ 188 */
192 public function testValidateJwtTokenInvalidTimeExpired() 189 public function testValidateJwtTokenInvalidTimeExpired()
193 { 190 {
191 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
192 $this->expectExceptionMessage('Invalid JWT issued time');
193
194 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() - 600) . '}', 'secret'); 194 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() - 600) . '}', 'secret');
195 ApiUtils::validateJwtToken($token, 'secret'); 195 ApiUtils::validateJwtToken($token, 'secret');
196 } 196 }
197 197
198 /** 198 /**
199 * Test validateJwtToken() with a JWT token issued in the future. 199 * Test validateJwtToken() with a JWT token issued in the future.
200 *
201 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
202 * @expectedExceptionMessage Invalid JWT issued time
203 */ 200 */
204 public function testValidateJwtTokenInvalidTimeFuture() 201 public function testValidateJwtTokenInvalidTimeFuture()
205 { 202 {
203 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
204 $this->expectExceptionMessage('Invalid JWT issued time');
205
206 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() + 60) . '}', 'secret'); 206 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() + 60) . '}', 'secret');
207 ApiUtils::validateJwtToken($token, 'secret'); 207 ApiUtils::validateJwtToken($token, 'secret');
208 } 208 }
diff --git a/tests/api/controllers/history/HistoryTest.php b/tests/api/controllers/history/HistoryTest.php
index f4d3b646..84f8716e 100644
--- a/tests/api/controllers/history/HistoryTest.php
+++ b/tests/api/controllers/history/HistoryTest.php
@@ -11,7 +11,7 @@ use Slim\Http\Response;
11 11
12require_once 'tests/utils/ReferenceHistory.php'; 12require_once 'tests/utils/ReferenceHistory.php';
13 13
14class HistoryTest extends \PHPUnit\Framework\TestCase 14class HistoryTest extends \Shaarli\TestCase
15{ 15{
16 /** 16 /**
17 * @var string datastore to test write operations 17 * @var string datastore to test write operations
@@ -41,7 +41,7 @@ class HistoryTest extends \PHPUnit\Framework\TestCase
41 /** 41 /**
42 * Before every test, instantiate a new Api with its config, plugins and bookmarks. 42 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
43 */ 43 */
44 public function setUp() 44 protected function setUp(): void
45 { 45 {
46 $this->conf = new ConfigManager('tests/utils/config/configJson'); 46 $this->conf = new ConfigManager('tests/utils/config/configJson');
47 $this->refHistory = new \ReferenceHistory(); 47 $this->refHistory = new \ReferenceHistory();
@@ -57,7 +57,7 @@ class HistoryTest extends \PHPUnit\Framework\TestCase
57 /** 57 /**
58 * After every test, remove the test datastore. 58 * After every test, remove the test datastore.
59 */ 59 */
60 public function tearDown() 60 protected function tearDown(): void
61 { 61 {
62 @unlink(self::$testHistory); 62 @unlink(self::$testHistory);
63 } 63 }
diff --git a/tests/api/controllers/info/InfoTest.php b/tests/api/controllers/info/InfoTest.php
index b5c938e1..10b29ab2 100644
--- a/tests/api/controllers/info/InfoTest.php
+++ b/tests/api/controllers/info/InfoTest.php
@@ -1,10 +1,11 @@
1<?php 1<?php
2namespace Shaarli\Api\Controllers; 2namespace Shaarli\Api\Controllers;
3 3
4use PHPUnit\Framework\TestCase; 4use malkusch\lock\mutex\NoMutex;
5use Shaarli\Bookmark\BookmarkFileService; 5use Shaarli\Bookmark\BookmarkFileService;
6use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
7use Shaarli\History; 7use Shaarli\History;
8use Shaarli\TestCase;
8use Slim\Container; 9use Slim\Container;
9use Slim\Http\Environment; 10use Slim\Http\Environment;
10use Slim\Http\Request; 11use Slim\Http\Request;
@@ -47,8 +48,9 @@ class InfoTest extends TestCase
47 /** 48 /**
48 * Before every test, instantiate a new Api with its config, plugins and bookmarks. 49 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
49 */ 50 */
50 public function setUp() 51 protected function setUp(): void
51 { 52 {
53 $mutex = new NoMutex();
52 $this->conf = new ConfigManager('tests/utils/config/configJson'); 54 $this->conf = new ConfigManager('tests/utils/config/configJson');
53 $this->conf->set('resource.datastore', self::$testDatastore); 55 $this->conf->set('resource.datastore', self::$testDatastore);
54 $this->refDB = new \ReferenceLinkDB(); 56 $this->refDB = new \ReferenceLinkDB();
@@ -58,7 +60,7 @@ class InfoTest extends TestCase
58 60
59 $this->container = new Container(); 61 $this->container = new Container();
60 $this->container['conf'] = $this->conf; 62 $this->container['conf'] = $this->conf;
61 $this->container['db'] = new BookmarkFileService($this->conf, $history, true); 63 $this->container['db'] = new BookmarkFileService($this->conf, $history, $mutex, true);
62 $this->container['history'] = null; 64 $this->container['history'] = null;
63 65
64 $this->controller = new Info($this->container); 66 $this->controller = new Info($this->container);
@@ -67,7 +69,7 @@ class InfoTest extends TestCase
67 /** 69 /**
68 * After every test, remove the test datastore. 70 * After every test, remove the test datastore.
69 */ 71 */
70 public function tearDown() 72 protected function tearDown(): void
71 { 73 {
72 @unlink(self::$testDatastore); 74 @unlink(self::$testDatastore);
73 } 75 }
diff --git a/tests/api/controllers/links/DeleteLinkTest.php b/tests/api/controllers/links/DeleteLinkTest.php
index 6c2b3698..805c9be3 100644
--- a/tests/api/controllers/links/DeleteLinkTest.php
+++ b/tests/api/controllers/links/DeleteLinkTest.php
@@ -3,6 +3,7 @@
3 3
4namespace Shaarli\Api\Controllers; 4namespace Shaarli\Api\Controllers;
5 5
6use malkusch\lock\mutex\NoMutex;
6use Shaarli\Bookmark\BookmarkFileService; 7use Shaarli\Bookmark\BookmarkFileService;
7use Shaarli\Config\ConfigManager; 8use Shaarli\Config\ConfigManager;
8use Shaarli\History; 9use Shaarli\History;
@@ -11,7 +12,7 @@ use Slim\Http\Environment;
11use Slim\Http\Request; 12use Slim\Http\Request;
12use Slim\Http\Response; 13use Slim\Http\Response;
13 14
14class DeleteLinkTest extends \PHPUnit\Framework\TestCase 15class DeleteLinkTest extends \Shaarli\TestCase
15{ 16{
16 /** 17 /**
17 * @var string datastore to test write operations 18 * @var string datastore to test write operations
@@ -53,11 +54,15 @@ class DeleteLinkTest extends \PHPUnit\Framework\TestCase
53 */ 54 */
54 protected $controller; 55 protected $controller;
55 56
57 /** @var NoMutex */
58 protected $mutex;
59
56 /** 60 /**
57 * Before each test, instantiate a new Api with its config, plugins and bookmarks. 61 * Before each test, instantiate a new Api with its config, plugins and bookmarks.
58 */ 62 */
59 public function setUp() 63 protected function setUp(): void
60 { 64 {
65 $this->mutex = new NoMutex();
61 $this->conf = new ConfigManager('tests/utils/config/configJson'); 66 $this->conf = new ConfigManager('tests/utils/config/configJson');
62 $this->conf->set('resource.datastore', self::$testDatastore); 67 $this->conf->set('resource.datastore', self::$testDatastore);
63 $this->refDB = new \ReferenceLinkDB(); 68 $this->refDB = new \ReferenceLinkDB();
@@ -65,7 +70,7 @@ class DeleteLinkTest extends \PHPUnit\Framework\TestCase
65 $refHistory = new \ReferenceHistory(); 70 $refHistory = new \ReferenceHistory();
66 $refHistory->write(self::$testHistory); 71 $refHistory->write(self::$testHistory);
67 $this->history = new History(self::$testHistory); 72 $this->history = new History(self::$testHistory);
68 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 73 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
69 74
70 $this->container = new Container(); 75 $this->container = new Container();
71 $this->container['conf'] = $this->conf; 76 $this->container['conf'] = $this->conf;
@@ -78,7 +83,7 @@ class DeleteLinkTest extends \PHPUnit\Framework\TestCase
78 /** 83 /**
79 * After each test, remove the test datastore. 84 * After each test, remove the test datastore.
80 */ 85 */
81 public function tearDown() 86 protected function tearDown(): void
82 { 87 {
83 @unlink(self::$testDatastore); 88 @unlink(self::$testDatastore);
84 @unlink(self::$testHistory); 89 @unlink(self::$testHistory);
@@ -100,7 +105,7 @@ class DeleteLinkTest extends \PHPUnit\Framework\TestCase
100 $this->assertEquals(204, $response->getStatusCode()); 105 $this->assertEquals(204, $response->getStatusCode());
101 $this->assertEmpty((string) $response->getBody()); 106 $this->assertEmpty((string) $response->getBody());
102 107
103 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 108 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
104 $this->assertFalse($this->bookmarkService->exists($id)); 109 $this->assertFalse($this->bookmarkService->exists($id));
105 110
106 $historyEntry = $this->history->getHistory()[0]; 111 $historyEntry = $this->history->getHistory()[0];
@@ -113,11 +118,11 @@ class DeleteLinkTest extends \PHPUnit\Framework\TestCase
113 118
114 /** 119 /**
115 * Test DELETE link endpoint: reach not existing ID. 120 * Test DELETE link endpoint: reach not existing ID.
116 *
117 * @expectedException \Shaarli\Api\Exceptions\ApiLinkNotFoundException
118 */ 121 */
119 public function testDeleteLink404() 122 public function testDeleteLink404()
120 { 123 {
124 $this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class);
125
121 $id = -1; 126 $id = -1;
122 $this->assertFalse($this->bookmarkService->exists($id)); 127 $this->assertFalse($this->bookmarkService->exists($id));
123 $env = Environment::mock([ 128 $env = Environment::mock([
diff --git a/tests/api/controllers/links/GetLinkIdTest.php b/tests/api/controllers/links/GetLinkIdTest.php
index c26411ac..1ec56ef3 100644
--- a/tests/api/controllers/links/GetLinkIdTest.php
+++ b/tests/api/controllers/links/GetLinkIdTest.php
@@ -2,6 +2,7 @@
2 2
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5use malkusch\lock\mutex\NoMutex;
5use Shaarli\Bookmark\Bookmark; 6use Shaarli\Bookmark\Bookmark;
6use Shaarli\Bookmark\BookmarkFileService; 7use Shaarli\Bookmark\BookmarkFileService;
7use Shaarli\Config\ConfigManager; 8use Shaarli\Config\ConfigManager;
@@ -20,7 +21,7 @@ use Slim\Http\Response;
20 * 21 *
21 * @package Shaarli\Api\Controllers 22 * @package Shaarli\Api\Controllers
22 */ 23 */
23class GetLinkIdTest extends \PHPUnit\Framework\TestCase 24class GetLinkIdTest extends \Shaarli\TestCase
24{ 25{
25 /** 26 /**
26 * @var string datastore to test write operations 27 * @var string datastore to test write operations
@@ -55,8 +56,9 @@ class GetLinkIdTest extends \PHPUnit\Framework\TestCase
55 /** 56 /**
56 * Before each test, instantiate a new Api with its config, plugins and bookmarks. 57 * Before each test, instantiate a new Api with its config, plugins and bookmarks.
57 */ 58 */
58 public function setUp() 59 protected function setUp(): void
59 { 60 {
61 $mutex = new NoMutex();
60 $this->conf = new ConfigManager('tests/utils/config/configJson'); 62 $this->conf = new ConfigManager('tests/utils/config/configJson');
61 $this->conf->set('resource.datastore', self::$testDatastore); 63 $this->conf->set('resource.datastore', self::$testDatastore);
62 $this->refDB = new \ReferenceLinkDB(); 64 $this->refDB = new \ReferenceLinkDB();
@@ -65,7 +67,7 @@ class GetLinkIdTest extends \PHPUnit\Framework\TestCase
65 67
66 $this->container = new Container(); 68 $this->container = new Container();
67 $this->container['conf'] = $this->conf; 69 $this->container['conf'] = $this->conf;
68 $this->container['db'] = new BookmarkFileService($this->conf, $history, true); 70 $this->container['db'] = new BookmarkFileService($this->conf, $history, $mutex, true);
69 $this->container['history'] = null; 71 $this->container['history'] = null;
70 72
71 $this->controller = new Links($this->container); 73 $this->controller = new Links($this->container);
@@ -74,7 +76,7 @@ class GetLinkIdTest extends \PHPUnit\Framework\TestCase
74 /** 76 /**
75 * After each test, remove the test datastore. 77 * After each test, remove the test datastore.
76 */ 78 */
77 public function tearDown() 79 protected function tearDown(): void
78 { 80 {
79 @unlink(self::$testDatastore); 81 @unlink(self::$testDatastore);
80 } 82 }
@@ -102,7 +104,7 @@ class GetLinkIdTest extends \PHPUnit\Framework\TestCase
102 $this->assertEquals($id, $data['id']); 104 $this->assertEquals($id, $data['id']);
103 105
104 // Check link elements 106 // Check link elements
105 $this->assertEquals('http://domain.tld/?WDWyig', $data['url']); 107 $this->assertEquals('http://domain.tld/shaare/WDWyig', $data['url']);
106 $this->assertEquals('WDWyig', $data['shorturl']); 108 $this->assertEquals('WDWyig', $data['shorturl']);
107 $this->assertEquals('Link title: @website', $data['title']); 109 $this->assertEquals('Link title: @website', $data['title']);
108 $this->assertEquals( 110 $this->assertEquals(
@@ -120,12 +122,12 @@ class GetLinkIdTest extends \PHPUnit\Framework\TestCase
120 122
121 /** 123 /**
122 * Test basic getLink service: get non existent link => ApiLinkNotFoundException. 124 * Test basic getLink service: get non existent link => ApiLinkNotFoundException.
123 *
124 * @expectedException Shaarli\Api\Exceptions\ApiLinkNotFoundException
125 * @expectedExceptionMessage Link not found
126 */ 125 */
127 public function testGetLink404() 126 public function testGetLink404()
128 { 127 {
128 $this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class);
129 $this->expectExceptionMessage('Link not found');
130
129 $env = Environment::mock([ 131 $env = Environment::mock([
130 'REQUEST_METHOD' => 'GET', 132 'REQUEST_METHOD' => 'GET',
131 ]); 133 ]);
diff --git a/tests/api/controllers/links/GetLinksTest.php b/tests/api/controllers/links/GetLinksTest.php
index 4e2d55ac..b1c46ee2 100644
--- a/tests/api/controllers/links/GetLinksTest.php
+++ b/tests/api/controllers/links/GetLinksTest.php
@@ -1,6 +1,7 @@
1<?php 1<?php
2namespace Shaarli\Api\Controllers; 2namespace Shaarli\Api\Controllers;
3 3
4use malkusch\lock\mutex\NoMutex;
4use Shaarli\Bookmark\Bookmark; 5use Shaarli\Bookmark\Bookmark;
5use Shaarli\Bookmark\BookmarkFileService; 6use Shaarli\Bookmark\BookmarkFileService;
6use Shaarli\Bookmark\LinkDB; 7use Shaarli\Bookmark\LinkDB;
@@ -20,7 +21,7 @@ use Slim\Http\Response;
20 * 21 *
21 * @package Shaarli\Api\Controllers 22 * @package Shaarli\Api\Controllers
22 */ 23 */
23class GetLinksTest extends \PHPUnit\Framework\TestCase 24class GetLinksTest extends \Shaarli\TestCase
24{ 25{
25 /** 26 /**
26 * @var string datastore to test write operations 27 * @var string datastore to test write operations
@@ -55,8 +56,9 @@ class GetLinksTest extends \PHPUnit\Framework\TestCase
55 /** 56 /**
56 * Before every test, instantiate a new Api with its config, plugins and bookmarks. 57 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
57 */ 58 */
58 public function setUp() 59 protected function setUp(): void
59 { 60 {
61 $mutex = new NoMutex();
60 $this->conf = new ConfigManager('tests/utils/config/configJson'); 62 $this->conf = new ConfigManager('tests/utils/config/configJson');
61 $this->conf->set('resource.datastore', self::$testDatastore); 63 $this->conf->set('resource.datastore', self::$testDatastore);
62 $this->refDB = new \ReferenceLinkDB(); 64 $this->refDB = new \ReferenceLinkDB();
@@ -65,7 +67,7 @@ class GetLinksTest extends \PHPUnit\Framework\TestCase
65 67
66 $this->container = new Container(); 68 $this->container = new Container();
67 $this->container['conf'] = $this->conf; 69 $this->container['conf'] = $this->conf;
68 $this->container['db'] = new BookmarkFileService($this->conf, $history, true); 70 $this->container['db'] = new BookmarkFileService($this->conf, $history, $mutex, true);
69 $this->container['history'] = null; 71 $this->container['history'] = null;
70 72
71 $this->controller = new Links($this->container); 73 $this->controller = new Links($this->container);
@@ -74,7 +76,7 @@ class GetLinksTest extends \PHPUnit\Framework\TestCase
74 /** 76 /**
75 * After every test, remove the test datastore. 77 * After every test, remove the test datastore.
76 */ 78 */
77 public function tearDown() 79 protected function tearDown(): void
78 { 80 {
79 @unlink(self::$testDatastore); 81 @unlink(self::$testDatastore);
80 } 82 }
@@ -109,7 +111,7 @@ class GetLinksTest extends \PHPUnit\Framework\TestCase
109 111
110 // Check first element fields 112 // Check first element fields
111 $first = $data[2]; 113 $first = $data[2];
112 $this->assertEquals('http://domain.tld/?WDWyig', $first['url']); 114 $this->assertEquals('http://domain.tld/shaare/WDWyig', $first['url']);
113 $this->assertEquals('WDWyig', $first['shorturl']); 115 $this->assertEquals('WDWyig', $first['shorturl']);
114 $this->assertEquals('Link title: @website', $first['title']); 116 $this->assertEquals('Link title: @website', $first['title']);
115 $this->assertEquals( 117 $this->assertEquals(
@@ -396,7 +398,7 @@ class GetLinksTest extends \PHPUnit\Framework\TestCase
396 $response = $this->controller->getLinks($request, new Response()); 398 $response = $this->controller->getLinks($request, new Response());
397 $this->assertEquals(200, $response->getStatusCode()); 399 $this->assertEquals(200, $response->getStatusCode());
398 $data = json_decode((string) $response->getBody(), true); 400 $data = json_decode((string) $response->getBody(), true);
399 $this->assertEquals(4, count($data)); 401 $this->assertEquals(5, count($data));
400 $this->assertEquals(6, $data[0]['id']); 402 $this->assertEquals(6, $data[0]['id']);
401 403
402 // wildcard: placeholder at the middle 404 // wildcard: placeholder at the middle
diff --git a/tests/api/controllers/links/PostLinkTest.php b/tests/api/controllers/links/PostLinkTest.php
index 969b9fd9..e12f803b 100644
--- a/tests/api/controllers/links/PostLinkTest.php
+++ b/tests/api/controllers/links/PostLinkTest.php
@@ -2,11 +2,12 @@
2 2
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5use PHPUnit\Framework\TestCase; 5use malkusch\lock\mutex\NoMutex;
6use Shaarli\Bookmark\Bookmark; 6use Shaarli\Bookmark\Bookmark;
7use Shaarli\Bookmark\BookmarkFileService; 7use Shaarli\Bookmark\BookmarkFileService;
8use Shaarli\Config\ConfigManager; 8use Shaarli\Config\ConfigManager;
9use Shaarli\History; 9use Shaarli\History;
10use Shaarli\TestCase;
10use Slim\Container; 11use Slim\Container;
11use Slim\Http\Environment; 12use Slim\Http\Environment;
12use Slim\Http\Request; 13use Slim\Http\Request;
@@ -70,8 +71,9 @@ class PostLinkTest extends TestCase
70 /** 71 /**
71 * Before every test, instantiate a new Api with its config, plugins and bookmarks. 72 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
72 */ 73 */
73 public function setUp() 74 protected function setUp(): void
74 { 75 {
76 $mutex = new NoMutex();
75 $this->conf = new ConfigManager('tests/utils/config/configJson'); 77 $this->conf = new ConfigManager('tests/utils/config/configJson');
76 $this->conf->set('resource.datastore', self::$testDatastore); 78 $this->conf->set('resource.datastore', self::$testDatastore);
77 $this->refDB = new \ReferenceLinkDB(); 79 $this->refDB = new \ReferenceLinkDB();
@@ -79,7 +81,7 @@ class PostLinkTest extends TestCase
79 $refHistory = new \ReferenceHistory(); 81 $refHistory = new \ReferenceHistory();
80 $refHistory->write(self::$testHistory); 82 $refHistory->write(self::$testHistory);
81 $this->history = new History(self::$testHistory); 83 $this->history = new History(self::$testHistory);
82 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 84 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $mutex, true);
83 85
84 $this->container = new Container(); 86 $this->container = new Container();
85 $this->container['conf'] = $this->conf; 87 $this->container['conf'] = $this->conf;
@@ -107,7 +109,7 @@ class PostLinkTest extends TestCase
107 /** 109 /**
108 * After every test, remove the test datastore. 110 * After every test, remove the test datastore.
109 */ 111 */
110 public function tearDown() 112 protected function tearDown(): void
111 { 113 {
112 @unlink(self::$testDatastore); 114 @unlink(self::$testDatastore);
113 @unlink(self::$testHistory); 115 @unlink(self::$testHistory);
@@ -131,8 +133,8 @@ class PostLinkTest extends TestCase
131 $this->assertEquals(self::NB_FIELDS_LINK, count($data)); 133 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
132 $this->assertEquals(43, $data['id']); 134 $this->assertEquals(43, $data['id']);
133 $this->assertRegExp('/[\w_-]{6}/', $data['shorturl']); 135 $this->assertRegExp('/[\w_-]{6}/', $data['shorturl']);
134 $this->assertEquals('http://domain.tld/?' . $data['shorturl'], $data['url']); 136 $this->assertEquals('http://domain.tld/shaare/' . $data['shorturl'], $data['url']);
135 $this->assertEquals('?' . $data['shorturl'], $data['title']); 137 $this->assertEquals('/shaare/' . $data['shorturl'], $data['title']);
136 $this->assertEquals('', $data['description']); 138 $this->assertEquals('', $data['description']);
137 $this->assertEquals([], $data['tags']); 139 $this->assertEquals([], $data['tags']);
138 $this->assertEquals(true, $data['private']); 140 $this->assertEquals(true, $data['private']);
@@ -160,6 +162,8 @@ class PostLinkTest extends TestCase
160 'description' => 'shaare description', 162 'description' => 'shaare description',
161 'tags' => ['one', 'two'], 163 'tags' => ['one', 'two'],
162 'private' => true, 164 'private' => true,
165 'created' => '2015-05-05T12:30:00+03:00',
166 'updated' => '2016-06-05T14:32:10+03:00',
163 ]; 167 ];
164 $env = Environment::mock([ 168 $env = Environment::mock([
165 'REQUEST_METHOD' => 'POST', 169 'REQUEST_METHOD' => 'POST',
@@ -181,10 +185,8 @@ class PostLinkTest extends TestCase
181 $this->assertEquals($link['description'], $data['description']); 185 $this->assertEquals($link['description'], $data['description']);
182 $this->assertEquals($link['tags'], $data['tags']); 186 $this->assertEquals($link['tags'], $data['tags']);
183 $this->assertEquals(true, $data['private']); 187 $this->assertEquals(true, $data['private']);
184 $this->assertTrue( 188 $this->assertSame($link['created'], $data['created']);
185 new \DateTime('2 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) 189 $this->assertSame($link['updated'], $data['updated']);
186 );
187 $this->assertEquals('', $data['updated']);
188 } 190 }
189 191
190 /** 192 /**
diff --git a/tests/api/controllers/links/PutLinkTest.php b/tests/api/controllers/links/PutLinkTest.php
index cb63742e..240ee323 100644
--- a/tests/api/controllers/links/PutLinkTest.php
+++ b/tests/api/controllers/links/PutLinkTest.php
@@ -3,6 +3,7 @@
3 3
4namespace Shaarli\Api\Controllers; 4namespace Shaarli\Api\Controllers;
5 5
6use malkusch\lock\mutex\NoMutex;
6use Shaarli\Bookmark\Bookmark; 7use Shaarli\Bookmark\Bookmark;
7use Shaarli\Bookmark\BookmarkFileService; 8use Shaarli\Bookmark\BookmarkFileService;
8use Shaarli\Config\ConfigManager; 9use Shaarli\Config\ConfigManager;
@@ -12,7 +13,7 @@ use Slim\Http\Environment;
12use Slim\Http\Request; 13use Slim\Http\Request;
13use Slim\Http\Response; 14use Slim\Http\Response;
14 15
15class PutLinkTest extends \PHPUnit\Framework\TestCase 16class PutLinkTest extends \Shaarli\TestCase
16{ 17{
17 /** 18 /**
18 * @var string datastore to test write operations 19 * @var string datastore to test write operations
@@ -62,8 +63,9 @@ class PutLinkTest extends \PHPUnit\Framework\TestCase
62 /** 63 /**
63 * Before every test, instantiate a new Api with its config, plugins and bookmarks. 64 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
64 */ 65 */
65 public function setUp() 66 protected function setUp(): void
66 { 67 {
68 $mutex = new NoMutex();
67 $this->conf = new ConfigManager('tests/utils/config/configJson'); 69 $this->conf = new ConfigManager('tests/utils/config/configJson');
68 $this->conf->set('resource.datastore', self::$testDatastore); 70 $this->conf->set('resource.datastore', self::$testDatastore);
69 $this->refDB = new \ReferenceLinkDB(); 71 $this->refDB = new \ReferenceLinkDB();
@@ -71,7 +73,7 @@ class PutLinkTest extends \PHPUnit\Framework\TestCase
71 $refHistory = new \ReferenceHistory(); 73 $refHistory = new \ReferenceHistory();
72 $refHistory->write(self::$testHistory); 74 $refHistory->write(self::$testHistory);
73 $this->history = new History(self::$testHistory); 75 $this->history = new History(self::$testHistory);
74 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 76 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $mutex, true);
75 77
76 $this->container = new Container(); 78 $this->container = new Container();
77 $this->container['conf'] = $this->conf; 79 $this->container['conf'] = $this->conf;
@@ -91,7 +93,7 @@ class PutLinkTest extends \PHPUnit\Framework\TestCase
91 /** 93 /**
92 * After every test, remove the test datastore. 94 * After every test, remove the test datastore.
93 */ 95 */
94 public function tearDown() 96 protected function tearDown(): void
95 { 97 {
96 @unlink(self::$testDatastore); 98 @unlink(self::$testDatastore);
97 @unlink(self::$testHistory); 99 @unlink(self::$testHistory);
@@ -114,8 +116,8 @@ class PutLinkTest extends \PHPUnit\Framework\TestCase
114 $this->assertEquals(self::NB_FIELDS_LINK, count($data)); 116 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
115 $this->assertEquals($id, $data['id']); 117 $this->assertEquals($id, $data['id']);
116 $this->assertEquals('WDWyig', $data['shorturl']); 118 $this->assertEquals('WDWyig', $data['shorturl']);
117 $this->assertEquals('http://domain.tld/?WDWyig', $data['url']); 119 $this->assertEquals('http://domain.tld/shaare/WDWyig', $data['url']);
118 $this->assertEquals('?WDWyig', $data['title']); 120 $this->assertEquals('/shaare/WDWyig', $data['title']);
119 $this->assertEquals('', $data['description']); 121 $this->assertEquals('', $data['description']);
120 $this->assertEquals([], $data['tags']); 122 $this->assertEquals([], $data['tags']);
121 $this->assertEquals(true, $data['private']); 123 $this->assertEquals(true, $data['private']);
@@ -218,12 +220,12 @@ class PutLinkTest extends \PHPUnit\Framework\TestCase
218 220
219 /** 221 /**
220 * Test link update on non existent link => ApiLinkNotFoundException. 222 * Test link update on non existent link => ApiLinkNotFoundException.
221 *
222 * @expectedException Shaarli\Api\Exceptions\ApiLinkNotFoundException
223 * @expectedExceptionMessage Link not found
224 */ 223 */
225 public function testGetLink404() 224 public function testGetLink404()
226 { 225 {
226 $this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class);
227 $this->expectExceptionMessage('Link not found');
228
227 $env = Environment::mock([ 229 $env = Environment::mock([
228 'REQUEST_METHOD' => 'PUT', 230 'REQUEST_METHOD' => 'PUT',
229 ]); 231 ]);
diff --git a/tests/api/controllers/tags/DeleteTagTest.php b/tests/api/controllers/tags/DeleteTagTest.php
index c6748872..37f07229 100644
--- a/tests/api/controllers/tags/DeleteTagTest.php
+++ b/tests/api/controllers/tags/DeleteTagTest.php
@@ -3,6 +3,7 @@
3 3
4namespace Shaarli\Api\Controllers; 4namespace Shaarli\Api\Controllers;
5 5
6use malkusch\lock\mutex\NoMutex;
6use Shaarli\Bookmark\BookmarkFileService; 7use Shaarli\Bookmark\BookmarkFileService;
7use Shaarli\Bookmark\LinkDB; 8use Shaarli\Bookmark\LinkDB;
8use Shaarli\Config\ConfigManager; 9use Shaarli\Config\ConfigManager;
@@ -12,7 +13,7 @@ use Slim\Http\Environment;
12use Slim\Http\Request; 13use Slim\Http\Request;
13use Slim\Http\Response; 14use Slim\Http\Response;
14 15
15class DeleteTagTest extends \PHPUnit\Framework\TestCase 16class DeleteTagTest extends \Shaarli\TestCase
16{ 17{
17 /** 18 /**
18 * @var string datastore to test write operations 19 * @var string datastore to test write operations
@@ -54,11 +55,15 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
54 */ 55 */
55 protected $controller; 56 protected $controller;
56 57
58 /** @var NoMutex */
59 protected $mutex;
60
57 /** 61 /**
58 * Before each test, instantiate a new Api with its config, plugins and bookmarks. 62 * Before each test, instantiate a new Api with its config, plugins and bookmarks.
59 */ 63 */
60 public function setUp() 64 protected function setUp(): void
61 { 65 {
66 $this->mutex = new NoMutex();
62 $this->conf = new ConfigManager('tests/utils/config/configJson'); 67 $this->conf = new ConfigManager('tests/utils/config/configJson');
63 $this->conf->set('resource.datastore', self::$testDatastore); 68 $this->conf->set('resource.datastore', self::$testDatastore);
64 $this->refDB = new \ReferenceLinkDB(); 69 $this->refDB = new \ReferenceLinkDB();
@@ -66,7 +71,7 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
66 $refHistory = new \ReferenceHistory(); 71 $refHistory = new \ReferenceHistory();
67 $refHistory->write(self::$testHistory); 72 $refHistory->write(self::$testHistory);
68 $this->history = new History(self::$testHistory); 73 $this->history = new History(self::$testHistory);
69 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 74 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
70 75
71 $this->container = new Container(); 76 $this->container = new Container();
72 $this->container['conf'] = $this->conf; 77 $this->container['conf'] = $this->conf;
@@ -79,7 +84,7 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
79 /** 84 /**
80 * After each test, remove the test datastore. 85 * After each test, remove the test datastore.
81 */ 86 */
82 public function tearDown() 87 protected function tearDown(): void
83 { 88 {
84 @unlink(self::$testDatastore); 89 @unlink(self::$testDatastore);
85 @unlink(self::$testHistory); 90 @unlink(self::$testHistory);
@@ -102,7 +107,7 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
102 $this->assertEquals(204, $response->getStatusCode()); 107 $this->assertEquals(204, $response->getStatusCode());
103 $this->assertEmpty((string) $response->getBody()); 108 $this->assertEmpty((string) $response->getBody());
104 109
105 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 110 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
106 $tags = $this->bookmarkService->bookmarksCountPerTag(); 111 $tags = $this->bookmarkService->bookmarksCountPerTag();
107 $this->assertFalse(isset($tags[$tagName])); 112 $this->assertFalse(isset($tags[$tagName]));
108 113
@@ -136,7 +141,7 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
136 $this->assertEquals(204, $response->getStatusCode()); 141 $this->assertEquals(204, $response->getStatusCode());
137 $this->assertEmpty((string) $response->getBody()); 142 $this->assertEmpty((string) $response->getBody());
138 143
139 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 144 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
140 $tags = $this->bookmarkService->bookmarksCountPerTag(); 145 $tags = $this->bookmarkService->bookmarksCountPerTag();
141 $this->assertFalse(isset($tags[$tagName])); 146 $this->assertFalse(isset($tags[$tagName]));
142 $this->assertTrue($tags[strtolower($tagName)] > 0); 147 $this->assertTrue($tags[strtolower($tagName)] > 0);
@@ -150,12 +155,12 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
150 155
151 /** 156 /**
152 * Test DELETE tag endpoint: reach not existing tag. 157 * Test DELETE tag endpoint: reach not existing tag.
153 *
154 * @expectedException Shaarli\Api\Exceptions\ApiTagNotFoundException
155 * @expectedExceptionMessage Tag not found
156 */ 158 */
157 public function testDeleteLink404() 159 public function testDeleteLink404()
158 { 160 {
161 $this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class);
162 $this->expectExceptionMessage('Tag not found');
163
159 $tagName = 'nopenope'; 164 $tagName = 'nopenope';
160 $tags = $this->bookmarkService->bookmarksCountPerTag(); 165 $tags = $this->bookmarkService->bookmarksCountPerTag();
161 $this->assertFalse(isset($tags[$tagName])); 166 $this->assertFalse(isset($tags[$tagName]));
diff --git a/tests/api/controllers/tags/GetTagNameTest.php b/tests/api/controllers/tags/GetTagNameTest.php
index b9a81f9b..878de5a4 100644
--- a/tests/api/controllers/tags/GetTagNameTest.php
+++ b/tests/api/controllers/tags/GetTagNameTest.php
@@ -2,6 +2,7 @@
2 2
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5use malkusch\lock\mutex\NoMutex;
5use Shaarli\Bookmark\BookmarkFileService; 6use Shaarli\Bookmark\BookmarkFileService;
6use Shaarli\Bookmark\LinkDB; 7use Shaarli\Bookmark\LinkDB;
7use Shaarli\Config\ConfigManager; 8use Shaarli\Config\ConfigManager;
@@ -18,7 +19,7 @@ use Slim\Http\Response;
18 * 19 *
19 * @package Shaarli\Api\Controllers 20 * @package Shaarli\Api\Controllers
20 */ 21 */
21class GetTagNameTest extends \PHPUnit\Framework\TestCase 22class GetTagNameTest extends \Shaarli\TestCase
22{ 23{
23 /** 24 /**
24 * @var string datastore to test write operations 25 * @var string datastore to test write operations
@@ -53,8 +54,9 @@ class GetTagNameTest extends \PHPUnit\Framework\TestCase
53 /** 54 /**
54 * Before each test, instantiate a new Api with its config, plugins and bookmarks. 55 * Before each test, instantiate a new Api with its config, plugins and bookmarks.
55 */ 56 */
56 public function setUp() 57 protected function setUp(): void
57 { 58 {
59 $mutex = new NoMutex();
58 $this->conf = new ConfigManager('tests/utils/config/configJson'); 60 $this->conf = new ConfigManager('tests/utils/config/configJson');
59 $this->conf->set('resource.datastore', self::$testDatastore); 61 $this->conf->set('resource.datastore', self::$testDatastore);
60 $this->refDB = new \ReferenceLinkDB(); 62 $this->refDB = new \ReferenceLinkDB();
@@ -63,7 +65,7 @@ class GetTagNameTest extends \PHPUnit\Framework\TestCase
63 65
64 $this->container = new Container(); 66 $this->container = new Container();
65 $this->container['conf'] = $this->conf; 67 $this->container['conf'] = $this->conf;
66 $this->container['db'] = new BookmarkFileService($this->conf, $history, true); 68 $this->container['db'] = new BookmarkFileService($this->conf, $history, $mutex, true);
67 $this->container['history'] = null; 69 $this->container['history'] = null;
68 70
69 $this->controller = new Tags($this->container); 71 $this->controller = new Tags($this->container);
@@ -72,7 +74,7 @@ class GetTagNameTest extends \PHPUnit\Framework\TestCase
72 /** 74 /**
73 * After each test, remove the test datastore. 75 * After each test, remove the test datastore.
74 */ 76 */
75 public function tearDown() 77 protected function tearDown(): void
76 { 78 {
77 @unlink(self::$testDatastore); 79 @unlink(self::$testDatastore);
78 } 80 }
@@ -117,12 +119,12 @@ class GetTagNameTest extends \PHPUnit\Framework\TestCase
117 119
118 /** 120 /**
119 * Test basic getTag service: get non existent tag => ApiTagNotFoundException. 121 * Test basic getTag service: get non existent tag => ApiTagNotFoundException.
120 *
121 * @expectedException Shaarli\Api\Exceptions\ApiTagNotFoundException
122 * @expectedExceptionMessage Tag not found
123 */ 122 */
124 public function testGetTag404() 123 public function testGetTag404()
125 { 124 {
125 $this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class);
126 $this->expectExceptionMessage('Tag not found');
127
126 $env = Environment::mock([ 128 $env = Environment::mock([
127 'REQUEST_METHOD' => 'GET', 129 'REQUEST_METHOD' => 'GET',
128 ]); 130 ]);
diff --git a/tests/api/controllers/tags/GetTagsTest.php b/tests/api/controllers/tags/GetTagsTest.php
index 53a3326d..b565a8c4 100644
--- a/tests/api/controllers/tags/GetTagsTest.php
+++ b/tests/api/controllers/tags/GetTagsTest.php
@@ -1,6 +1,7 @@
1<?php 1<?php
2namespace Shaarli\Api\Controllers; 2namespace Shaarli\Api\Controllers;
3 3
4use malkusch\lock\mutex\NoMutex;
4use Shaarli\Bookmark\BookmarkFileService; 5use Shaarli\Bookmark\BookmarkFileService;
5use Shaarli\Bookmark\LinkDB; 6use Shaarli\Bookmark\LinkDB;
6use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
@@ -17,7 +18,7 @@ use Slim\Http\Response;
17 * 18 *
18 * @package Shaarli\Api\Controllers 19 * @package Shaarli\Api\Controllers
19 */ 20 */
20class GetTagsTest extends \PHPUnit\Framework\TestCase 21class GetTagsTest extends \Shaarli\TestCase
21{ 22{
22 /** 23 /**
23 * @var string datastore to test write operations 24 * @var string datastore to test write operations
@@ -57,15 +58,16 @@ class GetTagsTest extends \PHPUnit\Framework\TestCase
57 /** 58 /**
58 * Before every test, instantiate a new Api with its config, plugins and bookmarks. 59 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
59 */ 60 */
60 public function setUp() 61 protected function setUp(): void
61 { 62 {
63 $mutex = new NoMutex();
62 $this->conf = new ConfigManager('tests/utils/config/configJson'); 64 $this->conf = new ConfigManager('tests/utils/config/configJson');
63 $this->conf->set('resource.datastore', self::$testDatastore); 65 $this->conf->set('resource.datastore', self::$testDatastore);
64 $this->refDB = new \ReferenceLinkDB(); 66 $this->refDB = new \ReferenceLinkDB();
65 $this->refDB->write(self::$testDatastore); 67 $this->refDB->write(self::$testDatastore);
66 $history = new History('sandbox/history.php'); 68 $history = new History('sandbox/history.php');
67 69
68 $this->bookmarkService = new BookmarkFileService($this->conf, $history, true); 70 $this->bookmarkService = new BookmarkFileService($this->conf, $history, $mutex, true);
69 71
70 $this->container = new Container(); 72 $this->container = new Container();
71 $this->container['conf'] = $this->conf; 73 $this->container['conf'] = $this->conf;
@@ -78,7 +80,7 @@ class GetTagsTest extends \PHPUnit\Framework\TestCase
78 /** 80 /**
79 * After every test, remove the test datastore. 81 * After every test, remove the test datastore.
80 */ 82 */
81 public function tearDown() 83 protected function tearDown(): void
82 { 84 {
83 @unlink(self::$testDatastore); 85 @unlink(self::$testDatastore);
84 } 86 }
diff --git a/tests/api/controllers/tags/PutTagTest.php b/tests/api/controllers/tags/PutTagTest.php
index 2a3cc15a..c73f6d3b 100644
--- a/tests/api/controllers/tags/PutTagTest.php
+++ b/tests/api/controllers/tags/PutTagTest.php
@@ -2,6 +2,7 @@
2 2
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5use malkusch\lock\mutex\NoMutex;
5use Shaarli\Api\Exceptions\ApiBadParametersException; 6use Shaarli\Api\Exceptions\ApiBadParametersException;
6use Shaarli\Bookmark\BookmarkFileService; 7use Shaarli\Bookmark\BookmarkFileService;
7use Shaarli\Bookmark\LinkDB; 8use Shaarli\Bookmark\LinkDB;
@@ -12,7 +13,7 @@ use Slim\Http\Environment;
12use Slim\Http\Request; 13use Slim\Http\Request;
13use Slim\Http\Response; 14use Slim\Http\Response;
14 15
15class PutTagTest extends \PHPUnit\Framework\TestCase 16class PutTagTest extends \Shaarli\TestCase
16{ 17{
17 /** 18 /**
18 * @var string datastore to test write operations 19 * @var string datastore to test write operations
@@ -62,8 +63,9 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
62 /** 63 /**
63 * Before every test, instantiate a new Api with its config, plugins and bookmarks. 64 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
64 */ 65 */
65 public function setUp() 66 protected function setUp(): void
66 { 67 {
68 $mutex = new NoMutex();
67 $this->conf = new ConfigManager('tests/utils/config/configJson'); 69 $this->conf = new ConfigManager('tests/utils/config/configJson');
68 $this->conf->set('resource.datastore', self::$testDatastore); 70 $this->conf->set('resource.datastore', self::$testDatastore);
69 $this->refDB = new \ReferenceLinkDB(); 71 $this->refDB = new \ReferenceLinkDB();
@@ -71,7 +73,7 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
71 $refHistory = new \ReferenceHistory(); 73 $refHistory = new \ReferenceHistory();
72 $refHistory->write(self::$testHistory); 74 $refHistory->write(self::$testHistory);
73 $this->history = new History(self::$testHistory); 75 $this->history = new History(self::$testHistory);
74 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 76 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $mutex, true);
75 77
76 $this->container = new Container(); 78 $this->container = new Container();
77 $this->container['conf'] = $this->conf; 79 $this->container['conf'] = $this->conf;
@@ -84,7 +86,7 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
84 /** 86 /**
85 * After every test, remove the test datastore. 87 * After every test, remove the test datastore.
86 */ 88 */
87 public function tearDown() 89 protected function tearDown(): void
88 { 90 {
89 @unlink(self::$testDatastore); 91 @unlink(self::$testDatastore);
90 @unlink(self::$testHistory); 92 @unlink(self::$testHistory);
@@ -159,12 +161,12 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
159 161
160 /** 162 /**
161 * Test tag update with an empty new tag name => ApiBadParametersException 163 * Test tag update with an empty new tag name => ApiBadParametersException
162 *
163 * @expectedException Shaarli\Api\Exceptions\ApiBadParametersException
164 * @expectedExceptionMessage New tag name is required in the request body
165 */ 164 */
166 public function testPutTagEmpty() 165 public function testPutTagEmpty()
167 { 166 {
167 $this->expectException(\Shaarli\Api\Exceptions\ApiBadParametersException::class);
168 $this->expectExceptionMessage('New tag name is required in the request body');
169
168 $tagName = 'gnu'; 170 $tagName = 'gnu';
169 $newName = ''; 171 $newName = '';
170 172
@@ -194,12 +196,12 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
194 196
195 /** 197 /**
196 * Test tag update on non existent tag => ApiTagNotFoundException. 198 * Test tag update on non existent tag => ApiTagNotFoundException.
197 *
198 * @expectedException Shaarli\Api\Exceptions\ApiTagNotFoundException
199 * @expectedExceptionMessage Tag not found
200 */ 199 */
201 public function testPutTag404() 200 public function testPutTag404()
202 { 201 {
202 $this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class);
203 $this->expectExceptionMessage('Tag not found');
204
203 $env = Environment::mock([ 205 $env = Environment::mock([
204 'REQUEST_METHOD' => 'PUT', 206 'REQUEST_METHOD' => 'PUT',
205 ]); 207 ]);
diff --git a/tests/bookmark/BookmarkArrayTest.php b/tests/bookmark/BookmarkArrayTest.php
index 0f8f04c5..1953078c 100644
--- a/tests/bookmark/BookmarkArrayTest.php
+++ b/tests/bookmark/BookmarkArrayTest.php
@@ -2,10 +2,7 @@
2 2
3namespace Shaarli\Bookmark; 3namespace Shaarli\Bookmark;
4 4
5use PHPUnit\Framework\TestCase; 5use Shaarli\TestCase;
6use Shaarli\Bookmark\Exception\InvalidBookmarkException;
7use Shaarli\Config\ConfigManager;
8use Shaarli\History;
9 6
10/** 7/**
11 * Class BookmarkArrayTest 8 * Class BookmarkArrayTest
@@ -47,22 +44,22 @@ class BookmarkArrayTest extends TestCase
47 44
48 /** 45 /**
49 * Test adding a bad entry: wrong type 46 * Test adding a bad entry: wrong type
50 *
51 * @expectedException Shaarli\Bookmark\Exception\InvalidBookmarkException
52 */ 47 */
53 public function testArrayAccessAddBadEntryInstance() 48 public function testArrayAccessAddBadEntryInstance()
54 { 49 {
50 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
51
55 $array = new BookmarkArray(); 52 $array = new BookmarkArray();
56 $array[] = 'nope'; 53 $array[] = 'nope';
57 } 54 }
58 55
59 /** 56 /**
60 * Test adding a bad entry: no id 57 * Test adding a bad entry: no id
61 *
62 * @expectedException Shaarli\Bookmark\Exception\InvalidBookmarkException
63 */ 58 */
64 public function testArrayAccessAddBadEntryNoId() 59 public function testArrayAccessAddBadEntryNoId()
65 { 60 {
61 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
62
66 $array = new BookmarkArray(); 63 $array = new BookmarkArray();
67 $bookmark = new Bookmark(); 64 $bookmark = new Bookmark();
68 $array[] = $bookmark; 65 $array[] = $bookmark;
@@ -70,11 +67,11 @@ class BookmarkArrayTest extends TestCase
70 67
71 /** 68 /**
72 * Test adding a bad entry: no url 69 * Test adding a bad entry: no url
73 *
74 * @expectedException Shaarli\Bookmark\Exception\InvalidBookmarkException
75 */ 70 */
76 public function testArrayAccessAddBadEntryNoUrl() 71 public function testArrayAccessAddBadEntryNoUrl()
77 { 72 {
73 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
74
78 $array = new BookmarkArray(); 75 $array = new BookmarkArray();
79 $bookmark = (new Bookmark())->setId(11); 76 $bookmark = (new Bookmark())->setId(11);
80 $array[] = $bookmark; 77 $array[] = $bookmark;
@@ -82,11 +79,11 @@ class BookmarkArrayTest extends TestCase
82 79
83 /** 80 /**
84 * Test adding a bad entry: invalid offset 81 * Test adding a bad entry: invalid offset
85 *
86 * @expectedException Shaarli\Bookmark\Exception\InvalidBookmarkException
87 */ 82 */
88 public function testArrayAccessAddBadEntryOffset() 83 public function testArrayAccessAddBadEntryOffset()
89 { 84 {
85 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
86
90 $array = new BookmarkArray(); 87 $array = new BookmarkArray();
91 $bookmark = (new Bookmark())->setId(11); 88 $bookmark = (new Bookmark())->setId(11);
92 $bookmark->validate(); 89 $bookmark->validate();
@@ -94,25 +91,12 @@ class BookmarkArrayTest extends TestCase
94 } 91 }
95 92
96 /** 93 /**
97 * Test adding a bad entry: invalid ID type
98 *
99 * @expectedException Shaarli\Bookmark\Exception\InvalidBookmarkException
100 */
101 public function testArrayAccessAddBadEntryIdType()
102 {
103 $array = new BookmarkArray();
104 $bookmark = (new Bookmark())->setId('nope');
105 $bookmark->validate();
106 $array[] = $bookmark;
107 }
108
109 /**
110 * Test adding a bad entry: ID/offset not consistent 94 * Test adding a bad entry: ID/offset not consistent
111 *
112 * @expectedException Shaarli\Bookmark\Exception\InvalidBookmarkException
113 */ 95 */
114 public function testArrayAccessAddBadEntryIdOffset() 96 public function testArrayAccessAddBadEntryIdOffset()
115 { 97 {
98 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
99
116 $array = new BookmarkArray(); 100 $array = new BookmarkArray();
117 $bookmark = (new Bookmark())->setId(11); 101 $bookmark = (new Bookmark())->setId(11);
118 $bookmark->validate(); 102 $bookmark->validate();
diff --git a/tests/bookmark/BookmarkFileServiceTest.php b/tests/bookmark/BookmarkFileServiceTest.php
index 4900d41d..f619aff3 100644
--- a/tests/bookmark/BookmarkFileServiceTest.php
+++ b/tests/bookmark/BookmarkFileServiceTest.php
@@ -6,7 +6,7 @@
6namespace Shaarli\Bookmark; 6namespace Shaarli\Bookmark;
7 7
8use DateTime; 8use DateTime;
9use PHPUnit\Framework\TestCase; 9use malkusch\lock\mutex\NoMutex;
10use ReferenceLinkDB; 10use ReferenceLinkDB;
11use ReflectionClass; 11use ReflectionClass;
12use Shaarli; 12use Shaarli;
@@ -14,6 +14,7 @@ use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
14use Shaarli\Config\ConfigManager; 14use Shaarli\Config\ConfigManager;
15use Shaarli\Formatter\BookmarkMarkdownFormatter; 15use Shaarli\Formatter\BookmarkMarkdownFormatter;
16use Shaarli\History; 16use Shaarli\History;
17use Shaarli\TestCase;
17 18
18/** 19/**
19 * Unitary tests for LegacyLinkDBTest 20 * Unitary tests for LegacyLinkDBTest
@@ -52,6 +53,9 @@ class BookmarkFileServiceTest extends TestCase
52 */ 53 */
53 protected $privateLinkDB = null; 54 protected $privateLinkDB = null;
54 55
56 /** @var NoMutex */
57 protected $mutex;
58
55 /** 59 /**
56 * Instantiates public and private LinkDBs with test data 60 * Instantiates public and private LinkDBs with test data
57 * 61 *
@@ -66,8 +70,10 @@ class BookmarkFileServiceTest extends TestCase
66 * 70 *
67 * Resets test data for each test 71 * Resets test data for each test
68 */ 72 */
69 protected function setUp() 73 protected function setUp(): void
70 { 74 {
75 $this->mutex = new NoMutex();
76
71 if (file_exists(self::$testDatastore)) { 77 if (file_exists(self::$testDatastore)) {
72 unlink(self::$testDatastore); 78 unlink(self::$testDatastore);
73 } 79 }
@@ -87,8 +93,8 @@ class BookmarkFileServiceTest extends TestCase
87 $this->refDB = new \ReferenceLinkDB(); 93 $this->refDB = new \ReferenceLinkDB();
88 $this->refDB->write(self::$testDatastore); 94 $this->refDB->write(self::$testDatastore);
89 $this->history = new History('sandbox/history.php'); 95 $this->history = new History('sandbox/history.php');
90 $this->publicLinkDB = new BookmarkFileService($this->conf, $this->history, false); 96 $this->publicLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, false);
91 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 97 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
92 } 98 }
93 99
94 /** 100 /**
@@ -105,7 +111,7 @@ class BookmarkFileServiceTest extends TestCase
105 $db = self::getMethod('migrate'); 111 $db = self::getMethod('migrate');
106 $db->invokeArgs($this->privateLinkDB, []); 112 $db->invokeArgs($this->privateLinkDB, []);
107 113
108 $db = new \FakeBookmarkService($this->conf, $this->history, true); 114 $db = new \FakeBookmarkService($this->conf, $this->history, $this->mutex, true);
109 $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks()); 115 $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks());
110 $this->assertEquals($this->refDB->countLinks(), $db->count()); 116 $this->assertEquals($this->refDB->countLinks(), $db->count());
111 } 117 }
@@ -134,11 +140,11 @@ class BookmarkFileServiceTest extends TestCase
134 140
135 /** 141 /**
136 * Test get() method for an undefined bookmark 142 * Test get() method for an undefined bookmark
137 *
138 * @expectedException Shaarli\Bookmark\Exception\BookmarkNotFoundException
139 */ 143 */
140 public function testGetUndefined() 144 public function testGetUndefined()
141 { 145 {
146 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
147
142 $this->privateLinkDB->get(666); 148 $this->privateLinkDB->get(666);
143 } 149 }
144 150
@@ -174,7 +180,7 @@ class BookmarkFileServiceTest extends TestCase
174 $this->assertEquals($updated, $bookmark->getUpdated()); 180 $this->assertEquals($updated, $bookmark->getUpdated());
175 181
176 // reload from file 182 // reload from file
177 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 183 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
178 184
179 $bookmark = $this->privateLinkDB->get(43); 185 $bookmark = $this->privateLinkDB->get(43);
180 $this->assertEquals(43, $bookmark->getId()); 186 $this->assertEquals(43, $bookmark->getId());
@@ -200,7 +206,7 @@ class BookmarkFileServiceTest extends TestCase
200 206
201 $bookmark = $this->privateLinkDB->get(43); 207 $bookmark = $this->privateLinkDB->get(43);
202 $this->assertEquals(43, $bookmark->getId()); 208 $this->assertEquals(43, $bookmark->getId());
203 $this->assertRegExp('/\?[\w\-]{6}/', $bookmark->getUrl()); 209 $this->assertRegExp('#/shaare/[\w\-]{6}#', $bookmark->getUrl());
204 $this->assertRegExp('/[\w\-]{6}/', $bookmark->getShortUrl()); 210 $this->assertRegExp('/[\w\-]{6}/', $bookmark->getShortUrl());
205 $this->assertEquals($bookmark->getUrl(), $bookmark->getTitle()); 211 $this->assertEquals($bookmark->getUrl(), $bookmark->getTitle());
206 $this->assertEmpty($bookmark->getDescription()); 212 $this->assertEmpty($bookmark->getDescription());
@@ -212,11 +218,11 @@ class BookmarkFileServiceTest extends TestCase
212 $this->assertNull($bookmark->getUpdated()); 218 $this->assertNull($bookmark->getUpdated());
213 219
214 // reload from file 220 // reload from file
215 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 221 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
216 222
217 $bookmark = $this->privateLinkDB->get(43); 223 $bookmark = $this->privateLinkDB->get(43);
218 $this->assertEquals(43, $bookmark->getId()); 224 $this->assertEquals(43, $bookmark->getId());
219 $this->assertRegExp('/\?[\w\-]{6}/', $bookmark->getUrl()); 225 $this->assertRegExp('#/shaare/[\w\-]{6}#', $bookmark->getUrl());
220 $this->assertRegExp('/[\w\-]{6}/', $bookmark->getShortUrl()); 226 $this->assertRegExp('/[\w\-]{6}/', $bookmark->getShortUrl());
221 $this->assertEquals($bookmark->getUrl(), $bookmark->getTitle()); 227 $this->assertEquals($bookmark->getUrl(), $bookmark->getTitle());
222 $this->assertEmpty($bookmark->getDescription()); 228 $this->assertEmpty($bookmark->getDescription());
@@ -230,53 +236,42 @@ class BookmarkFileServiceTest extends TestCase
230 236
231 /** 237 /**
232 * Test add() method for a bookmark without any field set and without writing the data store 238 * Test add() method for a bookmark without any field set and without writing the data store
233 *
234 * @expectedExceptionMessage Shaarli\Bookmark\Exception\BookmarkNotFoundException
235 */ 239 */
236 public function testAddMinimalNoWrite() 240 public function testAddMinimalNoWrite()
237 { 241 {
242 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
243
238 $bookmark = new Bookmark(); 244 $bookmark = new Bookmark();
239 $this->privateLinkDB->add($bookmark); 245 $this->privateLinkDB->add($bookmark, false);
240 246
241 $bookmark = $this->privateLinkDB->get(43); 247 $bookmark = $this->privateLinkDB->get(43);
242 $this->assertEquals(43, $bookmark->getId()); 248 $this->assertEquals(43, $bookmark->getId());
243 249
244 // reload from file 250 // reload from file
245 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 251 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
246 252
247 $this->privateLinkDB->get(43); 253 $this->privateLinkDB->get(43);
248 } 254 }
249 255
250 /** 256 /**
251 * Test add() method while logged out 257 * Test add() method while logged out
252 *
253 * @expectedException \Exception
254 * @expectedExceptionMessage You're not authorized to alter the datastore
255 */ 258 */
256 public function testAddLoggedOut() 259 public function testAddLoggedOut()
257 { 260 {
258 $this->publicLinkDB->add(new Bookmark()); 261 $this->expectException(\Exception::class);
259 } 262 $this->expectExceptionMessage('You\'re not authorized to alter the datastore');
260 263
261 /** 264 $this->publicLinkDB->add(new Bookmark());
262 * Test add() method with an entry which is not a bookmark instance
263 *
264 * @expectedException \Exception
265 * @expectedExceptionMessage Provided data is invalid
266 */
267 public function testAddNotABookmark()
268 {
269 $this->privateLinkDB->add(['title' => 'hi!']);
270 } 265 }
271 266
272 /** 267 /**
273 * Test add() method with a Bookmark already containing an ID 268 * Test add() method with a Bookmark already containing an ID
274 *
275 * @expectedException \Exception
276 * @expectedExceptionMessage This bookmarks already exists
277 */ 269 */
278 public function testAddWithId() 270 public function testAddWithId()
279 { 271 {
272 $this->expectException(\Exception::class);
273 $this->expectExceptionMessage('This bookmarks already exists');
274
280 $bookmark = new Bookmark(); 275 $bookmark = new Bookmark();
281 $bookmark->setId(43); 276 $bookmark->setId(43);
282 $this->privateLinkDB->add($bookmark); 277 $this->privateLinkDB->add($bookmark);
@@ -314,7 +309,7 @@ class BookmarkFileServiceTest extends TestCase
314 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated()); 309 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated());
315 310
316 // reload from file 311 // reload from file
317 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 312 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
318 313
319 $bookmark = $this->privateLinkDB->get(42); 314 $bookmark = $this->privateLinkDB->get(42);
320 $this->assertEquals(42, $bookmark->getId()); 315 $this->assertEquals(42, $bookmark->getId());
@@ -340,7 +335,7 @@ class BookmarkFileServiceTest extends TestCase
340 335
341 $bookmark = $this->privateLinkDB->get(42); 336 $bookmark = $this->privateLinkDB->get(42);
342 $this->assertEquals(42, $bookmark->getId()); 337 $this->assertEquals(42, $bookmark->getId());
343 $this->assertEquals('?WDWyig', $bookmark->getUrl()); 338 $this->assertEquals('/shaare/WDWyig', $bookmark->getUrl());
344 $this->assertEquals('1eYJ1Q', $bookmark->getShortUrl()); 339 $this->assertEquals('1eYJ1Q', $bookmark->getShortUrl());
345 $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle()); 340 $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle());
346 $this->assertEquals('Used to test bookmarks reordering.', $bookmark->getDescription()); 341 $this->assertEquals('Used to test bookmarks reordering.', $bookmark->getDescription());
@@ -355,11 +350,11 @@ class BookmarkFileServiceTest extends TestCase
355 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated()); 350 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated());
356 351
357 // reload from file 352 // reload from file
358 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 353 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
359 354
360 $bookmark = $this->privateLinkDB->get(42); 355 $bookmark = $this->privateLinkDB->get(42);
361 $this->assertEquals(42, $bookmark->getId()); 356 $this->assertEquals(42, $bookmark->getId());
362 $this->assertEquals('?WDWyig', $bookmark->getUrl()); 357 $this->assertEquals('/shaare/WDWyig', $bookmark->getUrl());
363 $this->assertEquals('1eYJ1Q', $bookmark->getShortUrl()); 358 $this->assertEquals('1eYJ1Q', $bookmark->getShortUrl());
364 $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle()); 359 $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle());
365 $this->assertEquals('Used to test bookmarks reordering.', $bookmark->getDescription()); 360 $this->assertEquals('Used to test bookmarks reordering.', $bookmark->getDescription());
@@ -388,7 +383,7 @@ class BookmarkFileServiceTest extends TestCase
388 $this->assertEquals($title, $bookmark->getTitle()); 383 $this->assertEquals($title, $bookmark->getTitle());
389 384
390 // reload from file 385 // reload from file
391 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 386 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
392 387
393 $bookmark = $this->privateLinkDB->get(42); 388 $bookmark = $this->privateLinkDB->get(42);
394 $this->assertEquals(42, $bookmark->getId()); 389 $this->assertEquals(42, $bookmark->getId());
@@ -397,44 +392,33 @@ class BookmarkFileServiceTest extends TestCase
397 392
398 /** 393 /**
399 * Test set() method while logged out 394 * Test set() method while logged out
400 *
401 * @expectedException \Exception
402 * @expectedExceptionMessage You're not authorized to alter the datastore
403 */ 395 */
404 public function testSetLoggedOut() 396 public function testSetLoggedOut()
405 { 397 {
406 $this->publicLinkDB->set(new Bookmark()); 398 $this->expectException(\Exception::class);
407 } 399 $this->expectExceptionMessage('You\'re not authorized to alter the datastore');
408 400
409 /** 401 $this->publicLinkDB->set(new Bookmark());
410 * Test set() method with an entry which is not a bookmark instance
411 *
412 * @expectedException \Exception
413 * @expectedExceptionMessage Provided data is invalid
414 */
415 public function testSetNotABookmark()
416 {
417 $this->privateLinkDB->set(['title' => 'hi!']);
418 } 402 }
419 403
420 /** 404 /**
421 * Test set() method with a Bookmark without an ID defined. 405 * Test set() method with a Bookmark without an ID defined.
422 *
423 * @expectedException Shaarli\Bookmark\Exception\BookmarkNotFoundException
424 */ 406 */
425 public function testSetWithoutId() 407 public function testSetWithoutId()
426 { 408 {
409 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
410
427 $bookmark = new Bookmark(); 411 $bookmark = new Bookmark();
428 $this->privateLinkDB->set($bookmark); 412 $this->privateLinkDB->set($bookmark);
429 } 413 }
430 414
431 /** 415 /**
432 * Test set() method with a Bookmark with an unknow ID 416 * Test set() method with a Bookmark with an unknow ID
433 *
434 * @expectedException Shaarli\Bookmark\Exception\BookmarkNotFoundException
435 */ 417 */
436 public function testSetWithUnknownId() 418 public function testSetWithUnknownId()
437 { 419 {
420 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
421
438 $bookmark = new Bookmark(); 422 $bookmark = new Bookmark();
439 $bookmark->setId(666); 423 $bookmark->setId(666);
440 $this->privateLinkDB->set($bookmark); 424 $this->privateLinkDB->set($bookmark);
@@ -452,7 +436,7 @@ class BookmarkFileServiceTest extends TestCase
452 $this->assertEquals(43, $bookmark->getId()); 436 $this->assertEquals(43, $bookmark->getId());
453 437
454 // reload from file 438 // reload from file
455 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 439 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
456 440
457 $bookmark = $this->privateLinkDB->get(43); 441 $bookmark = $this->privateLinkDB->get(43);
458 $this->assertEquals(43, $bookmark->getId()); 442 $this->assertEquals(43, $bookmark->getId());
@@ -472,7 +456,7 @@ class BookmarkFileServiceTest extends TestCase
472 $this->assertEquals($title, $bookmark->getTitle()); 456 $this->assertEquals($title, $bookmark->getTitle());
473 457
474 // reload from file 458 // reload from file
475 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 459 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
476 460
477 $bookmark = $this->privateLinkDB->get(42); 461 $bookmark = $this->privateLinkDB->get(42);
478 $this->assertEquals(42, $bookmark->getId()); 462 $this->assertEquals(42, $bookmark->getId());
@@ -481,24 +465,13 @@ class BookmarkFileServiceTest extends TestCase
481 465
482 /** 466 /**
483 * Test addOrSet() method while logged out 467 * Test addOrSet() method while logged out
484 *
485 * @expectedException \Exception
486 * @expectedExceptionMessage You're not authorized to alter the datastore
487 */ 468 */
488 public function testAddOrSetLoggedOut() 469 public function testAddOrSetLoggedOut()
489 { 470 {
490 $this->publicLinkDB->addOrSet(new Bookmark()); 471 $this->expectException(\Exception::class);
491 } 472 $this->expectExceptionMessage('You\'re not authorized to alter the datastore');
492 473
493 /** 474 $this->publicLinkDB->addOrSet(new Bookmark());
494 * Test addOrSet() method with an entry which is not a bookmark instance
495 *
496 * @expectedException \Exception
497 * @expectedExceptionMessage Provided data is invalid
498 */
499 public function testAddOrSetNotABookmark()
500 {
501 $this->privateLinkDB->addOrSet(['title' => 'hi!']);
502 } 475 }
503 476
504 /** 477 /**
@@ -515,7 +488,7 @@ class BookmarkFileServiceTest extends TestCase
515 $this->assertEquals($title, $bookmark->getTitle()); 488 $this->assertEquals($title, $bookmark->getTitle());
516 489
517 // reload from file 490 // reload from file
518 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 491 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
519 492
520 $bookmark = $this->privateLinkDB->get(42); 493 $bookmark = $this->privateLinkDB->get(42);
521 $this->assertEquals(42, $bookmark->getId()); 494 $this->assertEquals(42, $bookmark->getId());
@@ -524,11 +497,11 @@ class BookmarkFileServiceTest extends TestCase
524 497
525 /** 498 /**
526 * Test remove() method with an existing Bookmark 499 * Test remove() method with an existing Bookmark
527 *
528 * @expectedException Shaarli\Bookmark\Exception\BookmarkNotFoundException
529 */ 500 */
530 public function testRemoveExisting() 501 public function testRemoveExisting()
531 { 502 {
503 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
504
532 $bookmark = $this->privateLinkDB->get(42); 505 $bookmark = $this->privateLinkDB->get(42);
533 $this->privateLinkDB->remove($bookmark); 506 $this->privateLinkDB->remove($bookmark);
534 507
@@ -541,41 +514,30 @@ class BookmarkFileServiceTest extends TestCase
541 $this->assertInstanceOf(BookmarkNotFoundException::class, $exception); 514 $this->assertInstanceOf(BookmarkNotFoundException::class, $exception);
542 515
543 // reload from file 516 // reload from file
544 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 517 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
545 518
546 $this->privateLinkDB->get(42); 519 $this->privateLinkDB->get(42);
547 } 520 }
548 521
549 /** 522 /**
550 * Test remove() method while logged out 523 * Test remove() method while logged out
551 *
552 * @expectedException \Exception
553 * @expectedExceptionMessage You're not authorized to alter the datastore
554 */ 524 */
555 public function testRemoveLoggedOut() 525 public function testRemoveLoggedOut()
556 { 526 {
527 $this->expectException(\Exception::class);
528 $this->expectExceptionMessage('You\'re not authorized to alter the datastore');
529
557 $bookmark = $this->privateLinkDB->get(42); 530 $bookmark = $this->privateLinkDB->get(42);
558 $this->publicLinkDB->remove($bookmark); 531 $this->publicLinkDB->remove($bookmark);
559 } 532 }
560 533
561 /** 534 /**
562 * Test remove() method with an entry which is not a bookmark instance
563 *
564 * @expectedException \Exception
565 * @expectedExceptionMessage Provided data is invalid
566 */
567 public function testRemoveNotABookmark()
568 {
569 $this->privateLinkDB->remove(['title' => 'hi!']);
570 }
571
572 /**
573 * Test remove() method with a Bookmark with an unknown ID 535 * Test remove() method with a Bookmark with an unknown ID
574 *
575 * @expectedException Shaarli\Bookmark\Exception\BookmarkNotFoundException
576 */ 536 */
577 public function testRemoveWithUnknownId() 537 public function testRemoveWithUnknownId()
578 { 538 {
539 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
540
579 $bookmark = new Bookmark(); 541 $bookmark = new Bookmark();
580 $bookmark->setId(666); 542 $bookmark->setId(666);
581 $this->privateLinkDB->remove($bookmark); 543 $this->privateLinkDB->remove($bookmark);
@@ -615,14 +577,18 @@ class BookmarkFileServiceTest extends TestCase
615 { 577 {
616 $dbSize = $this->privateLinkDB->count(); 578 $dbSize = $this->privateLinkDB->count();
617 $this->privateLinkDB->initialize(); 579 $this->privateLinkDB->initialize();
618 $this->assertEquals($dbSize + 2, $this->privateLinkDB->count()); 580 $this->assertEquals($dbSize + 3, $this->privateLinkDB->count());
619 $this->assertEquals( 581 $this->assertStringStartsWith(
620 'My secret stuff... - Pastebin.com', 582 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.',
621 $this->privateLinkDB->get(43)->getTitle() 583 $this->privateLinkDB->get(43)->getDescription()
622 ); 584 );
623 $this->assertEquals( 585 $this->assertStringStartsWith(
624 'The personal, minimalist, super-fast, database free, bookmarking service', 586 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.',
625 $this->privateLinkDB->get(44)->getTitle() 587 $this->privateLinkDB->get(44)->getDescription()
588 );
589 $this->assertStringStartsWith(
590 'Welcome to Shaarli!',
591 $this->privateLinkDB->get(45)->getDescription()
626 ); 592 );
627 } 593 }
628 594
@@ -631,18 +597,17 @@ class BookmarkFileServiceTest extends TestCase
631 * to make sure that nothing have been broken in the migration process. 597 * to make sure that nothing have been broken in the migration process.
632 * They mostly cover search/filters. Some of them might be redundant with the previous ones. 598 * They mostly cover search/filters. Some of them might be redundant with the previous ones.
633 */ 599 */
634
635 /** 600 /**
636 * Attempt to instantiate a LinkDB whereas the datastore is not writable 601 * Attempt to instantiate a LinkDB whereas the datastore is not writable
637 *
638 * @expectedException Shaarli\Bookmark\Exception\NotWritableDataStoreException
639 * @expectedExceptionMessageRegExp #Couldn't load data from the data store file "null".*#
640 */ 602 */
641 public function testConstructDatastoreNotWriteable() 603 public function testConstructDatastoreNotWriteable()
642 { 604 {
605 $this->expectException(\Shaarli\Bookmark\Exception\NotWritableDataStoreException::class);
606 $this->expectExceptionMessageRegExp('#Couldn\'t load data from the data store file "null".*#');
607
643 $conf = new ConfigManager('tests/utils/config/configJson'); 608 $conf = new ConfigManager('tests/utils/config/configJson');
644 $conf->set('resource.datastore', 'null/store.db'); 609 $conf->set('resource.datastore', 'null/store.db');
645 new BookmarkFileService($conf, $this->history, true); 610 new BookmarkFileService($conf, $this->history, $this->mutex, true);
646 } 611 }
647 612
648 /** 613 /**
@@ -652,7 +617,7 @@ class BookmarkFileServiceTest extends TestCase
652 { 617 {
653 unlink(self::$testDatastore); 618 unlink(self::$testDatastore);
654 $this->assertFileNotExists(self::$testDatastore); 619 $this->assertFileNotExists(self::$testDatastore);
655 new BookmarkFileService($this->conf, $this->history, true); 620 new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
656 $this->assertFileExists(self::$testDatastore); 621 $this->assertFileExists(self::$testDatastore);
657 622
658 // ensure the correct data has been written 623 // ensure the correct data has been written
@@ -666,7 +631,7 @@ class BookmarkFileServiceTest extends TestCase
666 { 631 {
667 unlink(self::$testDatastore); 632 unlink(self::$testDatastore);
668 $this->assertFileNotExists(self::$testDatastore); 633 $this->assertFileNotExists(self::$testDatastore);
669 $db = new \FakeBookmarkService($this->conf, $this->history, false); 634 $db = new \FakeBookmarkService($this->conf, $this->history, $this->mutex, false);
670 $this->assertFileNotExists(self::$testDatastore); 635 $this->assertFileNotExists(self::$testDatastore);
671 $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks()); 636 $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks());
672 $this->assertCount(0, $db->getBookmarks()); 637 $this->assertCount(0, $db->getBookmarks());
@@ -699,13 +664,13 @@ class BookmarkFileServiceTest extends TestCase
699 */ 664 */
700 public function testSave() 665 public function testSave()
701 { 666 {
702 $testDB = new BookmarkFileService($this->conf, $this->history, true); 667 $testDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
703 $dbSize = $testDB->count(); 668 $dbSize = $testDB->count();
704 669
705 $bookmark = new Bookmark(); 670 $bookmark = new Bookmark();
706 $testDB->add($bookmark); 671 $testDB->add($bookmark);
707 672
708 $testDB = new BookmarkFileService($this->conf, $this->history, true); 673 $testDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
709 $this->assertEquals($dbSize + 1, $testDB->count()); 674 $this->assertEquals($dbSize + 1, $testDB->count());
710 } 675 }
711 676
@@ -715,28 +680,12 @@ class BookmarkFileServiceTest extends TestCase
715 public function testCountHiddenPublic() 680 public function testCountHiddenPublic()
716 { 681 {
717 $this->conf->set('privacy.hide_public_links', true); 682 $this->conf->set('privacy.hide_public_links', true);
718 $linkDB = new BookmarkFileService($this->conf, $this->history, false); 683 $linkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, false);
719 684
720 $this->assertEquals(0, $linkDB->count()); 685 $this->assertEquals(0, $linkDB->count());
721 } 686 }
722 687
723 /** 688 /**
724 * List the days for which bookmarks have been posted
725 */
726 public function testDays()
727 {
728 $this->assertEquals(
729 ['20100309', '20100310', '20121206', '20121207', '20130614', '20150310'],
730 $this->publicLinkDB->days()
731 );
732
733 $this->assertEquals(
734 ['20100309', '20100310', '20121206', '20121207', '20130614', '20141125', '20150310'],
735 $this->privateLinkDB->days()
736 );
737 }
738
739 /**
740 * The URL corresponds to an existing entry in the DB 689 * The URL corresponds to an existing entry in the DB
741 */ 690 */
742 public function testGetKnownLinkFromURL() 691 public function testGetKnownLinkFromURL()
@@ -744,7 +693,7 @@ class BookmarkFileServiceTest extends TestCase
744 $link = $this->publicLinkDB->findByUrl('http://mediagoblin.org/'); 693 $link = $this->publicLinkDB->findByUrl('http://mediagoblin.org/');
745 694
746 $this->assertNotEquals(false, $link); 695 $this->assertNotEquals(false, $link);
747 $this->assertContains( 696 $this->assertContainsPolyfill(
748 'A free software media publishing platform', 697 'A free software media publishing platform',
749 $link->getDescription() 698 $link->getDescription()
750 ); 699 );
@@ -783,6 +732,10 @@ class BookmarkFileServiceTest extends TestCase
783 // They need to be grouped with the first case found - order by date DESC: `sTuff`. 732 // They need to be grouped with the first case found - order by date DESC: `sTuff`.
784 'sTuff' => 2, 733 'sTuff' => 2,
785 'ut' => 1, 734 'ut' => 1,
735 'assurance' => 1,
736 'coding-style' => 1,
737 'quality' => 1,
738 'standards' => 1,
786 ], 739 ],
787 $this->publicLinkDB->bookmarksCountPerTag() 740 $this->publicLinkDB->bookmarksCountPerTag()
788 ); 741 );
@@ -811,12 +764,15 @@ class BookmarkFileServiceTest extends TestCase
811 'tag3' => 1, 764 'tag3' => 1,
812 'tag4' => 1, 765 'tag4' => 1,
813 'ut' => 1, 766 'ut' => 1,
767 'assurance' => 1,
768 'coding-style' => 1,
769 'quality' => 1,
770 'standards' => 1,
814 ], 771 ],
815 $this->privateLinkDB->bookmarksCountPerTag() 772 $this->privateLinkDB->bookmarksCountPerTag()
816 ); 773 );
817 $this->assertEquals( 774 $this->assertEquals(
818 [ 775 [
819 'web' => 4,
820 'cartoon' => 2, 776 'cartoon' => 2,
821 'gnu' => 1, 777 'gnu' => 1,
822 'dev' => 1, 778 'dev' => 1,
@@ -833,7 +789,6 @@ class BookmarkFileServiceTest extends TestCase
833 ); 789 );
834 $this->assertEquals( 790 $this->assertEquals(
835 [ 791 [
836 'web' => 1,
837 'html' => 1, 792 'html' => 1,
838 'w3c' => 1, 793 'w3c' => 1,
839 'css' => 1, 794 'css' => 1,
@@ -894,39 +849,70 @@ class BookmarkFileServiceTest extends TestCase
894 public function testFilterHashValid() 849 public function testFilterHashValid()
895 { 850 {
896 $request = smallHash('20150310_114651'); 851 $request = smallHash('20150310_114651');
897 $this->assertEquals( 852 $this->assertSame(
898 1, 853 $request,
899 count($this->publicLinkDB->findByHash($request)) 854 $this->publicLinkDB->findByHash($request)->getShortUrl()
900 ); 855 );
901 $request = smallHash('20150310_114633' . 8); 856 $request = smallHash('20150310_114633' . 8);
902 $this->assertEquals( 857 $this->assertSame(
903 1, 858 $request,
904 count($this->publicLinkDB->findByHash($request)) 859 $this->publicLinkDB->findByHash($request)->getShortUrl()
905 ); 860 );
906 } 861 }
907 862
908 /** 863 /**
909 * Test filterHash() with an invalid smallhash. 864 * Test filterHash() with an invalid smallhash.
910 *
911 * @expectedException \Shaarli\Bookmark\Exception\BookmarkNotFoundException
912 */ 865 */
913 public function testFilterHashInValid1() 866 public function testFilterHashInValid1()
914 { 867 {
868 $this->expectException(BookmarkNotFoundException::class);
869
915 $request = 'blabla'; 870 $request = 'blabla';
916 $this->publicLinkDB->findByHash($request); 871 $this->publicLinkDB->findByHash($request);
917 } 872 }
918 873
919 /** 874 /**
920 * Test filterHash() with an empty smallhash. 875 * Test filterHash() with an empty smallhash.
921 *
922 * @expectedException \Shaarli\Bookmark\Exception\BookmarkNotFoundException
923 */ 876 */
924 public function testFilterHashInValid() 877 public function testFilterHashInValid()
925 { 878 {
879 $this->expectException(BookmarkNotFoundException::class);
880
926 $this->publicLinkDB->findByHash(''); 881 $this->publicLinkDB->findByHash('');
927 } 882 }
928 883
929 /** 884 /**
885 * Test filterHash() on a private bookmark while logged out.
886 */
887 public function testFilterHashPrivateWhileLoggedOut()
888 {
889 $this->expectException(BookmarkNotFoundException::class);
890 $this->expectExceptionMessage('The link you are trying to reach does not exist or has been deleted');
891
892 $hash = smallHash('20141125_084734' . 6);
893
894 $this->publicLinkDB->findByHash($hash);
895 }
896
897 /**
898 * Test filterHash() with private key.
899 */
900 public function testFilterHashWithPrivateKey()
901 {
902 $hash = smallHash('20141125_084734' . 6);
903 $privateKey = 'this is usually auto generated';
904
905 $bookmark = $this->privateLinkDB->findByHash($hash);
906 $bookmark->addAdditionalContentEntry('private_key', $privateKey);
907 $this->privateLinkDB->save();
908
909 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, false);
910 $bookmark = $this->privateLinkDB->findByHash($hash, $privateKey);
911
912 static::assertSame(6, $bookmark->getId());
913 }
914
915 /**
930 * Test linksCountPerTag all tags without filter. 916 * Test linksCountPerTag all tags without filter.
931 * Equal occurrences should be sorted alphabetically. 917 * Equal occurrences should be sorted alphabetically.
932 */ 918 */
@@ -955,6 +941,10 @@ class BookmarkFileServiceTest extends TestCase
955 'tag4' => 1, 941 'tag4' => 1,
956 'ut' => 1, 942 'ut' => 1,
957 'w3c' => 1, 943 'w3c' => 1,
944 'assurance' => 1,
945 'coding-style' => 1,
946 'quality' => 1,
947 'standards' => 1,
958 ]; 948 ];
959 $tags = $this->privateLinkDB->bookmarksCountPerTag(); 949 $tags = $this->privateLinkDB->bookmarksCountPerTag();
960 950
@@ -968,7 +958,6 @@ class BookmarkFileServiceTest extends TestCase
968 public function testCountLinkPerTagAllWithFilter() 958 public function testCountLinkPerTagAllWithFilter()
969 { 959 {
970 $expected = [ 960 $expected = [
971 'gnu' => 2,
972 'hashtag' => 2, 961 'hashtag' => 2,
973 '-exclude' => 1, 962 '-exclude' => 1,
974 '.hidden' => 1, 963 '.hidden' => 1,
@@ -991,7 +980,6 @@ class BookmarkFileServiceTest extends TestCase
991 public function testCountLinkPerTagPublicWithFilter() 980 public function testCountLinkPerTagPublicWithFilter()
992 { 981 {
993 $expected = [ 982 $expected = [
994 'gnu' => 2,
995 'hashtag' => 2, 983 'hashtag' => 2,
996 '-exclude' => 1, 984 '-exclude' => 1,
997 '.hidden' => 1, 985 '.hidden' => 1,
@@ -1015,7 +1003,6 @@ class BookmarkFileServiceTest extends TestCase
1015 { 1003 {
1016 $expected = [ 1004 $expected = [
1017 'cartoon' => 1, 1005 'cartoon' => 1,
1018 'dev' => 1,
1019 'tag1' => 1, 1006 'tag1' => 1,
1020 'tag2' => 1, 1007 'tag2' => 1,
1021 'tag3' => 1, 1008 'tag3' => 1,
@@ -1056,6 +1043,10 @@ class BookmarkFileServiceTest extends TestCase
1056 'stallman' => 1, 1043 'stallman' => 1,
1057 'ut' => 1, 1044 'ut' => 1,
1058 'w3c' => 1, 1045 'w3c' => 1,
1046 'assurance' => 1,
1047 'coding-style' => 1,
1048 'quality' => 1,
1049 'standards' => 1,
1059 ]; 1050 ];
1060 $bookmark = new Bookmark(); 1051 $bookmark = new Bookmark();
1061 $bookmark->setTags(['newTagToCount', BookmarkMarkdownFormatter::NO_MD_TAG]); 1052 $bookmark->setTags(['newTagToCount', BookmarkMarkdownFormatter::NO_MD_TAG]);
@@ -1067,6 +1058,108 @@ class BookmarkFileServiceTest extends TestCase
1067 } 1058 }
1068 1059
1069 /** 1060 /**
1061 * Test find by dates in the middle of the datastore (sorted by dates) with a single bookmark as a result.
1062 */
1063 public function testFilterByDateMidTimePeriodSingleBookmark(): void
1064 {
1065 $bookmarks = $this->privateLinkDB->findByDate(
1066 DateTime::createFromFormat('Ymd_His', '20121206_150000'),
1067 DateTime::createFromFormat('Ymd_His', '20121206_160000'),
1068 $before,
1069 $after
1070 );
1071
1072 static::assertCount(1, $bookmarks);
1073
1074 static::assertSame(9, $bookmarks[0]->getId());
1075 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_142300'), $before);
1076 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_172539'), $after);
1077 }
1078
1079 /**
1080 * Test find by dates in the middle of the datastore (sorted by dates) with a multiple bookmarks as a result.
1081 */
1082 public function testFilterByDateMidTimePeriodMultipleBookmarks(): void
1083 {
1084 $bookmarks = $this->privateLinkDB->findByDate(
1085 DateTime::createFromFormat('Ymd_His', '20121206_150000'),
1086 DateTime::createFromFormat('Ymd_His', '20121206_180000'),
1087 $before,
1088 $after
1089 );
1090
1091 static::assertCount(2, $bookmarks);
1092
1093 static::assertSame(1, $bookmarks[0]->getId());
1094 static::assertSame(9, $bookmarks[1]->getId());
1095 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_142300'), $before);
1096 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_182539'), $after);
1097 }
1098
1099 /**
1100 * Test find by dates at the end of the datastore (sorted by dates).
1101 */
1102 public function testFilterByDateLastTimePeriod(): void
1103 {
1104 $after = new DateTime();
1105 $bookmarks = $this->privateLinkDB->findByDate(
1106 DateTime::createFromFormat('Ymd_His', '20150310_114640'),
1107 DateTime::createFromFormat('Ymd_His', '20450101_010101'),
1108 $before,
1109 $after
1110 );
1111
1112 static::assertCount(1, $bookmarks);
1113
1114 static::assertSame(41, $bookmarks[0]->getId());
1115 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20150310_114633'), $before);
1116 static::assertNull($after);
1117 }
1118
1119 /**
1120 * Test find by dates at the beginning of the datastore (sorted by dates).
1121 */
1122 public function testFilterByDateFirstTimePeriod(): void
1123 {
1124 $before = new DateTime();
1125 $bookmarks = $this->privateLinkDB->findByDate(
1126 DateTime::createFromFormat('Ymd_His', '20000101_101010'),
1127 DateTime::createFromFormat('Ymd_His', '20100309_110000'),
1128 $before,
1129 $after
1130 );
1131
1132 static::assertCount(1, $bookmarks);
1133
1134 static::assertSame(11, $bookmarks[0]->getId());
1135 static::assertNull($before);
1136 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20100310_101010'), $after);
1137 }
1138
1139 /**
1140 * Test getLatest with a sticky bookmark: it should be ignored and return the latest by creation date instead.
1141 */
1142 public function testGetLatestWithSticky(): void
1143 {
1144 $bookmark = $this->publicLinkDB->getLatest();
1145
1146 static::assertSame(41, $bookmark->getId());
1147 }
1148
1149 /**
1150 * Test getLatest with a sticky bookmark: it should be ignored and return the latest by creation date instead.
1151 */
1152 public function testGetLatestEmptyDatastore(): void
1153 {
1154 unlink($this->conf->get('resource.datastore'));
1155 $this->publicLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, false);
1156
1157 $bookmark = $this->publicLinkDB->getLatest();
1158
1159 static::assertNull($bookmark);
1160 }
1161
1162 /**
1070 * Allows to test LinkDB's private methods 1163 * Allows to test LinkDB's private methods
1071 * 1164 *
1072 * @see 1165 * @see
diff --git a/tests/bookmark/BookmarkFilterTest.php b/tests/bookmark/BookmarkFilterTest.php
index d4c71cb9..574d8e3f 100644
--- a/tests/bookmark/BookmarkFilterTest.php
+++ b/tests/bookmark/BookmarkFilterTest.php
@@ -2,12 +2,11 @@
2 2
3namespace Shaarli\Bookmark; 3namespace Shaarli\Bookmark;
4 4
5use Exception; 5use malkusch\lock\mutex\NoMutex;
6use PHPUnit\Framework\TestCase;
7use ReferenceLinkDB; 6use ReferenceLinkDB;
8use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
9use Shaarli\Formatter\FormatterFactory;
10use Shaarli\History; 8use Shaarli\History;
9use Shaarli\TestCase;
11 10
12/** 11/**
13 * Class BookmarkFilterTest. 12 * Class BookmarkFilterTest.
@@ -36,14 +35,15 @@ class BookmarkFilterTest extends TestCase
36 /** 35 /**
37 * Instantiate linkFilter with ReferenceLinkDB data. 36 * Instantiate linkFilter with ReferenceLinkDB data.
38 */ 37 */
39 public static function setUpBeforeClass() 38 public static function setUpBeforeClass(): void
40 { 39 {
40 $mutex = new NoMutex();
41 $conf = new ConfigManager('tests/utils/config/configJson'); 41 $conf = new ConfigManager('tests/utils/config/configJson');
42 $conf->set('resource.datastore', self::$testDatastore); 42 $conf->set('resource.datastore', self::$testDatastore);
43 self::$refDB = new \ReferenceLinkDB(); 43 self::$refDB = new \ReferenceLinkDB();
44 self::$refDB->write(self::$testDatastore); 44 self::$refDB->write(self::$testDatastore);
45 $history = new History('sandbox/history.php'); 45 $history = new History('sandbox/history.php');
46 self::$bookmarkService = new \FakeBookmarkService($conf, $history, true); 46 self::$bookmarkService = new \FakeBookmarkService($conf, $history, $mutex, true);
47 self::$linkFilter = new BookmarkFilter(self::$bookmarkService->getBookmarks()); 47 self::$linkFilter = new BookmarkFilter(self::$bookmarkService->getBookmarks());
48 } 48 }
49 49
@@ -190,6 +190,17 @@ class BookmarkFilterTest extends TestCase
190 } 190 }
191 191
192 /** 192 /**
193 * Return bookmarks for a given day
194 */
195 public function testFilterDayRestrictedVisibility(): void
196 {
197 $this->assertEquals(
198 3,
199 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, '20121206', false, BookmarkFilter::$PUBLIC))
200 );
201 }
202
203 /**
193 * 404 - day not found 204 * 404 - day not found
194 */ 205 */
195 public function testFilterUnknownDay() 206 public function testFilterUnknownDay()
@@ -202,21 +213,23 @@ class BookmarkFilterTest extends TestCase
202 213
203 /** 214 /**
204 * Use an invalid date format 215 * Use an invalid date format
205 * @expectedException Exception
206 * @expectedExceptionMessageRegExp /Invalid date format/
207 */ 216 */
208 public function testFilterInvalidDayWithChars() 217 public function testFilterInvalidDayWithChars()
209 { 218 {
219 $this->expectException(\Exception::class);
220 $this->expectExceptionMessageRegExp('/Invalid date format/');
221
210 self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, 'Rainy day, dream away'); 222 self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, 'Rainy day, dream away');
211 } 223 }
212 224
213 /** 225 /**
214 * Use an invalid date format 226 * Use an invalid date format
215 * @expectedException Exception
216 * @expectedExceptionMessageRegExp /Invalid date format/
217 */ 227 */
218 public function testFilterInvalidDayDigits() 228 public function testFilterInvalidDayDigits()
219 { 229 {
230 $this->expectException(\Exception::class);
231 $this->expectExceptionMessageRegExp('/Invalid date format/');
232
220 self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, '20'); 233 self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, '20');
221 } 234 }
222 235
@@ -240,11 +253,11 @@ class BookmarkFilterTest extends TestCase
240 253
241 /** 254 /**
242 * No link for this hash 255 * No link for this hash
243 *
244 * @expectedException \Shaarli\Bookmark\Exception\BookmarkNotFoundException
245 */ 256 */
246 public function testFilterUnknownSmallHash() 257 public function testFilterUnknownSmallHash()
247 { 258 {
259 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
260
248 self::$linkFilter->filter(BookmarkFilter::$FILTER_HASH, 'Iblaah'); 261 self::$linkFilter->filter(BookmarkFilter::$FILTER_HASH, 'Iblaah');
249 } 262 }
250 263
@@ -511,4 +524,43 @@ class BookmarkFilterTest extends TestCase
511 )) 524 ))
512 ); 525 );
513 } 526 }
527
528 /**
529 * Test search result highlights in every field of bookmark reference #9.
530 */
531 public function testFullTextSearchHighlight(): void
532 {
533 $bookmarks = self::$linkFilter->filter(
534 BookmarkFilter::$FILTER_TEXT,
535 '"psr-2" coding guide http fig "psr-2/" "This guide" basic standard. coding-style quality assurance'
536 );
537
538 static::assertCount(1, $bookmarks);
539 static::assertArrayHasKey(9, $bookmarks);
540
541 $bookmark = $bookmarks[9];
542 $expectedHighlights = [
543 'title' => [
544 ['start' => 0, 'end' => 5], // "psr-2"
545 ['start' => 7, 'end' => 13], // coding
546 ['start' => 20, 'end' => 25], // guide
547 ],
548 'description' => [
549 ['start' => 0, 'end' => 10], // "This guide"
550 ['start' => 45, 'end' => 50], // basic
551 ['start' => 58, 'end' => 67], // standard.
552 ],
553 'url' => [
554 ['start' => 0, 'end' => 4], // http
555 ['start' => 15, 'end' => 18], // fig
556 ['start' => 27, 'end' => 33], // "psr-2/"
557 ],
558 'tags' => [
559 ['start' => 0, 'end' => 12], // coding-style
560 ['start' => 23, 'end' => 30], // quality
561 ['start' => 31, 'end' => 40], // assurance
562 ],
563 ];
564 static::assertSame($expectedHighlights, $bookmark->getAdditionalContentEntry('search_highlight'));
565 }
514} 566}
diff --git a/tests/bookmark/BookmarkInitializerTest.php b/tests/bookmark/BookmarkInitializerTest.php
index d23eb069..0c8420ce 100644
--- a/tests/bookmark/BookmarkInitializerTest.php
+++ b/tests/bookmark/BookmarkInitializerTest.php
@@ -2,10 +2,10 @@
2 2
3namespace Shaarli\Bookmark; 3namespace Shaarli\Bookmark;
4 4
5use PHPUnit\Framework\TestCase; 5use malkusch\lock\mutex\NoMutex;
6use ReferenceLinkDB;
7use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
8use Shaarli\History; 7use Shaarli\History;
8use Shaarli\TestCase;
9 9
10/** 10/**
11 * Class BookmarkInitializerTest 11 * Class BookmarkInitializerTest
@@ -35,11 +35,15 @@ class BookmarkInitializerTest extends TestCase
35 /** @var BookmarkInitializer instance */ 35 /** @var BookmarkInitializer instance */
36 protected $initializer; 36 protected $initializer;
37 37
38 /** @var NoMutex */
39 protected $mutex;
40
38 /** 41 /**
39 * Initialize an empty BookmarkFileService 42 * Initialize an empty BookmarkFileService
40 */ 43 */
41 public function setUp() 44 public function setUp(): void
42 { 45 {
46 $this->mutex = new NoMutex();
43 if (file_exists(self::$testDatastore)) { 47 if (file_exists(self::$testDatastore)) {
44 unlink(self::$testDatastore); 48 unlink(self::$testDatastore);
45 } 49 }
@@ -48,72 +52,103 @@ class BookmarkInitializerTest extends TestCase
48 $this->conf = new ConfigManager(self::$testConf); 52 $this->conf = new ConfigManager(self::$testConf);
49 $this->conf->set('resource.datastore', self::$testDatastore); 53 $this->conf->set('resource.datastore', self::$testDatastore);
50 $this->history = new History('sandbox/history.php'); 54 $this->history = new History('sandbox/history.php');
51 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 55 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
52 56
53 $this->initializer = new BookmarkInitializer($this->bookmarkService); 57 $this->initializer = new BookmarkInitializer($this->bookmarkService);
54 } 58 }
55 59
56 /** 60 /**
57 * Test initialize() with an empty data store. 61 * Test initialize() with a data store containing bookmarks.
58 */ 62 */
59 public function testInitializeEmptyDataStore() 63 public function testInitializeNotEmptyDataStore(): void
60 { 64 {
61 $refDB = new \ReferenceLinkDB(); 65 $refDB = new \ReferenceLinkDB();
62 $refDB->write(self::$testDatastore); 66 $refDB->write(self::$testDatastore);
63 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 67 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
64 $this->initializer = new BookmarkInitializer($this->bookmarkService); 68 $this->initializer = new BookmarkInitializer($this->bookmarkService);
65 69
66 $this->initializer->initialize(); 70 $this->initializer->initialize();
67 71
68 $this->assertEquals($refDB->countLinks() + 2, $this->bookmarkService->count()); 72 $this->assertEquals($refDB->countLinks() + 3, $this->bookmarkService->count());
73
69 $bookmark = $this->bookmarkService->get(43); 74 $bookmark = $this->bookmarkService->get(43);
70 $this->assertEquals(43, $bookmark->getId()); 75 $this->assertStringStartsWith(
71 $this->assertEquals('My secret stuff... - Pastebin.com', $bookmark->getTitle()); 76 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.',
77 $bookmark->getDescription()
78 );
72 $this->assertTrue($bookmark->isPrivate()); 79 $this->assertTrue($bookmark->isPrivate());
73 80
74 $bookmark = $this->bookmarkService->get(44); 81 $bookmark = $this->bookmarkService->get(44);
75 $this->assertEquals(44, $bookmark->getId()); 82 $this->assertStringStartsWith(
76 $this->assertEquals( 83 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.',
77 'The personal, minimalist, super-fast, database free, bookmarking service', 84 $bookmark->getDescription()
78 $bookmark->getTitle() 85 );
86 $this->assertTrue($bookmark->isPrivate());
87
88 $bookmark = $this->bookmarkService->get(45);
89 $this->assertStringStartsWith(
90 'Welcome to Shaarli!',
91 $bookmark->getDescription()
79 ); 92 );
80 $this->assertFalse($bookmark->isPrivate()); 93 $this->assertFalse($bookmark->isPrivate());
81 94
95 $this->bookmarkService->save();
96
82 // Reload from file 97 // Reload from file
83 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 98 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
84 $this->assertEquals($refDB->countLinks() + 2, $this->bookmarkService->count()); 99 $this->assertEquals($refDB->countLinks() + 3, $this->bookmarkService->count());
100
85 $bookmark = $this->bookmarkService->get(43); 101 $bookmark = $this->bookmarkService->get(43);
86 $this->assertEquals(43, $bookmark->getId()); 102 $this->assertStringStartsWith(
87 $this->assertEquals('My secret stuff... - Pastebin.com', $bookmark->getTitle()); 103 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.',
104 $bookmark->getDescription()
105 );
88 $this->assertTrue($bookmark->isPrivate()); 106 $this->assertTrue($bookmark->isPrivate());
89 107
90 $bookmark = $this->bookmarkService->get(44); 108 $bookmark = $this->bookmarkService->get(44);
91 $this->assertEquals(44, $bookmark->getId()); 109 $this->assertStringStartsWith(
92 $this->assertEquals( 110 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.',
93 'The personal, minimalist, super-fast, database free, bookmarking service', 111 $bookmark->getDescription()
94 $bookmark->getTitle() 112 );
113 $this->assertTrue($bookmark->isPrivate());
114
115 $bookmark = $this->bookmarkService->get(45);
116 $this->assertStringStartsWith(
117 'Welcome to Shaarli!',
118 $bookmark->getDescription()
95 ); 119 );
96 $this->assertFalse($bookmark->isPrivate()); 120 $this->assertFalse($bookmark->isPrivate());
97 } 121 }
98 122
99 /** 123 /**
100 * Test initialize() with a data store containing bookmarks. 124 * Test initialize() with an a non existent datastore file .
101 */ 125 */
102 public function testInitializeNotEmptyDataStore() 126 public function testInitializeNonExistentDataStore(): void
103 { 127 {
128 $this->conf->set('resource.datastore', static::$testDatastore . '_empty');
129 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
130
104 $this->initializer->initialize(); 131 $this->initializer->initialize();
105 132
106 $this->assertEquals(2, $this->bookmarkService->count()); 133 $this->assertEquals(3, $this->bookmarkService->count());
107 $bookmark = $this->bookmarkService->get(0); 134 $bookmark = $this->bookmarkService->get(0);
108 $this->assertEquals(0, $bookmark->getId()); 135 $this->assertStringStartsWith(
109 $this->assertEquals('My secret stuff... - Pastebin.com', $bookmark->getTitle()); 136 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.',
137 $bookmark->getDescription()
138 );
110 $this->assertTrue($bookmark->isPrivate()); 139 $this->assertTrue($bookmark->isPrivate());
111 140
112 $bookmark = $this->bookmarkService->get(1); 141 $bookmark = $this->bookmarkService->get(1);
113 $this->assertEquals(1, $bookmark->getId()); 142 $this->assertStringStartsWith(
114 $this->assertEquals( 143 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.',
115 'The personal, minimalist, super-fast, database free, bookmarking service', 144 $bookmark->getDescription()
116 $bookmark->getTitle() 145 );
146 $this->assertTrue($bookmark->isPrivate());
147
148 $bookmark = $this->bookmarkService->get(2);
149 $this->assertStringStartsWith(
150 'Welcome to Shaarli!',
151 $bookmark->getDescription()
117 ); 152 );
118 $this->assertFalse($bookmark->isPrivate()); 153 $this->assertFalse($bookmark->isPrivate());
119 } 154 }
diff --git a/tests/bookmark/BookmarkTest.php b/tests/bookmark/BookmarkTest.php
index 9a3bbbfc..4c1ae25d 100644
--- a/tests/bookmark/BookmarkTest.php
+++ b/tests/bookmark/BookmarkTest.php
@@ -2,8 +2,8 @@
2 2
3namespace Shaarli\Bookmark; 3namespace Shaarli\Bookmark;
4 4
5use PHPUnit\Framework\TestCase;
6use Shaarli\Bookmark\Exception\InvalidBookmarkException; 5use Shaarli\Bookmark\Exception\InvalidBookmarkException;
6use Shaarli\TestCase;
7 7
8/** 8/**
9 * Class BookmarkTest 9 * Class BookmarkTest
@@ -124,8 +124,8 @@ class BookmarkTest extends TestCase
124 $this->assertEquals(1, $bookmark->getId()); 124 $this->assertEquals(1, $bookmark->getId());
125 $this->assertEquals('abc', $bookmark->getShortUrl()); 125 $this->assertEquals('abc', $bookmark->getShortUrl());
126 $this->assertEquals($date, $bookmark->getCreated()); 126 $this->assertEquals($date, $bookmark->getCreated());
127 $this->assertEquals('?abc', $bookmark->getUrl()); 127 $this->assertEquals('/shaare/abc', $bookmark->getUrl());
128 $this->assertEquals('?abc', $bookmark->getTitle()); 128 $this->assertEquals('/shaare/abc', $bookmark->getTitle());
129 $this->assertEquals('', $bookmark->getDescription()); 129 $this->assertEquals('', $bookmark->getDescription());
130 $this->assertEquals([], $bookmark->getTags()); 130 $this->assertEquals([], $bookmark->getTags());
131 $this->assertEquals('', $bookmark->getTagsString()); 131 $this->assertEquals('', $bookmark->getTagsString());
@@ -150,26 +150,7 @@ class BookmarkTest extends TestCase
150 $exception = $e; 150 $exception = $e;
151 } 151 }
152 $this->assertNotNull($exception); 152 $this->assertNotNull($exception);
153 $this->assertContains('- ID: '. PHP_EOL, $exception->getMessage()); 153 $this->assertContainsPolyfill('- ID: '. PHP_EOL, $exception->getMessage());
154 }
155
156 /**
157 * Test validate() with a a bookmark with a non integer ID.
158 */
159 public function testValidateNotValidStringId()
160 {
161 $bookmark = new Bookmark();
162 $bookmark->setId('str');
163 $bookmark->setShortUrl('abc');
164 $bookmark->setCreated(\DateTime::createFromFormat('Ymd_His', '20190514_200102'));
165 $exception = null;
166 try {
167 $bookmark->validate();
168 } catch (InvalidBookmarkException $e) {
169 $exception = $e;
170 }
171 $this->assertNotNull($exception);
172 $this->assertContains('- ID: str'. PHP_EOL, $exception->getMessage());
173 } 154 }
174 155
175 /** 156 /**
@@ -188,7 +169,7 @@ class BookmarkTest extends TestCase
188 $exception = $e; 169 $exception = $e;
189 } 170 }
190 $this->assertNotNull($exception); 171 $this->assertNotNull($exception);
191 $this->assertContains('- ShortUrl: '. PHP_EOL, $exception->getMessage()); 172 $this->assertContainsPolyfill('- ShortUrl: '. PHP_EOL, $exception->getMessage());
192 } 173 }
193 174
194 /** 175 /**
@@ -207,26 +188,7 @@ class BookmarkTest extends TestCase
207 $exception = $e; 188 $exception = $e;
208 } 189 }
209 $this->assertNotNull($exception); 190 $this->assertNotNull($exception);
210 $this->assertContains('- Created: '. PHP_EOL, $exception->getMessage()); 191 $this->assertContainsPolyfill('- Created: '. PHP_EOL, $exception->getMessage());
211 }
212
213 /**
214 * Test validate() with a a bookmark with a bad created datetime.
215 */
216 public function testValidateNotValidBadCreated()
217 {
218 $bookmark = new Bookmark();
219 $bookmark->setId(1);
220 $bookmark->setShortUrl('abc');
221 $bookmark->setCreated('hi!');
222 $exception = null;
223 try {
224 $bookmark->validate();
225 } catch (InvalidBookmarkException $e) {
226 $exception = $e;
227 }
228 $this->assertNotNull($exception);
229 $this->assertContains('- Created: Not a DateTime object'. PHP_EOL, $exception->getMessage());
230 } 192 }
231 193
232 /** 194 /**
@@ -385,4 +347,48 @@ class BookmarkTest extends TestCase
385 $bookmark->deleteTag('nope'); 347 $bookmark->deleteTag('nope');
386 $this->assertEquals(['tag1', 'tag2', 'chair'], $bookmark->getTags()); 348 $this->assertEquals(['tag1', 'tag2', 'chair'], $bookmark->getTags());
387 } 349 }
350
351 /**
352 * Test shouldUpdateThumbnail() with bookmarks needing an update.
353 */
354 public function testShouldUpdateThumbnail(): void
355 {
356 $bookmark = (new Bookmark())->setUrl('http://domain.tld/with-image');
357
358 static::assertTrue($bookmark->shouldUpdateThumbnail());
359
360 $bookmark = (new Bookmark())
361 ->setUrl('http://domain.tld/with-image')
362 ->setThumbnail('unknown file')
363 ;
364
365 static::assertTrue($bookmark->shouldUpdateThumbnail());
366 }
367
368 /**
369 * Test shouldUpdateThumbnail() with bookmarks that should not update.
370 */
371 public function testShouldNotUpdateThumbnail(): void
372 {
373 $bookmark = (new Bookmark());
374
375 static::assertFalse($bookmark->shouldUpdateThumbnail());
376
377 $bookmark = (new Bookmark())
378 ->setUrl('ftp://domain.tld/other-protocol', ['ftp'])
379 ;
380
381 static::assertFalse($bookmark->shouldUpdateThumbnail());
382
383 $bookmark = (new Bookmark())
384 ->setUrl('http://domain.tld/with-image')
385 ->setThumbnail(__FILE__)
386 ;
387
388 static::assertFalse($bookmark->shouldUpdateThumbnail());
389
390 $bookmark = (new Bookmark())->setUrl('/shaare/abcdef');
391
392 static::assertFalse($bookmark->shouldUpdateThumbnail());
393 }
388} 394}
diff --git a/tests/bookmark/LinkUtilsTest.php b/tests/bookmark/LinkUtilsTest.php
index 591976f2..3321242f 100644
--- a/tests/bookmark/LinkUtilsTest.php
+++ b/tests/bookmark/LinkUtilsTest.php
@@ -2,9 +2,7 @@
2 2
3namespace Shaarli\Bookmark; 3namespace Shaarli\Bookmark;
4 4
5use PHPUnit\Framework\TestCase; 5use Shaarli\TestCase;
6use ReferenceLinkDB;
7use Shaarli\Config\ConfigManager;
8 6
9require_once 'tests/utils/CurlUtils.php'; 7require_once 'tests/utils/CurlUtils.php';
10 8
@@ -45,6 +43,19 @@ class LinkUtilsTest extends TestCase
45 } 43 }
46 44
47 /** 45 /**
46 * Test headers_extract_charset() when the charset is found with odd quotes.
47 */
48 public function testHeadersExtractExistentCharsetWithQuotes()
49 {
50 $charset = 'x-MacCroatian';
51 $headers = 'text/html; charset="' . $charset . '"otherstuff="test"';
52 $this->assertEquals(strtolower($charset), header_extract_charset($headers));
53
54 $headers = 'text/html; charset=\'' . $charset . '\'otherstuff="test"';
55 $this->assertEquals(strtolower($charset), header_extract_charset($headers));
56 }
57
58 /**
48 * Test headers_extract_charset() when the charset is not found. 59 * Test headers_extract_charset() when the charset is not found.
49 */ 60 */
50 public function testHeadersExtractNonExistentCharset() 61 public function testHeadersExtractNonExistentCharset()
@@ -83,8 +94,78 @@ class LinkUtilsTest extends TestCase
83 public function testHtmlExtractExistentNameTag() 94 public function testHtmlExtractExistentNameTag()
84 { 95 {
85 $description = 'Bob and Alice share cookies.'; 96 $description = 'Bob and Alice share cookies.';
97
98 // Simple one line
86 $html = '<html><meta>stuff2</meta><meta name="description" content="' . $description . '"/></html>'; 99 $html = '<html><meta>stuff2</meta><meta name="description" content="' . $description . '"/></html>';
87 $this->assertEquals($description, html_extract_tag('description', $html)); 100 $this->assertEquals($description, html_extract_tag('description', $html));
101
102 // Simple OpenGraph
103 $html = '<meta property="og:description" content="' . $description . '">';
104 $this->assertEquals($description, html_extract_tag('description', $html));
105
106 // Simple reversed OpenGraph
107 $html = '<meta content="' . $description . '" property="og:description">';
108 $this->assertEquals($description, html_extract_tag('description', $html));
109
110 // ItemProp OpenGraph
111 $html = '<meta itemprop="og:description" content="' . $description . '">';
112 $this->assertEquals($description, html_extract_tag('description', $html));
113
114 // OpenGraph without quotes
115 $html = '<meta property=og:description content="' . $description . '">';
116 $this->assertEquals($description, html_extract_tag('description', $html));
117
118 // OpenGraph reversed without quotes
119 $html = '<meta content="' . $description . '" property=og:description>';
120 $this->assertEquals($description, html_extract_tag('description', $html));
121
122 // OpenGraph with noise
123 $html = '<meta tag1="content1" property="og:description" tag2="content2" content="' .
124 $description . '" tag3="content3">';
125 $this->assertEquals($description, html_extract_tag('description', $html));
126
127 // OpenGraph reversed with noise
128 $html = '<meta tag1="content1" content="' . $description . '" ' .
129 'tag3="content3" tag2="content2" property="og:description">';
130 $this->assertEquals($description, html_extract_tag('description', $html));
131
132 // OpenGraph multiple properties start
133 $html = '<meta property="unrelated og:description" content="' . $description . '">';
134 $this->assertEquals($description, html_extract_tag('description', $html));
135
136 // OpenGraph multiple properties end
137 $html = '<meta property="og:description unrelated" content="' . $description . '">';
138 $this->assertEquals($description, html_extract_tag('description', $html));
139
140 // OpenGraph multiple properties both end
141 $html = '<meta property="og:unrelated1 og:description og:unrelated2" content="' . $description . '">';
142 $this->assertEquals($description, html_extract_tag('description', $html));
143
144 // OpenGraph multiple properties both end with noise
145 $html = '<meta tag1="content1" property="og:unrelated1 og:description og:unrelated2" '.
146 'tag2="content2" content="' . $description . '" tag3="content3">';
147 $this->assertEquals($description, html_extract_tag('description', $html));
148
149 // OpenGraph reversed multiple properties start
150 $html = '<meta content="' . $description . '" property="unrelated og:description">';
151 $this->assertEquals($description, html_extract_tag('description', $html));
152
153 // OpenGraph reversed multiple properties end
154 $html = '<meta content="' . $description . '" property="og:description unrelated">';
155 $this->assertEquals($description, html_extract_tag('description', $html));
156
157 // OpenGraph reversed multiple properties both end
158 $html = '<meta content="' . $description . '" property="og:unrelated1 og:description og:unrelated2">';
159 $this->assertEquals($description, html_extract_tag('description', $html));
160
161 // OpenGraph reversed multiple properties both end with noise
162 $html = '<meta tag1="content1" content="' . $description . '" tag2="content2" '.
163 'property="og:unrelated1 og:description og:unrelated2" tag3="content3">';
164 $this->assertEquals($description, html_extract_tag('description', $html));
165
166 // Suggestion from #1375
167 $html = '<meta property="og:description" name="description" content="' . $description . '">';
168 $this->assertEquals($description, html_extract_tag('description', $html));
88 } 169 }
89 170
90 /** 171 /**
@@ -94,6 +175,25 @@ class LinkUtilsTest extends TestCase
94 { 175 {
95 $html = '<html><meta>stuff2</meta><meta name="image" content="img"/></html>'; 176 $html = '<html><meta>stuff2</meta><meta name="image" content="img"/></html>';
96 $this->assertFalse(html_extract_tag('description', $html)); 177 $this->assertFalse(html_extract_tag('description', $html));
178
179 // Partial meta tag
180 $html = '<meta content="Brief description">';
181 $this->assertFalse(html_extract_tag('description', $html));
182
183 $html = '<meta property="og:description">';
184 $this->assertFalse(html_extract_tag('description', $html));
185
186 $html = '<meta tag1="content1" property="og:description">';
187 $this->assertFalse(html_extract_tag('description', $html));
188
189 $html = '<meta property="og:description" tag1="content1">';
190 $this->assertFalse(html_extract_tag('description', $html));
191
192 $html = '<meta tag1="content1" content="Brief description">';
193 $this->assertFalse(html_extract_tag('description', $html));
194
195 $html = '<meta content="Brief description" tag1="content1">';
196 $this->assertFalse(html_extract_tag('description', $html));
97 } 197 }
98 198
99 /** 199 /**
@@ -116,60 +216,91 @@ class LinkUtilsTest extends TestCase
116 } 216 }
117 217
118 /** 218 /**
219 * Test the header callback with valid value
220 */
221 public function testCurlHeaderCallbackOk(): void
222 {
223 $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_ok');
224 $data = [
225 'HTTP/1.1 200 OK',
226 'Server: GitHub.com',
227 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
228 'Content-Type: text/html; charset=utf-8',
229 'Status: 200 OK',
230 ];
231
232 foreach ($data as $chunk) {
233 static::assertIsInt($callback(null, $chunk));
234 }
235
236 static::assertSame('utf-8', $charset);
237 }
238
239 /**
119 * Test the download callback with valid value 240 * Test the download callback with valid value
120 */ 241 */
121 public function testCurlDownloadCallbackOk() 242 public function testCurlDownloadCallbackOk(): void
122 { 243 {
244 $charset = 'utf-8';
123 $callback = get_curl_download_callback( 245 $callback = get_curl_download_callback(
124 $charset, 246 $charset,
125 $title, 247 $title,
126 $desc, 248 $desc,
127 $keywords, 249 $keywords,
128 false, 250 false
129 'ut_curl_getinfo_ok'
130 ); 251 );
252
131 $data = [ 253 $data = [
132 'HTTP/1.1 200 OK', 254 'th=device-width">'
133 'Server: GitHub.com',
134 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
135 'Content-Type: text/html; charset=utf-8',
136 'Status: 200 OK',
137 'end' => 'th=device-width">'
138 . '<title>Refactoring · GitHub</title>' 255 . '<title>Refactoring · GitHub</title>'
139 . '<link rel="search" type="application/opensea', 256 . '<link rel="search" type="application/opensea',
140 '<title>ignored</title>' 257 '<title>ignored</title>'
141 . '<meta name="description" content="desc" />' 258 . '<meta name="description" content="desc" />'
142 . '<meta name="keywords" content="key1,key2" />', 259 . '<meta name="keywords" content="key1,key2" />',
143 ]; 260 ];
144 foreach ($data as $key => $line) { 261
145 $ignore = null; 262 foreach ($data as $chunk) {
146 $expected = $key !== 'end' ? strlen($line) : false; 263 static::assertSame(strlen($chunk), $callback(null, $chunk));
147 $this->assertEquals($expected, $callback($ignore, $line));
148 if ($expected === false) {
149 break;
150 }
151 } 264 }
152 $this->assertEquals('utf-8', $charset); 265
153 $this->assertEquals('Refactoring · GitHub', $title); 266 static::assertSame('utf-8', $charset);
154 $this->assertEmpty($desc); 267 static::assertSame('Refactoring · GitHub', $title);
155 $this->assertEmpty($keywords); 268 static::assertEmpty($desc);
269 static::assertEmpty($keywords);
270 }
271
272 /**
273 * Test the header callback with valid value
274 */
275 public function testCurlHeaderCallbackNoCharset(): void
276 {
277 $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_no_charset');
278 $data = [
279 'HTTP/1.1 200 OK',
280 ];
281
282 foreach ($data as $chunk) {
283 static::assertSame(strlen($chunk), $callback(null, $chunk));
284 }
285
286 static::assertFalse($charset);
156 } 287 }
157 288
158 /** 289 /**
159 * Test the download callback with valid values and no charset 290 * Test the download callback with valid values and no charset
160 */ 291 */
161 public function testCurlDownloadCallbackOkNoCharset() 292 public function testCurlDownloadCallbackOkNoCharset(): void
162 { 293 {
294 $charset = null;
163 $callback = get_curl_download_callback( 295 $callback = get_curl_download_callback(
164 $charset, 296 $charset,
165 $title, 297 $title,
166 $desc, 298 $desc,
167 $keywords, 299 $keywords,
168 false, 300 false
169 'ut_curl_getinfo_no_charset'
170 ); 301 );
302
171 $data = [ 303 $data = [
172 'HTTP/1.1 200 OK',
173 'end' => 'th=device-width">' 304 'end' => 'th=device-width">'
174 . '<title>Refactoring · GitHub</title>' 305 . '<title>Refactoring · GitHub</title>'
175 . '<link rel="search" type="application/opensea', 306 . '<link rel="search" type="application/opensea',
@@ -177,10 +308,11 @@ class LinkUtilsTest extends TestCase
177 . '<meta name="description" content="desc" />' 308 . '<meta name="description" content="desc" />'
178 . '<meta name="keywords" content="key1,key2" />', 309 . '<meta name="keywords" content="key1,key2" />',
179 ]; 310 ];
180 foreach ($data as $key => $line) { 311
181 $ignore = null; 312 foreach ($data as $chunk) {
182 $this->assertEquals(strlen($line), $callback($ignore, $line)); 313 static::assertSame(strlen($chunk), $callback(null, $chunk));
183 } 314 }
315
184 $this->assertEmpty($charset); 316 $this->assertEmpty($charset);
185 $this->assertEquals('Refactoring · GitHub', $title); 317 $this->assertEquals('Refactoring · GitHub', $title);
186 $this->assertEmpty($desc); 318 $this->assertEmpty($desc);
@@ -190,18 +322,18 @@ class LinkUtilsTest extends TestCase
190 /** 322 /**
191 * Test the download callback with valid values and no charset 323 * Test the download callback with valid values and no charset
192 */ 324 */
193 public function testCurlDownloadCallbackOkHtmlCharset() 325 public function testCurlDownloadCallbackOkHtmlCharset(): void
194 { 326 {
327 $charset = null;
195 $callback = get_curl_download_callback( 328 $callback = get_curl_download_callback(
196 $charset, 329 $charset,
197 $title, 330 $title,
198 $desc, 331 $desc,
199 $keywords, 332 $keywords,
200 false, 333 false
201 'ut_curl_getinfo_no_charset'
202 ); 334 );
335
203 $data = [ 336 $data = [
204 'HTTP/1.1 200 OK',
205 '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />', 337 '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',
206 'end' => 'th=device-width">' 338 'end' => 'th=device-width">'
207 . '<title>Refactoring · GitHub</title>' 339 . '<title>Refactoring · GitHub</title>'
@@ -210,14 +342,10 @@ class LinkUtilsTest extends TestCase
210 . '<meta name="description" content="desc" />' 342 . '<meta name="description" content="desc" />'
211 . '<meta name="keywords" content="key1,key2" />', 343 . '<meta name="keywords" content="key1,key2" />',
212 ]; 344 ];
213 foreach ($data as $key => $line) { 345 foreach ($data as $chunk) {
214 $ignore = null; 346 static::assertSame(strlen($chunk), $callback(null, $chunk));
215 $expected = $key !== 'end' ? strlen($line) : false;
216 $this->assertEquals($expected, $callback($ignore, $line));
217 if ($expected === false) {
218 break;
219 }
220 } 347 }
348
221 $this->assertEquals('utf-8', $charset); 349 $this->assertEquals('utf-8', $charset);
222 $this->assertEquals('Refactoring · GitHub', $title); 350 $this->assertEquals('Refactoring · GitHub', $title);
223 $this->assertEmpty($desc); 351 $this->assertEmpty($desc);
@@ -227,25 +355,26 @@ class LinkUtilsTest extends TestCase
227 /** 355 /**
228 * Test the download callback with valid values and no title 356 * Test the download callback with valid values and no title
229 */ 357 */
230 public function testCurlDownloadCallbackOkNoTitle() 358 public function testCurlDownloadCallbackOkNoTitle(): void
231 { 359 {
360 $charset = 'utf-8';
232 $callback = get_curl_download_callback( 361 $callback = get_curl_download_callback(
233 $charset, 362 $charset,
234 $title, 363 $title,
235 $desc, 364 $desc,
236 $keywords, 365 $keywords,
237 false, 366 false
238 'ut_curl_getinfo_ok'
239 ); 367 );
368
240 $data = [ 369 $data = [
241 'HTTP/1.1 200 OK',
242 'end' => 'th=device-width">Refactoring · GitHub<link rel="search" type="application/opensea', 370 'end' => 'th=device-width">Refactoring · GitHub<link rel="search" type="application/opensea',
243 'ignored', 371 'ignored',
244 ]; 372 ];
245 foreach ($data as $key => $line) { 373
246 $ignore = null; 374 foreach ($data as $chunk) {
247 $this->assertEquals(strlen($line), $callback($ignore, $line)); 375 static::assertSame(strlen($chunk), $callback(null, $chunk));
248 } 376 }
377
249 $this->assertEquals('utf-8', $charset); 378 $this->assertEquals('utf-8', $charset);
250 $this->assertEmpty($title); 379 $this->assertEmpty($title);
251 $this->assertEmpty($desc); 380 $this->assertEmpty($desc);
@@ -253,81 +382,55 @@ class LinkUtilsTest extends TestCase
253 } 382 }
254 383
255 /** 384 /**
256 * Test the download callback with an invalid content type. 385 * Test the header callback with an invalid content type.
257 */ 386 */
258 public function testCurlDownloadCallbackInvalidContentType() 387 public function testCurlHeaderCallbackInvalidContentType(): void
259 { 388 {
260 $callback = get_curl_download_callback( 389 $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_ct_ko');
261 $charset, 390 $data = [
262 $title, 391 'HTTP/1.1 200 OK',
263 $desc, 392 ];
264 $keywords, 393
265 false, 394 static::assertFalse($callback(null, $data[0]));
266 'ut_curl_getinfo_ct_ko' 395 static::assertNull($charset);
267 );
268 $ignore = null;
269 $this->assertFalse($callback($ignore, ''));
270 $this->assertEmpty($charset);
271 $this->assertEmpty($title);
272 } 396 }
273 397
274 /** 398 /**
275 * Test the download callback with an invalid response code. 399 * Test the header callback with an invalid response code.
276 */ 400 */
277 public function testCurlDownloadCallbackInvalidResponseCode() 401 public function testCurlHeaderCallbackInvalidResponseCode(): void
278 { 402 {
279 $callback = $callback = get_curl_download_callback( 403 $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_rc_ko');
280 $charset, 404
281 $title, 405 static::assertFalse($callback(null, ''));
282 $desc, 406 static::assertNull($charset);
283 $keywords,
284 false,
285 'ut_curl_getinfo_rc_ko'
286 );
287 $ignore = null;
288 $this->assertFalse($callback($ignore, ''));
289 $this->assertEmpty($charset);
290 $this->assertEmpty($title);
291 } 407 }
292 408
293 /** 409 /**
294 * Test the download callback with an invalid content type and response code. 410 * Test the header callback with an invalid content type and response code.
295 */ 411 */
296 public function testCurlDownloadCallbackInvalidContentTypeAndResponseCode() 412 public function testCurlHeaderCallbackInvalidContentTypeAndResponseCode(): void
297 { 413 {
298 $callback = $callback = get_curl_download_callback( 414 $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_rs_ct_ko');
299 $charset, 415
300 $title, 416 static::assertFalse($callback(null, ''));
301 $desc, 417 static::assertNull($charset);
302 $keywords,
303 false,
304 'ut_curl_getinfo_rs_ct_ko'
305 );
306 $ignore = null;
307 $this->assertFalse($callback($ignore, ''));
308 $this->assertEmpty($charset);
309 $this->assertEmpty($title);
310 } 418 }
311 419
312 /** 420 /**
313 * Test the download callback with valid value, and retrieve_description option enabled. 421 * Test the download callback with valid value, and retrieve_description option enabled.
314 */ 422 */
315 public function testCurlDownloadCallbackOkWithDesc() 423 public function testCurlDownloadCallbackOkWithDesc(): void
316 { 424 {
425 $charset = 'utf-8';
317 $callback = get_curl_download_callback( 426 $callback = get_curl_download_callback(
318 $charset, 427 $charset,
319 $title, 428 $title,
320 $desc, 429 $desc,
321 $keywords, 430 $keywords,
322 true, 431 true
323 'ut_curl_getinfo_ok'
324 ); 432 );
325 $data = [ 433 $data = [
326 'HTTP/1.1 200 OK',
327 'Server: GitHub.com',
328 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
329 'Content-Type: text/html; charset=utf-8',
330 'Status: 200 OK',
331 'th=device-width">' 434 'th=device-width">'
332 . '<title>Refactoring · GitHub</title>' 435 . '<title>Refactoring · GitHub</title>'
333 . '<link rel="search" type="application/opensea', 436 . '<link rel="search" type="application/opensea',
@@ -335,14 +438,11 @@ class LinkUtilsTest extends TestCase
335 . '<meta name="description" content="link desc" />' 438 . '<meta name="description" content="link desc" />'
336 . '<meta name="keywords" content="key1,key2" />', 439 . '<meta name="keywords" content="key1,key2" />',
337 ]; 440 ];
338 foreach ($data as $key => $line) { 441
339 $ignore = null; 442 foreach ($data as $chunk) {
340 $expected = $key !== 'end' ? strlen($line) : false; 443 static::assertSame(strlen($chunk), $callback(null, $chunk));
341 $this->assertEquals($expected, $callback($ignore, $line));
342 if ($expected === false) {
343 break;
344 }
345 } 444 }
445
346 $this->assertEquals('utf-8', $charset); 446 $this->assertEquals('utf-8', $charset);
347 $this->assertEquals('Refactoring · GitHub', $title); 447 $this->assertEquals('Refactoring · GitHub', $title);
348 $this->assertEquals('link desc', $desc); 448 $this->assertEquals('link desc', $desc);
@@ -353,8 +453,9 @@ class LinkUtilsTest extends TestCase
353 * Test the download callback with valid value, and retrieve_description option enabled, 453 * Test the download callback with valid value, and retrieve_description option enabled,
354 * but no desc or keyword defined in the page. 454 * but no desc or keyword defined in the page.
355 */ 455 */
356 public function testCurlDownloadCallbackOkWithDescNotFound() 456 public function testCurlDownloadCallbackOkWithDescNotFound(): void
357 { 457 {
458 $charset = 'utf-8';
358 $callback = get_curl_download_callback( 459 $callback = get_curl_download_callback(
359 $charset, 460 $charset,
360 $title, 461 $title,
@@ -364,24 +465,16 @@ class LinkUtilsTest extends TestCase
364 'ut_curl_getinfo_ok' 465 'ut_curl_getinfo_ok'
365 ); 466 );
366 $data = [ 467 $data = [
367 'HTTP/1.1 200 OK',
368 'Server: GitHub.com',
369 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
370 'Content-Type: text/html; charset=utf-8',
371 'Status: 200 OK',
372 'th=device-width">' 468 'th=device-width">'
373 . '<title>Refactoring · GitHub</title>' 469 . '<title>Refactoring · GitHub</title>'
374 . '<link rel="search" type="application/opensea', 470 . '<link rel="search" type="application/opensea',
375 'end' => '<title>ignored</title>', 471 'end' => '<title>ignored</title>',
376 ]; 472 ];
377 foreach ($data as $key => $line) { 473
378 $ignore = null; 474 foreach ($data as $chunk) {
379 $expected = $key !== 'end' ? strlen($line) : false; 475 static::assertSame(strlen($chunk), $callback(null, $chunk));
380 $this->assertEquals($expected, $callback($ignore, $line));
381 if ($expected === false) {
382 break;
383 }
384 } 476 }
477
385 $this->assertEquals('utf-8', $charset); 478 $this->assertEquals('utf-8', $charset);
386 $this->assertEquals('Refactoring · GitHub', $title); 479 $this->assertEquals('Refactoring · GitHub', $title);
387 $this->assertEmpty($desc); 480 $this->assertEmpty($desc);
@@ -439,13 +532,13 @@ class LinkUtilsTest extends TestCase
439 カタカナ #カタカナ」カタカナ\n'; 532 カタカナ #カタカナ」カタカナ\n';
440 $autolinkedDescription = hashtag_autolink($rawDescription, $index); 533 $autolinkedDescription = hashtag_autolink($rawDescription, $index);
441 534
442 $this->assertContains($this->getHashtagLink('hashtag', $index), $autolinkedDescription); 535 $this->assertContainsPolyfill($this->getHashtagLink('hashtag', $index), $autolinkedDescription);
443 $this->assertNotContains(' #hashtag', $autolinkedDescription); 536 $this->assertNotContainsPolyfill(' #hashtag', $autolinkedDescription);
444 $this->assertNotContains('>#nothashtag', $autolinkedDescription); 537 $this->assertNotContainsPolyfill('>#nothashtag', $autolinkedDescription);
445 $this->assertContains($this->getHashtagLink('ашок', $index), $autolinkedDescription); 538 $this->assertContainsPolyfill($this->getHashtagLink('ашок', $index), $autolinkedDescription);
446 $this->assertContains($this->getHashtagLink('カタカナ', $index), $autolinkedDescription); 539 $this->assertContainsPolyfill($this->getHashtagLink('カタカナ', $index), $autolinkedDescription);
447 $this->assertContains($this->getHashtagLink('hashtag_hashtag', $index), $autolinkedDescription); 540 $this->assertContainsPolyfill($this->getHashtagLink('hashtag_hashtag', $index), $autolinkedDescription);
448 $this->assertNotContains($this->getHashtagLink('hashtag-nothashtag', $index), $autolinkedDescription); 541 $this->assertNotContainsPolyfill($this->getHashtagLink('hashtag-nothashtag', $index), $autolinkedDescription);
449 } 542 }
450 543
451 /** 544 /**
@@ -456,9 +549,9 @@ class LinkUtilsTest extends TestCase
456 $rawDescription = 'blabla #hashtag x#nothashtag'; 549 $rawDescription = 'blabla #hashtag x#nothashtag';
457 $autolinkedDescription = hashtag_autolink($rawDescription); 550 $autolinkedDescription = hashtag_autolink($rawDescription);
458 551
459 $this->assertContains($this->getHashtagLink('hashtag'), $autolinkedDescription); 552 $this->assertContainsPolyfill($this->getHashtagLink('hashtag'), $autolinkedDescription);
460 $this->assertNotContains(' #hashtag', $autolinkedDescription); 553 $this->assertNotContainsPolyfill(' #hashtag', $autolinkedDescription);
461 $this->assertNotContains('>#nothashtag', $autolinkedDescription); 554 $this->assertNotContainsPolyfill('>#nothashtag', $autolinkedDescription);
462 } 555 }
463 556
464 /** 557 /**
@@ -491,7 +584,7 @@ class LinkUtilsTest extends TestCase
491 */ 584 */
492 private function getHashtagLink($hashtag, $index = '') 585 private function getHashtagLink($hashtag, $index = '')
493 { 586 {
494 $hashtagLink = '<a href="' . $index . '?addtag=$1" title="Hashtag $1">#$1</a>'; 587 $hashtagLink = '<a href="' . $index . './add-tag/$1" title="Hashtag $1">#$1</a>';
495 return str_replace('$1', $hashtag, $hashtagLink); 588 return str_replace('$1', $hashtag, $hashtagLink);
496 } 589 }
497} 590}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 0afbcba6..3508a7b1 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -18,7 +18,19 @@ require_once 'application/bookmark/LinkUtils.php';
18require_once 'application/Utils.php'; 18require_once 'application/Utils.php';
19require_once 'application/http/UrlUtils.php'; 19require_once 'application/http/UrlUtils.php';
20require_once 'application/http/HttpUtils.php'; 20require_once 'application/http/HttpUtils.php';
21require_once 'application/feed/Cache.php'; 21require_once 'tests/TestCase.php';
22require_once 'tests/utils/ReferenceLinkDB.php'; 22require_once 'tests/container/ShaarliTestContainer.php';
23require_once 'tests/utils/ReferenceHistory.php'; 23require_once 'tests/front/controller/visitor/FrontControllerMockHelper.php';
24require_once 'tests/front/controller/admin/FrontAdminControllerMockHelper.php';
25require_once 'tests/updater/DummyUpdater.php';
24require_once 'tests/utils/FakeBookmarkService.php'; 26require_once 'tests/utils/FakeBookmarkService.php';
27require_once 'tests/utils/FakeConfigManager.php';
28require_once 'tests/utils/ReferenceHistory.php';
29require_once 'tests/utils/ReferenceLinkDB.php';
30require_once 'tests/utils/ReferenceSessionIdHashes.php';
31
32\ReferenceSessionIdHashes::genAllHashes();
33
34if (!defined('SHAARLI_MUTEX_FILE')) {
35 define('SHAARLI_MUTEX_FILE', __FILE__);
36}
diff --git a/tests/config/ConfigJsonTest.php b/tests/config/ConfigJsonTest.php
index 33160eb0..c0ba5b8f 100644
--- a/tests/config/ConfigJsonTest.php
+++ b/tests/config/ConfigJsonTest.php
@@ -4,14 +4,14 @@ namespace Shaarli\Config;
4/** 4/**
5 * Class ConfigJsonTest 5 * Class ConfigJsonTest
6 */ 6 */
7class ConfigJsonTest extends \PHPUnit\Framework\TestCase 7class ConfigJsonTest extends \Shaarli\TestCase
8{ 8{
9 /** 9 /**
10 * @var ConfigJson 10 * @var ConfigJson
11 */ 11 */
12 protected $configIO; 12 protected $configIO;
13 13
14 public function setUp() 14 protected function setUp(): void
15 { 15 {
16 $this->configIO = new ConfigJson(); 16 $this->configIO = new ConfigJson();
17 } 17 }
@@ -38,12 +38,12 @@ class ConfigJsonTest extends \PHPUnit\Framework\TestCase
38 38
39 /** 39 /**
40 * Read a non existent config file -> empty array. 40 * Read a non existent config file -> empty array.
41 *
42 * @expectedException \Exception
43 * @expectedExceptionMessageRegExp /An error occurred while parsing JSON configuration file \([\w\/\.]+\): error code #4/
44 */ 41 */
45 public function testReadInvalidJson() 42 public function testReadInvalidJson()
46 { 43 {
44 $this->expectException(\Exception::class);
45 $this->expectExceptionMessageRegExp('/An error occurred while parsing JSON configuration file \\([\\w\\/\\.]+\\): error code #4/');
46
47 $this->configIO->read('tests/utils/config/configInvalid.json.php'); 47 $this->configIO->read('tests/utils/config/configInvalid.json.php');
48 } 48 }
49 49
@@ -110,22 +110,11 @@ class ConfigJsonTest extends \PHPUnit\Framework\TestCase
110 110
111 /** 111 /**
112 * Write to invalid path. 112 * Write to invalid path.
113 *
114 * @expectedException \Shaarli\Exceptions\IOException
115 */
116 public function testWriteInvalidArray()
117 {
118 $conf = array('conf' => 'value');
119 @$this->configIO->write(array(), $conf);
120 }
121
122 /**
123 * Write to invalid path.
124 *
125 * @expectedException \Shaarli\Exceptions\IOException
126 */ 113 */
127 public function testWriteInvalidBlank() 114 public function testWriteInvalidBlank()
128 { 115 {
116 $this->expectException(\Shaarli\Exceptions\IOException::class);
117
129 $conf = array('conf' => 'value'); 118 $conf = array('conf' => 'value');
130 @$this->configIO->write('', $conf); 119 @$this->configIO->write('', $conf);
131 } 120 }
diff --git a/tests/config/ConfigManagerTest.php b/tests/config/ConfigManagerTest.php
index 33830bc9..65d8ba2c 100644
--- a/tests/config/ConfigManagerTest.php
+++ b/tests/config/ConfigManagerTest.php
@@ -7,14 +7,14 @@ namespace Shaarli\Config;
7 * Note: it only test the manager with ConfigJson, 7 * Note: it only test the manager with ConfigJson,
8 * ConfigPhp is only a workaround to handle the transition to JSON type. 8 * ConfigPhp is only a workaround to handle the transition to JSON type.
9 */ 9 */
10class ConfigManagerTest extends \PHPUnit\Framework\TestCase 10class ConfigManagerTest extends \Shaarli\TestCase
11{ 11{
12 /** 12 /**
13 * @var ConfigManager 13 * @var ConfigManager
14 */ 14 */
15 protected $conf; 15 protected $conf;
16 16
17 public function setUp() 17 protected function setUp(): void
18 { 18 {
19 $this->conf = new ConfigManager('tests/utils/config/configJson'); 19 $this->conf = new ConfigManager('tests/utils/config/configJson');
20 } 20 }
@@ -95,44 +95,44 @@ class ConfigManagerTest extends \PHPUnit\Framework\TestCase
95 95
96 /** 96 /**
97 * Set with an empty key. 97 * Set with an empty key.
98 *
99 * @expectedException \Exception
100 * @expectedExceptionMessageRegExp #^Invalid setting key parameter. String expected, got.*#
101 */ 98 */
102 public function testSetEmptyKey() 99 public function testSetEmptyKey()
103 { 100 {
101 $this->expectException(\Exception::class);
102 $this->expectExceptionMessageRegExp('#^Invalid setting key parameter. String expected, got.*#');
103
104 $this->conf->set('', 'stuff'); 104 $this->conf->set('', 'stuff');
105 } 105 }
106 106
107 /** 107 /**
108 * Set with an array key. 108 * Set with an array key.
109 *
110 * @expectedException \Exception
111 * @expectedExceptionMessageRegExp #^Invalid setting key parameter. String expected, got.*#
112 */ 109 */
113 public function testSetArrayKey() 110 public function testSetArrayKey()
114 { 111 {
112 $this->expectException(\Exception::class);
113 $this->expectExceptionMessageRegExp('#^Invalid setting key parameter. String expected, got.*#');
114
115 $this->conf->set(array('foo' => 'bar'), 'stuff'); 115 $this->conf->set(array('foo' => 'bar'), 'stuff');
116 } 116 }
117 117
118 /** 118 /**
119 * Remove with an empty key. 119 * Remove with an empty key.
120 *
121 * @expectedException \Exception
122 * @expectedExceptionMessageRegExp #^Invalid setting key parameter. String expected, got.*#
123 */ 120 */
124 public function testRmoveEmptyKey() 121 public function testRmoveEmptyKey()
125 { 122 {
123 $this->expectException(\Exception::class);
124 $this->expectExceptionMessageRegExp('#^Invalid setting key parameter. String expected, got.*#');
125
126 $this->conf->remove(''); 126 $this->conf->remove('');
127 } 127 }
128 128
129 /** 129 /**
130 * Try to write the config without mandatory parameter (e.g. 'login'). 130 * Try to write the config without mandatory parameter (e.g. 'login').
131 *
132 * @expectedException Shaarli\Config\Exception\MissingFieldConfigException
133 */ 131 */
134 public function testWriteMissingParameter() 132 public function testWriteMissingParameter()
135 { 133 {
134 $this->expectException(\Shaarli\Config\Exception\MissingFieldConfigException::class);
135
136 $this->conf->setConfigFile('tests/utils/config/configTmp'); 136 $this->conf->setConfigFile('tests/utils/config/configTmp');
137 $this->assertFalse(file_exists($this->conf->getConfigFileExt())); 137 $this->assertFalse(file_exists($this->conf->getConfigFileExt()));
138 $this->conf->reload(); 138 $this->conf->reload();
diff --git a/tests/config/ConfigPhpTest.php b/tests/config/ConfigPhpTest.php
index fb91b51b..7bf9fe64 100644
--- a/tests/config/ConfigPhpTest.php
+++ b/tests/config/ConfigPhpTest.php
@@ -8,14 +8,14 @@ namespace Shaarli\Config;
8 * which are kept between tests. 8 * which are kept between tests.
9 * @runTestsInSeparateProcesses 9 * @runTestsInSeparateProcesses
10 */ 10 */
11class ConfigPhpTest extends \PHPUnit\Framework\TestCase 11class ConfigPhpTest extends \Shaarli\TestCase
12{ 12{
13 /** 13 /**
14 * @var ConfigPhp 14 * @var ConfigPhp
15 */ 15 */
16 protected $configIO; 16 protected $configIO;
17 17
18 public function setUp() 18 protected function setUp(): void
19 { 19 {
20 $this->configIO = new ConfigPhp(); 20 $this->configIO = new ConfigPhp();
21 } 21 }
diff --git a/tests/config/ConfigPluginTest.php b/tests/config/ConfigPluginTest.php
index d7a70e68..fa72d8c4 100644
--- a/tests/config/ConfigPluginTest.php
+++ b/tests/config/ConfigPluginTest.php
@@ -2,13 +2,14 @@
2namespace Shaarli\Config; 2namespace Shaarli\Config;
3 3
4use Shaarli\Config\Exception\PluginConfigOrderException; 4use Shaarli\Config\Exception\PluginConfigOrderException;
5use Shaarli\Plugin\PluginManager;
5 6
6require_once 'application/config/ConfigPlugin.php'; 7require_once 'application/config/ConfigPlugin.php';
7 8
8/** 9/**
9 * Unitary tests for Shaarli config related functions 10 * Unitary tests for Shaarli config related functions
10 */ 11 */
11class ConfigPluginTest extends \PHPUnit\Framework\TestCase 12class ConfigPluginTest extends \Shaarli\TestCase
12{ 13{
13 /** 14 /**
14 * Test save_plugin_config with valid data. 15 * Test save_plugin_config with valid data.
@@ -17,32 +18,39 @@ class ConfigPluginTest extends \PHPUnit\Framework\TestCase
17 */ 18 */
18 public function testSavePluginConfigValid() 19 public function testSavePluginConfigValid()
19 { 20 {
20 $data = array( 21 $data = [
21 'order_plugin1' => 2, // no plugin related 22 'order_plugin1' => 2, // no plugin related
22 'plugin2' => 0, // new - at the end 23 'plugin2' => 0, // new - at the end
23 'plugin3' => 0, // 2nd 24 'plugin3' => 0, // 2nd
24 'order_plugin3' => 8, 25 'order_plugin3' => 8,
25 'plugin4' => 0, // 1st 26 'plugin4' => 0, // 1st
26 'order_plugin4' => 5, 27 'order_plugin4' => 5,
27 ); 28 ];
28 29
29 $expected = array( 30 $expected = [
30 'plugin3', 31 'plugin3',
31 'plugin4', 32 'plugin4',
32 'plugin2', 33 'plugin2',
33 ); 34 ];
35
36 mkdir($path = __DIR__ . '/folder');
37 PluginManager::$PLUGINS_PATH = $path;
38 array_map(function (string $plugin) use ($path) { touch($path . '/' . $plugin); }, $expected);
34 39
35 $out = save_plugin_config($data); 40 $out = save_plugin_config($data);
36 $this->assertEquals($expected, $out); 41 $this->assertEquals($expected, $out);
42
43 array_map(function (string $plugin) use ($path) { unlink($path . '/' . $plugin); }, $expected);
44 rmdir($path);
37 } 45 }
38 46
39 /** 47 /**
40 * Test save_plugin_config with invalid data. 48 * Test save_plugin_config with invalid data.
41 *
42 * @expectedException Shaarli\Config\Exception\PluginConfigOrderException
43 */ 49 */
44 public function testSavePluginConfigInvalid() 50 public function testSavePluginConfigInvalid()
45 { 51 {
52 $this->expectException(\Shaarli\Config\Exception\PluginConfigOrderException::class);
53
46 $data = array( 54 $data = array(
47 'plugin2' => 0, 55 'plugin2' => 0,
48 'plugin3' => 0, 56 'plugin3' => 0,
diff --git a/tests/container/ContainerBuilderTest.php b/tests/container/ContainerBuilderTest.php
index 9b97ed6d..3d43c344 100644
--- a/tests/container/ContainerBuilderTest.php
+++ b/tests/container/ContainerBuilderTest.php
@@ -4,13 +4,27 @@ declare(strict_types=1);
4 4
5namespace Shaarli\Container; 5namespace Shaarli\Container;
6 6
7use PHPUnit\Framework\TestCase; 7use Psr\Log\LoggerInterface;
8use Shaarli\Bookmark\BookmarkServiceInterface; 8use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager; 9use Shaarli\Config\ConfigManager;
10use Shaarli\Feed\FeedBuilder;
11use Shaarli\Formatter\FormatterFactory;
12use Shaarli\Front\Controller\Visitor\ErrorController;
13use Shaarli\Front\Controller\Visitor\ErrorNotFoundController;
10use Shaarli\History; 14use Shaarli\History;
15use Shaarli\Http\HttpAccess;
16use Shaarli\Http\MetadataRetriever;
17use Shaarli\Netscape\NetscapeBookmarkUtils;
18use Shaarli\Plugin\PluginManager;
11use Shaarli\Render\PageBuilder; 19use Shaarli\Render\PageBuilder;
20use Shaarli\Render\PageCacheManager;
21use Shaarli\Security\CookieManager;
12use Shaarli\Security\LoginManager; 22use Shaarli\Security\LoginManager;
13use Shaarli\Security\SessionManager; 23use Shaarli\Security\SessionManager;
24use Shaarli\TestCase;
25use Shaarli\Thumbnailer;
26use Shaarli\Updater\Updater;
27use Slim\Http\Environment;
14 28
15class ContainerBuilderTest extends TestCase 29class ContainerBuilderTest extends TestCase
16{ 30{
@@ -26,24 +40,54 @@ class ContainerBuilderTest extends TestCase
26 /** @var ContainerBuilder */ 40 /** @var ContainerBuilder */
27 protected $containerBuilder; 41 protected $containerBuilder;
28 42
43 /** @var CookieManager */
44 protected $cookieManager;
45
29 public function setUp(): void 46 public function setUp(): void
30 { 47 {
31 $this->conf = new ConfigManager('tests/utils/config/configJson'); 48 $this->conf = new ConfigManager('tests/utils/config/configJson');
32 $this->sessionManager = $this->createMock(SessionManager::class); 49 $this->sessionManager = $this->createMock(SessionManager::class);
50 $this->cookieManager = $this->createMock(CookieManager::class);
51
33 $this->loginManager = $this->createMock(LoginManager::class); 52 $this->loginManager = $this->createMock(LoginManager::class);
53 $this->loginManager->method('isLoggedIn')->willReturn(true);
34 54
35 $this->containerBuilder = new ContainerBuilder($this->conf, $this->sessionManager, $this->loginManager); 55 $this->containerBuilder = new ContainerBuilder(
56 $this->conf,
57 $this->sessionManager,
58 $this->cookieManager,
59 $this->loginManager,
60 $this->createMock(LoggerInterface::class)
61 );
36 } 62 }
37 63
38 public function testBuildContainer(): void 64 public function testBuildContainer(): void
39 { 65 {
40 $container = $this->containerBuilder->build(); 66 $container = $this->containerBuilder->build();
41 67
68 static::assertInstanceOf(BookmarkServiceInterface::class, $container->bookmarkService);
69 static::assertInstanceOf(CookieManager::class, $container->cookieManager);
42 static::assertInstanceOf(ConfigManager::class, $container->conf); 70 static::assertInstanceOf(ConfigManager::class, $container->conf);
43 static::assertInstanceOf(SessionManager::class, $container->sessionManager); 71 static::assertInstanceOf(ErrorController::class, $container->errorHandler);
44 static::assertInstanceOf(LoginManager::class, $container->loginManager); 72 static::assertInstanceOf(Environment::class, $container->environment);
73 static::assertInstanceOf(FeedBuilder::class, $container->feedBuilder);
74 static::assertInstanceOf(FormatterFactory::class, $container->formatterFactory);
45 static::assertInstanceOf(History::class, $container->history); 75 static::assertInstanceOf(History::class, $container->history);
46 static::assertInstanceOf(BookmarkServiceInterface::class, $container->bookmarkService); 76 static::assertInstanceOf(HttpAccess::class, $container->httpAccess);
77 static::assertInstanceOf(LoginManager::class, $container->loginManager);
78 static::assertInstanceOf(LoggerInterface::class, $container->logger);
79 static::assertInstanceOf(MetadataRetriever::class, $container->metadataRetriever);
80 static::assertInstanceOf(NetscapeBookmarkUtils::class, $container->netscapeBookmarkUtils);
47 static::assertInstanceOf(PageBuilder::class, $container->pageBuilder); 81 static::assertInstanceOf(PageBuilder::class, $container->pageBuilder);
82 static::assertInstanceOf(PageCacheManager::class, $container->pageCacheManager);
83 static::assertInstanceOf(ErrorController::class, $container->phpErrorHandler);
84 static::assertInstanceOf(ErrorNotFoundController::class, $container->notFoundHandler);
85 static::assertInstanceOf(PluginManager::class, $container->pluginManager);
86 static::assertInstanceOf(SessionManager::class, $container->sessionManager);
87 static::assertInstanceOf(Thumbnailer::class, $container->thumbnailer);
88 static::assertInstanceOf(Updater::class, $container->updater);
89
90 // Set by the middleware
91 static::assertNull($container->basePath);
48 } 92 }
49} 93}
diff --git a/tests/container/ShaarliTestContainer.php b/tests/container/ShaarliTestContainer.php
new file mode 100644
index 00000000..7dbe914c
--- /dev/null
+++ b/tests/container/ShaarliTestContainer.php
@@ -0,0 +1,42 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Container;
6
7use PHPUnit\Framework\MockObject\MockObject;
8use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager;
10use Shaarli\Feed\FeedBuilder;
11use Shaarli\Formatter\FormatterFactory;
12use Shaarli\History;
13use Shaarli\Http\HttpAccess;
14use Shaarli\Plugin\PluginManager;
15use Shaarli\Render\PageBuilder;
16use Shaarli\Render\PageCacheManager;
17use Shaarli\Security\LoginManager;
18use Shaarli\Security\SessionManager;
19use Shaarli\Thumbnailer;
20
21/**
22 * Test helper allowing auto-completion for MockObjects.
23 *
24 * @property mixed[] $environment $_SERVER automatically injected by Slim
25 * @property MockObject|ConfigManager $conf
26 * @property MockObject|SessionManager $sessionManager
27 * @property MockObject|LoginManager $loginManager
28 * @property MockObject|string $webPath
29 * @property MockObject|History $history
30 * @property MockObject|BookmarkServiceInterface $bookmarkService
31 * @property MockObject|PageBuilder $pageBuilder
32 * @property MockObject|PluginManager $pluginManager
33 * @property MockObject|FormatterFactory $formatterFactory
34 * @property MockObject|PageCacheManager $pageCacheManager
35 * @property MockObject|FeedBuilder $feedBuilder
36 * @property MockObject|Thumbnailer $thumbnailer
37 * @property MockObject|HttpAccess $httpAccess
38 */
39class ShaarliTestContainer extends ShaarliContainer
40{
41
42}
diff --git a/tests/feed/CachedPageTest.php b/tests/feed/CachedPageTest.php
index 363028a2..904db9dc 100644
--- a/tests/feed/CachedPageTest.php
+++ b/tests/feed/CachedPageTest.php
@@ -7,17 +7,17 @@ namespace Shaarli\Feed;
7/** 7/**
8 * Unitary tests for cached pages 8 * Unitary tests for cached pages
9 */ 9 */
10class CachedPageTest extends \PHPUnit\Framework\TestCase 10class CachedPageTest extends \Shaarli\TestCase
11{ 11{
12 // test cache directory 12 // test cache directory
13 protected static $testCacheDir = 'sandbox/pagecache'; 13 protected static $testCacheDir = 'sandbox/pagecache';
14 protected static $url = 'http://shaar.li/?do=atom'; 14 protected static $url = 'http://shaar.li/feed/atom';
15 protected static $filename; 15 protected static $filename;
16 16
17 /** 17 /**
18 * Create the cache directory if needed 18 * Create the cache directory if needed
19 */ 19 */
20 public static function setUpBeforeClass() 20 public static function setUpBeforeClass(): void
21 { 21 {
22 if (!is_dir(self::$testCacheDir)) { 22 if (!is_dir(self::$testCacheDir)) {
23 mkdir(self::$testCacheDir); 23 mkdir(self::$testCacheDir);
@@ -28,7 +28,7 @@ class CachedPageTest extends \PHPUnit\Framework\TestCase
28 /** 28 /**
29 * Reset the page cache 29 * Reset the page cache
30 */ 30 */
31 public function setUp() 31 protected function setUp(): void
32 { 32 {
33 if (file_exists(self::$filename)) { 33 if (file_exists(self::$filename)) {
34 unlink(self::$filename); 34 unlink(self::$filename);
@@ -42,8 +42,8 @@ class CachedPageTest extends \PHPUnit\Framework\TestCase
42 { 42 {
43 new CachedPage(self::$testCacheDir, '', true); 43 new CachedPage(self::$testCacheDir, '', true);
44 new CachedPage(self::$testCacheDir, '', false); 44 new CachedPage(self::$testCacheDir, '', false);
45 new CachedPage(self::$testCacheDir, 'http://shaar.li/?do=rss', true); 45 new CachedPage(self::$testCacheDir, 'http://shaar.li/feed/rss', true);
46 new CachedPage(self::$testCacheDir, 'http://shaar.li/?do=atom', false); 46 new CachedPage(self::$testCacheDir, 'http://shaar.li/feed/atom', false);
47 $this->addToAssertionCount(1); 47 $this->addToAssertionCount(1);
48 } 48 }
49 49
diff --git a/tests/feed/FeedBuilderTest.php b/tests/feed/FeedBuilderTest.php
index 54671891..6b9204eb 100644
--- a/tests/feed/FeedBuilderTest.php
+++ b/tests/feed/FeedBuilderTest.php
@@ -3,6 +3,7 @@
3namespace Shaarli\Feed; 3namespace Shaarli\Feed;
4 4
5use DateTime; 5use DateTime;
6use malkusch\lock\mutex\NoMutex;
6use ReferenceLinkDB; 7use ReferenceLinkDB;
7use Shaarli\Bookmark\Bookmark; 8use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\BookmarkFileService; 9use Shaarli\Bookmark\BookmarkFileService;
@@ -10,13 +11,14 @@ use Shaarli\Bookmark\LinkDB;
10use Shaarli\Config\ConfigManager; 11use Shaarli\Config\ConfigManager;
11use Shaarli\Formatter\FormatterFactory; 12use Shaarli\Formatter\FormatterFactory;
12use Shaarli\History; 13use Shaarli\History;
14use Shaarli\TestCase;
13 15
14/** 16/**
15 * FeedBuilderTest class. 17 * FeedBuilderTest class.
16 * 18 *
17 * Unit tests for FeedBuilder. 19 * Unit tests for FeedBuilder.
18 */ 20 */
19class FeedBuilderTest extends \PHPUnit\Framework\TestCase 21class FeedBuilderTest extends TestCase
20{ 22{
21 /** 23 /**
22 * @var string locale Basque (Spain). 24 * @var string locale Basque (Spain).
@@ -44,8 +46,9 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
44 /** 46 /**
45 * Called before every test method. 47 * Called before every test method.
46 */ 48 */
47 public static function setUpBeforeClass() 49 public static function setUpBeforeClass(): void
48 { 50 {
51 $mutex = new NoMutex();
49 $conf = new ConfigManager('tests/utils/config/configJson'); 52 $conf = new ConfigManager('tests/utils/config/configJson');
50 $conf->set('resource.datastore', self::$testDatastore); 53 $conf->set('resource.datastore', self::$testDatastore);
51 $refLinkDB = new \ReferenceLinkDB(); 54 $refLinkDB = new \ReferenceLinkDB();
@@ -53,35 +56,18 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
53 $history = new History('sandbox/history.php'); 56 $history = new History('sandbox/history.php');
54 $factory = new FormatterFactory($conf, true); 57 $factory = new FormatterFactory($conf, true);
55 self::$formatter = $factory->getFormatter(); 58 self::$formatter = $factory->getFormatter();
56 self::$bookmarkService = new BookmarkFileService($conf, $history, true); 59 self::$bookmarkService = new BookmarkFileService($conf, $history, $mutex, true);
57 60
58 self::$serverInfo = array( 61 self::$serverInfo = array(
59 'HTTPS' => 'Off', 62 'HTTPS' => 'Off',
60 'SERVER_NAME' => 'host.tld', 63 'SERVER_NAME' => 'host.tld',
61 'SERVER_PORT' => '80', 64 'SERVER_PORT' => '80',
62 'SCRIPT_NAME' => '/index.php', 65 'SCRIPT_NAME' => '/index.php',
63 'REQUEST_URI' => '/index.php?do=feed', 66 'REQUEST_URI' => '/feed/atom',
64 ); 67 );
65 } 68 }
66 69
67 /** 70 /**
68 * Test GetTypeLanguage().
69 */
70 public function testGetTypeLanguage()
71 {
72 $feedBuilder = new FeedBuilder(null, self::$formatter, FeedBuilder::$FEED_ATOM, null, null, false);
73 $feedBuilder->setLocale(self::$LOCALE);
74 $this->assertEquals(self::$ATOM_LANGUAGUE, $feedBuilder->getTypeLanguage());
75 $feedBuilder = new FeedBuilder(null, self::$formatter, FeedBuilder::$FEED_RSS, null, null, false);
76 $feedBuilder->setLocale(self::$LOCALE);
77 $this->assertEquals(self::$RSS_LANGUAGE, $feedBuilder->getTypeLanguage());
78 $feedBuilder = new FeedBuilder(null, self::$formatter, FeedBuilder::$FEED_ATOM, null, null, false);
79 $this->assertEquals('en', $feedBuilder->getTypeLanguage());
80 $feedBuilder = new FeedBuilder(null, self::$formatter, FeedBuilder::$FEED_RSS, null, null, false);
81 $this->assertEquals('en-en', $feedBuilder->getTypeLanguage());
82 }
83
84 /**
85 * Test buildData with RSS feed. 71 * Test buildData with RSS feed.
86 */ 72 */
87 public function testRSSBuildData() 73 public function testRSSBuildData()
@@ -89,35 +75,33 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
89 $feedBuilder = new FeedBuilder( 75 $feedBuilder = new FeedBuilder(
90 self::$bookmarkService, 76 self::$bookmarkService,
91 self::$formatter, 77 self::$formatter,
92 FeedBuilder::$FEED_RSS, 78 static::$serverInfo,
93 self::$serverInfo,
94 null,
95 false 79 false
96 ); 80 );
97 $feedBuilder->setLocale(self::$LOCALE); 81 $feedBuilder->setLocale(self::$LOCALE);
98 $data = $feedBuilder->buildData(); 82 $data = $feedBuilder->buildData(FeedBuilder::$FEED_RSS, null);
99 // Test headers (RSS) 83 // Test headers (RSS)
100 $this->assertEquals(self::$RSS_LANGUAGE, $data['language']); 84 $this->assertEquals(self::$RSS_LANGUAGE, $data['language']);
101 $this->assertRegExp('/Wed, 03 Aug 2016 09:30:33 \+\d{4}/', $data['last_update']); 85 $this->assertRegExp('/Wed, 03 Aug 2016 09:30:33 \+\d{4}/', $data['last_update']);
102 $this->assertEquals(true, $data['show_dates']); 86 $this->assertEquals(true, $data['show_dates']);
103 $this->assertEquals('http://host.tld/index.php?do=feed', $data['self_link']); 87 $this->assertEquals('http://host.tld/feed/atom', $data['self_link']);
104 $this->assertEquals('http://host.tld/', $data['index_url']); 88 $this->assertEquals('http://host.tld/', $data['index_url']);
105 $this->assertFalse($data['usepermalinks']); 89 $this->assertFalse($data['usepermalinks']);
106 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 90 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
107 91
108 // Test first not pinned link (note link) 92 // Test first not pinned link (note link)
109 $link = $data['links'][array_keys($data['links'])[2]]; 93 $link = $data['links'][array_keys($data['links'])[0]];
110 $this->assertEquals(41, $link['id']); 94 $this->assertEquals(41, $link['id']);
111 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 95 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
112 $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); 96 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']);
113 $this->assertEquals('http://host.tld/?WDWyig', $link['url']); 97 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['url']);
114 $this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']); 98 $this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']);
115 $pub = DateTime::createFromFormat(DateTime::RSS, $link['pub_iso_date']); 99 $pub = DateTime::createFromFormat(DateTime::RSS, $link['pub_iso_date']);
116 $up = DateTime::createFromFormat(DateTime::ATOM, $link['up_iso_date']); 100 $up = DateTime::createFromFormat(DateTime::ATOM, $link['up_iso_date']);
117 $this->assertEquals($pub, $up); 101 $this->assertEquals($pub, $up);
118 $this->assertContains('Stallman has a beard', $link['description']); 102 $this->assertContainsPolyfill('Stallman has a beard', $link['description']);
119 $this->assertContains('Permalink', $link['description']); 103 $this->assertContainsPolyfill('Permalink', $link['description']);
120 $this->assertContains('http://host.tld/?WDWyig', $link['description']); 104 $this->assertContainsPolyfill('http://host.tld/shaare/WDWyig', $link['description']);
121 $this->assertEquals(1, count($link['taglist'])); 105 $this->assertEquals(1, count($link['taglist']));
122 $this->assertEquals('sTuff', $link['taglist'][0]); 106 $this->assertEquals('sTuff', $link['taglist'][0]);
123 107
@@ -140,16 +124,14 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
140 $feedBuilder = new FeedBuilder( 124 $feedBuilder = new FeedBuilder(
141 self::$bookmarkService, 125 self::$bookmarkService,
142 self::$formatter, 126 self::$formatter,
143 FeedBuilder::$FEED_ATOM, 127 static::$serverInfo,
144 self::$serverInfo,
145 null,
146 false 128 false
147 ); 129 );
148 $feedBuilder->setLocale(self::$LOCALE); 130 $feedBuilder->setLocale(self::$LOCALE);
149 $data = $feedBuilder->buildData(); 131 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
150 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 132 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
151 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']); 133 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']);
152 $link = $data['links'][array_keys($data['links'])[2]]; 134 $link = $data['links'][array_keys($data['links'])[0]];
153 $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']); 135 $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']);
154 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']); 136 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']);
155 } 137 }
@@ -166,13 +148,11 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
166 $feedBuilder = new FeedBuilder( 148 $feedBuilder = new FeedBuilder(
167 self::$bookmarkService, 149 self::$bookmarkService,
168 self::$formatter, 150 self::$formatter,
169 FeedBuilder::$FEED_ATOM, 151 static::$serverInfo,
170 self::$serverInfo,
171 $criteria,
172 false 152 false
173 ); 153 );
174 $feedBuilder->setLocale(self::$LOCALE); 154 $feedBuilder->setLocale(self::$LOCALE);
175 $data = $feedBuilder->buildData(); 155 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, $criteria);
176 $this->assertEquals(1, count($data['links'])); 156 $this->assertEquals(1, count($data['links']));
177 $link = array_shift($data['links']); 157 $link = array_shift($data['links']);
178 $this->assertEquals(41, $link['id']); 158 $this->assertEquals(41, $link['id']);
@@ -190,15 +170,13 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
190 $feedBuilder = new FeedBuilder( 170 $feedBuilder = new FeedBuilder(
191 self::$bookmarkService, 171 self::$bookmarkService,
192 self::$formatter, 172 self::$formatter,
193 FeedBuilder::$FEED_ATOM, 173 static::$serverInfo,
194 self::$serverInfo,
195 $criteria,
196 false 174 false
197 ); 175 );
198 $feedBuilder->setLocale(self::$LOCALE); 176 $feedBuilder->setLocale(self::$LOCALE);
199 $data = $feedBuilder->buildData(); 177 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, $criteria);
200 $this->assertEquals(3, count($data['links'])); 178 $this->assertEquals(3, count($data['links']));
201 $link = $data['links'][array_keys($data['links'])[2]]; 179 $link = $data['links'][array_keys($data['links'])[0]];
202 $this->assertEquals(41, $link['id']); 180 $this->assertEquals(41, $link['id']);
203 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 181 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
204 } 182 }
@@ -211,32 +189,30 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
211 $feedBuilder = new FeedBuilder( 189 $feedBuilder = new FeedBuilder(
212 self::$bookmarkService, 190 self::$bookmarkService,
213 self::$formatter, 191 self::$formatter,
214 FeedBuilder::$FEED_ATOM, 192 static::$serverInfo,
215 self::$serverInfo,
216 null,
217 false 193 false
218 ); 194 );
219 $feedBuilder->setLocale(self::$LOCALE); 195 $feedBuilder->setLocale(self::$LOCALE);
220 $feedBuilder->setUsePermalinks(true); 196 $feedBuilder->setUsePermalinks(true);
221 $data = $feedBuilder->buildData(); 197 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
222 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 198 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
223 $this->assertTrue($data['usepermalinks']); 199 $this->assertTrue($data['usepermalinks']);
224 // First link is a permalink 200 // First link is a permalink
225 $link = $data['links'][array_keys($data['links'])[2]]; 201 $link = $data['links'][array_keys($data['links'])[0]];
226 $this->assertEquals(41, $link['id']); 202 $this->assertEquals(41, $link['id']);
227 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 203 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
228 $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); 204 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']);
229 $this->assertEquals('http://host.tld/?WDWyig', $link['url']); 205 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['url']);
230 $this->assertContains('Direct link', $link['description']); 206 $this->assertContainsPolyfill('Direct link', $link['description']);
231 $this->assertContains('http://host.tld/?WDWyig', $link['description']); 207 $this->assertContainsPolyfill('http://host.tld/shaare/WDWyig', $link['description']);
232 // Second link is a direct link 208 // Second link is a direct link
233 $link = $data['links'][array_keys($data['links'])[3]]; 209 $link = $data['links'][array_keys($data['links'])[1]];
234 $this->assertEquals(8, $link['id']); 210 $this->assertEquals(8, $link['id']);
235 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114633'), $link['created']); 211 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114633'), $link['created']);
236 $this->assertEquals('http://host.tld/?RttfEw', $link['guid']); 212 $this->assertEquals('http://host.tld/shaare/RttfEw', $link['guid']);
237 $this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['url']); 213 $this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['url']);
238 $this->assertContains('Direct link', $link['description']); 214 $this->assertContainsPolyfill('Direct link', $link['description']);
239 $this->assertContains('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['description']); 215 $this->assertContainsPolyfill('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['description']);
240 } 216 }
241 217
242 /** 218 /**
@@ -247,14 +223,12 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
247 $feedBuilder = new FeedBuilder( 223 $feedBuilder = new FeedBuilder(
248 self::$bookmarkService, 224 self::$bookmarkService,
249 self::$formatter, 225 self::$formatter,
250 FeedBuilder::$FEED_ATOM, 226 static::$serverInfo,
251 self::$serverInfo,
252 null,
253 false 227 false
254 ); 228 );
255 $feedBuilder->setLocale(self::$LOCALE); 229 $feedBuilder->setLocale(self::$LOCALE);
256 $feedBuilder->setHideDates(true); 230 $feedBuilder->setHideDates(true);
257 $data = $feedBuilder->buildData(); 231 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
258 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 232 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
259 $this->assertFalse($data['show_dates']); 233 $this->assertFalse($data['show_dates']);
260 234
@@ -262,14 +236,12 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
262 $feedBuilder = new FeedBuilder( 236 $feedBuilder = new FeedBuilder(
263 self::$bookmarkService, 237 self::$bookmarkService,
264 self::$formatter, 238 self::$formatter,
265 FeedBuilder::$FEED_ATOM, 239 static::$serverInfo,
266 self::$serverInfo,
267 null,
268 true 240 true
269 ); 241 );
270 $feedBuilder->setLocale(self::$LOCALE); 242 $feedBuilder->setLocale(self::$LOCALE);
271 $feedBuilder->setHideDates(true); 243 $feedBuilder->setHideDates(true);
272 $data = $feedBuilder->buildData(); 244 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
273 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 245 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
274 $this->assertTrue($data['show_dates']); 246 $this->assertTrue($data['show_dates']);
275 } 247 }
@@ -284,28 +256,26 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
284 'SERVER_NAME' => 'host.tld', 256 'SERVER_NAME' => 'host.tld',
285 'SERVER_PORT' => '8080', 257 'SERVER_PORT' => '8080',
286 'SCRIPT_NAME' => '/~user/shaarli/index.php', 258 'SCRIPT_NAME' => '/~user/shaarli/index.php',
287 'REQUEST_URI' => '/~user/shaarli/index.php?do=feed', 259 'REQUEST_URI' => '/~user/shaarli/feed/atom',
288 ); 260 );
289 $feedBuilder = new FeedBuilder( 261 $feedBuilder = new FeedBuilder(
290 self::$bookmarkService, 262 self::$bookmarkService,
291 self::$formatter, 263 self::$formatter,
292 FeedBuilder::$FEED_ATOM,
293 $serverInfo, 264 $serverInfo,
294 null,
295 false 265 false
296 ); 266 );
297 $feedBuilder->setLocale(self::$LOCALE); 267 $feedBuilder->setLocale(self::$LOCALE);
298 $data = $feedBuilder->buildData(); 268 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
299 269
300 $this->assertEquals( 270 $this->assertEquals(
301 'http://host.tld:8080/~user/shaarli/index.php?do=feed', 271 'http://host.tld:8080/~user/shaarli/feed/atom',
302 $data['self_link'] 272 $data['self_link']
303 ); 273 );
304 274
305 // Test first link (note link) 275 // Test first link (note link)
306 $link = $data['links'][array_keys($data['links'])[2]]; 276 $link = $data['links'][array_keys($data['links'])[0]];
307 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['guid']); 277 $this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['guid']);
308 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['url']); 278 $this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['url']);
309 $this->assertContains('http://host.tld:8080/~user/shaarli/?addtag=hashtag', $link['description']); 279 $this->assertContainsPolyfill('http://host.tld:8080/~user/shaarli/./add-tag/hashtag', $link['description']);
310 } 280 }
311} 281}
diff --git a/tests/formatter/BookmarkDefaultFormatterTest.php b/tests/formatter/BookmarkDefaultFormatterTest.php
index 382a560e..3fc6f8dc 100644
--- a/tests/formatter/BookmarkDefaultFormatterTest.php
+++ b/tests/formatter/BookmarkDefaultFormatterTest.php
@@ -3,9 +3,9 @@
3namespace Shaarli\Formatter; 3namespace Shaarli\Formatter;
4 4
5use DateTime; 5use DateTime;
6use PHPUnit\Framework\TestCase;
7use Shaarli\Bookmark\Bookmark; 6use Shaarli\Bookmark\Bookmark;
8use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\TestCase;
9 9
10/** 10/**
11 * Class BookmarkDefaultFormatterTest 11 * Class BookmarkDefaultFormatterTest
@@ -25,7 +25,7 @@ class BookmarkDefaultFormatterTest extends TestCase
25 /** 25 /**
26 * Initialize formatter instance. 26 * Initialize formatter instance.
27 */ 27 */
28 public function setUp() 28 protected function setUp(): void
29 { 29 {
30 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php'); 30 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php');
31 $this->conf = new ConfigManager(self::$testConf); 31 $this->conf = new ConfigManager(self::$testConf);
@@ -123,7 +123,7 @@ class BookmarkDefaultFormatterTest extends TestCase
123 $description[0] = 'This a &lt;strong&gt;description&lt;/strong&gt;<br />'; 123 $description[0] = 'This a &lt;strong&gt;description&lt;/strong&gt;<br />';
124 $url = 'https://sub.domain.tld?query=here&amp;for=real#hash'; 124 $url = 'https://sub.domain.tld?query=here&amp;for=real#hash';
125 $description[1] = 'text <a href="'. $url .'">'. $url .'</a> more text<br />'; 125 $description[1] = 'text <a href="'. $url .'">'. $url .'</a> more text<br />';
126 $description[2] = 'Also, there is an <a href="?addtag=hashtag" '. 126 $description[2] = 'Also, there is an <a href="./add-tag/hashtag" '.
127 'title="Hashtag hashtag">#hashtag</a> added<br />'; 127 'title="Hashtag hashtag">#hashtag</a> added<br />';
128 $description[3] = '&nbsp; &nbsp; A &nbsp;N &nbsp;D KEEP &nbsp; &nbsp; '. 128 $description[3] = '&nbsp; &nbsp; A &nbsp;N &nbsp;D KEEP &nbsp; &nbsp; '.
129 'SPACES &nbsp; &nbsp;! &nbsp; <br />'; 129 'SPACES &nbsp; &nbsp;! &nbsp; <br />';
@@ -148,7 +148,7 @@ class BookmarkDefaultFormatterTest extends TestCase
148 $this->assertEquals($root . $short, $link['url']); 148 $this->assertEquals($root . $short, $link['url']);
149 $this->assertEquals($root . $short, $link['real_url']); 149 $this->assertEquals($root . $short, $link['real_url']);
150 $this->assertEquals( 150 $this->assertEquals(
151 'Text <a href="'. $root .'?addtag=hashtag" title="Hashtag hashtag">'. 151 'Text <a href="'. $root .'./add-tag/hashtag" title="Hashtag hashtag">'.
152 '#hashtag</a> more text', 152 '#hashtag</a> more text',
153 $link['description'] 153 $link['description']
154 ); 154 );
@@ -174,4 +174,119 @@ class BookmarkDefaultFormatterTest extends TestCase
174 $this->assertSame($tags, $link['taglist']); 174 $this->assertSame($tags, $link['taglist']);
175 $this->assertSame(implode(' ', $tags), $link['tags']); 175 $this->assertSame(implode(' ', $tags), $link['tags']);
176 } 176 }
177
178 /**
179 * Test formatTitleHtml with search result highlight.
180 */
181 public function testFormatTitleHtmlWithSearchHighlight(): void
182 {
183 $this->formatter = new BookmarkDefaultFormatter($this->conf, false);
184
185 $bookmark = new Bookmark();
186 $bookmark->setTitle('PSR-2: Coding Style Guide');
187 $bookmark->addAdditionalContentEntry(
188 'search_highlight',
189 ['title' => [
190 ['start' => 0, 'end' => 5], // "psr-2"
191 ['start' => 7, 'end' => 13], // coding
192 ['start' => 20, 'end' => 25], // guide
193 ]]
194 );
195
196 $link = $this->formatter->format($bookmark);
197
198 $this->assertSame(
199 '<span class="search-highlight">PSR-2</span>: ' .
200 '<span class="search-highlight">Coding</span> Style ' .
201 '<span class="search-highlight">Guide</span>',
202 $link['title_html']
203 );
204 }
205
206 /**
207 * Test formatDescription with search result highlight.
208 */
209 public function testFormatDescriptionWithSearchHighlight(): void
210 {
211 $this->formatter = new BookmarkDefaultFormatter($this->conf, false);
212
213 $bookmark = new Bookmark();
214 $bookmark->setDescription('This guide extends and expands on PSR-1, the basic coding standard.');
215 $bookmark->addAdditionalContentEntry(
216 'search_highlight',
217 ['description' => [
218 ['start' => 0, 'end' => 10], // "This guide"
219 ['start' => 45, 'end' => 50], // basic
220 ['start' => 58, 'end' => 67], // standard.
221 ]]
222 );
223
224 $link = $this->formatter->format($bookmark);
225
226 $this->assertSame(
227 '<span class="search-highlight">This guide</span> extends and expands on PSR-1, the ' .
228 '<span class="search-highlight">basic</span> coding ' .
229 '<span class="search-highlight">standard.</span>',
230 $link['description']
231 );
232 }
233
234 /**
235 * Test formatUrlHtml with search result highlight.
236 */
237 public function testFormatUrlHtmlWithSearchHighlight(): void
238 {
239 $this->formatter = new BookmarkDefaultFormatter($this->conf, false);
240
241 $bookmark = new Bookmark();
242 $bookmark->setUrl('http://www.php-fig.org/psr/psr-2/');
243 $bookmark->addAdditionalContentEntry(
244 'search_highlight',
245 ['url' => [
246 ['start' => 0, 'end' => 4], // http
247 ['start' => 15, 'end' => 18], // fig
248 ['start' => 27, 'end' => 33], // "psr-2/"
249 ]]
250 );
251
252 $link = $this->formatter->format($bookmark);
253
254 $this->assertSame(
255 '<span class="search-highlight">http</span>://www.php-' .
256 '<span class="search-highlight">fig</span>.org/psr/' .
257 '<span class="search-highlight">psr-2/</span>',
258 $link['url_html']
259 );
260 }
261
262 /**
263 * Test formatTagListHtml with search result highlight.
264 */
265 public function testFormatTagListHtmlWithSearchHighlight(): void
266 {
267 $this->formatter = new BookmarkDefaultFormatter($this->conf, false);
268
269 $bookmark = new Bookmark();
270 $bookmark->setTagsString('coding-style standards quality assurance');
271 $bookmark->addAdditionalContentEntry(
272 'search_highlight',
273 ['tags' => [
274 ['start' => 0, 'end' => 12], // coding-style
275 ['start' => 23, 'end' => 30], // quality
276 ['start' => 31, 'end' => 40], // assurance
277 ],]
278 );
279
280 $link = $this->formatter->format($bookmark);
281
282 $this->assertSame(
283 [
284 '<span class="search-highlight">coding-style</span>',
285 'standards',
286 '<span class="search-highlight">quality</span>',
287 '<span class="search-highlight">assurance</span>',
288 ],
289 $link['taglist_html']
290 );
291 }
177} 292}
diff --git a/tests/formatter/BookmarkMarkdownExtraFormatterTest.php b/tests/formatter/BookmarkMarkdownExtraFormatterTest.php
new file mode 100644
index 00000000..d4941ef3
--- /dev/null
+++ b/tests/formatter/BookmarkMarkdownExtraFormatterTest.php
@@ -0,0 +1,162 @@
1<?php
2
3namespace Shaarli\Formatter;
4
5use DateTime;
6use PHPUnit\Framework\TestCase;
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Config\ConfigManager;
9
10/**
11 * Class BookmarkMarkdownExtraFormatterTest
12 * @package Shaarli\Formatter
13 */
14class BookmarkMarkdownExtraFormatterTest extends TestCase
15{
16 /** @var string Path of test config file */
17 protected static $testConf = 'sandbox/config';
18
19 /** @var BookmarkFormatter */
20 protected $formatter;
21
22 /** @var ConfigManager instance */
23 protected $conf;
24
25 /**
26 * Initialize formatter instance.
27 */
28 public function setUp(): void
29 {
30 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php');
31 $this->conf = new ConfigManager(self::$testConf);
32 $this->formatter = new BookmarkMarkdownExtraFormatter($this->conf, true);
33 }
34
35 /**
36 * Test formatting a bookmark with all its attribute filled.
37 */
38 public function testFormatExtra(): void
39 {
40 $bookmark = new Bookmark();
41 $bookmark->setId($id = 11);
42 $bookmark->setShortUrl($short = 'abcdef');
43 $bookmark->setUrl('https://sub.domain.tld?query=here&for=real#hash');
44 $bookmark->setTitle($title = 'This is a <strong>bookmark</strong>');
45 $bookmark->setDescription('<h2>Content</h2><p>`Here is some content</p>');
46 $bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '<script>alert("xss");</script>']);
47 $bookmark->setThumbnail('http://domain2.tdl2/?type=img&name=file.png');
48 $bookmark->setSticky(true);
49 $bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190521_190412'));
50 $bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190521_191213'));
51 $bookmark->setPrivate(true);
52
53 $link = $this->formatter->format($bookmark);
54 $this->assertEquals($id, $link['id']);
55 $this->assertEquals($short, $link['shorturl']);
56 $this->assertEquals('https://sub.domain.tld?query=here&amp;for=real#hash', $link['url']);
57 $this->assertEquals(
58 'https://sub.domain.tld?query=here&amp;for=real#hash',
59 $link['real_url']
60 );
61 $this->assertEquals('This is a &lt;strong&gt;bookmark&lt;/strong&gt;', $link['title']);
62 $this->assertEquals(
63 '<div class="markdown"><p>'.
64 '&lt;h2&gt;Content&lt;/h2&gt;&lt;p&gt;`Here is some content&lt;/p&gt;'.
65 '</p></div>',
66 $link['description']
67 );
68 $tags[3] = '&lt;script&gt;alert(&quot;xss&quot;);&lt;/script&gt;';
69 $this->assertEquals($tags, $link['taglist']);
70 $this->assertEquals(implode(' ', $tags), $link['tags']);
71 $this->assertEquals(
72 'http://domain2.tdl2/?type=img&amp;name=file.png',
73 $link['thumbnail']
74 );
75 $this->assertEquals($created, $link['created']);
76 $this->assertEquals($created->getTimestamp(), $link['timestamp']);
77 $this->assertEquals($updated, $link['updated']);
78 $this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']);
79 $this->assertTrue($link['private']);
80 $this->assertTrue($link['sticky']);
81 $this->assertEquals('private', $link['class']);
82 }
83
84 /**
85 * Test formatting a bookmark with all its attribute filled.
86 */
87 public function testFormatExtraMinimal(): void
88 {
89 $bookmark = new Bookmark();
90
91 $link = $this->formatter->format($bookmark);
92 $this->assertEmpty($link['id']);
93 $this->assertEmpty($link['shorturl']);
94 $this->assertEmpty($link['url']);
95 $this->assertEmpty($link['real_url']);
96 $this->assertEmpty($link['title']);
97 $this->assertEmpty($link['description']);
98 $this->assertEmpty($link['taglist']);
99 $this->assertEmpty($link['tags']);
100 $this->assertEmpty($link['thumbnail']);
101 $this->assertEmpty($link['created']);
102 $this->assertEmpty($link['timestamp']);
103 $this->assertEmpty($link['updated']);
104 $this->assertEmpty($link['updated_timestamp']);
105 $this->assertFalse($link['private']);
106 $this->assertFalse($link['sticky']);
107 $this->assertEmpty($link['class']);
108 }
109
110 /**
111 * Make sure that the description is properly formatted by the default formatter.
112 */
113 public function testFormatExtrraDescription(): void
114 {
115 $description = 'This a <strong>description</strong>'. PHP_EOL;
116 $description .= 'text https://sub.domain.tld?query=here&for=real#hash more text'. PHP_EOL;
117 $description .= 'Also, there is an #hashtag added'. PHP_EOL;
118 $description .= ' A N D KEEP SPACES ! '. PHP_EOL;
119 $description .= '# Header {.class}'. PHP_EOL;
120
121 $bookmark = new Bookmark();
122 $bookmark->setDescription($description);
123 $link = $this->formatter->format($bookmark);
124
125 $description = '<div class="markdown"><p>';
126 $description .= 'This a &lt;strong&gt;description&lt;/strong&gt;<br />'. PHP_EOL;
127 $url = 'https://sub.domain.tld?query=here&amp;for=real#hash';
128 $description .= 'text <a href="'. $url .'">'. $url .'</a> more text<br />'. PHP_EOL;
129 $description .= 'Also, there is an <a href="./add-tag/hashtag">#hashtag</a> added<br />'. PHP_EOL;
130 $description .= 'A N D KEEP SPACES ! </p>' . PHP_EOL;
131 $description .= '<h1 class="class">Header</h1>';
132 $description .= '</div>';
133
134 $this->assertEquals($description, $link['description']);
135 }
136
137 /**
138 * Test formatting URL with an index_url set
139 * It should prepend relative links.
140 */
141 public function testFormatExtraNoteWithIndexUrl(): void
142 {
143 $bookmark = new Bookmark();
144 $bookmark->setUrl($short = '?abcdef');
145 $description = 'Text #hashtag more text';
146 $bookmark->setDescription($description);
147
148 $this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/');
149
150 $description = '<div class="markdown"><p>';
151 $description .= 'Text <a href="'. $root .'./add-tag/hashtag">#hashtag</a> more text';
152 $description .= '</p></div>';
153
154 $link = $this->formatter->format($bookmark);
155 $this->assertEquals($root . $short, $link['url']);
156 $this->assertEquals($root . $short, $link['real_url']);
157 $this->assertEquals(
158 $description,
159 $link['description']
160 );
161 }
162}
diff --git a/tests/formatter/BookmarkMarkdownFormatterTest.php b/tests/formatter/BookmarkMarkdownFormatterTest.php
index f1f12c04..ab6b4080 100644
--- a/tests/formatter/BookmarkMarkdownFormatterTest.php
+++ b/tests/formatter/BookmarkMarkdownFormatterTest.php
@@ -3,9 +3,9 @@
3namespace Shaarli\Formatter; 3namespace Shaarli\Formatter;
4 4
5use DateTime; 5use DateTime;
6use PHPUnit\Framework\TestCase;
7use Shaarli\Bookmark\Bookmark; 6use Shaarli\Bookmark\Bookmark;
8use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\TestCase;
9 9
10/** 10/**
11 * Class BookmarkMarkdownFormatterTest 11 * Class BookmarkMarkdownFormatterTest
@@ -25,7 +25,7 @@ class BookmarkMarkdownFormatterTest extends TestCase
25 /** 25 /**
26 * Initialize formatter instance. 26 * Initialize formatter instance.
27 */ 27 */
28 public function setUp() 28 protected function setUp(): void
29 { 29 {
30 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php'); 30 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php');
31 $this->conf = new ConfigManager(self::$testConf); 31 $this->conf = new ConfigManager(self::$testConf);
@@ -125,7 +125,7 @@ class BookmarkMarkdownFormatterTest extends TestCase
125 $description .= 'This a &lt;strong&gt;description&lt;/strong&gt;<br />'. PHP_EOL; 125 $description .= 'This a &lt;strong&gt;description&lt;/strong&gt;<br />'. PHP_EOL;
126 $url = 'https://sub.domain.tld?query=here&amp;for=real#hash'; 126 $url = 'https://sub.domain.tld?query=here&amp;for=real#hash';
127 $description .= 'text <a href="'. $url .'">'. $url .'</a> more text<br />'. PHP_EOL; 127 $description .= 'text <a href="'. $url .'">'. $url .'</a> more text<br />'. PHP_EOL;
128 $description .= 'Also, there is an <a href="?addtag=hashtag">#hashtag</a> added<br />'. PHP_EOL; 128 $description .= 'Also, there is an <a href="./add-tag/hashtag">#hashtag</a> added<br />'. PHP_EOL;
129 $description .= 'A N D KEEP SPACES ! '; 129 $description .= 'A N D KEEP SPACES ! ';
130 $description .= '</p></div>'; 130 $description .= '</p></div>';
131 131
@@ -146,7 +146,7 @@ class BookmarkMarkdownFormatterTest extends TestCase
146 $this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/'); 146 $this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/');
147 147
148 $description = '<div class="markdown"><p>'; 148 $description = '<div class="markdown"><p>';
149 $description .= 'Text <a href="'. $root .'?addtag=hashtag">#hashtag</a> more text'; 149 $description .= 'Text <a href="'. $root .'./add-tag/hashtag">#hashtag</a> more text';
150 $description .= '</p></div>'; 150 $description .= '</p></div>';
151 151
152 $link = $this->formatter->format($bookmark); 152 $link = $this->formatter->format($bookmark);
diff --git a/tests/formatter/BookmarkRawFormatterTest.php b/tests/formatter/BookmarkRawFormatterTest.php
index 4491b035..c76bb7b9 100644
--- a/tests/formatter/BookmarkRawFormatterTest.php
+++ b/tests/formatter/BookmarkRawFormatterTest.php
@@ -3,9 +3,9 @@
3namespace Shaarli\Formatter; 3namespace Shaarli\Formatter;
4 4
5use DateTime; 5use DateTime;
6use PHPUnit\Framework\TestCase;
7use Shaarli\Bookmark\Bookmark; 6use Shaarli\Bookmark\Bookmark;
8use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\TestCase;
9 9
10/** 10/**
11 * Class BookmarkRawFormatterTest 11 * Class BookmarkRawFormatterTest
@@ -25,7 +25,7 @@ class BookmarkRawFormatterTest extends TestCase
25 /** 25 /**
26 * Initialize formatter instance. 26 * Initialize formatter instance.
27 */ 27 */
28 public function setUp() 28 protected function setUp(): void
29 { 29 {
30 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php'); 30 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php');
31 $this->conf = new ConfigManager(self::$testConf); 31 $this->conf = new ConfigManager(self::$testConf);
diff --git a/tests/formatter/FormatterFactoryTest.php b/tests/formatter/FormatterFactoryTest.php
index 5adf3ffd..ae476cb5 100644
--- a/tests/formatter/FormatterFactoryTest.php
+++ b/tests/formatter/FormatterFactoryTest.php
@@ -2,8 +2,8 @@
2 2
3namespace Shaarli\Formatter; 3namespace Shaarli\Formatter;
4 4
5use PHPUnit\Framework\TestCase;
6use Shaarli\Config\ConfigManager; 5use Shaarli\Config\ConfigManager;
6use Shaarli\TestCase;
7 7
8/** 8/**
9 * Class FormatterFactoryTest 9 * Class FormatterFactoryTest
@@ -24,7 +24,7 @@ class FormatterFactoryTest extends TestCase
24 /** 24 /**
25 * Initialize FormatterFactory instance 25 * Initialize FormatterFactory instance
26 */ 26 */
27 public function setUp() 27 protected function setUp(): void
28 { 28 {
29 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php'); 29 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php');
30 $this->conf = new ConfigManager(self::$testConf); 30 $this->conf = new ConfigManager(self::$testConf);
diff --git a/tests/front/ShaarliAdminMiddlewareTest.php b/tests/front/ShaarliAdminMiddlewareTest.php
new file mode 100644
index 00000000..44025f11
--- /dev/null
+++ b/tests/front/ShaarliAdminMiddlewareTest.php
@@ -0,0 +1,100 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front;
6
7use Shaarli\Config\ConfigManager;
8use Shaarli\Container\ShaarliContainer;
9use Shaarli\Security\LoginManager;
10use Shaarli\TestCase;
11use Shaarli\Updater\Updater;
12use Slim\Http\Request;
13use Slim\Http\Response;
14use Slim\Http\Uri;
15
16class ShaarliAdminMiddlewareTest extends TestCase
17{
18 protected const TMP_MOCK_FILE = '.tmp';
19
20 /** @var ShaarliContainer */
21 protected $container;
22
23 /** @var ShaarliMiddleware */
24 protected $middleware;
25
26 public function setUp(): void
27 {
28 $this->container = $this->createMock(ShaarliContainer::class);
29
30 touch(static::TMP_MOCK_FILE);
31
32 $this->container->conf = $this->createMock(ConfigManager::class);
33 $this->container->conf->method('getConfigFileExt')->willReturn(static::TMP_MOCK_FILE);
34
35 $this->container->loginManager = $this->createMock(LoginManager::class);
36 $this->container->updater = $this->createMock(Updater::class);
37
38 $this->container->environment = ['REQUEST_URI' => 'http://shaarli/subfolder/path'];
39
40 $this->middleware = new ShaarliAdminMiddleware($this->container);
41 }
42
43 public function tearDown(): void
44 {
45 unlink(static::TMP_MOCK_FILE);
46 }
47
48 /**
49 * Try to access an admin controller while logged out -> redirected to login page.
50 */
51 public function testMiddlewareWhileLoggedOut(): void
52 {
53 $this->container->loginManager->expects(static::once())->method('isLoggedIn')->willReturn(false);
54
55 $request = $this->createMock(Request::class);
56 $request->method('getUri')->willReturnCallback(function (): Uri {
57 $uri = $this->createMock(Uri::class);
58 $uri->method('getBasePath')->willReturn('/subfolder');
59
60 return $uri;
61 });
62
63 $response = new Response();
64
65 /** @var Response $result */
66 $result = $this->middleware->__invoke($request, $response, function () {});
67
68 static::assertSame(302, $result->getStatusCode());
69 static::assertSame(
70 '/subfolder/login?returnurl=' . urlencode('http://shaarli/subfolder/path'),
71 $result->getHeader('location')[0]
72 );
73 }
74
75 /**
76 * Process controller while logged in.
77 */
78 public function testMiddlewareWhileLoggedIn(): void
79 {
80 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
81
82 $request = $this->createMock(Request::class);
83 $request->method('getUri')->willReturnCallback(function (): Uri {
84 $uri = $this->createMock(Uri::class);
85 $uri->method('getBasePath')->willReturn('/subfolder');
86
87 return $uri;
88 });
89
90 $response = new Response();
91 $controller = function (Request $request, Response $response): Response {
92 return $response->withStatus(418); // I'm a tea pot
93 };
94
95 /** @var Response $result */
96 $result = $this->middleware->__invoke($request, $response, $controller);
97
98 static::assertSame(418, $result->getStatusCode());
99 }
100}
diff --git a/tests/front/ShaarliMiddlewareTest.php b/tests/front/ShaarliMiddlewareTest.php
index 80974f37..655c5bba 100644
--- a/tests/front/ShaarliMiddlewareTest.php
+++ b/tests/front/ShaarliMiddlewareTest.php
@@ -4,16 +4,23 @@ declare(strict_types=1);
4 4
5namespace Shaarli\Front; 5namespace Shaarli\Front;
6 6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
9use Shaarli\Container\ShaarliContainer; 8use Shaarli\Container\ShaarliContainer;
10use Shaarli\Front\Exception\LoginBannedException; 9use Shaarli\Front\Exception\LoginBannedException;
10use Shaarli\Front\Exception\UnauthorizedException;
11use Shaarli\Render\PageBuilder; 11use Shaarli\Render\PageBuilder;
12use Shaarli\Render\PageCacheManager;
13use Shaarli\Security\LoginManager;
14use Shaarli\TestCase;
15use Shaarli\Updater\Updater;
12use Slim\Http\Request; 16use Slim\Http\Request;
13use Slim\Http\Response; 17use Slim\Http\Response;
18use Slim\Http\Uri;
14 19
15class ShaarliMiddlewareTest extends TestCase 20class ShaarliMiddlewareTest extends TestCase
16{ 21{
22 protected const TMP_MOCK_FILE = '.tmp';
23
17 /** @var ShaarliContainer */ 24 /** @var ShaarliContainer */
18 protected $container; 25 protected $container;
19 26
@@ -23,12 +30,37 @@ class ShaarliMiddlewareTest extends TestCase
23 public function setUp(): void 30 public function setUp(): void
24 { 31 {
25 $this->container = $this->createMock(ShaarliContainer::class); 32 $this->container = $this->createMock(ShaarliContainer::class);
33
34 touch(static::TMP_MOCK_FILE);
35
36 $this->container->conf = $this->createMock(ConfigManager::class);
37 $this->container->conf->method('getConfigFileExt')->willReturn(static::TMP_MOCK_FILE);
38
39 $this->container->loginManager = $this->createMock(LoginManager::class);
40
41 $this->container->environment = ['REQUEST_URI' => 'http://shaarli/subfolder/path'];
42
26 $this->middleware = new ShaarliMiddleware($this->container); 43 $this->middleware = new ShaarliMiddleware($this->container);
27 } 44 }
28 45
46 public function tearDown(): void
47 {
48 unlink(static::TMP_MOCK_FILE);
49 }
50
51 /**
52 * Test middleware execution with valid controller call
53 */
29 public function testMiddlewareExecution(): void 54 public function testMiddlewareExecution(): void
30 { 55 {
31 $request = $this->createMock(Request::class); 56 $request = $this->createMock(Request::class);
57 $request->method('getUri')->willReturnCallback(function (): Uri {
58 $uri = $this->createMock(Uri::class);
59 $uri->method('getBasePath')->willReturn('/subfolder');
60
61 return $uri;
62 });
63
32 $response = new Response(); 64 $response = new Response();
33 $controller = function (Request $request, Response $response): Response { 65 $controller = function (Request $request, Response $response): Response {
34 return $response->withStatus(418); // I'm a tea pot 66 return $response->withStatus(418); // I'm a tea pot
@@ -41,9 +73,20 @@ class ShaarliMiddlewareTest extends TestCase
41 static::assertSame(418, $result->getStatusCode()); 73 static::assertSame(418, $result->getStatusCode());
42 } 74 }
43 75
44 public function testMiddlewareExecutionWithException(): void 76 /**
77 * Test middleware execution with controller throwing a known front exception.
78 * The exception should be thrown to be later handled by the error handler.
79 */
80 public function testMiddlewareExecutionWithFrontException(): void
45 { 81 {
46 $request = $this->createMock(Request::class); 82 $request = $this->createMock(Request::class);
83 $request->method('getUri')->willReturnCallback(function (): Uri {
84 $uri = $this->createMock(Uri::class);
85 $uri->method('getBasePath')->willReturn('/subfolder');
86
87 return $uri;
88 });
89
47 $response = new Response(); 90 $response = new Response();
48 $controller = function (): void { 91 $controller = function (): void {
49 $exception = new LoginBannedException(); 92 $exception = new LoginBannedException();
@@ -57,14 +100,122 @@ class ShaarliMiddlewareTest extends TestCase
57 }); 100 });
58 $this->container->pageBuilder = $pageBuilder; 101 $this->container->pageBuilder = $pageBuilder;
59 102
60 $conf = $this->createMock(ConfigManager::class); 103 $this->expectException(LoginBannedException::class);
61 $this->container->conf = $conf; 104
105 $this->middleware->__invoke($request, $response, $controller);
106 }
107
108 /**
109 * Test middleware execution with controller throwing a not authorized exception
110 * The middle should send a redirection response to the login page.
111 */
112 public function testMiddlewareExecutionWithUnauthorizedException(): void
113 {
114 $request = $this->createMock(Request::class);
115 $request->method('getUri')->willReturnCallback(function (): Uri {
116 $uri = $this->createMock(Uri::class);
117 $uri->method('getBasePath')->willReturn('/subfolder');
118
119 return $uri;
120 });
121
122 $response = new Response();
123 $controller = function (): void {
124 throw new UnauthorizedException();
125 };
126
127 /** @var Response $result */
128 $result = $this->middleware->__invoke($request, $response, $controller);
129
130 static::assertSame(302, $result->getStatusCode());
131 static::assertSame(
132 '/subfolder/login?returnurl=' . urlencode('http://shaarli/subfolder/path'),
133 $result->getHeader('location')[0]
134 );
135 }
136
137 /**
138 * Test middleware execution with controller throwing a not authorized exception.
139 * The exception should be thrown to be later handled by the error handler.
140 */
141 public function testMiddlewareExecutionWithServerException(): void
142 {
143 $request = $this->createMock(Request::class);
144 $request->method('getUri')->willReturnCallback(function (): Uri {
145 $uri = $this->createMock(Uri::class);
146 $uri->method('getBasePath')->willReturn('/subfolder');
147
148 return $uri;
149 });
150
151 $dummyException = new class() extends \Exception {};
152
153 $response = new Response();
154 $controller = function () use ($dummyException): void {
155 throw $dummyException;
156 };
157
158 $parameters = [];
159 $this->container->pageBuilder = $this->createMock(PageBuilder::class);
160 $this->container->pageBuilder->method('render')->willReturnCallback(function (string $message): string {
161 return $message;
162 });
163 $this->container->pageBuilder
164 ->method('assign')
165 ->willReturnCallback(function (string $key, string $value) use (&$parameters): void {
166 $parameters[$key] = $value;
167 })
168 ;
169
170 $this->expectException(get_class($dummyException));
171
172 $this->middleware->__invoke($request, $response, $controller);
173 }
174
175 public function testMiddlewareExecutionWithUpdates(): void
176 {
177 $request = $this->createMock(Request::class);
178 $request->method('getUri')->willReturnCallback(function (): Uri {
179 $uri = $this->createMock(Uri::class);
180 $uri->method('getBasePath')->willReturn('/subfolder');
181
182 return $uri;
183 });
184
185 $response = new Response();
186 $controller = function (Request $request, Response $response): Response {
187 return $response->withStatus(418); // I'm a tea pot
188 };
189
190 $this->container->loginManager = $this->createMock(LoginManager::class);
191 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
192
193 $this->container->conf = $this->createMock(ConfigManager::class);
194 $this->container->conf->method('get')->willReturnCallback(function (string $key): string {
195 return $key;
196 });
197 $this->container->conf->method('getConfigFileExt')->willReturn(static::TMP_MOCK_FILE);
198
199 $this->container->pageCacheManager = $this->createMock(PageCacheManager::class);
200 $this->container->pageCacheManager->expects(static::once())->method('invalidateCaches');
201
202 $this->container->updater = $this->createMock(Updater::class);
203 $this->container->updater
204 ->expects(static::once())
205 ->method('update')
206 ->willReturn(['update123'])
207 ;
208 $this->container->updater->method('getDoneUpdates')->willReturn($updates = ['update123', 'other']);
209 $this->container->updater
210 ->expects(static::once())
211 ->method('writeUpdates')
212 ->with('resource.updates', $updates)
213 ;
62 214
63 /** @var Response $result */ 215 /** @var Response $result */
64 $result = $this->middleware->__invoke($request, $response, $controller); 216 $result = $this->middleware->__invoke($request, $response, $controller);
65 217
66 static::assertInstanceOf(Response::class, $result); 218 static::assertInstanceOf(Response::class, $result);
67 static::assertSame(401, $result->getStatusCode()); 219 static::assertSame(418, $result->getStatusCode());
68 static::assertContains('error', (string) $result->getBody());
69 } 220 }
70} 221}
diff --git a/tests/front/controller/LoginControllerTest.php b/tests/front/controller/LoginControllerTest.php
deleted file mode 100644
index 8cf8ece7..00000000
--- a/tests/front/controller/LoginControllerTest.php
+++ /dev/null
@@ -1,178 +0,0 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\BookmarkServiceInterface;
9use Shaarli\Config\ConfigManager;
10use Shaarli\Container\ShaarliContainer;
11use Shaarli\Front\Exception\LoginBannedException;
12use Shaarli\Plugin\PluginManager;
13use Shaarli\Render\PageBuilder;
14use Shaarli\Security\LoginManager;
15use Slim\Http\Request;
16use Slim\Http\Response;
17
18class LoginControllerTest extends TestCase
19{
20 /** @var ShaarliContainer */
21 protected $container;
22
23 /** @var LoginController */
24 protected $controller;
25
26 public function setUp(): void
27 {
28 $this->container = $this->createMock(ShaarliContainer::class);
29 $this->controller = new LoginController($this->container);
30 }
31
32 public function testValidControllerInvoke(): void
33 {
34 $this->createValidContainerMockSet();
35
36 $request = $this->createMock(Request::class);
37 $request->expects(static::once())->method('getServerParam')->willReturn('> referer');
38 $response = new Response();
39
40 $assignedVariables = [];
41 $this->container->pageBuilder
42 ->method('assign')
43 ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
44 $assignedVariables[$key] = $value;
45
46 return $this;
47 })
48 ;
49
50 $result = $this->controller->index($request, $response);
51
52 static::assertInstanceOf(Response::class, $result);
53 static::assertSame(200, $result->getStatusCode());
54 static::assertSame('loginform', (string) $result->getBody());
55
56 static::assertSame('&gt; referer', $assignedVariables['returnurl']);
57 static::assertSame(true, $assignedVariables['remember_user_default']);
58 static::assertSame('Login - Shaarli', $assignedVariables['pagetitle']);
59 }
60
61 public function testValidControllerInvokeWithUserName(): void
62 {
63 $this->createValidContainerMockSet();
64
65 $request = $this->createMock(Request::class);
66 $request->expects(static::once())->method('getServerParam')->willReturn('> referer');
67 $request->expects(static::exactly(2))->method('getParam')->willReturn('myUser>');
68 $response = new Response();
69
70 $assignedVariables = [];
71 $this->container->pageBuilder
72 ->method('assign')
73 ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
74 $assignedVariables[$key] = $value;
75
76 return $this;
77 })
78 ;
79
80 $result = $this->controller->index($request, $response);
81
82 static::assertInstanceOf(Response::class, $result);
83 static::assertSame(200, $result->getStatusCode());
84 static::assertSame('loginform', (string) $result->getBody());
85
86 static::assertSame('myUser&gt;', $assignedVariables['username']);
87 static::assertSame('&gt; referer', $assignedVariables['returnurl']);
88 static::assertSame(true, $assignedVariables['remember_user_default']);
89 static::assertSame('Login - Shaarli', $assignedVariables['pagetitle']);
90 }
91
92 public function testLoginControllerWhileLoggedIn(): void
93 {
94 $request = $this->createMock(Request::class);
95 $response = new Response();
96
97 $loginManager = $this->createMock(LoginManager::class);
98 $loginManager->expects(static::once())->method('isLoggedIn')->willReturn(true);
99 $this->container->loginManager = $loginManager;
100
101 $result = $this->controller->index($request, $response);
102
103 static::assertInstanceOf(Response::class, $result);
104 static::assertSame(302, $result->getStatusCode());
105 static::assertSame(['./'], $result->getHeader('Location'));
106 }
107
108 public function testLoginControllerOpenShaarli(): void
109 {
110 $this->createValidContainerMockSet();
111
112 $request = $this->createMock(Request::class);
113 $response = new Response();
114
115 $conf = $this->createMock(ConfigManager::class);
116 $conf->method('get')->willReturnCallback(function (string $parameter, $default) {
117 if ($parameter === 'security.open_shaarli') {
118 return true;
119 }
120 return $default;
121 });
122 $this->container->conf = $conf;
123
124 $result = $this->controller->index($request, $response);
125
126 static::assertInstanceOf(Response::class, $result);
127 static::assertSame(302, $result->getStatusCode());
128 static::assertSame(['./'], $result->getHeader('Location'));
129 }
130
131 public function testLoginControllerWhileBanned(): void
132 {
133 $this->createValidContainerMockSet();
134
135 $request = $this->createMock(Request::class);
136 $response = new Response();
137
138 $loginManager = $this->createMock(LoginManager::class);
139 $loginManager->method('isLoggedIn')->willReturn(false);
140 $loginManager->method('canLogin')->willReturn(false);
141 $this->container->loginManager = $loginManager;
142
143 $this->expectException(LoginBannedException::class);
144
145 $this->controller->index($request, $response);
146 }
147
148 protected function createValidContainerMockSet(): void
149 {
150 // User logged out
151 $loginManager = $this->createMock(LoginManager::class);
152 $loginManager->method('isLoggedIn')->willReturn(false);
153 $loginManager->method('canLogin')->willReturn(true);
154 $this->container->loginManager = $loginManager;
155
156 // Config
157 $conf = $this->createMock(ConfigManager::class);
158 $conf->method('get')->willReturnCallback(function (string $parameter, $default) {
159 return $default;
160 });
161 $this->container->conf = $conf;
162
163 // PageBuilder
164 $pageBuilder = $this->createMock(PageBuilder::class);
165 $pageBuilder
166 ->method('render')
167 ->willReturnCallback(function (string $template): string {
168 return $template;
169 })
170 ;
171 $this->container->pageBuilder = $pageBuilder;
172
173 $pluginManager = $this->createMock(PluginManager::class);
174 $this->container->pluginManager = $pluginManager;
175 $bookmarkService = $this->createMock(BookmarkServiceInterface::class);
176 $this->container->bookmarkService = $bookmarkService;
177 }
178}
diff --git a/tests/front/controller/ShaarliControllerTest.php b/tests/front/controller/ShaarliControllerTest.php
deleted file mode 100644
index 6fa3feb9..00000000
--- a/tests/front/controller/ShaarliControllerTest.php
+++ /dev/null
@@ -1,116 +0,0 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Bookmark\BookmarkFilter;
9use Shaarli\Bookmark\BookmarkServiceInterface;
10use Shaarli\Container\ShaarliContainer;
11use Shaarli\Plugin\PluginManager;
12use Shaarli\Render\PageBuilder;
13use Shaarli\Security\LoginManager;
14
15/**
16 * Class ShaarliControllerTest
17 *
18 * This class is used to test default behavior of ShaarliController abstract class.
19 * It uses a dummy non abstract controller.
20 */
21class ShaarliControllerTest extends TestCase
22{
23 /** @var ShaarliContainer */
24 protected $container;
25
26 /** @var LoginController */
27 protected $controller;
28
29 /** @var mixed[] List of variable assigned to the template */
30 protected $assignedValues;
31
32 public function setUp(): void
33 {
34 $this->container = $this->createMock(ShaarliContainer::class);
35 $this->controller = new class($this->container) extends ShaarliController
36 {
37 public function assignView(string $key, $value): ShaarliController
38 {
39 return parent::assignView($key, $value);
40 }
41
42 public function render(string $template): string
43 {
44 return parent::render($template);
45 }
46 };
47 $this->assignedValues = [];
48 }
49
50 public function testAssignView(): void
51 {
52 $this->createValidContainerMockSet();
53
54 $self = $this->controller->assignView('variableName', 'variableValue');
55
56 static::assertInstanceOf(ShaarliController::class, $self);
57 static::assertSame('variableValue', $this->assignedValues['variableName']);
58 }
59
60 public function testRender(): void
61 {
62 $this->createValidContainerMockSet();
63
64 $render = $this->controller->render('templateName');
65
66 static::assertSame('templateName', $render);
67
68 static::assertSame(10, $this->assignedValues['linkcount']);
69 static::assertSame(5, $this->assignedValues['privateLinkcount']);
70 static::assertSame(['error'], $this->assignedValues['plugin_errors']);
71
72 static::assertSame('templateName', $this->assignedValues['plugins_includes']['render_includes']['target']);
73 static::assertTrue($this->assignedValues['plugins_includes']['render_includes']['loggedin']);
74 static::assertSame('templateName', $this->assignedValues['plugins_header']['render_header']['target']);
75 static::assertTrue($this->assignedValues['plugins_header']['render_header']['loggedin']);
76 static::assertSame('templateName', $this->assignedValues['plugins_footer']['render_footer']['target']);
77 static::assertTrue($this->assignedValues['plugins_footer']['render_footer']['loggedin']);
78 }
79
80 protected function createValidContainerMockSet(): void
81 {
82 $pageBuilder = $this->createMock(PageBuilder::class);
83 $pageBuilder
84 ->method('assign')
85 ->willReturnCallback(function (string $key, $value): void {
86 $this->assignedValues[$key] = $value;
87 });
88 $pageBuilder
89 ->method('render')
90 ->willReturnCallback(function (string $template): string {
91 return $template;
92 });
93 $this->container->pageBuilder = $pageBuilder;
94
95 $bookmarkService = $this->createMock(BookmarkServiceInterface::class);
96 $bookmarkService
97 ->method('count')
98 ->willReturnCallback(function (string $visibility): int {
99 return $visibility === BookmarkFilter::$PRIVATE ? 5 : 10;
100 });
101 $this->container->bookmarkService = $bookmarkService;
102
103 $pluginManager = $this->createMock(PluginManager::class);
104 $pluginManager
105 ->method('executeHooks')
106 ->willReturnCallback(function (string $hook, array &$data, array $params): array {
107 return $data[$hook] = $params;
108 });
109 $pluginManager->method('getErrors')->willReturn(['error']);
110 $this->container->pluginManager = $pluginManager;
111
112 $loginManager = $this->createMock(LoginManager::class);
113 $loginManager->method('isLoggedIn')->willReturn(true);
114 $this->container->loginManager = $loginManager;
115 }
116}
diff --git a/tests/front/controller/admin/ConfigureControllerTest.php b/tests/front/controller/admin/ConfigureControllerTest.php
new file mode 100644
index 00000000..d82db0a7
--- /dev/null
+++ b/tests/front/controller/admin/ConfigureControllerTest.php
@@ -0,0 +1,252 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Config\ConfigManager;
8use Shaarli\Front\Exception\WrongTokenException;
9use Shaarli\Security\SessionManager;
10use Shaarli\TestCase;
11use Shaarli\Thumbnailer;
12use Slim\Http\Request;
13use Slim\Http\Response;
14
15class ConfigureControllerTest extends TestCase
16{
17 use FrontAdminControllerMockHelper;
18
19 /** @var ConfigureController */
20 protected $controller;
21
22 public function setUp(): void
23 {
24 $this->createContainer();
25
26 $this->controller = new ConfigureController($this->container);
27 }
28
29 /**
30 * Test displaying configure page - it should display all config variables
31 */
32 public function testIndex(): void
33 {
34 $assignedVariables = [];
35 $this->assignTemplateVars($assignedVariables);
36
37 $request = $this->createMock(Request::class);
38 $response = new Response();
39
40 $this->container->conf = $this->createMock(ConfigManager::class);
41 $this->container->conf->method('get')->willReturnCallback(function (string $key) {
42 return $key;
43 });
44
45 $result = $this->controller->index($request, $response);
46
47 static::assertSame(200, $result->getStatusCode());
48 static::assertSame('configure', (string) $result->getBody());
49
50 static::assertSame('Configure - general.title', $assignedVariables['pagetitle']);
51 static::assertSame('general.title', $assignedVariables['title']);
52 static::assertSame('resource.theme', $assignedVariables['theme']);
53 static::assertEmpty($assignedVariables['theme_available']);
54 static::assertSame(['default', 'markdown', 'markdownExtra'], $assignedVariables['formatter_available']);
55 static::assertNotEmpty($assignedVariables['continents']);
56 static::assertNotEmpty($assignedVariables['cities']);
57 static::assertSame('general.retrieve_description', $assignedVariables['retrieve_description']);
58 static::assertSame('privacy.default_private_links', $assignedVariables['private_links_default']);
59 static::assertSame('security.session_protection_disabled', $assignedVariables['session_protection_disabled']);
60 static::assertSame('feed.rss_permalinks', $assignedVariables['enable_rss_permalinks']);
61 static::assertSame('updates.check_updates', $assignedVariables['enable_update_check']);
62 static::assertSame('privacy.hide_public_links', $assignedVariables['hide_public_links']);
63 static::assertSame('api.enabled', $assignedVariables['api_enabled']);
64 static::assertSame('api.secret', $assignedVariables['api_secret']);
65 static::assertCount(5, $assignedVariables['languages']);
66 static::assertArrayHasKey('gd_enabled', $assignedVariables);
67 static::assertSame('thumbnails.mode', $assignedVariables['thumbnails_mode']);
68 }
69
70 /**
71 * Test posting a new config - make sure that everything is saved properly, without errors.
72 */
73 public function testSaveNewConfig(): void
74 {
75 $session = [];
76 $this->assignSessionVars($session);
77
78 $parameters = [
79 'token' => 'token',
80 'continent' => 'Europe',
81 'city' => 'Moscow',
82 'title' => 'Shaarli',
83 'titleLink' => './',
84 'retrieveDescription' => 'on',
85 'theme' => 'vintage',
86 'disablesessionprotection' => null,
87 'privateLinkByDefault' => true,
88 'enableRssPermalinks' => true,
89 'updateCheck' => false,
90 'hidePublicLinks' => 'on',
91 'enableApi' => 'on',
92 'apiSecret' => 'abcdef',
93 'formatter' => 'markdown',
94 'language' => 'fr',
95 'enableThumbnails' => Thumbnailer::MODE_NONE,
96 ];
97
98 $parametersConfigMapping = [
99 'general.timezone' => $parameters['continent'] . '/' . $parameters['city'],
100 'general.title' => $parameters['title'],
101 'general.header_link' => $parameters['titleLink'],
102 'general.retrieve_description' => !!$parameters['retrieveDescription'],
103 'resource.theme' => $parameters['theme'],
104 'security.session_protection_disabled' => !!$parameters['disablesessionprotection'],
105 'privacy.default_private_links' => !!$parameters['privateLinkByDefault'],
106 'feed.rss_permalinks' => !!$parameters['enableRssPermalinks'],
107 'updates.check_updates' => !!$parameters['updateCheck'],
108 'privacy.hide_public_links' => !!$parameters['hidePublicLinks'],
109 'api.enabled' => !!$parameters['enableApi'],
110 'api.secret' => $parameters['apiSecret'],
111 'formatter' => $parameters['formatter'],
112 'translation.language' => $parameters['language'],
113 'thumbnails.mode' => $parameters['enableThumbnails'],
114 ];
115
116 $request = $this->createMock(Request::class);
117 $request
118 ->expects(static::atLeastOnce())
119 ->method('getParam')->willReturnCallback(function (string $key) use ($parameters) {
120 if (false === array_key_exists($key, $parameters)) {
121 static::fail('unknown key: ' . $key);
122 }
123
124 return $parameters[$key];
125 }
126 );
127
128 $response = new Response();
129
130 $this->container->conf = $this->createMock(ConfigManager::class);
131 $this->container->conf
132 ->expects(static::atLeastOnce())
133 ->method('set')
134 ->willReturnCallback(function (string $key, $value) use ($parametersConfigMapping): void {
135 if (false === array_key_exists($key, $parametersConfigMapping)) {
136 static::fail('unknown key: ' . $key);
137 }
138
139 static::assertSame($parametersConfigMapping[$key], $value);
140 }
141 );
142
143 $result = $this->controller->save($request, $response);
144 static::assertSame(302, $result->getStatusCode());
145 static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location'));
146
147 static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
148 static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
149 static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
150 static::assertSame(['Configuration was saved.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]);
151 }
152
153 /**
154 * Test posting a new config - wrong token.
155 */
156 public function testSaveNewConfigWrongToken(): void
157 {
158 $this->container->sessionManager = $this->createMock(SessionManager::class);
159 $this->container->sessionManager->method('checkToken')->willReturn(false);
160
161 $this->container->conf->expects(static::never())->method('set');
162 $this->container->conf->expects(static::never())->method('write');
163
164 $request = $this->createMock(Request::class);
165 $response = new Response();
166
167 $this->expectException(WrongTokenException::class);
168
169 $this->controller->save($request, $response);
170 }
171
172 /**
173 * Test posting a new config - thumbnail activation.
174 */
175 public function testSaveNewConfigThumbnailsActivation(): void
176 {
177 $session = [];
178 $this->assignSessionVars($session);
179
180 $request = $this->createMock(Request::class);
181 $request
182 ->expects(static::atLeastOnce())
183 ->method('getParam')->willReturnCallback(function (string $key) {
184 if ('enableThumbnails' === $key) {
185 return Thumbnailer::MODE_ALL;
186 }
187
188 return $key;
189 })
190 ;
191 $response = new Response();
192
193 $result = $this->controller->save($request, $response);
194
195 static::assertSame(302, $result->getStatusCode());
196 static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location'));
197
198 static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
199 static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
200 static::assertStringContainsString(
201 'You have enabled or changed thumbnails mode',
202 $session[SessionManager::KEY_WARNING_MESSAGES][0]
203 );
204 static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
205 static::assertSame(['Configuration was saved.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]);
206 }
207
208 /**
209 * Test posting a new config - thumbnail activation.
210 */
211 public function testSaveNewConfigThumbnailsAlreadyActive(): void
212 {
213 $session = [];
214 $this->assignSessionVars($session);
215
216 $request = $this->createMock(Request::class);
217 $request
218 ->expects(static::atLeastOnce())
219 ->method('getParam')->willReturnCallback(function (string $key) {
220 if ('enableThumbnails' === $key) {
221 return Thumbnailer::MODE_ALL;
222 }
223
224 return $key;
225 })
226 ;
227 $response = new Response();
228
229 $this->container->conf = $this->createMock(ConfigManager::class);
230 $this->container->conf
231 ->expects(static::atLeastOnce())
232 ->method('get')
233 ->willReturnCallback(function (string $key): string {
234 if ('thumbnails.mode' === $key) {
235 return Thumbnailer::MODE_ALL;
236 }
237
238 return $key;
239 })
240 ;
241
242 $result = $this->controller->save($request, $response);
243
244 static::assertSame(302, $result->getStatusCode());
245 static::assertSame(['/subfolder/admin/configure'], $result->getHeader('Location'));
246
247 static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
248 static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
249 static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
250 static::assertSame(['Configuration was saved.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]);
251 }
252}
diff --git a/tests/front/controller/admin/ExportControllerTest.php b/tests/front/controller/admin/ExportControllerTest.php
new file mode 100644
index 00000000..0e6f2762
--- /dev/null
+++ b/tests/front/controller/admin/ExportControllerTest.php
@@ -0,0 +1,163 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Formatter\BookmarkFormatter;
9use Shaarli\Formatter\BookmarkRawFormatter;
10use Shaarli\Netscape\NetscapeBookmarkUtils;
11use Shaarli\Security\SessionManager;
12use Shaarli\TestCase;
13use Slim\Http\Request;
14use Slim\Http\Response;
15
16class ExportControllerTest extends TestCase
17{
18 use FrontAdminControllerMockHelper;
19
20 /** @var ExportController */
21 protected $controller;
22
23 public function setUp(): void
24 {
25 $this->createContainer();
26
27 $this->controller = new ExportController($this->container);
28 }
29
30 /**
31 * Test displaying export page
32 */
33 public function testIndex(): void
34 {
35 $assignedVariables = [];
36 $this->assignTemplateVars($assignedVariables);
37
38 $request = $this->createMock(Request::class);
39 $response = new Response();
40
41 $result = $this->controller->index($request, $response);
42
43 static::assertSame(200, $result->getStatusCode());
44 static::assertSame('export', (string) $result->getBody());
45
46 static::assertSame('Export - Shaarli', $assignedVariables['pagetitle']);
47 }
48
49 /**
50 * Test posting an export request
51 */
52 public function testExportDefault(): void
53 {
54 $assignedVariables = [];
55 $this->assignTemplateVars($assignedVariables);
56
57 $parameters = [
58 'selection' => 'all',
59 'prepend_note_url' => 'on',
60 ];
61
62 $request = $this->createMock(Request::class);
63 $request->method('getParam')->willReturnCallback(function (string $key) use ($parameters) {
64 return $parameters[$key] ?? null;
65 });
66 $response = new Response();
67
68 $bookmarks = [
69 (new Bookmark())->setUrl('http://link1.tld')->setTitle('Title 1'),
70 (new Bookmark())->setUrl('http://link2.tld')->setTitle('Title 2'),
71 ];
72
73 $this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class);
74 $this->container->netscapeBookmarkUtils
75 ->expects(static::once())
76 ->method('filterAndFormat')
77 ->willReturnCallback(
78 function (
79 BookmarkFormatter $formatter,
80 string $selection,
81 bool $prependNoteUrl,
82 string $indexUrl
83 ) use ($parameters, $bookmarks): array {
84 static::assertInstanceOf(BookmarkRawFormatter::class, $formatter);
85 static::assertSame($parameters['selection'], $selection);
86 static::assertTrue($prependNoteUrl);
87 static::assertSame('http://shaarli/subfolder/', $indexUrl);
88
89 return $bookmarks;
90 }
91 )
92 ;
93
94 $result = $this->controller->export($request, $response);
95
96 static::assertSame(200, $result->getStatusCode());
97 static::assertSame('export.bookmarks', (string) $result->getBody());
98 static::assertSame(['text/html; charset=utf-8'], $result->getHeader('content-type'));
99 static::assertRegExp(
100 '/attachment; filename=bookmarks_all_[\d]{8}_[\d]{6}\.html/',
101 $result->getHeader('content-disposition')[0]
102 );
103
104 static::assertNotEmpty($assignedVariables['date']);
105 static::assertSame(PHP_EOL, $assignedVariables['eol']);
106 static::assertSame('all', $assignedVariables['selection']);
107 static::assertSame($bookmarks, $assignedVariables['links']);
108 }
109
110 /**
111 * Test posting an export request - without selection parameter
112 */
113 public function testExportSelectionMissing(): void
114 {
115 $request = $this->createMock(Request::class);
116 $response = new Response();
117
118 $this->container->sessionManager
119 ->expects(static::once())
120 ->method('setSessionParameter')
121 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Please select an export mode.'])
122 ;
123
124 $result = $this->controller->export($request, $response);
125
126 static::assertSame(302, $result->getStatusCode());
127 static::assertSame(['/subfolder/admin/export'], $result->getHeader('location'));
128 }
129
130 /**
131 * Test posting an export request - without selection parameter
132 */
133 public function testExportErrorEncountered(): void
134 {
135 $parameters = [
136 'selection' => 'all',
137 ];
138
139 $request = $this->createMock(Request::class);
140 $request->method('getParam')->willReturnCallback(function (string $key) use ($parameters) {
141 return $parameters[$key] ?? null;
142 });
143 $response = new Response();
144
145 $this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class);
146 $this->container->netscapeBookmarkUtils
147 ->expects(static::once())
148 ->method('filterAndFormat')
149 ->willThrowException(new \Exception($message = 'error message'));
150 ;
151
152 $this->container->sessionManager
153 ->expects(static::once())
154 ->method('setSessionParameter')
155 ->with(SessionManager::KEY_ERROR_MESSAGES, [$message])
156 ;
157
158 $result = $this->controller->export($request, $response);
159
160 static::assertSame(302, $result->getStatusCode());
161 static::assertSame(['/subfolder/admin/export'], $result->getHeader('location'));
162 }
163}
diff --git a/tests/front/controller/admin/FrontAdminControllerMockHelper.php b/tests/front/controller/admin/FrontAdminControllerMockHelper.php
new file mode 100644
index 00000000..2b9f2ef1
--- /dev/null
+++ b/tests/front/controller/admin/FrontAdminControllerMockHelper.php
@@ -0,0 +1,56 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Container\ShaarliTestContainer;
8use Shaarli\Front\Controller\Visitor\FrontControllerMockHelper;
9use Shaarli\History;
10
11/**
12 * Trait FrontControllerMockHelper
13 *
14 * Helper trait used to initialize the ShaarliContainer and mock its services for admin controller tests.
15 *
16 * @property ShaarliTestContainer $container
17 */
18trait FrontAdminControllerMockHelper
19{
20 use FrontControllerMockHelper {
21 FrontControllerMockHelper::createContainer as parentCreateContainer;
22 }
23
24 /**
25 * Mock the container instance
26 */
27 protected function createContainer(): void
28 {
29 $this->parentCreateContainer();
30
31 $this->container->history = $this->createMock(History::class);
32
33 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
34 $this->container->sessionManager->method('checkToken')->willReturn(true);
35 }
36
37
38 /**
39 * Pass a reference of an array which will be populated by `sessionManager->setSessionParameter`
40 * calls during execution.
41 *
42 * @param mixed $variables Array reference to populate.
43 */
44 protected function assignSessionVars(array &$variables): void
45 {
46 $this->container->sessionManager
47 ->expects(static::atLeastOnce())
48 ->method('setSessionParameter')
49 ->willReturnCallback(function ($key, $value) use (&$variables) {
50 $variables[$key] = $value;
51
52 return $this->container->sessionManager;
53 })
54 ;
55 }
56}
diff --git a/tests/front/controller/admin/ImportControllerTest.php b/tests/front/controller/admin/ImportControllerTest.php
new file mode 100644
index 00000000..c266caa5
--- /dev/null
+++ b/tests/front/controller/admin/ImportControllerTest.php
@@ -0,0 +1,148 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Psr\Http\Message\UploadedFileInterface;
8use Shaarli\Netscape\NetscapeBookmarkUtils;
9use Shaarli\Security\SessionManager;
10use Shaarli\TestCase;
11use Slim\Http\Request;
12use Slim\Http\Response;
13use Slim\Http\UploadedFile;
14
15class ImportControllerTest extends TestCase
16{
17 use FrontAdminControllerMockHelper;
18
19 /** @var ImportController */
20 protected $controller;
21
22 public function setUp(): void
23 {
24 $this->createContainer();
25
26 $this->controller = new ImportController($this->container);
27 }
28
29 /**
30 * Test displaying import page
31 */
32 public function testIndex(): void
33 {
34 $assignedVariables = [];
35 $this->assignTemplateVars($assignedVariables);
36
37 $request = $this->createMock(Request::class);
38 $response = new Response();
39
40 $result = $this->controller->index($request, $response);
41
42 static::assertSame(200, $result->getStatusCode());
43 static::assertSame('import', (string) $result->getBody());
44
45 static::assertSame('Import - Shaarli', $assignedVariables['pagetitle']);
46 static::assertIsInt($assignedVariables['maxfilesize']);
47 static::assertRegExp('/\d+[KM]iB/', $assignedVariables['maxfilesizeHuman']);
48 }
49
50 /**
51 * Test importing a file with default and valid parameters
52 */
53 public function testImportDefault(): void
54 {
55 $parameters = [
56 'abc' => 'def',
57 'other' => 'param',
58 ];
59
60 $requestFile = new UploadedFile('file', 'name', 'type', 123);
61
62 $request = $this->createMock(Request::class);
63 $request->method('getParams')->willReturnCallback(function () use ($parameters) {
64 return $parameters;
65 });
66 $request->method('getUploadedFiles')->willReturn(['filetoupload' => $requestFile]);
67 $response = new Response();
68
69 $this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class);
70 $this->container->netscapeBookmarkUtils
71 ->expects(static::once())
72 ->method('import')
73 ->willReturnCallback(
74 function (
75 array $post,
76 UploadedFileInterface $file
77 ) use ($parameters, $requestFile): string {
78 static::assertSame($parameters, $post);
79 static::assertSame($requestFile, $file);
80
81 return 'status';
82 }
83 )
84 ;
85
86 $this->container->sessionManager
87 ->expects(static::once())
88 ->method('setSessionParameter')
89 ->with(SessionManager::KEY_SUCCESS_MESSAGES, ['status'])
90 ;
91
92 $result = $this->controller->import($request, $response);
93
94 static::assertSame(302, $result->getStatusCode());
95 static::assertSame(['/subfolder/admin/import'], $result->getHeader('location'));
96 }
97
98 /**
99 * Test posting an import request - without import file
100 */
101 public function testImportFileMissing(): void
102 {
103 $request = $this->createMock(Request::class);
104 $response = new Response();
105
106 $this->container->sessionManager
107 ->expects(static::once())
108 ->method('setSessionParameter')
109 ->with(SessionManager::KEY_ERROR_MESSAGES, ['No import file provided.'])
110 ;
111
112 $result = $this->controller->import($request, $response);
113
114 static::assertSame(302, $result->getStatusCode());
115 static::assertSame(['/subfolder/admin/import'], $result->getHeader('location'));
116 }
117
118 /**
119 * Test posting an import request - with an empty file
120 */
121 public function testImportEmptyFile(): void
122 {
123 $requestFile = new UploadedFile('file', 'name', 'type', 0);
124
125 $request = $this->createMock(Request::class);
126 $request->method('getUploadedFiles')->willReturn(['filetoupload' => $requestFile]);
127 $response = new Response();
128
129 $this->container->netscapeBookmarkUtils = $this->createMock(NetscapeBookmarkUtils::class);
130 $this->container->netscapeBookmarkUtils->expects(static::never())->method('filterAndFormat');
131
132 $this->container->sessionManager
133 ->expects(static::once())
134 ->method('setSessionParameter')
135 ->willReturnCallback(function (string $key, array $value): SessionManager {
136 static::assertSame(SessionManager::KEY_ERROR_MESSAGES, $key);
137 static::assertStringStartsWith('The file you are trying to upload is probably bigger', $value[0]);
138
139 return $this->container->sessionManager;
140 })
141 ;
142
143 $result = $this->controller->import($request, $response);
144
145 static::assertSame(302, $result->getStatusCode());
146 static::assertSame(['/subfolder/admin/import'], $result->getHeader('location'));
147 }
148}
diff --git a/tests/front/controller/admin/LogoutControllerTest.php b/tests/front/controller/admin/LogoutControllerTest.php
new file mode 100644
index 00000000..94e53019
--- /dev/null
+++ b/tests/front/controller/admin/LogoutControllerTest.php
@@ -0,0 +1,50 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Security\CookieManager;
8use Shaarli\Security\SessionManager;
9use Shaarli\TestCase;
10use Slim\Http\Request;
11use Slim\Http\Response;
12
13class LogoutControllerTest extends TestCase
14{
15 use FrontAdminControllerMockHelper;
16
17 /** @var LogoutController */
18 protected $controller;
19
20 public function setUp(): void
21 {
22 $this->createContainer();
23
24 $this->controller = new LogoutController($this->container);
25 }
26
27 public function testValidControllerInvoke(): void
28 {
29 $request = $this->createMock(Request::class);
30 $response = new Response();
31
32 $this->container->pageCacheManager->expects(static::once())->method('invalidateCaches');
33
34 $this->container->sessionManager = $this->createMock(SessionManager::class);
35 $this->container->sessionManager->expects(static::once())->method('logout');
36
37 $this->container->cookieManager = $this->createMock(CookieManager::class);
38 $this->container->cookieManager
39 ->expects(static::once())
40 ->method('setCookieParameter')
41 ->with(CookieManager::STAY_SIGNED_IN, 'false', 0, '/subfolder/')
42 ;
43
44 $result = $this->controller->index($request, $response);
45
46 static::assertInstanceOf(Response::class, $result);
47 static::assertSame(302, $result->getStatusCode());
48 static::assertSame(['/subfolder/'], $result->getHeader('location'));
49 }
50}
diff --git a/tests/front/controller/admin/ManageTagControllerTest.php b/tests/front/controller/admin/ManageTagControllerTest.php
new file mode 100644
index 00000000..8a0ff7a9
--- /dev/null
+++ b/tests/front/controller/admin/ManageTagControllerTest.php
@@ -0,0 +1,272 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\BookmarkFilter;
9use Shaarli\Front\Exception\WrongTokenException;
10use Shaarli\Security\SessionManager;
11use Shaarli\TestCase;
12use Slim\Http\Request;
13use Slim\Http\Response;
14
15class ManageTagControllerTest extends TestCase
16{
17 use FrontAdminControllerMockHelper;
18
19 /** @var ManageTagController */
20 protected $controller;
21
22 public function setUp(): void
23 {
24 $this->createContainer();
25
26 $this->controller = new ManageTagController($this->container);
27 }
28
29 /**
30 * Test displaying manage tag page
31 */
32 public function testIndex(): void
33 {
34 $assignedVariables = [];
35 $this->assignTemplateVars($assignedVariables);
36
37 $request = $this->createMock(Request::class);
38 $request->method('getParam')->with('fromtag')->willReturn('fromtag');
39 $response = new Response();
40
41 $result = $this->controller->index($request, $response);
42
43 static::assertSame(200, $result->getStatusCode());
44 static::assertSame('changetag', (string) $result->getBody());
45
46 static::assertSame('fromtag', $assignedVariables['fromtag']);
47 static::assertSame('Manage tags - Shaarli', $assignedVariables['pagetitle']);
48 }
49
50 /**
51 * Test posting a tag update - rename tag - valid info provided.
52 */
53 public function testSaveRenameTagValid(): void
54 {
55 $session = [];
56 $this->assignSessionVars($session);
57
58 $requestParameters = [
59 'renametag' => 'rename',
60 'fromtag' => 'old-tag',
61 'totag' => 'new-tag',
62 ];
63 $request = $this->createMock(Request::class);
64 $request
65 ->expects(static::atLeastOnce())
66 ->method('getParam')
67 ->willReturnCallback(function (string $key) use ($requestParameters): ?string {
68 return $requestParameters[$key] ?? null;
69 })
70 ;
71 $response = new Response();
72
73 $bookmark1 = $this->createMock(Bookmark::class);
74 $bookmark2 = $this->createMock(Bookmark::class);
75 $this->container->bookmarkService
76 ->expects(static::once())
77 ->method('search')
78 ->with(['searchtags' => 'old-tag'], BookmarkFilter::$ALL, true)
79 ->willReturnCallback(function () use ($bookmark1, $bookmark2): array {
80 $bookmark1->expects(static::once())->method('renameTag')->with('old-tag', 'new-tag');
81 $bookmark2->expects(static::once())->method('renameTag')->with('old-tag', 'new-tag');
82
83 return [$bookmark1, $bookmark2];
84 })
85 ;
86 $this->container->bookmarkService
87 ->expects(static::exactly(2))
88 ->method('set')
89 ->withConsecutive([$bookmark1, false], [$bookmark2, false])
90 ;
91 $this->container->bookmarkService->expects(static::once())->method('save');
92
93 $result = $this->controller->save($request, $response);
94
95 static::assertSame(302, $result->getStatusCode());
96 static::assertSame(['/subfolder/?searchtags=new-tag'], $result->getHeader('location'));
97
98 static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
99 static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
100 static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
101 static::assertSame(['The tag was renamed in 2 bookmarks.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]);
102 }
103
104 /**
105 * Test posting a tag update - delete tag - valid info provided.
106 */
107 public function testSaveDeleteTagValid(): void
108 {
109 $session = [];
110 $this->assignSessionVars($session);
111
112 $requestParameters = [
113 'deletetag' => 'delete',
114 'fromtag' => 'old-tag',
115 ];
116 $request = $this->createMock(Request::class);
117 $request
118 ->expects(static::atLeastOnce())
119 ->method('getParam')
120 ->willReturnCallback(function (string $key) use ($requestParameters): ?string {
121 return $requestParameters[$key] ?? null;
122 })
123 ;
124 $response = new Response();
125
126 $bookmark1 = $this->createMock(Bookmark::class);
127 $bookmark2 = $this->createMock(Bookmark::class);
128 $this->container->bookmarkService
129 ->expects(static::once())
130 ->method('search')
131 ->with(['searchtags' => 'old-tag'], BookmarkFilter::$ALL, true)
132 ->willReturnCallback(function () use ($bookmark1, $bookmark2): array {
133 $bookmark1->expects(static::once())->method('deleteTag')->with('old-tag');
134 $bookmark2->expects(static::once())->method('deleteTag')->with('old-tag');
135
136 return [$bookmark1, $bookmark2];
137 })
138 ;
139 $this->container->bookmarkService
140 ->expects(static::exactly(2))
141 ->method('set')
142 ->withConsecutive([$bookmark1, false], [$bookmark2, false])
143 ;
144 $this->container->bookmarkService->expects(static::once())->method('save');
145
146 $result = $this->controller->save($request, $response);
147
148 static::assertSame(302, $result->getStatusCode());
149 static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
150
151 static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
152 static::assertArrayNotHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
153 static::assertArrayHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
154 static::assertSame(['The tag was removed from 2 bookmarks.'], $session[SessionManager::KEY_SUCCESS_MESSAGES]);
155 }
156
157 /**
158 * Test posting a tag update - wrong token.
159 */
160 public function testSaveWrongToken(): void
161 {
162 $this->container->sessionManager = $this->createMock(SessionManager::class);
163 $this->container->sessionManager->method('checkToken')->willReturn(false);
164
165 $this->container->conf->expects(static::never())->method('set');
166 $this->container->conf->expects(static::never())->method('write');
167
168 $request = $this->createMock(Request::class);
169 $response = new Response();
170
171 $this->expectException(WrongTokenException::class);
172
173 $this->controller->save($request, $response);
174 }
175
176 /**
177 * Test posting a tag update - rename tag - missing "FROM" tag.
178 */
179 public function testSaveRenameTagMissingFrom(): void
180 {
181 $session = [];
182 $this->assignSessionVars($session);
183
184 $requestParameters = [
185 'renametag' => 'rename',
186 ];
187 $request = $this->createMock(Request::class);
188 $request
189 ->expects(static::atLeastOnce())
190 ->method('getParam')
191 ->willReturnCallback(function (string $key) use ($requestParameters): ?string {
192 return $requestParameters[$key] ?? null;
193 })
194 ;
195 $response = new Response();
196
197 $result = $this->controller->save($request, $response);
198
199 static::assertSame(302, $result->getStatusCode());
200 static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
201
202 static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
203 static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
204 static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
205 static::assertSame(['Invalid tags provided.'], $session[SessionManager::KEY_WARNING_MESSAGES]);
206 }
207
208 /**
209 * Test posting a tag update - delete tag - missing "FROM" tag.
210 */
211 public function testSaveDeleteTagMissingFrom(): void
212 {
213 $session = [];
214 $this->assignSessionVars($session);
215
216 $requestParameters = [
217 'deletetag' => 'delete',
218 ];
219 $request = $this->createMock(Request::class);
220 $request
221 ->expects(static::atLeastOnce())
222 ->method('getParam')
223 ->willReturnCallback(function (string $key) use ($requestParameters): ?string {
224 return $requestParameters[$key] ?? null;
225 })
226 ;
227 $response = new Response();
228
229 $result = $this->controller->save($request, $response);
230
231 static::assertSame(302, $result->getStatusCode());
232 static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
233
234 static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
235 static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
236 static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
237 static::assertSame(['Invalid tags provided.'], $session[SessionManager::KEY_WARNING_MESSAGES]);
238 }
239
240 /**
241 * Test posting a tag update - rename tag - missing "TO" tag.
242 */
243 public function testSaveRenameTagMissingTo(): void
244 {
245 $session = [];
246 $this->assignSessionVars($session);
247
248 $requestParameters = [
249 'renametag' => 'rename',
250 'fromtag' => 'old-tag'
251 ];
252 $request = $this->createMock(Request::class);
253 $request
254 ->expects(static::atLeastOnce())
255 ->method('getParam')
256 ->willReturnCallback(function (string $key) use ($requestParameters): ?string {
257 return $requestParameters[$key] ?? null;
258 })
259 ;
260 $response = new Response();
261
262 $result = $this->controller->save($request, $response);
263
264 static::assertSame(302, $result->getStatusCode());
265 static::assertSame(['/subfolder/admin/tags'], $result->getHeader('location'));
266
267 static::assertArrayNotHasKey(SessionManager::KEY_ERROR_MESSAGES, $session);
268 static::assertArrayHasKey(SessionManager::KEY_WARNING_MESSAGES, $session);
269 static::assertArrayNotHasKey(SessionManager::KEY_SUCCESS_MESSAGES, $session);
270 static::assertSame(['Invalid tags provided.'], $session[SessionManager::KEY_WARNING_MESSAGES]);
271 }
272}
diff --git a/tests/front/controller/admin/PasswordControllerTest.php b/tests/front/controller/admin/PasswordControllerTest.php
new file mode 100644
index 00000000..58f47b49
--- /dev/null
+++ b/tests/front/controller/admin/PasswordControllerTest.php
@@ -0,0 +1,203 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Config\ConfigManager;
8use Shaarli\Front\Exception\OpenShaarliPasswordException;
9use Shaarli\Front\Exception\WrongTokenException;
10use Shaarli\Security\SessionManager;
11use Shaarli\TestCase;
12use Slim\Http\Request;
13use Slim\Http\Response;
14
15class PasswordControllerTest extends TestCase
16{
17 use FrontAdminControllerMockHelper;
18
19 /** @var PasswordController */
20 protected $controller;
21
22 /** @var mixed[] Variables assigned to the template */
23 protected $assignedVariables = [];
24
25 public function setUp(): void
26 {
27 $this->createContainer();
28 $this->assignTemplateVars($this->assignedVariables);
29
30 $this->controller = new PasswordController($this->container);
31 }
32
33 /**
34 * Test displaying the change password page.
35 */
36 public function testGetPage(): void
37 {
38 $request = $this->createMock(Request::class);
39 $response = new Response();
40
41 $result = $this->controller->index($request, $response);
42
43 static::assertSame(200, $result->getStatusCode());
44 static::assertSame('changepassword', (string) $result->getBody());
45 static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
46 }
47
48 /**
49 * Change the password with valid parameters
50 */
51 public function testPostNewPasswordDefault(): void
52 {
53 $request = $this->createMock(Request::class);
54 $request->method('getParam')->willReturnCallback(function (string $key): string {
55 if ('oldpassword' === $key) {
56 return 'old';
57 }
58 if ('setpassword' === $key) {
59 return 'new';
60 }
61
62 return $key;
63 });
64 $response = new Response();
65
66 $this->container->conf = $this->createMock(ConfigManager::class);
67 $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
68 if ('credentials.hash' === $key) {
69 return sha1('old' . 'credentials.login' . 'credentials.salt');
70 }
71
72 return strpos($key, 'credentials') !== false ? $key : $default;
73 });
74 $this->container->conf->expects(static::once())->method('write')->with(true);
75
76 $this->container->conf
77 ->method('set')
78 ->willReturnCallback(function (string $key, string $value) {
79 if ('credentials.hash' === $key) {
80 static::assertSame(sha1('new' . 'credentials.login' . 'credentials.salt'), $value);
81 }
82 })
83 ;
84
85 $result = $this->controller->change($request, $response);
86
87 static::assertSame(200, $result->getStatusCode());
88 static::assertSame('changepassword', (string) $result->getBody());
89 static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
90 }
91
92 /**
93 * Change the password with a wrong existing password
94 */
95 public function testPostNewPasswordWrongOldPassword(): void
96 {
97 $request = $this->createMock(Request::class);
98 $request->method('getParam')->willReturnCallback(function (string $key): string {
99 if ('oldpassword' === $key) {
100 return 'wrong';
101 }
102 if ('setpassword' === $key) {
103 return 'new';
104 }
105
106 return $key;
107 });
108 $response = new Response();
109
110 $this->container->conf = $this->createMock(ConfigManager::class);
111 $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
112 if ('credentials.hash' === $key) {
113 return sha1('old' . 'credentials.login' . 'credentials.salt');
114 }
115
116 return strpos($key, 'credentials') !== false ? $key : $default;
117 });
118
119 $this->container->conf->expects(static::never())->method('set');
120 $this->container->conf->expects(static::never())->method('write');
121
122 $this->container->sessionManager
123 ->expects(static::once())
124 ->method('setSessionParameter')
125 ->with(SessionManager::KEY_ERROR_MESSAGES, ['The old password is not correct.'])
126 ;
127
128 $result = $this->controller->change($request, $response);
129
130 static::assertSame(400, $result->getStatusCode());
131 static::assertSame('changepassword', (string) $result->getBody());
132 static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
133 }
134
135 /**
136 * Change the password with a wrong existing password
137 */
138 public function testPostNewPasswordWrongToken(): void
139 {
140 $this->container->sessionManager = $this->createMock(SessionManager::class);
141 $this->container->sessionManager->method('checkToken')->willReturn(false);
142
143 $this->container->conf->expects(static::never())->method('set');
144 $this->container->conf->expects(static::never())->method('write');
145
146 $request = $this->createMock(Request::class);
147 $response = new Response();
148
149 $this->expectException(WrongTokenException::class);
150
151 $this->controller->change($request, $response);
152 }
153
154 /**
155 * Change the password with an empty new password
156 */
157 public function testPostNewEmptyPassword(): void
158 {
159 $this->container->sessionManager
160 ->expects(static::once())
161 ->method('setSessionParameter')
162 ->with(SessionManager::KEY_ERROR_MESSAGES, ['You must provide the current and new password to change it.'])
163 ;
164
165 $this->container->conf->expects(static::never())->method('set');
166 $this->container->conf->expects(static::never())->method('write');
167
168 $request = $this->createMock(Request::class);
169 $request->method('getParam')->willReturnCallback(function (string $key): string {
170 if ('oldpassword' === $key) {
171 return 'old';
172 }
173 if ('setpassword' === $key) {
174 return '';
175 }
176
177 return $key;
178 });
179 $response = new Response();
180
181 $result = $this->controller->change($request, $response);
182
183 static::assertSame(400, $result->getStatusCode());
184 static::assertSame('changepassword', (string) $result->getBody());
185 static::assertSame('Change password - Shaarli', $this->assignedVariables['pagetitle']);
186 }
187
188 /**
189 * Change the password on an open shaarli
190 */
191 public function testPostNewPasswordOnOpenShaarli(): void
192 {
193 $this->container->conf = $this->createMock(ConfigManager::class);
194 $this->container->conf->method('get')->with('security.open_shaarli')->willReturn(true);
195
196 $request = $this->createMock(Request::class);
197 $response = new Response();
198
199 $this->expectException(OpenShaarliPasswordException::class);
200
201 $this->controller->change($request, $response);
202 }
203}
diff --git a/tests/front/controller/admin/PluginsControllerTest.php b/tests/front/controller/admin/PluginsControllerTest.php
new file mode 100644
index 00000000..974d614d
--- /dev/null
+++ b/tests/front/controller/admin/PluginsControllerTest.php
@@ -0,0 +1,205 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Config\ConfigManager;
8use Shaarli\Front\Exception\WrongTokenException;
9use Shaarli\Plugin\PluginManager;
10use Shaarli\Security\SessionManager;
11use Shaarli\TestCase;
12use Slim\Http\Request;
13use Slim\Http\Response;
14
15class PluginsControllerTest extends TestCase
16{
17 use FrontAdminControllerMockHelper;
18
19 const PLUGIN_NAMES = ['plugin1', 'plugin2', 'plugin3', 'plugin4'];
20
21 /** @var PluginsController */
22 protected $controller;
23
24 public function setUp(): void
25 {
26 $this->createContainer();
27
28 $this->controller = new PluginsController($this->container);
29
30 mkdir($path = __DIR__ . '/folder');
31 PluginManager::$PLUGINS_PATH = $path;
32 array_map(function (string $plugin) use ($path) { touch($path . '/' . $plugin); }, static::PLUGIN_NAMES);
33 }
34
35 public function tearDown(): void
36 {
37 $path = __DIR__ . '/folder';
38 array_map(function (string $plugin) use ($path) { unlink($path . '/' . $plugin); }, static::PLUGIN_NAMES);
39 rmdir($path);
40 }
41
42 /**
43 * Test displaying plugins admin page
44 */
45 public function testIndex(): void
46 {
47 $assignedVariables = [];
48 $this->assignTemplateVars($assignedVariables);
49
50 $request = $this->createMock(Request::class);
51 $response = new Response();
52
53 $data = [
54 'plugin1' => ['order' => 2, 'other' => 'field'],
55 'plugin2' => ['order' => 1],
56 'plugin3' => ['order' => false, 'abc' => 'def'],
57 'plugin4' => [],
58 ];
59
60 $this->container->pluginManager
61 ->expects(static::once())
62 ->method('getPluginsMeta')
63 ->willReturn($data);
64
65 $result = $this->controller->index($request, $response);
66
67 static::assertSame(200, $result->getStatusCode());
68 static::assertSame('pluginsadmin', (string) $result->getBody());
69
70 static::assertSame('Plugin Administration - Shaarli', $assignedVariables['pagetitle']);
71 static::assertSame(
72 ['plugin2' => $data['plugin2'], 'plugin1' => $data['plugin1']],
73 $assignedVariables['enabledPlugins']
74 );
75 static::assertSame(
76 ['plugin3' => $data['plugin3'], 'plugin4' => $data['plugin4']],
77 $assignedVariables['disabledPlugins']
78 );
79 }
80
81 /**
82 * Test save plugins admin page
83 */
84 public function testSaveEnabledPlugins(): void
85 {
86 $parameters = [
87 'plugin1' => 'on',
88 'order_plugin1' => '2',
89 'plugin2' => 'on',
90 ];
91
92 $request = $this->createMock(Request::class);
93 $request
94 ->expects(static::atLeastOnce())
95 ->method('getParams')
96 ->willReturnCallback(function () use ($parameters): array {
97 return $parameters;
98 })
99 ;
100 $response = new Response();
101
102 $this->container->pluginManager
103 ->expects(static::once())
104 ->method('executeHooks')
105 ->with('save_plugin_parameters', $parameters)
106 ;
107 $this->container->conf
108 ->expects(static::atLeastOnce())
109 ->method('set')
110 ->with('general.enabled_plugins', ['plugin1', 'plugin2'])
111 ;
112
113 $result = $this->controller->save($request, $response);
114
115 static::assertSame(302, $result->getStatusCode());
116 static::assertSame(['/subfolder/admin/plugins'], $result->getHeader('location'));
117 }
118
119 /**
120 * Test save plugin parameters
121 */
122 public function testSavePluginParameters(): void
123 {
124 $parameters = [
125 'parameters_form' => true,
126 'parameter1' => 'blip',
127 'parameter2' => 'blop',
128 'token' => 'this parameter should not be saved'
129 ];
130
131 $request = $this->createMock(Request::class);
132 $request
133 ->expects(static::atLeastOnce())
134 ->method('getParams')
135 ->willReturnCallback(function () use ($parameters): array {
136 return $parameters;
137 })
138 ;
139 $response = new Response();
140
141 $this->container->pluginManager
142 ->expects(static::once())
143 ->method('executeHooks')
144 ->with('save_plugin_parameters', $parameters)
145 ;
146 $this->container->conf
147 ->expects(static::exactly(2))
148 ->method('set')
149 ->withConsecutive(['plugins.parameter1', 'blip'], ['plugins.parameter2', 'blop'])
150 ;
151
152 $result = $this->controller->save($request, $response);
153
154 static::assertSame(302, $result->getStatusCode());
155 static::assertSame(['/subfolder/admin/plugins'], $result->getHeader('location'));
156 }
157
158 /**
159 * Test save plugin parameters - error encountered
160 */
161 public function testSaveWithError(): void
162 {
163 $request = $this->createMock(Request::class);
164 $response = new Response();
165
166 $this->container->conf = $this->createMock(ConfigManager::class);
167 $this->container->conf
168 ->expects(static::atLeastOnce())
169 ->method('write')
170 ->willThrowException(new \Exception($message = 'error message'))
171 ;
172
173 $this->container->sessionManager = $this->createMock(SessionManager::class);
174 $this->container->sessionManager->method('checkToken')->willReturn(true);
175 $this->container->sessionManager
176 ->expects(static::once())
177 ->method('setSessionParameter')
178 ->with(
179 SessionManager::KEY_ERROR_MESSAGES,
180 ['Error while saving plugin configuration: ' . PHP_EOL . $message]
181 )
182 ;
183
184 $result = $this->controller->save($request, $response);
185
186 static::assertSame(302, $result->getStatusCode());
187 static::assertSame(['/subfolder/admin/plugins'], $result->getHeader('location'));
188 }
189
190 /**
191 * Test save plugin parameters - wrong token
192 */
193 public function testSaveWrongToken(): void
194 {
195 $this->container->sessionManager = $this->createMock(SessionManager::class);
196 $this->container->sessionManager->method('checkToken')->willReturn(false);
197
198 $request = $this->createMock(Request::class);
199 $response = new Response();
200
201 $this->expectException(WrongTokenException::class);
202
203 $this->controller->save($request, $response);
204 }
205}
diff --git a/tests/front/controller/admin/ServerControllerTest.php b/tests/front/controller/admin/ServerControllerTest.php
new file mode 100644
index 00000000..355cce7d
--- /dev/null
+++ b/tests/front/controller/admin/ServerControllerTest.php
@@ -0,0 +1,184 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Config\ConfigManager;
8use Shaarli\Security\SessionManager;
9use Shaarli\TestCase;
10use Slim\Http\Request;
11use Slim\Http\Response;
12
13/**
14 * Test Server administration controller.
15 */
16class ServerControllerTest extends TestCase
17{
18 use FrontAdminControllerMockHelper;
19
20 /** @var ServerController */
21 protected $controller;
22
23 public function setUp(): void
24 {
25 $this->createContainer();
26
27 $this->controller = new ServerController($this->container);
28
29 // initialize dummy cache
30 @mkdir('sandbox/');
31 foreach (['pagecache', 'tmp', 'cache'] as $folder) {
32 @mkdir('sandbox/' . $folder);
33 @touch('sandbox/' . $folder . '/.htaccess');
34 @touch('sandbox/' . $folder . '/1');
35 @touch('sandbox/' . $folder . '/2');
36 }
37 }
38
39 public function tearDown(): void
40 {
41 foreach (['pagecache', 'tmp', 'cache'] as $folder) {
42 @unlink('sandbox/' . $folder . '/.htaccess');
43 @unlink('sandbox/' . $folder . '/1');
44 @unlink('sandbox/' . $folder . '/2');
45 @rmdir('sandbox/' . $folder);
46 }
47 }
48
49 /**
50 * Test default display of server administration page.
51 */
52 public function testIndex(): void
53 {
54 $request = $this->createMock(Request::class);
55 $response = new Response();
56
57 // Save RainTPL assigned variables
58 $assignedVariables = [];
59 $this->assignTemplateVars($assignedVariables);
60
61 $result = $this->controller->index($request, $response);
62
63 static::assertSame(200, $result->getStatusCode());
64 static::assertSame('server', (string) $result->getBody());
65
66 static::assertSame(PHP_VERSION, $assignedVariables['php_version']);
67 static::assertArrayHasKey('php_has_reached_eol', $assignedVariables);
68 static::assertArrayHasKey('php_eol', $assignedVariables);
69 static::assertArrayHasKey('php_extensions', $assignedVariables);
70 static::assertArrayHasKey('permissions', $assignedVariables);
71 static::assertEmpty($assignedVariables['permissions']);
72
73 static::assertRegExp(
74 '#https://github\.com/shaarli/Shaarli/releases/tag/v\d+\.\d+\.\d+#',
75 $assignedVariables['release_url']
76 );
77 static::assertRegExp('#v\d+\.\d+\.\d+#', $assignedVariables['latest_version']);
78 static::assertRegExp('#(v\d+\.\d+\.\d+|dev)#', $assignedVariables['current_version']);
79 static::assertArrayHasKey('index_url', $assignedVariables);
80 static::assertArrayHasKey('client_ip', $assignedVariables);
81 static::assertArrayHasKey('trusted_proxies', $assignedVariables);
82
83 static::assertSame('Server administration - Shaarli', $assignedVariables['pagetitle']);
84 }
85
86 /**
87 * Test clearing the main cache
88 */
89 public function testClearMainCache(): void
90 {
91 $this->container->conf = $this->createMock(ConfigManager::class);
92 $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
93 if ($key === 'resource.page_cache') {
94 return 'sandbox/pagecache';
95 } elseif ($key === 'resource.raintpl_tmp') {
96 return 'sandbox/tmp';
97 } elseif ($key === 'resource.thumbnails_cache') {
98 return 'sandbox/cache';
99 } else {
100 return $default;
101 }
102 });
103
104 $this->container->sessionManager
105 ->expects(static::once())
106 ->method('setSessionParameter')
107 ->with(SessionManager::KEY_SUCCESS_MESSAGES, ['Shaarli\'s cache folder has been cleared!'])
108 ;
109
110 $request = $this->createMock(Request::class);
111 $request->method('getQueryParam')->with('type')->willReturn('main');
112 $response = new Response();
113
114 $result = $this->controller->clearCache($request, $response);
115
116 static::assertSame(302, $result->getStatusCode());
117 static::assertSame('/subfolder/admin/server', (string) $result->getHeaderLine('Location'));
118
119 static::assertFileNotExists('sandbox/pagecache/1');
120 static::assertFileNotExists('sandbox/pagecache/2');
121 static::assertFileNotExists('sandbox/tmp/1');
122 static::assertFileNotExists('sandbox/tmp/2');
123
124 static::assertFileExists('sandbox/pagecache/.htaccess');
125 static::assertFileExists('sandbox/tmp/.htaccess');
126 static::assertFileExists('sandbox/cache');
127 static::assertFileExists('sandbox/cache/.htaccess');
128 static::assertFileExists('sandbox/cache/1');
129 static::assertFileExists('sandbox/cache/2');
130 }
131
132 /**
133 * Test clearing thumbnails cache
134 */
135 public function testClearThumbnailsCache(): void
136 {
137 $this->container->conf = $this->createMock(ConfigManager::class);
138 $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
139 if ($key === 'resource.page_cache') {
140 return 'sandbox/pagecache';
141 } elseif ($key === 'resource.raintpl_tmp') {
142 return 'sandbox/tmp';
143 } elseif ($key === 'resource.thumbnails_cache') {
144 return 'sandbox/cache';
145 } else {
146 return $default;
147 }
148 });
149
150 $this->container->sessionManager
151 ->expects(static::once())
152 ->method('setSessionParameter')
153 ->willReturnCallback(function (string $key, array $value): SessionManager {
154 static::assertSame(SessionManager::KEY_WARNING_MESSAGES, $key);
155 static::assertCount(1, $value);
156 static::assertStringStartsWith('Thumbnails cache has been cleared.', $value[0]);
157
158 return $this->container->sessionManager;
159 });
160 ;
161
162 $request = $this->createMock(Request::class);
163 $request->method('getQueryParam')->with('type')->willReturn('thumbnails');
164 $response = new Response();
165
166 $result = $this->controller->clearCache($request, $response);
167
168 static::assertSame(302, $result->getStatusCode());
169 static::assertSame('/subfolder/admin/server', (string) $result->getHeaderLine('Location'));
170
171 static::assertFileNotExists('sandbox/cache/1');
172 static::assertFileNotExists('sandbox/cache/2');
173
174 static::assertFileExists('sandbox/cache/.htaccess');
175 static::assertFileExists('sandbox/pagecache');
176 static::assertFileExists('sandbox/pagecache/.htaccess');
177 static::assertFileExists('sandbox/pagecache/1');
178 static::assertFileExists('sandbox/pagecache/2');
179 static::assertFileExists('sandbox/tmp');
180 static::assertFileExists('sandbox/tmp/.htaccess');
181 static::assertFileExists('sandbox/tmp/1');
182 static::assertFileExists('sandbox/tmp/2');
183 }
184}
diff --git a/tests/front/controller/admin/SessionFilterControllerTest.php b/tests/front/controller/admin/SessionFilterControllerTest.php
new file mode 100644
index 00000000..712a625b
--- /dev/null
+++ b/tests/front/controller/admin/SessionFilterControllerTest.php
@@ -0,0 +1,177 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Security\LoginManager;
8use Shaarli\Security\SessionManager;
9use Shaarli\TestCase;
10use Slim\Http\Request;
11use Slim\Http\Response;
12
13class SessionFilterControllerTest extends TestCase
14{
15 use FrontAdminControllerMockHelper;
16
17 /** @var SessionFilterController */
18 protected $controller;
19
20 public function setUp(): void
21 {
22 $this->createContainer();
23
24 $this->controller = new SessionFilterController($this->container);
25 }
26
27 /**
28 * Visibility - Default call for private filter while logged in without current value
29 */
30 public function testVisibility(): void
31 {
32 $arg = ['visibility' => 'private'];
33
34 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
35
36 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
37 $this->container->sessionManager
38 ->expects(static::once())
39 ->method('setSessionParameter')
40 ->with(SessionManager::KEY_VISIBILITY, 'private')
41 ;
42
43 $request = $this->createMock(Request::class);
44 $response = new Response();
45
46 $result = $this->controller->visibility($request, $response, $arg);
47
48 static::assertInstanceOf(Response::class, $result);
49 static::assertSame(302, $result->getStatusCode());
50 static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
51 }
52
53 /**
54 * Visibility - Toggle off private visibility
55 */
56 public function testVisibilityToggleOff(): void
57 {
58 $arg = ['visibility' => 'private'];
59
60 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
61
62 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
63 $this->container->sessionManager
64 ->method('getSessionParameter')
65 ->with(SessionManager::KEY_VISIBILITY)
66 ->willReturn('private')
67 ;
68 $this->container->sessionManager
69 ->expects(static::never())
70 ->method('setSessionParameter')
71 ;
72 $this->container->sessionManager
73 ->expects(static::once())
74 ->method('deleteSessionParameter')
75 ->with(SessionManager::KEY_VISIBILITY)
76 ;
77
78 $request = $this->createMock(Request::class);
79 $response = new Response();
80
81 $result = $this->controller->visibility($request, $response, $arg);
82
83 static::assertInstanceOf(Response::class, $result);
84 static::assertSame(302, $result->getStatusCode());
85 static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
86 }
87
88 /**
89 * Visibility - Change private to public
90 */
91 public function testVisibilitySwitch(): void
92 {
93 $arg = ['visibility' => 'private'];
94
95 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
96 $this->container->sessionManager
97 ->method('getSessionParameter')
98 ->with(SessionManager::KEY_VISIBILITY)
99 ->willReturn('public')
100 ;
101 $this->container->sessionManager
102 ->expects(static::once())
103 ->method('setSessionParameter')
104 ->with(SessionManager::KEY_VISIBILITY, 'private')
105 ;
106
107 $request = $this->createMock(Request::class);
108 $response = new Response();
109
110 $result = $this->controller->visibility($request, $response, $arg);
111
112 static::assertInstanceOf(Response::class, $result);
113 static::assertSame(302, $result->getStatusCode());
114 static::assertSame(['/subfolder/'], $result->getHeader('location'));
115 }
116
117 /**
118 * Visibility - With invalid value - should remove any visibility setting
119 */
120 public function testVisibilityInvalidValue(): void
121 {
122 $arg = ['visibility' => 'test'];
123
124 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
125
126 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
127 $this->container->sessionManager
128 ->expects(static::never())
129 ->method('setSessionParameter')
130 ;
131 $this->container->sessionManager
132 ->expects(static::once())
133 ->method('deleteSessionParameter')
134 ->with(SessionManager::KEY_VISIBILITY)
135 ;
136
137 $request = $this->createMock(Request::class);
138 $response = new Response();
139
140 $result = $this->controller->visibility($request, $response, $arg);
141
142 static::assertInstanceOf(Response::class, $result);
143 static::assertSame(302, $result->getStatusCode());
144 static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
145 }
146
147 /**
148 * Visibility - Try to change visibility while logged out
149 */
150 public function testVisibilityLoggedOut(): void
151 {
152 $arg = ['visibility' => 'test'];
153
154 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
155
156 $this->container->loginManager = $this->createMock(LoginManager::class);
157 $this->container->loginManager->method('isLoggedIn')->willReturn(false);
158 $this->container->sessionManager
159 ->expects(static::never())
160 ->method('setSessionParameter')
161 ;
162 $this->container->sessionManager
163 ->expects(static::never())
164 ->method('deleteSessionParameter')
165 ->with(SessionManager::KEY_VISIBILITY)
166 ;
167
168 $request = $this->createMock(Request::class);
169 $response = new Response();
170
171 $result = $this->controller->visibility($request, $response, $arg);
172
173 static::assertInstanceOf(Response::class, $result);
174 static::assertSame(302, $result->getStatusCode());
175 static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
176 }
177}
diff --git a/tests/front/controller/admin/ShaareAddControllerTest.php b/tests/front/controller/admin/ShaareAddControllerTest.php
new file mode 100644
index 00000000..a27ebe64
--- /dev/null
+++ b/tests/front/controller/admin/ShaareAddControllerTest.php
@@ -0,0 +1,97 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Config\ConfigManager;
8use Shaarli\Formatter\BookmarkMarkdownFormatter;
9use Shaarli\Http\HttpAccess;
10use Shaarli\TestCase;
11use Slim\Http\Request;
12use Slim\Http\Response;
13
14class ShaareAddControllerTest extends TestCase
15{
16 use FrontAdminControllerMockHelper;
17
18 /** @var ShaareAddController */
19 protected $controller;
20
21 public function setUp(): void
22 {
23 $this->createContainer();
24
25 $this->container->httpAccess = $this->createMock(HttpAccess::class);
26 $this->controller = new ShaareAddController($this->container);
27 }
28
29 /**
30 * Test displaying add link page
31 */
32 public function testAddShaare(): void
33 {
34 $assignedVariables = [];
35 $this->assignTemplateVars($assignedVariables);
36
37 $request = $this->createMock(Request::class);
38 $response = new Response();
39
40 $expectedTags = [
41 'tag1' => 32,
42 'tag2' => 24,
43 'tag3' => 1,
44 ];
45 $this->container->bookmarkService
46 ->expects(static::once())
47 ->method('bookmarksCountPerTag')
48 ->willReturn($expectedTags)
49 ;
50 $expectedTags = array_merge($expectedTags, [BookmarkMarkdownFormatter::NO_MD_TAG => 1]);
51
52 $this->container->conf = $this->createMock(ConfigManager::class);
53 $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
54 return $key === 'formatter' ? 'markdown' : $default;
55 });
56
57 $result = $this->controller->addShaare($request, $response);
58
59 static::assertSame(200, $result->getStatusCode());
60 static::assertSame('addlink', (string) $result->getBody());
61
62 static::assertSame('Shaare a new link - Shaarli', $assignedVariables['pagetitle']);
63 static::assertFalse($assignedVariables['default_private_links']);
64 static::assertTrue($assignedVariables['async_metadata']);
65 static::assertSame($expectedTags, $assignedVariables['tags']);
66 }
67
68 /**
69 * Test displaying add link page
70 */
71 public function testAddShaareWithoutMd(): void
72 {
73 $assignedVariables = [];
74 $this->assignTemplateVars($assignedVariables);
75
76 $request = $this->createMock(Request::class);
77 $response = new Response();
78
79 $expectedTags = [
80 'tag1' => 32,
81 'tag2' => 24,
82 'tag3' => 1,
83 ];
84 $this->container->bookmarkService
85 ->expects(static::once())
86 ->method('bookmarksCountPerTag')
87 ->willReturn($expectedTags)
88 ;
89
90 $result = $this->controller->addShaare($request, $response);
91
92 static::assertSame(200, $result->getStatusCode());
93 static::assertSame('addlink', (string) $result->getBody());
94
95 static::assertSame($expectedTags, $assignedVariables['tags']);
96 }
97}
diff --git a/tests/front/controller/admin/ShaareManageControllerTest/ChangeVisibilityBookmarkTest.php b/tests/front/controller/admin/ShaareManageControllerTest/ChangeVisibilityBookmarkTest.php
new file mode 100644
index 00000000..28b1c023
--- /dev/null
+++ b/tests/front/controller/admin/ShaareManageControllerTest/ChangeVisibilityBookmarkTest.php
@@ -0,0 +1,418 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ShaareManageControllerTest;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
9use Shaarli\Formatter\BookmarkFormatter;
10use Shaarli\Formatter\BookmarkRawFormatter;
11use Shaarli\Formatter\FormatterFactory;
12use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
13use Shaarli\Front\Controller\Admin\ShaareManageController;
14use Shaarli\Http\HttpAccess;
15use Shaarli\Security\SessionManager;
16use Shaarli\TestCase;
17use Slim\Http\Request;
18use Slim\Http\Response;
19
20class ChangeVisibilityBookmarkTest extends TestCase
21{
22 use FrontAdminControllerMockHelper;
23
24 /** @var ShaareManageController */
25 protected $controller;
26
27 public function setUp(): void
28 {
29 $this->createContainer();
30
31 $this->container->httpAccess = $this->createMock(HttpAccess::class);
32 $this->controller = new ShaareManageController($this->container);
33 }
34
35 /**
36 * Change bookmark visibility - Set private - Single public bookmark with valid parameters
37 */
38 public function testSetSingleBookmarkPrivate(): void
39 {
40 $parameters = ['id' => '123', 'newVisibility' => 'private'];
41
42 $request = $this->createMock(Request::class);
43 $request
44 ->method('getParam')
45 ->willReturnCallback(function (string $key) use ($parameters): ?string {
46 return $parameters[$key] ?? null;
47 })
48 ;
49 $response = new Response();
50
51 $bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(false);
52
53 static::assertFalse($bookmark->isPrivate());
54
55 $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
56 $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false);
57 $this->container->bookmarkService->expects(static::once())->method('save');
58 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
59 $this->container->formatterFactory
60 ->expects(static::once())
61 ->method('getFormatter')
62 ->with('raw')
63 ->willReturnCallback(function () use ($bookmark): BookmarkFormatter {
64 return new BookmarkRawFormatter($this->container->conf, true);
65 })
66 ;
67
68 // Make sure that PluginManager hook is triggered
69 $this->container->pluginManager
70 ->expects(static::once())
71 ->method('executeHooks')
72 ->with('save_link')
73 ;
74
75 $result = $this->controller->changeVisibility($request, $response);
76
77 static::assertTrue($bookmark->isPrivate());
78
79 static::assertSame(302, $result->getStatusCode());
80 static::assertSame(['/subfolder/'], $result->getHeader('location'));
81 }
82
83 /**
84 * Change bookmark visibility - Set public - Single private bookmark with valid parameters
85 */
86 public function testSetSingleBookmarkPublic(): void
87 {
88 $parameters = ['id' => '123', 'newVisibility' => 'public'];
89
90 $request = $this->createMock(Request::class);
91 $request
92 ->method('getParam')
93 ->willReturnCallback(function (string $key) use ($parameters): ?string {
94 return $parameters[$key] ?? null;
95 })
96 ;
97 $response = new Response();
98
99 $bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(true);
100
101 static::assertTrue($bookmark->isPrivate());
102
103 $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
104 $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false);
105 $this->container->bookmarkService->expects(static::once())->method('save');
106 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
107 $this->container->formatterFactory
108 ->expects(static::once())
109 ->method('getFormatter')
110 ->with('raw')
111 ->willReturn(new BookmarkRawFormatter($this->container->conf, true))
112 ;
113
114 // Make sure that PluginManager hook is triggered
115 $this->container->pluginManager
116 ->expects(static::once())
117 ->method('executeHooks')
118 ->with('save_link')
119 ;
120
121 $result = $this->controller->changeVisibility($request, $response);
122
123 static::assertFalse($bookmark->isPrivate());
124
125 static::assertSame(302, $result->getStatusCode());
126 static::assertSame(['/subfolder/'], $result->getHeader('location'));
127 }
128
129 /**
130 * Change bookmark visibility - Set private on single already private bookmark
131 */
132 public function testSetSinglePrivateBookmarkPrivate(): void
133 {
134 $parameters = ['id' => '123', 'newVisibility' => 'private'];
135
136 $request = $this->createMock(Request::class);
137 $request
138 ->method('getParam')
139 ->willReturnCallback(function (string $key) use ($parameters): ?string {
140 return $parameters[$key] ?? null;
141 })
142 ;
143 $response = new Response();
144
145 $bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(true);
146
147 static::assertTrue($bookmark->isPrivate());
148
149 $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
150 $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, false);
151 $this->container->bookmarkService->expects(static::once())->method('save');
152 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
153 $this->container->formatterFactory
154 ->expects(static::once())
155 ->method('getFormatter')
156 ->with('raw')
157 ->willReturn(new BookmarkRawFormatter($this->container->conf, true))
158 ;
159
160 // Make sure that PluginManager hook is triggered
161 $this->container->pluginManager
162 ->expects(static::once())
163 ->method('executeHooks')
164 ->with('save_link')
165 ;
166
167 $result = $this->controller->changeVisibility($request, $response);
168
169 static::assertTrue($bookmark->isPrivate());
170
171 static::assertSame(302, $result->getStatusCode());
172 static::assertSame(['/subfolder/'], $result->getHeader('location'));
173 }
174
175 /**
176 * Change bookmark visibility - Set multiple bookmarks private
177 */
178 public function testSetMultipleBookmarksPrivate(): void
179 {
180 $parameters = ['id' => '123 456 789', 'newVisibility' => 'private'];
181
182 $request = $this->createMock(Request::class);
183 $request
184 ->method('getParam')
185 ->willReturnCallback(function (string $key) use ($parameters): ?string {
186 return $parameters[$key] ?? null;
187 })
188 ;
189 $response = new Response();
190
191 $bookmarks = [
192 (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(false),
193 (new Bookmark())->setId(456)->setUrl('http://domain.tld')->setTitle('Title 456')->setPrivate(true),
194 (new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789')->setPrivate(false),
195 ];
196
197 $this->container->bookmarkService
198 ->expects(static::exactly(3))
199 ->method('get')
200 ->withConsecutive([123], [456], [789])
201 ->willReturnOnConsecutiveCalls(...$bookmarks)
202 ;
203 $this->container->bookmarkService
204 ->expects(static::exactly(3))
205 ->method('set')
206 ->withConsecutive(...array_map(function (Bookmark $bookmark): array {
207 return [$bookmark, false];
208 }, $bookmarks))
209 ;
210 $this->container->bookmarkService->expects(static::once())->method('save');
211 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
212 $this->container->formatterFactory
213 ->expects(static::once())
214 ->method('getFormatter')
215 ->with('raw')
216 ->willReturn(new BookmarkRawFormatter($this->container->conf, true))
217 ;
218
219 // Make sure that PluginManager hook is triggered
220 $this->container->pluginManager
221 ->expects(static::exactly(3))
222 ->method('executeHooks')
223 ->with('save_link')
224 ;
225
226 $result = $this->controller->changeVisibility($request, $response);
227
228 static::assertTrue($bookmarks[0]->isPrivate());
229 static::assertTrue($bookmarks[1]->isPrivate());
230 static::assertTrue($bookmarks[2]->isPrivate());
231
232 static::assertSame(302, $result->getStatusCode());
233 static::assertSame(['/subfolder/'], $result->getHeader('location'));
234 }
235
236 /**
237 * Change bookmark visibility - Single bookmark not found.
238 */
239 public function testChangeVisibilitySingleBookmarkNotFound(): void
240 {
241 $parameters = ['id' => '123', 'newVisibility' => 'private'];
242
243 $request = $this->createMock(Request::class);
244 $request
245 ->method('getParam')
246 ->willReturnCallback(function (string $key) use ($parameters): ?string {
247 return $parameters[$key] ?? null;
248 })
249 ;
250 $response = new Response();
251
252 $this->container->bookmarkService
253 ->expects(static::once())
254 ->method('get')
255 ->willThrowException(new BookmarkNotFoundException())
256 ;
257 $this->container->bookmarkService->expects(static::never())->method('set');
258 $this->container->bookmarkService->expects(static::never())->method('save');
259 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
260 $this->container->formatterFactory
261 ->expects(static::once())
262 ->method('getFormatter')
263 ->with('raw')
264 ->willReturn(new BookmarkRawFormatter($this->container->conf, true))
265 ;
266
267 // Make sure that PluginManager hook is not triggered
268 $this->container->pluginManager
269 ->expects(static::never())
270 ->method('executeHooks')
271 ->with('save_link')
272 ;
273
274 $result = $this->controller->changeVisibility($request, $response);
275
276 static::assertSame(302, $result->getStatusCode());
277 static::assertSame(['/subfolder/'], $result->getHeader('location'));
278 }
279
280 /**
281 * Change bookmark visibility - Multiple bookmarks with one not found.
282 */
283 public function testChangeVisibilityMultipleBookmarksOneNotFound(): void
284 {
285 $parameters = ['id' => '123 456 789', 'newVisibility' => 'public'];
286
287 $request = $this->createMock(Request::class);
288 $request
289 ->method('getParam')
290 ->willReturnCallback(function (string $key) use ($parameters): ?string {
291 return $parameters[$key] ?? null;
292 })
293 ;
294 $response = new Response();
295
296 $bookmarks = [
297 (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')->setPrivate(true),
298 (new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789')->setPrivate(false),
299 ];
300
301 $this->container->bookmarkService
302 ->expects(static::exactly(3))
303 ->method('get')
304 ->withConsecutive([123], [456], [789])
305 ->willReturnCallback(function (int $id) use ($bookmarks): Bookmark {
306 if ($id === 123) {
307 return $bookmarks[0];
308 }
309 if ($id === 789) {
310 return $bookmarks[1];
311 }
312 throw new BookmarkNotFoundException();
313 })
314 ;
315 $this->container->bookmarkService
316 ->expects(static::exactly(2))
317 ->method('set')
318 ->withConsecutive(...array_map(function (Bookmark $bookmark): array {
319 return [$bookmark, false];
320 }, $bookmarks))
321 ;
322 $this->container->bookmarkService->expects(static::once())->method('save');
323
324 // Make sure that PluginManager hook is not triggered
325 $this->container->pluginManager
326 ->expects(static::exactly(2))
327 ->method('executeHooks')
328 ->with('save_link')
329 ;
330
331 $this->container->sessionManager
332 ->expects(static::once())
333 ->method('setSessionParameter')
334 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 456 could not be found.'])
335 ;
336
337 $result = $this->controller->changeVisibility($request, $response);
338
339 static::assertSame(302, $result->getStatusCode());
340 static::assertSame(['/subfolder/'], $result->getHeader('location'));
341 }
342
343 /**
344 * Change bookmark visibility - Invalid ID
345 */
346 public function testChangeVisibilityInvalidId(): void
347 {
348 $parameters = ['id' => 'nope not an ID', 'newVisibility' => 'private'];
349
350 $request = $this->createMock(Request::class);
351 $request
352 ->method('getParam')
353 ->willReturnCallback(function (string $key) use ($parameters): ?string {
354 return $parameters[$key] ?? null;
355 })
356 ;
357 $response = new Response();
358
359 $this->container->sessionManager
360 ->expects(static::once())
361 ->method('setSessionParameter')
362 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
363 ;
364
365 $result = $this->controller->changeVisibility($request, $response);
366
367 static::assertSame(302, $result->getStatusCode());
368 static::assertSame(['/subfolder/'], $result->getHeader('location'));
369 }
370
371 /**
372 * Change bookmark visibility - Empty ID
373 */
374 public function testChangeVisibilityEmptyId(): void
375 {
376 $request = $this->createMock(Request::class);
377 $response = new Response();
378
379 $this->container->sessionManager
380 ->expects(static::once())
381 ->method('setSessionParameter')
382 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
383 ;
384
385 $result = $this->controller->changeVisibility($request, $response);
386
387 static::assertSame(302, $result->getStatusCode());
388 static::assertSame(['/subfolder/'], $result->getHeader('location'));
389 }
390
391 /**
392 * Change bookmark visibility - with invalid visibility
393 */
394 public function testChangeVisibilityWithInvalidVisibility(): void
395 {
396 $parameters = ['id' => '123', 'newVisibility' => 'invalid'];
397
398 $request = $this->createMock(Request::class);
399 $request
400 ->method('getParam')
401 ->willReturnCallback(function (string $key) use ($parameters): ?string {
402 return $parameters[$key] ?? null;
403 })
404 ;
405 $response = new Response();
406
407 $this->container->sessionManager
408 ->expects(static::once())
409 ->method('setSessionParameter')
410 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid visibility provided.'])
411 ;
412
413 $result = $this->controller->changeVisibility($request, $response);
414
415 static::assertSame(302, $result->getStatusCode());
416 static::assertSame(['/subfolder/'], $result->getHeader('location'));
417 }
418}
diff --git a/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php b/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php
new file mode 100644
index 00000000..770a16d7
--- /dev/null
+++ b/tests/front/controller/admin/ShaareManageControllerTest/DeleteBookmarkTest.php
@@ -0,0 +1,380 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ShaareManageControllerTest;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
9use Shaarli\Formatter\BookmarkFormatter;
10use Shaarli\Formatter\FormatterFactory;
11use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
12use Shaarli\Front\Controller\Admin\ShaareManageController;
13use Shaarli\Http\HttpAccess;
14use Shaarli\Security\SessionManager;
15use Shaarli\TestCase;
16use Slim\Http\Request;
17use Slim\Http\Response;
18
19class DeleteBookmarkTest extends TestCase
20{
21 use FrontAdminControllerMockHelper;
22
23 /** @var ShaareManageController */
24 protected $controller;
25
26 public function setUp(): void
27 {
28 $this->createContainer();
29
30 $this->container->httpAccess = $this->createMock(HttpAccess::class);
31 $this->controller = new ShaareManageController($this->container);
32 }
33
34 /**
35 * Delete bookmark - Single bookmark with valid parameters
36 */
37 public function testDeleteSingleBookmark(): void
38 {
39 $parameters = ['id' => '123'];
40
41 $request = $this->createMock(Request::class);
42 $request
43 ->method('getParam')
44 ->willReturnCallback(function (string $key) use ($parameters): ?string {
45 return $parameters[$key] ?? null;
46 })
47 ;
48 $response = new Response();
49
50 $bookmark = (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123');
51
52 $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
53 $this->container->bookmarkService->expects(static::once())->method('remove')->with($bookmark, false);
54 $this->container->bookmarkService->expects(static::once())->method('save');
55 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
56 $this->container->formatterFactory
57 ->expects(static::once())
58 ->method('getFormatter')
59 ->with('raw')
60 ->willReturnCallback(function () use ($bookmark): BookmarkFormatter {
61 $formatter = $this->createMock(BookmarkFormatter::class);
62 $formatter
63 ->expects(static::once())
64 ->method('format')
65 ->with($bookmark)
66 ->willReturn(['formatted' => $bookmark])
67 ;
68
69 return $formatter;
70 })
71 ;
72
73 // Make sure that PluginManager hook is triggered
74 $this->container->pluginManager
75 ->expects(static::once())
76 ->method('executeHooks')
77 ->with('delete_link', ['formatted' => $bookmark])
78 ;
79
80 $result = $this->controller->deleteBookmark($request, $response);
81
82 static::assertSame(302, $result->getStatusCode());
83 static::assertSame(['/subfolder/'], $result->getHeader('location'));
84 }
85
86 /**
87 * Delete bookmark - Multiple bookmarks with valid parameters
88 */
89 public function testDeleteMultipleBookmarks(): void
90 {
91 $parameters = ['id' => '123 456 789'];
92
93 $request = $this->createMock(Request::class);
94 $request
95 ->method('getParam')
96 ->willReturnCallback(function (string $key) use ($parameters): ?string {
97 return $parameters[$key] ?? null;
98 })
99 ;
100 $response = new Response();
101
102 $bookmarks = [
103 (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123'),
104 (new Bookmark())->setId(456)->setUrl('http://domain.tld')->setTitle('Title 456'),
105 (new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789'),
106 ];
107
108 $this->container->bookmarkService
109 ->expects(static::exactly(3))
110 ->method('get')
111 ->withConsecutive([123], [456], [789])
112 ->willReturnOnConsecutiveCalls(...$bookmarks)
113 ;
114 $this->container->bookmarkService
115 ->expects(static::exactly(3))
116 ->method('remove')
117 ->withConsecutive(...array_map(function (Bookmark $bookmark): array {
118 return [$bookmark, false];
119 }, $bookmarks))
120 ;
121 $this->container->bookmarkService->expects(static::once())->method('save');
122 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
123 $this->container->formatterFactory
124 ->expects(static::once())
125 ->method('getFormatter')
126 ->with('raw')
127 ->willReturnCallback(function () use ($bookmarks): BookmarkFormatter {
128 $formatter = $this->createMock(BookmarkFormatter::class);
129
130 $formatter
131 ->expects(static::exactly(3))
132 ->method('format')
133 ->withConsecutive(...array_map(function (Bookmark $bookmark): array {
134 return [$bookmark];
135 }, $bookmarks))
136 ->willReturnOnConsecutiveCalls(...array_map(function (Bookmark $bookmark): array {
137 return ['formatted' => $bookmark];
138 }, $bookmarks))
139 ;
140
141 return $formatter;
142 })
143 ;
144
145 // Make sure that PluginManager hook is triggered
146 $this->container->pluginManager
147 ->expects(static::exactly(3))
148 ->method('executeHooks')
149 ->with('delete_link')
150 ;
151
152 $result = $this->controller->deleteBookmark($request, $response);
153
154 static::assertSame(302, $result->getStatusCode());
155 static::assertSame(['/subfolder/'], $result->getHeader('location'));
156 }
157
158 /**
159 * Delete bookmark - Single bookmark not found in the data store
160 */
161 public function testDeleteSingleBookmarkNotFound(): void
162 {
163 $parameters = ['id' => '123'];
164
165 $request = $this->createMock(Request::class);
166 $request
167 ->method('getParam')
168 ->willReturnCallback(function (string $key) use ($parameters): ?string {
169 return $parameters[$key] ?? null;
170 })
171 ;
172 $response = new Response();
173
174 $this->container->bookmarkService
175 ->expects(static::once())
176 ->method('get')
177 ->willThrowException(new BookmarkNotFoundException())
178 ;
179 $this->container->bookmarkService->expects(static::never())->method('remove');
180 $this->container->bookmarkService->expects(static::never())->method('save');
181 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
182 $this->container->formatterFactory
183 ->expects(static::once())
184 ->method('getFormatter')
185 ->with('raw')
186 ->willReturnCallback(function (): BookmarkFormatter {
187 $formatter = $this->createMock(BookmarkFormatter::class);
188
189 $formatter->expects(static::never())->method('format');
190
191 return $formatter;
192 })
193 ;
194 // Make sure that PluginManager hook is not triggered
195 $this->container->pluginManager
196 ->expects(static::never())
197 ->method('executeHooks')
198 ->with('delete_link')
199 ;
200
201 $result = $this->controller->deleteBookmark($request, $response);
202
203 static::assertSame(302, $result->getStatusCode());
204 static::assertSame(['/subfolder/'], $result->getHeader('location'));
205 }
206
207 /**
208 * Delete bookmark - Multiple bookmarks with one not found in the data store
209 */
210 public function testDeleteMultipleBookmarksOneNotFound(): void
211 {
212 $parameters = ['id' => '123 456 789'];
213
214 $request = $this->createMock(Request::class);
215 $request
216 ->method('getParam')
217 ->willReturnCallback(function (string $key) use ($parameters): ?string {
218 return $parameters[$key] ?? null;
219 })
220 ;
221 $response = new Response();
222
223 $bookmarks = [
224 (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123'),
225 (new Bookmark())->setId(789)->setUrl('http://domain.tld')->setTitle('Title 789'),
226 ];
227
228 $this->container->bookmarkService
229 ->expects(static::exactly(3))
230 ->method('get')
231 ->withConsecutive([123], [456], [789])
232 ->willReturnCallback(function (int $id) use ($bookmarks): Bookmark {
233 if ($id === 123) {
234 return $bookmarks[0];
235 }
236 if ($id === 789) {
237 return $bookmarks[1];
238 }
239 throw new BookmarkNotFoundException();
240 })
241 ;
242 $this->container->bookmarkService
243 ->expects(static::exactly(2))
244 ->method('remove')
245 ->withConsecutive(...array_map(function (Bookmark $bookmark): array {
246 return [$bookmark, false];
247 }, $bookmarks))
248 ;
249 $this->container->bookmarkService->expects(static::once())->method('save');
250 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
251 $this->container->formatterFactory
252 ->expects(static::once())
253 ->method('getFormatter')
254 ->with('raw')
255 ->willReturnCallback(function () use ($bookmarks): BookmarkFormatter {
256 $formatter = $this->createMock(BookmarkFormatter::class);
257
258 $formatter
259 ->expects(static::exactly(2))
260 ->method('format')
261 ->withConsecutive(...array_map(function (Bookmark $bookmark): array {
262 return [$bookmark];
263 }, $bookmarks))
264 ->willReturnOnConsecutiveCalls(...array_map(function (Bookmark $bookmark): array {
265 return ['formatted' => $bookmark];
266 }, $bookmarks))
267 ;
268
269 return $formatter;
270 })
271 ;
272
273 // Make sure that PluginManager hook is not triggered
274 $this->container->pluginManager
275 ->expects(static::exactly(2))
276 ->method('executeHooks')
277 ->with('delete_link')
278 ;
279
280 $this->container->sessionManager
281 ->expects(static::once())
282 ->method('setSessionParameter')
283 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 456 could not be found.'])
284 ;
285
286 $result = $this->controller->deleteBookmark($request, $response);
287
288 static::assertSame(302, $result->getStatusCode());
289 static::assertSame(['/subfolder/'], $result->getHeader('location'));
290 }
291
292 /**
293 * Delete bookmark - Invalid ID
294 */
295 public function testDeleteInvalidId(): void
296 {
297 $parameters = ['id' => 'nope not an ID'];
298
299 $request = $this->createMock(Request::class);
300 $request
301 ->method('getParam')
302 ->willReturnCallback(function (string $key) use ($parameters): ?string {
303 return $parameters[$key] ?? null;
304 })
305 ;
306 $response = new Response();
307
308 $this->container->sessionManager
309 ->expects(static::once())
310 ->method('setSessionParameter')
311 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
312 ;
313
314 $result = $this->controller->deleteBookmark($request, $response);
315
316 static::assertSame(302, $result->getStatusCode());
317 static::assertSame(['/subfolder/'], $result->getHeader('location'));
318 }
319
320 /**
321 * Delete bookmark - Empty ID
322 */
323 public function testDeleteEmptyId(): void
324 {
325 $request = $this->createMock(Request::class);
326 $response = new Response();
327
328 $this->container->sessionManager
329 ->expects(static::once())
330 ->method('setSessionParameter')
331 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Invalid bookmark ID provided.'])
332 ;
333
334 $result = $this->controller->deleteBookmark($request, $response);
335
336 static::assertSame(302, $result->getStatusCode());
337 static::assertSame(['/subfolder/'], $result->getHeader('location'));
338 }
339
340 /**
341 * Delete bookmark - from bookmarklet
342 */
343 public function testDeleteBookmarkFromBookmarklet(): void
344 {
345 $parameters = [
346 'id' => '123',
347 'source' => 'bookmarklet',
348 ];
349
350 $request = $this->createMock(Request::class);
351 $request
352 ->method('getParam')
353 ->willReturnCallback(function (string $key) use ($parameters): ?string {
354 return $parameters[$key] ?? null;
355 })
356 ;
357 $response = new Response();
358
359 $this->container->bookmarkService->method('get')->with('123')->willReturn(
360 (new Bookmark())->setId(123)->setUrl('http://domain.tld')->setTitle('Title 123')
361 );
362
363 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
364 $this->container->formatterFactory
365 ->expects(static::once())
366 ->method('getFormatter')
367 ->willReturnCallback(function (): BookmarkFormatter {
368 $formatter = $this->createMock(BookmarkFormatter::class);
369 $formatter->method('format')->willReturn(['formatted']);
370
371 return $formatter;
372 })
373 ;
374
375 $result = $this->controller->deleteBookmark($request, $response);
376
377 static::assertSame(200, $result->getStatusCode());
378 static::assertSame('<script>self.close();</script>', (string) $result->getBody('location'));
379 }
380}
diff --git a/tests/front/controller/admin/ShaareManageControllerTest/PinBookmarkTest.php b/tests/front/controller/admin/ShaareManageControllerTest/PinBookmarkTest.php
new file mode 100644
index 00000000..b89206ce
--- /dev/null
+++ b/tests/front/controller/admin/ShaareManageControllerTest/PinBookmarkTest.php
@@ -0,0 +1,145 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ShaareManageControllerTest;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
9use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
10use Shaarli\Front\Controller\Admin\ShaareManageController;
11use Shaarli\Http\HttpAccess;
12use Shaarli\Security\SessionManager;
13use Shaarli\TestCase;
14use Slim\Http\Request;
15use Slim\Http\Response;
16
17class PinBookmarkTest extends TestCase
18{
19 use FrontAdminControllerMockHelper;
20
21 /** @var ShaareManageController */
22 protected $controller;
23
24 public function setUp(): void
25 {
26 $this->createContainer();
27
28 $this->container->httpAccess = $this->createMock(HttpAccess::class);
29 $this->controller = new ShaareManageController($this->container);
30 }
31
32 /**
33 * Test pin bookmark - with valid input
34 *
35 * @dataProvider initialStickyValuesProvider()
36 */
37 public function testPinBookmarkIsStickyNull(?bool $sticky, bool $expectedValue): void
38 {
39 $id = 123;
40
41 $request = $this->createMock(Request::class);
42 $response = new Response();
43
44 $bookmark = (new Bookmark())
45 ->setId(123)
46 ->setUrl('http://domain.tld')
47 ->setTitle('Title 123')
48 ->setSticky($sticky)
49 ;
50
51 $this->container->bookmarkService->expects(static::once())->method('get')->with(123)->willReturn($bookmark);
52 $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, true);
53
54 // Make sure that PluginManager hook is triggered
55 $this->container->pluginManager
56 ->expects(static::once())
57 ->method('executeHooks')
58 ->with('save_link')
59 ;
60
61 $result = $this->controller->pinBookmark($request, $response, ['id' => (string) $id]);
62
63 static::assertSame(302, $result->getStatusCode());
64 static::assertSame(['/subfolder/'], $result->getHeader('location'));
65
66 static::assertSame($expectedValue, $bookmark->isSticky());
67 }
68
69 public function initialStickyValuesProvider(): array
70 {
71 // [initialStickyState, isStickyAfterPin]
72 return [[null, true], [false, true], [true, false]];
73 }
74
75 /**
76 * Test pin bookmark - invalid bookmark ID
77 */
78 public function testDisplayEditFormInvalidId(): void
79 {
80 $id = 'invalid';
81
82 $request = $this->createMock(Request::class);
83 $response = new Response();
84
85 $this->container->sessionManager
86 ->expects(static::once())
87 ->method('setSessionParameter')
88 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier invalid could not be found.'])
89 ;
90
91 $result = $this->controller->pinBookmark($request, $response, ['id' => $id]);
92
93 static::assertSame(302, $result->getStatusCode());
94 static::assertSame(['/subfolder/'], $result->getHeader('location'));
95 }
96
97 /**
98 * Test pin bookmark - Bookmark ID not provided
99 */
100 public function testDisplayEditFormIdNotProvided(): void
101 {
102 $request = $this->createMock(Request::class);
103 $response = new Response();
104
105 $this->container->sessionManager
106 ->expects(static::once())
107 ->method('setSessionParameter')
108 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier could not be found.'])
109 ;
110
111 $result = $this->controller->pinBookmark($request, $response, []);
112
113 static::assertSame(302, $result->getStatusCode());
114 static::assertSame(['/subfolder/'], $result->getHeader('location'));
115 }
116
117 /**
118 * Test pin bookmark - bookmark not found
119 */
120 public function testDisplayEditFormBookmarkNotFound(): void
121 {
122 $id = 123;
123
124 $request = $this->createMock(Request::class);
125 $response = new Response();
126
127 $this->container->bookmarkService
128 ->expects(static::once())
129 ->method('get')
130 ->with($id)
131 ->willThrowException(new BookmarkNotFoundException())
132 ;
133
134 $this->container->sessionManager
135 ->expects(static::once())
136 ->method('setSessionParameter')
137 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 123 could not be found.'])
138 ;
139
140 $result = $this->controller->pinBookmark($request, $response, ['id' => (string) $id]);
141
142 static::assertSame(302, $result->getStatusCode());
143 static::assertSame(['/subfolder/'], $result->getHeader('location'));
144 }
145}
diff --git a/tests/front/controller/admin/ShaareManageControllerTest/SharePrivateTest.php b/tests/front/controller/admin/ShaareManageControllerTest/SharePrivateTest.php
new file mode 100644
index 00000000..ae61dfb7
--- /dev/null
+++ b/tests/front/controller/admin/ShaareManageControllerTest/SharePrivateTest.php
@@ -0,0 +1,139 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ShaareManageControllerTest;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
9use Shaarli\Front\Controller\Admin\ShaareManageController;
10use Shaarli\Http\HttpAccess;
11use Shaarli\TestCase;
12use Slim\Http\Request;
13use Slim\Http\Response;
14
15/**
16 * Test GET /admin/shaare/private/{hash}
17 */
18class SharePrivateTest extends TestCase
19{
20 use FrontAdminControllerMockHelper;
21
22 /** @var ShaareManageController */
23 protected $controller;
24
25 public function setUp(): void
26 {
27 $this->createContainer();
28
29 $this->container->httpAccess = $this->createMock(HttpAccess::class);
30 $this->controller = new ShaareManageController($this->container);
31 }
32
33 /**
34 * Test shaare private with a private bookmark which does not have a key yet.
35 */
36 public function testSharePrivateWithNewPrivateBookmark(): void
37 {
38 $hash = 'abcdcef';
39 $request = $this->createMock(Request::class);
40 $response = new Response();
41
42 $bookmark = (new Bookmark())
43 ->setId(123)
44 ->setUrl('http://domain.tld')
45 ->setTitle('Title 123')
46 ->setPrivate(true)
47 ;
48
49 $this->container->bookmarkService
50 ->expects(static::once())
51 ->method('findByHash')
52 ->with($hash)
53 ->willReturn($bookmark)
54 ;
55 $this->container->bookmarkService
56 ->expects(static::once())
57 ->method('set')
58 ->with($bookmark, true)
59 ->willReturnCallback(function (Bookmark $bookmark): Bookmark {
60 static::assertSame(32, strlen($bookmark->getAdditionalContentEntry('private_key')));
61
62 return $bookmark;
63 })
64 ;
65
66 $result = $this->controller->sharePrivate($request, $response, ['hash' => $hash]);
67
68 static::assertSame(302, $result->getStatusCode());
69 static::assertRegExp('#/subfolder/shaare/' . $hash . '\?key=\w{32}#', $result->getHeaderLine('Location'));
70 }
71
72 /**
73 * Test shaare private with a private bookmark which does already have a key.
74 */
75 public function testSharePrivateWithExistingPrivateBookmark(): void
76 {
77 $hash = 'abcdcef';
78 $existingKey = 'this is a private key';
79 $request = $this->createMock(Request::class);
80 $response = new Response();
81
82 $bookmark = (new Bookmark())
83 ->setId(123)
84 ->setUrl('http://domain.tld')
85 ->setTitle('Title 123')
86 ->setPrivate(true)
87 ->addAdditionalContentEntry('private_key', $existingKey)
88 ;
89
90 $this->container->bookmarkService
91 ->expects(static::once())
92 ->method('findByHash')
93 ->with($hash)
94 ->willReturn($bookmark)
95 ;
96 $this->container->bookmarkService
97 ->expects(static::never())
98 ->method('set')
99 ;
100
101 $result = $this->controller->sharePrivate($request, $response, ['hash' => $hash]);
102
103 static::assertSame(302, $result->getStatusCode());
104 static::assertSame('/subfolder/shaare/' . $hash . '?key=' . $existingKey, $result->getHeaderLine('Location'));
105 }
106
107 /**
108 * Test shaare private with a public bookmark.
109 */
110 public function testSharePrivateWithPublicBookmark(): void
111 {
112 $hash = 'abcdcef';
113 $request = $this->createMock(Request::class);
114 $response = new Response();
115
116 $bookmark = (new Bookmark())
117 ->setId(123)
118 ->setUrl('http://domain.tld')
119 ->setTitle('Title 123')
120 ->setPrivate(false)
121 ;
122
123 $this->container->bookmarkService
124 ->expects(static::once())
125 ->method('findByHash')
126 ->with($hash)
127 ->willReturn($bookmark)
128 ;
129 $this->container->bookmarkService
130 ->expects(static::never())
131 ->method('set')
132 ;
133
134 $result = $this->controller->sharePrivate($request, $response, ['hash' => $hash]);
135
136 static::assertSame(302, $result->getStatusCode());
137 static::assertSame('/subfolder/shaare/' . $hash, $result->getHeaderLine('Location'));
138 }
139}
diff --git a/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateBatchFormTest.php b/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateBatchFormTest.php
new file mode 100644
index 00000000..ce8e112b
--- /dev/null
+++ b/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateBatchFormTest.php
@@ -0,0 +1,63 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ShaarePublishControllerTest;
6
7use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
8use Shaarli\Front\Controller\Admin\ShaarePublishController;
9use Shaarli\Http\HttpAccess;
10use Shaarli\Http\MetadataRetriever;
11use Shaarli\TestCase;
12use Slim\Http\Request;
13use Slim\Http\Response;
14
15class DisplayCreateBatchFormTest extends TestCase
16{
17 use FrontAdminControllerMockHelper;
18
19 /** @var ShaarePublishController */
20 protected $controller;
21
22 public function setUp(): void
23 {
24 $this->createContainer();
25
26 $this->container->httpAccess = $this->createMock(HttpAccess::class);
27 $this->container->metadataRetriever = $this->createMock(MetadataRetriever::class);
28 $this->controller = new ShaarePublishController($this->container);
29 }
30
31 /**
32 * TODO
33 */
34 public function testDisplayCreateFormBatch(): void
35 {
36 $urls = [
37 'https://domain1.tld/url1',
38 'https://domain2.tld/url2',
39 ' ',
40 'https://domain3.tld/url3',
41 ];
42
43 $request = $this->createMock(Request::class);
44 $request->method('getParam')->willReturnCallback(function (string $key) use ($urls): ?string {
45 return $key === 'urls' ? implode(PHP_EOL, $urls) : null;
46 });
47 $response = new Response();
48
49 $assignedVariables = [];
50 $this->assignTemplateVars($assignedVariables);
51
52 $result = $this->controller->displayCreateBatchForms($request, $response);
53
54 static::assertSame(200, $result->getStatusCode());
55 static::assertSame('editlink.batch', (string) $result->getBody());
56
57 static::assertTrue($assignedVariables['batch_mode']);
58 static::assertCount(3, $assignedVariables['links']);
59 static::assertSame($urls[0], $assignedVariables['links'][0]['link']['url']);
60 static::assertSame($urls[1], $assignedVariables['links'][1]['link']['url']);
61 static::assertSame($urls[3], $assignedVariables['links'][2]['link']['url']);
62 }
63}
diff --git a/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateFormTest.php b/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateFormTest.php
new file mode 100644
index 00000000..f20b1def
--- /dev/null
+++ b/tests/front/controller/admin/ShaarePublishControllerTest/DisplayCreateFormTest.php
@@ -0,0 +1,367 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ShaarePublishControllerTest;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Config\ConfigManager;
9use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
10use Shaarli\Front\Controller\Admin\ShaarePublishController;
11use Shaarli\Http\HttpAccess;
12use Shaarli\Http\MetadataRetriever;
13use Shaarli\TestCase;
14use Slim\Http\Request;
15use Slim\Http\Response;
16
17class DisplayCreateFormTest extends TestCase
18{
19 use FrontAdminControllerMockHelper;
20
21 /** @var ShaarePublishController */
22 protected $controller;
23
24 public function setUp(): void
25 {
26 $this->createContainer();
27
28 $this->container->httpAccess = $this->createMock(HttpAccess::class);
29 $this->container->metadataRetriever = $this->createMock(MetadataRetriever::class);
30 $this->controller = new ShaarePublishController($this->container);
31 }
32
33 /**
34 * Test displaying bookmark create form
35 * Ensure that every step of the standard workflow works properly.
36 */
37 public function testDisplayCreateFormWithUrlAndWithMetadataRetrieval(): void
38 {
39 $this->container->environment = [
40 'HTTP_REFERER' => $referer = 'http://shaarli/subfolder/controller/?searchtag=abc'
41 ];
42
43 $assignedVariables = [];
44 $this->assignTemplateVars($assignedVariables);
45
46 $url = 'http://url.tld/other?part=3&utm_ad=pay#hash';
47 $expectedUrl = str_replace('&utm_ad=pay', '', $url);
48 $remoteTitle = 'Remote Title';
49 $remoteDesc = 'Sometimes the meta description is relevant.';
50 $remoteTags = 'abc def';
51
52 $request = $this->createMock(Request::class);
53 $request->method('getParam')->willReturnCallback(function (string $key) use ($url): ?string {
54 return $key === 'post' ? $url : null;
55 });
56 $response = new Response();
57
58 $this->container->conf = $this->createMock(ConfigManager::class);
59 $this->container->conf->method('get')->willReturnCallback(function (string $param, $default) {
60 if ($param === 'general.enable_async_metadata') {
61 return false;
62 }
63
64 return $default;
65 });
66
67 $this->container->metadataRetriever->expects(static::once())->method('retrieve')->willReturn([
68 'title' => $remoteTitle,
69 'description' => $remoteDesc,
70 'tags' => $remoteTags,
71 ]);
72
73 $this->container->bookmarkService
74 ->expects(static::once())
75 ->method('bookmarksCountPerTag')
76 ->willReturn($tags = ['tag1' => 2, 'tag2' => 1])
77 ;
78
79 // Make sure that PluginManager hook is triggered
80 $this->container->pluginManager
81 ->expects(static::atLeastOnce())
82 ->method('executeHooks')
83 ->withConsecutive(['render_editlink'], ['render_includes'])
84 ->willReturnCallback(function (string $hook, array $data) use ($remoteTitle, $remoteDesc): array {
85 if ('render_editlink' === $hook) {
86 static::assertSame($remoteTitle, $data['link']['title']);
87 static::assertSame($remoteDesc, $data['link']['description']);
88 }
89
90 return $data;
91 })
92 ;
93
94 $result = $this->controller->displayCreateForm($request, $response);
95
96 static::assertSame(200, $result->getStatusCode());
97 static::assertSame('editlink', (string) $result->getBody());
98
99 static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']);
100
101 static::assertSame($expectedUrl, $assignedVariables['link']['url']);
102 static::assertSame($remoteTitle, $assignedVariables['link']['title']);
103 static::assertSame($remoteDesc, $assignedVariables['link']['description']);
104 static::assertSame($remoteTags, $assignedVariables['link']['tags']);
105 static::assertFalse($assignedVariables['link']['private']);
106
107 static::assertTrue($assignedVariables['link_is_new']);
108 static::assertSame($referer, $assignedVariables['http_referer']);
109 static::assertSame($tags, $assignedVariables['tags']);
110 static::assertArrayHasKey('source', $assignedVariables);
111 static::assertArrayHasKey('default_private_links', $assignedVariables);
112 static::assertArrayHasKey('async_metadata', $assignedVariables);
113 static::assertArrayHasKey('retrieve_description', $assignedVariables);
114 }
115
116 /**
117 * Test displaying bookmark create form without any external metadata retrieval attempt
118 */
119 public function testDisplayCreateFormWithUrlAndWithoutMetadata(): void
120 {
121 $this->container->environment = [
122 'HTTP_REFERER' => $referer = 'http://shaarli/subfolder/controller/?searchtag=abc'
123 ];
124
125 $assignedVariables = [];
126 $this->assignTemplateVars($assignedVariables);
127
128 $url = 'http://url.tld/other?part=3&utm_ad=pay#hash';
129 $expectedUrl = str_replace('&utm_ad=pay', '', $url);
130
131 $request = $this->createMock(Request::class);
132 $request->method('getParam')->willReturnCallback(function (string $key) use ($url): ?string {
133 return $key === 'post' ? $url : null;
134 });
135 $response = new Response();
136
137 $this->container->metadataRetriever->expects(static::never())->method('retrieve');
138
139 $this->container->bookmarkService
140 ->expects(static::once())
141 ->method('bookmarksCountPerTag')
142 ->willReturn($tags = ['tag1' => 2, 'tag2' => 1])
143 ;
144
145 // Make sure that PluginManager hook is triggered
146 $this->container->pluginManager
147 ->expects(static::atLeastOnce())
148 ->method('executeHooks')
149 ->withConsecutive(['render_editlink'], ['render_includes'])
150 ->willReturnCallback(function (string $hook, array $data): array {
151 if ('render_editlink' === $hook) {
152 static::assertSame('', $data['link']['title']);
153 static::assertSame('', $data['link']['description']);
154 }
155
156 return $data;
157 })
158 ;
159
160 $result = $this->controller->displayCreateForm($request, $response);
161
162 static::assertSame(200, $result->getStatusCode());
163 static::assertSame('editlink', (string) $result->getBody());
164
165 static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']);
166
167 static::assertSame($expectedUrl, $assignedVariables['link']['url']);
168 static::assertSame('', $assignedVariables['link']['title']);
169 static::assertSame('', $assignedVariables['link']['description']);
170 static::assertSame('', $assignedVariables['link']['tags']);
171 static::assertFalse($assignedVariables['link']['private']);
172
173 static::assertTrue($assignedVariables['link_is_new']);
174 static::assertSame($referer, $assignedVariables['http_referer']);
175 static::assertSame($tags, $assignedVariables['tags']);
176 static::assertArrayHasKey('source', $assignedVariables);
177 static::assertArrayHasKey('default_private_links', $assignedVariables);
178 static::assertArrayHasKey('async_metadata', $assignedVariables);
179 static::assertArrayHasKey('retrieve_description', $assignedVariables);
180 }
181
182 /**
183 * Test displaying bookmark create form
184 * Ensure all available query parameters are handled properly.
185 */
186 public function testDisplayCreateFormWithFullParameters(): void
187 {
188 $assignedVariables = [];
189 $this->assignTemplateVars($assignedVariables);
190
191 $parameters = [
192 'post' => 'http://url.tld/other?part=3&utm_ad=pay#hash',
193 'title' => 'Provided Title',
194 'description' => 'Provided description.',
195 'tags' => 'abc def',
196 'private' => '1',
197 'source' => 'apps',
198 ];
199 $expectedUrl = str_replace('&utm_ad=pay', '', $parameters['post']);
200
201 $request = $this->createMock(Request::class);
202 $request
203 ->method('getParam')
204 ->willReturnCallback(function (string $key) use ($parameters): ?string {
205 return $parameters[$key] ?? null;
206 });
207 $response = new Response();
208
209 $result = $this->controller->displayCreateForm($request, $response);
210
211 static::assertSame(200, $result->getStatusCode());
212 static::assertSame('editlink', (string) $result->getBody());
213
214 static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']);
215
216 static::assertSame($expectedUrl, $assignedVariables['link']['url']);
217 static::assertSame($parameters['title'], $assignedVariables['link']['title']);
218 static::assertSame($parameters['description'], $assignedVariables['link']['description']);
219 static::assertSame($parameters['tags'], $assignedVariables['link']['tags']);
220 static::assertTrue($assignedVariables['link']['private']);
221 static::assertTrue($assignedVariables['link_is_new']);
222 static::assertSame($parameters['source'], $assignedVariables['source']);
223 }
224
225 /**
226 * Test displaying bookmark create form
227 * Without any parameter.
228 */
229 public function testDisplayCreateFormEmpty(): void
230 {
231 $assignedVariables = [];
232 $this->assignTemplateVars($assignedVariables);
233
234 $request = $this->createMock(Request::class);
235 $response = new Response();
236
237 $this->container->httpAccess->expects(static::never())->method('getHttpResponse');
238 $this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback');
239
240 $result = $this->controller->displayCreateForm($request, $response);
241
242 static::assertSame(200, $result->getStatusCode());
243 static::assertSame('editlink', (string) $result->getBody());
244 static::assertSame('', $assignedVariables['link']['url']);
245 static::assertSame('Note: ', $assignedVariables['link']['title']);
246 static::assertSame('', $assignedVariables['link']['description']);
247 static::assertSame('', $assignedVariables['link']['tags']);
248 static::assertFalse($assignedVariables['link']['private']);
249 static::assertTrue($assignedVariables['link_is_new']);
250 }
251
252 /**
253 * Test displaying bookmark create form
254 * URL not using HTTP protocol: do not try to retrieve the title
255 */
256 public function testDisplayCreateFormNotHttp(): void
257 {
258 $assignedVariables = [];
259 $this->assignTemplateVars($assignedVariables);
260
261 $url = 'magnet://kubuntu.torrent';
262 $request = $this->createMock(Request::class);
263 $request
264 ->method('getParam')
265 ->willReturnCallback(function (string $key) use ($url): ?string {
266 return $key === 'post' ? $url : null;
267 });
268 $response = new Response();
269
270 $this->container->httpAccess->expects(static::never())->method('getHttpResponse');
271 $this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback');
272
273 $result = $this->controller->displayCreateForm($request, $response);
274
275 static::assertSame(200, $result->getStatusCode());
276 static::assertSame('editlink', (string) $result->getBody());
277 static::assertSame($url, $assignedVariables['link']['url']);
278 static::assertTrue($assignedVariables['link_is_new']);
279 }
280
281 /**
282 * Test displaying bookmark create form
283 * When markdown formatter is enabled, the no markdown tag should be added to existing tags.
284 */
285 public function testDisplayCreateFormWithMarkdownEnabled(): void
286 {
287 $assignedVariables = [];
288 $this->assignTemplateVars($assignedVariables);
289
290 $this->container->conf = $this->createMock(ConfigManager::class);
291 $this->container->conf
292 ->expects(static::atLeastOnce())
293 ->method('get')->willReturnCallback(function (string $key): ?string {
294 if ($key === 'formatter') {
295 return 'markdown';
296 }
297
298 return $key;
299 })
300 ;
301
302 $request = $this->createMock(Request::class);
303 $response = new Response();
304
305 $result = $this->controller->displayCreateForm($request, $response);
306
307 static::assertSame(200, $result->getStatusCode());
308 static::assertSame('editlink', (string) $result->getBody());
309 static::assertSame(['nomarkdown' => 1], $assignedVariables['tags']);
310 }
311
312 /**
313 * Test displaying bookmark create form
314 * When an existing URL is submitted, we want to edit the existing link.
315 */
316 public function testDisplayCreateFormWithExistingUrl(): void
317 {
318 $assignedVariables = [];
319 $this->assignTemplateVars($assignedVariables);
320
321 $url = 'http://url.tld/other?part=3&utm_ad=pay#hash';
322 $expectedUrl = str_replace('&utm_ad=pay', '', $url);
323
324 $request = $this->createMock(Request::class);
325 $request
326 ->method('getParam')
327 ->willReturnCallback(function (string $key) use ($url): ?string {
328 return $key === 'post' ? $url : null;
329 });
330 $response = new Response();
331
332 $this->container->httpAccess->expects(static::never())->method('getHttpResponse');
333 $this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback');
334
335 $this->container->bookmarkService
336 ->expects(static::once())
337 ->method('findByUrl')
338 ->with($expectedUrl)
339 ->willReturn(
340 (new Bookmark())
341 ->setId($id = 23)
342 ->setUrl($expectedUrl)
343 ->setTitle($title = 'Bookmark Title')
344 ->setDescription($description = 'Bookmark description.')
345 ->setTags($tags = ['abc', 'def'])
346 ->setPrivate(true)
347 ->setCreated($createdAt = new \DateTime('2020-06-10 18:45:44'))
348 )
349 ;
350
351 $result = $this->controller->displayCreateForm($request, $response);
352
353 static::assertSame(200, $result->getStatusCode());
354 static::assertSame('editlink', (string) $result->getBody());
355
356 static::assertSame('Edit Shaare - Shaarli', $assignedVariables['pagetitle']);
357 static::assertFalse($assignedVariables['link_is_new']);
358
359 static::assertSame($id, $assignedVariables['link']['id']);
360 static::assertSame($expectedUrl, $assignedVariables['link']['url']);
361 static::assertSame($title, $assignedVariables['link']['title']);
362 static::assertSame($description, $assignedVariables['link']['description']);
363 static::assertSame(implode(' ', $tags), $assignedVariables['link']['tags']);
364 static::assertTrue($assignedVariables['link']['private']);
365 static::assertSame($createdAt, $assignedVariables['link']['created']);
366 }
367}
diff --git a/tests/front/controller/admin/ShaarePublishControllerTest/DisplayEditFormTest.php b/tests/front/controller/admin/ShaarePublishControllerTest/DisplayEditFormTest.php
new file mode 100644
index 00000000..da393e49
--- /dev/null
+++ b/tests/front/controller/admin/ShaarePublishControllerTest/DisplayEditFormTest.php
@@ -0,0 +1,155 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ShaarePublishControllerTest;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
9use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
10use Shaarli\Front\Controller\Admin\ShaarePublishController;
11use Shaarli\Http\HttpAccess;
12use Shaarli\Security\SessionManager;
13use Shaarli\TestCase;
14use Slim\Http\Request;
15use Slim\Http\Response;
16
17class DisplayEditFormTest extends TestCase
18{
19 use FrontAdminControllerMockHelper;
20
21 /** @var ShaarePublishController */
22 protected $controller;
23
24 public function setUp(): void
25 {
26 $this->createContainer();
27
28 $this->container->httpAccess = $this->createMock(HttpAccess::class);
29 $this->controller = new ShaarePublishController($this->container);
30 }
31
32 /**
33 * Test displaying bookmark edit form
34 * When an existing ID is provided, ensure that default workflow works properly.
35 */
36 public function testDisplayEditFormDefault(): void
37 {
38 $assignedVariables = [];
39 $this->assignTemplateVars($assignedVariables);
40
41 $id = 11;
42
43 $request = $this->createMock(Request::class);
44 $response = new Response();
45
46 $this->container->httpAccess->expects(static::never())->method('getHttpResponse');
47 $this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback');
48
49 $this->container->bookmarkService
50 ->expects(static::once())
51 ->method('get')
52 ->with($id)
53 ->willReturn(
54 (new Bookmark())
55 ->setId($id)
56 ->setUrl($url = 'http://domain.tld')
57 ->setTitle($title = 'Bookmark Title')
58 ->setDescription($description = 'Bookmark description.')
59 ->setTags($tags = ['abc', 'def'])
60 ->setPrivate(true)
61 ->setCreated($createdAt = new \DateTime('2020-06-10 18:45:44'))
62 )
63 ;
64
65 $result = $this->controller->displayEditForm($request, $response, ['id' => (string) $id]);
66
67 static::assertSame(200, $result->getStatusCode());
68 static::assertSame('editlink', (string) $result->getBody());
69
70 static::assertSame('Edit Shaare - Shaarli', $assignedVariables['pagetitle']);
71 static::assertFalse($assignedVariables['link_is_new']);
72
73 static::assertSame($id, $assignedVariables['link']['id']);
74 static::assertSame($url, $assignedVariables['link']['url']);
75 static::assertSame($title, $assignedVariables['link']['title']);
76 static::assertSame($description, $assignedVariables['link']['description']);
77 static::assertSame(implode(' ', $tags), $assignedVariables['link']['tags']);
78 static::assertTrue($assignedVariables['link']['private']);
79 static::assertSame($createdAt, $assignedVariables['link']['created']);
80 }
81
82 /**
83 * Test displaying bookmark edit form
84 * Invalid ID provided.
85 */
86 public function testDisplayEditFormInvalidId(): void
87 {
88 $id = 'invalid';
89
90 $request = $this->createMock(Request::class);
91 $response = new Response();
92
93 $this->container->sessionManager
94 ->expects(static::once())
95 ->method('setSessionParameter')
96 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier invalid could not be found.'])
97 ;
98
99 $result = $this->controller->displayEditForm($request, $response, ['id' => $id]);
100
101 static::assertSame(302, $result->getStatusCode());
102 static::assertSame(['/subfolder/'], $result->getHeader('location'));
103 }
104
105 /**
106 * Test displaying bookmark edit form
107 * ID not provided.
108 */
109 public function testDisplayEditFormIdNotProvided(): void
110 {
111 $request = $this->createMock(Request::class);
112 $response = new Response();
113
114 $this->container->sessionManager
115 ->expects(static::once())
116 ->method('setSessionParameter')
117 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier could not be found.'])
118 ;
119
120 $result = $this->controller->displayEditForm($request, $response, []);
121
122 static::assertSame(302, $result->getStatusCode());
123 static::assertSame(['/subfolder/'], $result->getHeader('location'));
124 }
125
126 /**
127 * Test displaying bookmark edit form
128 * Bookmark not found.
129 */
130 public function testDisplayEditFormBookmarkNotFound(): void
131 {
132 $id = 123;
133
134 $request = $this->createMock(Request::class);
135 $response = new Response();
136
137 $this->container->bookmarkService
138 ->expects(static::once())
139 ->method('get')
140 ->with($id)
141 ->willThrowException(new BookmarkNotFoundException())
142 ;
143
144 $this->container->sessionManager
145 ->expects(static::once())
146 ->method('setSessionParameter')
147 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Bookmark with identifier 123 could not be found.'])
148 ;
149
150 $result = $this->controller->displayEditForm($request, $response, ['id' => (string) $id]);
151
152 static::assertSame(302, $result->getStatusCode());
153 static::assertSame(['/subfolder/'], $result->getHeader('location'));
154 }
155}
diff --git a/tests/front/controller/admin/ShaarePublishControllerTest/SaveBookmarkTest.php b/tests/front/controller/admin/ShaarePublishControllerTest/SaveBookmarkTest.php
new file mode 100644
index 00000000..b6a861bc
--- /dev/null
+++ b/tests/front/controller/admin/ShaarePublishControllerTest/SaveBookmarkTest.php
@@ -0,0 +1,369 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ShaarePublishControllerTest;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Config\ConfigManager;
9use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
10use Shaarli\Front\Controller\Admin\ShaarePublishController;
11use Shaarli\Front\Exception\WrongTokenException;
12use Shaarli\Http\HttpAccess;
13use Shaarli\Security\SessionManager;
14use Shaarli\TestCase;
15use Shaarli\Thumbnailer;
16use Slim\Http\Request;
17use Slim\Http\Response;
18
19class SaveBookmarkTest extends TestCase
20{
21 use FrontAdminControllerMockHelper;
22
23 /** @var ShaarePublishController */
24 protected $controller;
25
26 public function setUp(): void
27 {
28 $this->createContainer();
29
30 $this->container->httpAccess = $this->createMock(HttpAccess::class);
31 $this->controller = new ShaarePublishController($this->container);
32 }
33
34 /**
35 * Test save a new bookmark
36 */
37 public function testSaveBookmark(): void
38 {
39 $id = 21;
40 $parameters = [
41 'lf_url' => 'http://url.tld/other?part=3#hash',
42 'lf_title' => 'Provided Title',
43 'lf_description' => 'Provided description.',
44 'lf_tags' => 'abc def',
45 'lf_private' => '1',
46 'returnurl' => 'http://shaarli/subfolder/admin/add-shaare'
47 ];
48
49 $request = $this->createMock(Request::class);
50 $request
51 ->method('getParam')
52 ->willReturnCallback(function (string $key) use ($parameters): ?string {
53 return $parameters[$key] ?? null;
54 })
55 ;
56 $response = new Response();
57
58 $checkBookmark = function (Bookmark $bookmark) use ($parameters) {
59 static::assertSame($parameters['lf_url'], $bookmark->getUrl());
60 static::assertSame($parameters['lf_title'], $bookmark->getTitle());
61 static::assertSame($parameters['lf_description'], $bookmark->getDescription());
62 static::assertSame($parameters['lf_tags'], $bookmark->getTagsString());
63 static::assertTrue($bookmark->isPrivate());
64 };
65
66 $this->container->bookmarkService
67 ->expects(static::once())
68 ->method('addOrSet')
69 ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark {
70 static::assertFalse($save);
71
72 $checkBookmark($bookmark);
73
74 $bookmark->setId($id);
75
76 return $bookmark;
77 })
78 ;
79 $this->container->bookmarkService
80 ->expects(static::once())
81 ->method('set')
82 ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark {
83 static::assertTrue($save);
84
85 $checkBookmark($bookmark);
86
87 static::assertSame($id, $bookmark->getId());
88
89 return $bookmark;
90 })
91 ;
92
93 // Make sure that PluginManager hook is triggered
94 $this->container->pluginManager
95 ->expects(static::atLeastOnce())
96 ->method('executeHooks')
97 ->withConsecutive(['save_link'])
98 ->willReturnCallback(function (string $hook, array $data) use ($parameters, $id): array {
99 if ('save_link' === $hook) {
100 static::assertSame($id, $data['id']);
101 static::assertSame($parameters['lf_url'], $data['url']);
102 static::assertSame($parameters['lf_title'], $data['title']);
103 static::assertSame($parameters['lf_description'], $data['description']);
104 static::assertSame($parameters['lf_tags'], $data['tags']);
105 static::assertTrue($data['private']);
106 }
107
108 return $data;
109 })
110 ;
111
112 $result = $this->controller->save($request, $response);
113
114 static::assertSame(302, $result->getStatusCode());
115 static::assertRegExp('@/subfolder/#[\w\-]{6}@', $result->getHeader('location')[0]);
116 }
117
118
119 /**
120 * Test save an existing bookmark
121 */
122 public function testSaveExistingBookmark(): void
123 {
124 $id = 21;
125 $parameters = [
126 'lf_id' => (string) $id,
127 'lf_url' => 'http://url.tld/other?part=3#hash',
128 'lf_title' => 'Provided Title',
129 'lf_description' => 'Provided description.',
130 'lf_tags' => 'abc def',
131 'lf_private' => '1',
132 'returnurl' => 'http://shaarli/subfolder/?page=2'
133 ];
134
135 $request = $this->createMock(Request::class);
136 $request
137 ->method('getParam')
138 ->willReturnCallback(function (string $key) use ($parameters): ?string {
139 return $parameters[$key] ?? null;
140 })
141 ;
142 $response = new Response();
143
144 $checkBookmark = function (Bookmark $bookmark) use ($parameters, $id) {
145 static::assertSame($id, $bookmark->getId());
146 static::assertSame($parameters['lf_url'], $bookmark->getUrl());
147 static::assertSame($parameters['lf_title'], $bookmark->getTitle());
148 static::assertSame($parameters['lf_description'], $bookmark->getDescription());
149 static::assertSame($parameters['lf_tags'], $bookmark->getTagsString());
150 static::assertTrue($bookmark->isPrivate());
151 };
152
153 $this->container->bookmarkService->expects(static::atLeastOnce())->method('exists')->willReturn(true);
154 $this->container->bookmarkService
155 ->expects(static::once())
156 ->method('get')
157 ->willReturn((new Bookmark())->setId($id)->setUrl('http://other.url'))
158 ;
159 $this->container->bookmarkService
160 ->expects(static::once())
161 ->method('addOrSet')
162 ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark {
163 static::assertFalse($save);
164
165 $checkBookmark($bookmark);
166
167 return $bookmark;
168 })
169 ;
170 $this->container->bookmarkService
171 ->expects(static::once())
172 ->method('set')
173 ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): Bookmark {
174 static::assertTrue($save);
175
176 $checkBookmark($bookmark);
177
178 static::assertSame($id, $bookmark->getId());
179
180 return $bookmark;
181 })
182 ;
183
184 // Make sure that PluginManager hook is triggered
185 $this->container->pluginManager
186 ->expects(static::atLeastOnce())
187 ->method('executeHooks')
188 ->withConsecutive(['save_link'])
189 ->willReturnCallback(function (string $hook, array $data) use ($parameters, $id): array {
190 if ('save_link' === $hook) {
191 static::assertSame($id, $data['id']);
192 static::assertSame($parameters['lf_url'], $data['url']);
193 static::assertSame($parameters['lf_title'], $data['title']);
194 static::assertSame($parameters['lf_description'], $data['description']);
195 static::assertSame($parameters['lf_tags'], $data['tags']);
196 static::assertTrue($data['private']);
197 }
198
199 return $data;
200 })
201 ;
202
203 $result = $this->controller->save($request, $response);
204
205 static::assertSame(302, $result->getStatusCode());
206 static::assertRegExp('@/subfolder/\?page=2#[\w\-]{6}@', $result->getHeader('location')[0]);
207 }
208
209 /**
210 * Test save a bookmark - try to retrieve the thumbnail
211 */
212 public function testSaveBookmarkWithThumbnailSync(): void
213 {
214 $parameters = ['lf_url' => 'http://url.tld/other?part=3#hash'];
215
216 $request = $this->createMock(Request::class);
217 $request
218 ->method('getParam')
219 ->willReturnCallback(function (string $key) use ($parameters): ?string {
220 return $parameters[$key] ?? null;
221 })
222 ;
223 $response = new Response();
224
225 $this->container->conf = $this->createMock(ConfigManager::class);
226 $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
227 if ($key === 'thumbnails.mode') {
228 return Thumbnailer::MODE_ALL;
229 } elseif ($key === 'general.enable_async_metadata') {
230 return false;
231 }
232
233 return $default;
234 });
235
236 $this->container->thumbnailer = $this->createMock(Thumbnailer::class);
237 $this->container->thumbnailer
238 ->expects(static::once())
239 ->method('get')
240 ->with($parameters['lf_url'])
241 ->willReturn($thumb = 'http://thumb.url')
242 ;
243
244 $this->container->bookmarkService
245 ->expects(static::once())
246 ->method('addOrSet')
247 ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($thumb): Bookmark {
248 static::assertSame($thumb, $bookmark->getThumbnail());
249
250 return $bookmark;
251 })
252 ;
253
254 $result = $this->controller->save($request, $response);
255
256 static::assertSame(302, $result->getStatusCode());
257 }
258
259 /**
260 * Test save a bookmark - with ID #0
261 */
262 public function testSaveBookmarkWithIdZero(): void
263 {
264 $parameters = ['lf_id' => '0'];
265
266 $request = $this->createMock(Request::class);
267 $request
268 ->method('getParam')
269 ->willReturnCallback(function (string $key) use ($parameters): ?string {
270 return $parameters[$key] ?? null;
271 })
272 ;
273 $response = new Response();
274
275 $this->container->bookmarkService->expects(static::once())->method('exists')->with(0)->willReturn(true);
276 $this->container->bookmarkService->expects(static::once())->method('get')->with(0)->willReturn(new Bookmark());
277
278 $result = $this->controller->save($request, $response);
279
280 static::assertSame(302, $result->getStatusCode());
281 }
282
283 /**
284 * Test save a bookmark - do not attempt to retrieve thumbnails if async mode is enabled.
285 */
286 public function testSaveBookmarkWithThumbnailAsync(): void
287 {
288 $parameters = ['lf_url' => 'http://url.tld/other?part=3#hash'];
289
290 $request = $this->createMock(Request::class);
291 $request
292 ->method('getParam')
293 ->willReturnCallback(function (string $key) use ($parameters): ?string {
294 return $parameters[$key] ?? null;
295 })
296 ;
297 $response = new Response();
298
299 $this->container->conf = $this->createMock(ConfigManager::class);
300 $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
301 if ($key === 'thumbnails.mode') {
302 return Thumbnailer::MODE_ALL;
303 } elseif ($key === 'general.enable_async_metadata') {
304 return true;
305 }
306
307 return $default;
308 });
309
310 $this->container->thumbnailer = $this->createMock(Thumbnailer::class);
311 $this->container->thumbnailer->expects(static::never())->method('get');
312
313 $this->container->bookmarkService
314 ->expects(static::once())
315 ->method('addOrSet')
316 ->willReturnCallback(function (Bookmark $bookmark): Bookmark {
317 static::assertNull($bookmark->getThumbnail());
318
319 return $bookmark;
320 })
321 ;
322
323 $result = $this->controller->save($request, $response);
324
325 static::assertSame(302, $result->getStatusCode());
326 }
327
328 /**
329 * Change the password with a wrong existing password
330 */
331 public function testSaveBookmarkFromBookmarklet(): void
332 {
333 $parameters = ['source' => 'bookmarklet'];
334
335 $request = $this->createMock(Request::class);
336 $request
337 ->method('getParam')
338 ->willReturnCallback(function (string $key) use ($parameters): ?string {
339 return $parameters[$key] ?? null;
340 })
341 ;
342 $response = new Response();
343
344 $result = $this->controller->save($request, $response);
345
346 static::assertSame(200, $result->getStatusCode());
347 static::assertSame('<script>self.close();</script>', (string) $result->getBody());
348 }
349
350 /**
351 * Change the password with a wrong existing password
352 */
353 public function testSaveBookmarkWrongToken(): void
354 {
355 $this->container->sessionManager = $this->createMock(SessionManager::class);
356 $this->container->sessionManager->method('checkToken')->willReturn(false);
357
358 $this->container->bookmarkService->expects(static::never())->method('addOrSet');
359 $this->container->bookmarkService->expects(static::never())->method('set');
360
361 $request = $this->createMock(Request::class);
362 $response = new Response();
363
364 $this->expectException(WrongTokenException::class);
365
366 $this->controller->save($request, $response);
367 }
368
369}
diff --git a/tests/front/controller/admin/ShaarliAdminControllerTest.php b/tests/front/controller/admin/ShaarliAdminControllerTest.php
new file mode 100644
index 00000000..486d5d2d
--- /dev/null
+++ b/tests/front/controller/admin/ShaarliAdminControllerTest.php
@@ -0,0 +1,184 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Front\Exception\WrongTokenException;
8use Shaarli\Security\SessionManager;
9use Shaarli\TestCase;
10use Slim\Http\Request;
11
12/**
13 * Class ShaarliControllerTest
14 *
15 * This class is used to test default behavior of ShaarliAdminController abstract class.
16 * It uses a dummy non abstract controller.
17 */
18class ShaarliAdminControllerTest extends TestCase
19{
20 use FrontAdminControllerMockHelper;
21
22 /** @var ShaarliAdminController */
23 protected $controller;
24
25 public function setUp(): void
26 {
27 $this->createContainer();
28
29 $this->controller = new class($this->container) extends ShaarliAdminController
30 {
31 public function checkToken(Request $request): bool
32 {
33 return parent::checkToken($request);
34 }
35
36 public function saveSuccessMessage(string $message): void
37 {
38 parent::saveSuccessMessage($message);
39 }
40
41 public function saveWarningMessage(string $message): void
42 {
43 parent::saveWarningMessage($message);
44 }
45
46 public function saveErrorMessage(string $message): void
47 {
48 parent::saveErrorMessage($message);
49 }
50 };
51 }
52
53 /**
54 * Trigger controller's checkToken with a valid token.
55 */
56 public function testCheckTokenWithValidToken(): void
57 {
58 $request = $this->createMock(Request::class);
59 $request->method('getParam')->with('token')->willReturn($token = '12345');
60
61 $this->container->sessionManager = $this->createMock(SessionManager::class);
62 $this->container->sessionManager->method('checkToken')->with($token)->willReturn(true);
63
64 static::assertTrue($this->controller->checkToken($request));
65 }
66
67 /**
68 * Trigger controller's checkToken with na valid token should raise an exception.
69 */
70 public function testCheckTokenWithNotValidToken(): void
71 {
72 $request = $this->createMock(Request::class);
73 $request->method('getParam')->with('token')->willReturn($token = '12345');
74
75 $this->container->sessionManager = $this->createMock(SessionManager::class);
76 $this->container->sessionManager->method('checkToken')->with($token)->willReturn(false);
77
78 $this->expectException(WrongTokenException::class);
79
80 $this->controller->checkToken($request);
81 }
82
83 /**
84 * Test saveSuccessMessage() with a first message.
85 */
86 public function testSaveSuccessMessage(): void
87 {
88 $this->container->sessionManager
89 ->expects(static::once())
90 ->method('setSessionParameter')
91 ->with(SessionManager::KEY_SUCCESS_MESSAGES, [$message = 'bravo!'])
92 ;
93
94 $this->controller->saveSuccessMessage($message);
95 }
96
97 /**
98 * Test saveSuccessMessage() with existing messages.
99 */
100 public function testSaveSuccessMessageWithExistingMessages(): void
101 {
102 $this->container->sessionManager
103 ->expects(static::once())
104 ->method('getSessionParameter')
105 ->with(SessionManager::KEY_SUCCESS_MESSAGES)
106 ->willReturn(['success1', 'success2'])
107 ;
108 $this->container->sessionManager
109 ->expects(static::once())
110 ->method('setSessionParameter')
111 ->with(SessionManager::KEY_SUCCESS_MESSAGES, ['success1', 'success2', $message = 'bravo!'])
112 ;
113
114 $this->controller->saveSuccessMessage($message);
115 }
116
117 /**
118 * Test saveWarningMessage() with a first message.
119 */
120 public function testSaveWarningMessage(): void
121 {
122 $this->container->sessionManager
123 ->expects(static::once())
124 ->method('setSessionParameter')
125 ->with(SessionManager::KEY_WARNING_MESSAGES, [$message = 'warning!'])
126 ;
127
128 $this->controller->saveWarningMessage($message);
129 }
130
131 /**
132 * Test saveWarningMessage() with existing messages.
133 */
134 public function testSaveWarningMessageWithExistingMessages(): void
135 {
136 $this->container->sessionManager
137 ->expects(static::once())
138 ->method('getSessionParameter')
139 ->with(SessionManager::KEY_WARNING_MESSAGES)
140 ->willReturn(['warning1', 'warning2'])
141 ;
142 $this->container->sessionManager
143 ->expects(static::once())
144 ->method('setSessionParameter')
145 ->with(SessionManager::KEY_WARNING_MESSAGES, ['warning1', 'warning2', $message = 'warning!'])
146 ;
147
148 $this->controller->saveWarningMessage($message);
149 }
150
151 /**
152 * Test saveErrorMessage() with a first message.
153 */
154 public function testSaveErrorMessage(): void
155 {
156 $this->container->sessionManager
157 ->expects(static::once())
158 ->method('setSessionParameter')
159 ->with(SessionManager::KEY_ERROR_MESSAGES, [$message = 'error!'])
160 ;
161
162 $this->controller->saveErrorMessage($message);
163 }
164
165 /**
166 * Test saveErrorMessage() with existing messages.
167 */
168 public function testSaveErrorMessageWithExistingMessages(): void
169 {
170 $this->container->sessionManager
171 ->expects(static::once())
172 ->method('getSessionParameter')
173 ->with(SessionManager::KEY_ERROR_MESSAGES)
174 ->willReturn(['error1', 'error2'])
175 ;
176 $this->container->sessionManager
177 ->expects(static::once())
178 ->method('setSessionParameter')
179 ->with(SessionManager::KEY_ERROR_MESSAGES, ['error1', 'error2', $message = 'error!'])
180 ;
181
182 $this->controller->saveErrorMessage($message);
183 }
184}
diff --git a/tests/front/controller/admin/ThumbnailsControllerTest.php b/tests/front/controller/admin/ThumbnailsControllerTest.php
new file mode 100644
index 00000000..e5749654
--- /dev/null
+++ b/tests/front/controller/admin/ThumbnailsControllerTest.php
@@ -0,0 +1,156 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
9use Shaarli\TestCase;
10use Shaarli\Thumbnailer;
11use Slim\Http\Request;
12use Slim\Http\Response;
13
14class ThumbnailsControllerTest extends TestCase
15{
16 use FrontAdminControllerMockHelper;
17
18 /** @var ThumbnailsController */
19 protected $controller;
20
21 public function setUp(): void
22 {
23 $this->createContainer();
24
25 $this->controller = new ThumbnailsController($this->container);
26 }
27
28 /**
29 * Test displaying the thumbnails update page
30 * Note that only non-note and HTTP bookmarks should be returned.
31 */
32 public function testIndex(): void
33 {
34 $assignedVariables = [];
35 $this->assignTemplateVars($assignedVariables);
36
37 $request = $this->createMock(Request::class);
38 $response = new Response();
39
40 $this->container->bookmarkService
41 ->expects(static::once())
42 ->method('search')
43 ->willReturn([
44 (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
45 (new Bookmark())->setId(2)->setUrl('?abcdef')->setTitle('Note 1'),
46 (new Bookmark())->setId(3)->setUrl('http://url2.tld')->setTitle('Title 2'),
47 (new Bookmark())->setId(4)->setUrl('ftp://domain.tld', ['ftp'])->setTitle('FTP'),
48 ])
49 ;
50
51 $result = $this->controller->index($request, $response);
52
53 static::assertSame(200, $result->getStatusCode());
54 static::assertSame('thumbnails', (string) $result->getBody());
55
56 static::assertSame('Thumbnails update - Shaarli', $assignedVariables['pagetitle']);
57 static::assertSame([1, 3], $assignedVariables['ids']);
58 }
59
60 /**
61 * Test updating a bookmark thumbnail with valid parameters
62 */
63 public function testAjaxUpdateValid(): void
64 {
65 $request = $this->createMock(Request::class);
66 $response = new Response();
67
68 $bookmark = (new Bookmark())
69 ->setId($id = 123)
70 ->setUrl($url = 'http://url1.tld')
71 ->setTitle('Title 1')
72 ->setThumbnail(false)
73 ;
74
75 $this->container->thumbnailer = $this->createMock(Thumbnailer::class);
76 $this->container->thumbnailer
77 ->expects(static::once())
78 ->method('get')
79 ->with($url)
80 ->willReturn($thumb = 'http://img.tld/pic.png')
81 ;
82
83 $this->container->bookmarkService
84 ->expects(static::once())
85 ->method('get')
86 ->with($id)
87 ->willReturn($bookmark)
88 ;
89 $this->container->bookmarkService
90 ->expects(static::once())
91 ->method('set')
92 ->willReturnCallback(function (Bookmark $bookmark) use ($thumb): Bookmark {
93 static::assertSame($thumb, $bookmark->getThumbnail());
94
95 return $bookmark;
96 })
97 ;
98
99 $result = $this->controller->ajaxUpdate($request, $response, ['id' => (string) $id]);
100
101 static::assertSame(200, $result->getStatusCode());
102
103 $payload = json_decode((string) $result->getBody(), true);
104
105 static::assertSame($id, $payload['id']);
106 static::assertSame($url, $payload['url']);
107 static::assertSame($thumb, $payload['thumbnail']);
108 }
109
110 /**
111 * Test updating a bookmark thumbnail - Invalid ID
112 */
113 public function testAjaxUpdateInvalidId(): void
114 {
115 $request = $this->createMock(Request::class);
116 $response = new Response();
117
118 $result = $this->controller->ajaxUpdate($request, $response, ['id' => 'nope']);
119
120 static::assertSame(400, $result->getStatusCode());
121 }
122
123 /**
124 * Test updating a bookmark thumbnail - No ID
125 */
126 public function testAjaxUpdateNoId(): void
127 {
128 $request = $this->createMock(Request::class);
129 $response = new Response();
130
131 $result = $this->controller->ajaxUpdate($request, $response, []);
132
133 static::assertSame(400, $result->getStatusCode());
134 }
135
136 /**
137 * Test updating a bookmark thumbnail with valid parameters
138 */
139 public function testAjaxUpdateBookmarkNotFound(): void
140 {
141 $id = 123;
142 $request = $this->createMock(Request::class);
143 $response = new Response();
144
145 $this->container->bookmarkService
146 ->expects(static::once())
147 ->method('get')
148 ->with($id)
149 ->willThrowException(new BookmarkNotFoundException())
150 ;
151
152 $result = $this->controller->ajaxUpdate($request, $response, ['id' => (string) $id]);
153
154 static::assertSame(404, $result->getStatusCode());
155 }
156}
diff --git a/tests/front/controller/admin/TokenControllerTest.php b/tests/front/controller/admin/TokenControllerTest.php
new file mode 100644
index 00000000..d2f0907f
--- /dev/null
+++ b/tests/front/controller/admin/TokenControllerTest.php
@@ -0,0 +1,41 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\TestCase;
8use Slim\Http\Request;
9use Slim\Http\Response;
10
11class TokenControllerTest extends TestCase
12{
13 use FrontAdminControllerMockHelper;
14
15 /** @var TokenController */
16 protected $controller;
17
18 public function setUp(): void
19 {
20 $this->createContainer();
21
22 $this->controller = new TokenController($this->container);
23 }
24
25 public function testGetToken(): void
26 {
27 $request = $this->createMock(Request::class);
28 $response = new Response();
29
30 $this->container->sessionManager
31 ->expects(static::once())
32 ->method('generateToken')
33 ->willReturn($token = 'token1234')
34 ;
35
36 $result = $this->controller->getToken($request, $response);
37
38 static::assertSame(200, $result->getStatusCode());
39 static::assertSame($token, (string) $result->getBody());
40 }
41}
diff --git a/tests/front/controller/admin/ToolsControllerTest.php b/tests/front/controller/admin/ToolsControllerTest.php
new file mode 100644
index 00000000..e82f8b14
--- /dev/null
+++ b/tests/front/controller/admin/ToolsControllerTest.php
@@ -0,0 +1,69 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin;
6
7use Shaarli\TestCase;
8use Slim\Http\Request;
9use Slim\Http\Response;
10
11class ToolsControllerTest extends TestCase
12{
13 use FrontAdminControllerMockHelper;
14
15 /** @var ToolsController */
16 protected $controller;
17
18 public function setUp(): void
19 {
20 $this->createContainer();
21
22 $this->controller = new ToolsController($this->container);
23 }
24
25 public function testDefaultInvokeWithHttps(): void
26 {
27 $request = $this->createMock(Request::class);
28 $response = new Response();
29
30 $this->container->environment = [
31 'SERVER_NAME' => 'shaarli',
32 'SERVER_PORT' => 443,
33 'HTTPS' => 'on',
34 ];
35
36 // Save RainTPL assigned variables
37 $assignedVariables = [];
38 $this->assignTemplateVars($assignedVariables);
39
40 $result = $this->controller->index($request, $response);
41
42 static::assertSame(200, $result->getStatusCode());
43 static::assertSame('tools', (string) $result->getBody());
44 static::assertSame('https://shaarli/', $assignedVariables['pageabsaddr']);
45 static::assertTrue($assignedVariables['sslenabled']);
46 }
47
48 public function testDefaultInvokeWithoutHttps(): void
49 {
50 $request = $this->createMock(Request::class);
51 $response = new Response();
52
53 $this->container->environment = [
54 'SERVER_NAME' => 'shaarli',
55 'SERVER_PORT' => 80,
56 ];
57
58 // Save RainTPL assigned variables
59 $assignedVariables = [];
60 $this->assignTemplateVars($assignedVariables);
61
62 $result = $this->controller->index($request, $response);
63
64 static::assertSame(200, $result->getStatusCode());
65 static::assertSame('tools', (string) $result->getBody());
66 static::assertSame('http://shaarli/', $assignedVariables['pageabsaddr']);
67 static::assertFalse($assignedVariables['sslenabled']);
68 }
69}
diff --git a/tests/front/controller/visitor/BookmarkListControllerTest.php b/tests/front/controller/visitor/BookmarkListControllerTest.php
new file mode 100644
index 00000000..5cbc8c73
--- /dev/null
+++ b/tests/front/controller/visitor/BookmarkListControllerTest.php
@@ -0,0 +1,532 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
9use Shaarli\Config\ConfigManager;
10use Shaarli\Security\LoginManager;
11use Shaarli\TestCase;
12use Shaarli\Thumbnailer;
13use Slim\Http\Request;
14use Slim\Http\Response;
15
16class BookmarkListControllerTest extends TestCase
17{
18 use FrontControllerMockHelper;
19
20 /** @var BookmarkListController */
21 protected $controller;
22
23 public function setUp(): void
24 {
25 $this->createContainer();
26
27 $this->controller = new BookmarkListController($this->container);
28 }
29
30 /**
31 * Test rendering list of bookmarks with default parameters (first page).
32 */
33 public function testIndexDefaultFirstPage(): void
34 {
35 $assignedVariables = [];
36 $this->assignTemplateVars($assignedVariables);
37
38 $request = $this->createMock(Request::class);
39 $response = new Response();
40
41 $this->container->bookmarkService
42 ->expects(static::once())
43 ->method('search')
44 ->with(
45 ['searchtags' => '', 'searchterm' => ''],
46 null,
47 false,
48 false
49 )
50 ->willReturn([
51 (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
52 (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
53 (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
54 ]
55 );
56
57 $this->container->sessionManager
58 ->method('getSessionParameter')
59 ->willReturnCallback(function (string $parameter, $default = null) {
60 if ('LINKS_PER_PAGE' === $parameter) {
61 return 2;
62 }
63
64 return $default;
65 })
66 ;
67
68 $result = $this->controller->index($request, $response);
69
70 static::assertSame(200, $result->getStatusCode());
71 static::assertSame('linklist', (string) $result->getBody());
72
73 static::assertSame('Shaarli', $assignedVariables['pagetitle']);
74 static::assertSame('?page=2', $assignedVariables['previous_page_url']);
75 static::assertSame('', $assignedVariables['next_page_url']);
76 static::assertSame(2, $assignedVariables['page_max']);
77 static::assertSame('', $assignedVariables['search_tags']);
78 static::assertSame(3, $assignedVariables['result_count']);
79 static::assertSame(1, $assignedVariables['page_current']);
80 static::assertSame('', $assignedVariables['search_term']);
81 static::assertNull($assignedVariables['visibility']);
82 static::assertCount(2, $assignedVariables['links']);
83
84 $link = $assignedVariables['links'][0];
85
86 static::assertSame(1, $link['id']);
87 static::assertSame('http://url1.tld', $link['url']);
88 static::assertSame('Title 1', $link['title']);
89
90 $link = $assignedVariables['links'][1];
91
92 static::assertSame(2, $link['id']);
93 static::assertSame('http://url2.tld', $link['url']);
94 static::assertSame('Title 2', $link['title']);
95 }
96
97 /**
98 * Test rendering list of bookmarks with default parameters (second page).
99 */
100 public function testIndexDefaultSecondPage(): void
101 {
102 $assignedVariables = [];
103 $this->assignTemplateVars($assignedVariables);
104
105 $request = $this->createMock(Request::class);
106 $request->method('getParam')->willReturnCallback(function (string $key) {
107 if ('page' === $key) {
108 return '2';
109 }
110
111 return null;
112 });
113 $response = new Response();
114
115 $this->container->bookmarkService
116 ->expects(static::once())
117 ->method('search')
118 ->with(
119 ['searchtags' => '', 'searchterm' => ''],
120 null,
121 false,
122 false
123 )
124 ->willReturn([
125 (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
126 (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
127 (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
128 ])
129 ;
130
131 $this->container->sessionManager
132 ->method('getSessionParameter')
133 ->willReturnCallback(function (string $parameter, $default = null) {
134 if ('LINKS_PER_PAGE' === $parameter) {
135 return 2;
136 }
137
138 return $default;
139 })
140 ;
141
142 $result = $this->controller->index($request, $response);
143
144 static::assertSame(200, $result->getStatusCode());
145 static::assertSame('linklist', (string) $result->getBody());
146
147 static::assertSame('Shaarli', $assignedVariables['pagetitle']);
148 static::assertSame('', $assignedVariables['previous_page_url']);
149 static::assertSame('?page=1', $assignedVariables['next_page_url']);
150 static::assertSame(2, $assignedVariables['page_max']);
151 static::assertSame('', $assignedVariables['search_tags']);
152 static::assertSame(3, $assignedVariables['result_count']);
153 static::assertSame(2, $assignedVariables['page_current']);
154 static::assertSame('', $assignedVariables['search_term']);
155 static::assertNull($assignedVariables['visibility']);
156 static::assertCount(1, $assignedVariables['links']);
157
158 $link = $assignedVariables['links'][2];
159
160 static::assertSame(3, $link['id']);
161 static::assertSame('http://url3.tld', $link['url']);
162 static::assertSame('Title 3', $link['title']);
163 }
164
165 /**
166 * Test rendering list of bookmarks with filters.
167 */
168 public function testIndexDefaultWithFilters(): void
169 {
170 $assignedVariables = [];
171 $this->assignTemplateVars($assignedVariables);
172
173 $request = $this->createMock(Request::class);
174 $request->method('getParam')->willReturnCallback(function (string $key) {
175 if ('searchtags' === $key) {
176 return 'abc def';
177 }
178 if ('searchterm' === $key) {
179 return 'ghi jkl';
180 }
181
182 return null;
183 });
184 $response = new Response();
185
186 $this->container->sessionManager
187 ->method('getSessionParameter')
188 ->willReturnCallback(function (string $key, $default) {
189 if ('LINKS_PER_PAGE' === $key) {
190 return 2;
191 }
192 if ('visibility' === $key) {
193 return 'private';
194 }
195 if ('untaggedonly' === $key) {
196 return true;
197 }
198
199 return $default;
200 })
201 ;
202
203 $this->container->bookmarkService
204 ->expects(static::once())
205 ->method('search')
206 ->with(
207 ['searchtags' => 'abc def', 'searchterm' => 'ghi jkl'],
208 'private',
209 false,
210 true
211 )
212 ->willReturn([
213 (new Bookmark())->setId(1)->setUrl('http://url1.tld')->setTitle('Title 1'),
214 (new Bookmark())->setId(2)->setUrl('http://url2.tld')->setTitle('Title 2'),
215 (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setTitle('Title 3'),
216 ])
217 ;
218
219 $result = $this->controller->index($request, $response);
220
221 static::assertSame(200, $result->getStatusCode());
222 static::assertSame('linklist', (string) $result->getBody());
223
224 static::assertSame('Search: ghi jkl [abc] [def] - Shaarli', $assignedVariables['pagetitle']);
225 static::assertSame('?page=2&searchterm=ghi+jkl&searchtags=abc+def', $assignedVariables['previous_page_url']);
226 }
227
228 /**
229 * Test displaying a permalink with valid parameters
230 */
231 public function testPermalinkValid(): void
232 {
233 $hash = 'abcdef';
234
235 $assignedVariables = [];
236 $this->assignTemplateVars($assignedVariables);
237
238 $request = $this->createMock(Request::class);
239 $response = new Response();
240
241 $this->container->bookmarkService
242 ->expects(static::once())
243 ->method('findByHash')
244 ->with($hash)
245 ->willReturn((new Bookmark())->setId(123)->setTitle('Title 1')->setUrl('http://url1.tld'))
246 ;
247
248 $result = $this->controller->permalink($request, $response, ['hash' => $hash]);
249
250 static::assertSame(200, $result->getStatusCode());
251 static::assertSame('linklist', (string) $result->getBody());
252
253 static::assertSame('Title 1 - Shaarli', $assignedVariables['pagetitle']);
254 static::assertCount(1, $assignedVariables['links']);
255
256 $link = $assignedVariables['links'][0];
257
258 static::assertSame(123, $link['id']);
259 static::assertSame('http://url1.tld', $link['url']);
260 static::assertSame('Title 1', $link['title']);
261 }
262
263 /**
264 * Test displaying a permalink with an unknown small hash : renders a 404 template error
265 */
266 public function testPermalinkNotFound(): void
267 {
268 $hash = 'abcdef';
269
270 $assignedVariables = [];
271 $this->assignTemplateVars($assignedVariables);
272
273 $request = $this->createMock(Request::class);
274 $response = new Response();
275
276 $this->container->bookmarkService
277 ->expects(static::once())
278 ->method('findByHash')
279 ->with($hash)
280 ->willThrowException(new BookmarkNotFoundException())
281 ;
282
283 $result = $this->controller->permalink($request, $response, ['hash' => $hash]);
284
285 static::assertSame(200, $result->getStatusCode());
286 static::assertSame('404', (string) $result->getBody());
287
288 static::assertSame(
289 'The link you are trying to reach does not exist or has been deleted.',
290 $assignedVariables['error_message']
291 );
292 }
293
294 /**
295 * Test GET /shaare/{hash}?key={key} - Find a link by hash using a private link.
296 */
297 public function testPermalinkWithPrivateKey(): void
298 {
299 $hash = 'abcdef';
300 $privateKey = 'this is a private key';
301
302 $assignedVariables = [];
303 $this->assignTemplateVars($assignedVariables);
304
305 $request = $this->createMock(Request::class);
306 $request->method('getParam')->willReturnCallback(function (string $key, $default = null) use ($privateKey) {
307 return $key === 'key' ? $privateKey : $default;
308 });
309 $response = new Response();
310
311 $this->container->bookmarkService
312 ->expects(static::once())
313 ->method('findByHash')
314 ->with($hash, $privateKey)
315 ->willReturn((new Bookmark())->setId(123)->setTitle('Title 1')->setUrl('http://url1.tld'))
316 ;
317
318 $result = $this->controller->permalink($request, $response, ['hash' => $hash]);
319
320 static::assertSame(200, $result->getStatusCode());
321 static::assertSame('linklist', (string) $result->getBody());
322 static::assertCount(1, $assignedVariables['links']);
323 }
324
325 /**
326 * Test getting link list with thumbnail updates.
327 * -> 2 thumbnails update, only 1 datastore write
328 */
329 public function testThumbnailUpdateFromLinkList(): void
330 {
331 $request = $this->createMock(Request::class);
332 $response = new Response();
333
334 $this->container->loginManager = $this->createMock(LoginManager::class);
335 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
336
337 $this->container->conf = $this->createMock(ConfigManager::class);
338 $this->container->conf
339 ->method('get')
340 ->willReturnCallback(function (string $key, $default) {
341 if ($key === 'thumbnails.mode') {
342 return Thumbnailer::MODE_ALL;
343 } elseif ($key === 'general.enable_async_metadata') {
344 return false;
345 }
346
347 return $default;
348 })
349 ;
350
351 $this->container->thumbnailer = $this->createMock(Thumbnailer::class);
352 $this->container->thumbnailer
353 ->expects(static::exactly(2))
354 ->method('get')
355 ->withConsecutive(['https://url2.tld'], ['https://url4.tld'])
356 ;
357
358 $this->container->bookmarkService
359 ->expects(static::once())
360 ->method('search')
361 ->willReturn([
362 (new Bookmark())->setId(1)->setUrl('https://url1.tld')->setTitle('Title 1')->setThumbnail(false),
363 $b1 = (new Bookmark())->setId(2)->setUrl('https://url2.tld')->setTitle('Title 2'),
364 (new Bookmark())->setId(3)->setUrl('https://url3.tld')->setTitle('Title 3')->setThumbnail(false),
365 $b2 = (new Bookmark())->setId(2)->setUrl('https://url4.tld')->setTitle('Title 4'),
366 (new Bookmark())->setId(2)->setUrl('ftp://url5.tld', ['ftp'])->setTitle('Title 5'),
367 ])
368 ;
369 $this->container->bookmarkService
370 ->expects(static::exactly(2))
371 ->method('set')
372 ->withConsecutive([$b1, false], [$b2, false])
373 ;
374 $this->container->bookmarkService->expects(static::once())->method('save');
375
376 $result = $this->controller->index($request, $response);
377
378 static::assertSame(200, $result->getStatusCode());
379 static::assertSame('linklist', (string) $result->getBody());
380 }
381
382 /**
383 * Test getting a permalink with thumbnail update.
384 */
385 public function testThumbnailUpdateFromPermalink(): void
386 {
387 $request = $this->createMock(Request::class);
388 $response = new Response();
389
390 $this->container->loginManager = $this->createMock(LoginManager::class);
391 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
392
393 $this->container->conf = $this->createMock(ConfigManager::class);
394 $this->container->conf
395 ->method('get')
396 ->willReturnCallback(function (string $key, $default) {
397 if ($key === 'thumbnails.mode') {
398 return Thumbnailer::MODE_ALL;
399 } elseif ($key === 'general.enable_async_metadata') {
400 return false;
401 }
402
403 return $default;
404 })
405 ;
406
407 $this->container->thumbnailer = $this->createMock(Thumbnailer::class);
408 $this->container->thumbnailer->expects(static::once())->method('get')->withConsecutive(['https://url.tld']);
409
410 $this->container->bookmarkService
411 ->expects(static::once())
412 ->method('findByHash')
413 ->willReturn($bookmark = (new Bookmark())->setId(2)->setUrl('https://url.tld')->setTitle('Title 1'))
414 ;
415 $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, true);
416 $this->container->bookmarkService->expects(static::never())->method('save');
417
418 $result = $this->controller->permalink($request, $response, ['hash' => 'abc']);
419
420 static::assertSame(200, $result->getStatusCode());
421 static::assertSame('linklist', (string) $result->getBody());
422 }
423
424 /**
425 * Test getting a permalink with thumbnail update with async setting: no update should run.
426 */
427 public function testThumbnailUpdateFromPermalinkAsync(): void
428 {
429 $request = $this->createMock(Request::class);
430 $response = new Response();
431
432 $this->container->loginManager = $this->createMock(LoginManager::class);
433 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
434
435 $this->container->conf = $this->createMock(ConfigManager::class);
436 $this->container->conf
437 ->method('get')
438 ->willReturnCallback(function (string $key, $default) {
439 if ($key === 'thumbnails.mode') {
440 return Thumbnailer::MODE_ALL;
441 } elseif ($key === 'general.enable_async_metadata') {
442 return true;
443 }
444
445 return $default;
446 })
447 ;
448
449 $this->container->thumbnailer = $this->createMock(Thumbnailer::class);
450 $this->container->thumbnailer->expects(static::never())->method('get');
451
452 $this->container->bookmarkService
453 ->expects(static::once())
454 ->method('findByHash')
455 ->willReturn((new Bookmark())->setId(2)->setUrl('https://url.tld')->setTitle('Title 1'))
456 ;
457 $this->container->bookmarkService->expects(static::never())->method('set');
458 $this->container->bookmarkService->expects(static::never())->method('save');
459
460 $result = $this->controller->permalink($request, $response, ['hash' => 'abc']);
461
462 static::assertSame(200, $result->getStatusCode());
463 }
464
465 /**
466 * Trigger legacy controller in link list controller: permalink
467 */
468 public function testLegacyControllerPermalink(): void
469 {
470 $hash = 'abcdef';
471 $this->container->environment['QUERY_STRING'] = $hash;
472
473 $request = $this->createMock(Request::class);
474 $response = new Response();
475
476 $result = $this->controller->index($request, $response);
477
478 static::assertSame(302, $result->getStatusCode());
479 static::assertSame('/subfolder/shaare/' . $hash, $result->getHeader('location')[0]);
480 }
481
482 /**
483 * Trigger legacy controller in link list controller: ?do= query parameter
484 */
485 public function testLegacyControllerDoPage(): void
486 {
487 $request = $this->createMock(Request::class);
488 $request->method('getQueryParam')->with('do')->willReturn('picwall');
489 $response = new Response();
490
491 $result = $this->controller->index($request, $response);
492
493 static::assertSame(302, $result->getStatusCode());
494 static::assertSame('/subfolder/picture-wall', $result->getHeader('location')[0]);
495 }
496
497 /**
498 * Trigger legacy controller in link list controller: ?do= query parameter with unknown legacy route
499 */
500 public function testLegacyControllerUnknownDoPage(): void
501 {
502 $request = $this->createMock(Request::class);
503 $request->method('getQueryParam')->with('do')->willReturn('nope');
504 $response = new Response();
505
506 $result = $this->controller->index($request, $response);
507
508 static::assertSame(200, $result->getStatusCode());
509 static::assertSame('linklist', (string) $result->getBody());
510 }
511
512 /**
513 * Trigger legacy controller in link list controller: other GET route (e.g. ?post)
514 */
515 public function testLegacyControllerGetParameter(): void
516 {
517 $request = $this->createMock(Request::class);
518 $request->method('getQueryParams')->willReturn(['post' => $url = 'http://url.tld']);
519 $response = new Response();
520
521 $this->container->loginManager = $this->createMock(LoginManager::class);
522 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
523
524 $result = $this->controller->index($request, $response);
525
526 static::assertSame(302, $result->getStatusCode());
527 static::assertSame(
528 '/subfolder/admin/shaare?post=' . urlencode($url),
529 $result->getHeader('location')[0]
530 );
531 }
532}
diff --git a/tests/front/controller/visitor/DailyControllerTest.php b/tests/front/controller/visitor/DailyControllerTest.php
new file mode 100644
index 00000000..758e7219
--- /dev/null
+++ b/tests/front/controller/visitor/DailyControllerTest.php
@@ -0,0 +1,716 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Feed\CachedPage;
9use Shaarli\TestCase;
10use Slim\Http\Request;
11use Slim\Http\Response;
12
13class DailyControllerTest extends TestCase
14{
15 use FrontControllerMockHelper;
16
17 /** @var DailyController */
18 protected $controller;
19
20 public function setUp(): void
21 {
22 $this->createContainer();
23
24 $this->controller = new DailyController($this->container);
25 DailyController::$DAILY_RSS_NB_DAYS = 2;
26 }
27
28 public function testValidIndexControllerInvokeDefault(): void
29 {
30 $currentDay = new \DateTimeImmutable('2020-05-13');
31 $previousDate = new \DateTime('2 days ago 00:00:00');
32 $nextDate = new \DateTime('today 00:00:00');
33
34 $request = $this->createMock(Request::class);
35 $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
36 return $key === 'day' ? $currentDay->format('Ymd') : null;
37 });
38 $response = new Response();
39
40 // Save RainTPL assigned variables
41 $assignedVariables = [];
42 $this->assignTemplateVars($assignedVariables);
43
44 $this->container->bookmarkService
45 ->expects(static::once())
46 ->method('findByDate')
47 ->willReturnCallback(
48 function ($from, $to, &$previous, &$next) use ($currentDay, $previousDate, $nextDate): array {
49 $previous = $previousDate;
50 $next = $nextDate;
51
52 return [
53 (new Bookmark())
54 ->setId(1)
55 ->setUrl('http://url.tld')
56 ->setTitle(static::generateString(50))
57 ->setDescription(static::generateString(500))
58 ,
59 (new Bookmark())
60 ->setId(2)
61 ->setUrl('http://url2.tld')
62 ->setTitle(static::generateString(50))
63 ->setDescription(static::generateString(500))
64 ,
65 (new Bookmark())
66 ->setId(3)
67 ->setUrl('http://url3.tld')
68 ->setTitle(static::generateString(50))
69 ->setDescription(static::generateString(500))
70 ,
71 ];
72 }
73 )
74 ;
75
76 // Make sure that PluginManager hook is triggered
77 $this->container->pluginManager
78 ->expects(static::atLeastOnce())
79 ->method('executeHooks')
80 ->withConsecutive(['render_daily'])
81 ->willReturnCallback(
82 function (string $hook, array $data, array $param) use ($currentDay, $previousDate, $nextDate): array {
83 if ('render_daily' === $hook) {
84 static::assertArrayHasKey('linksToDisplay', $data);
85 static::assertCount(3, $data['linksToDisplay']);
86 static::assertSame(1, $data['linksToDisplay'][0]['id']);
87 static::assertSame($currentDay->getTimestamp(), $data['day']);
88 static::assertSame($previousDate->format('Ymd'), $data['previousday']);
89 static::assertSame($nextDate->format('Ymd'), $data['nextday']);
90
91 static::assertArrayHasKey('loggedin', $param);
92 }
93
94 return $data;
95 }
96 )
97 ;
98
99 $result = $this->controller->index($request, $response);
100
101 static::assertSame(200, $result->getStatusCode());
102 static::assertSame('daily', (string) $result->getBody());
103 static::assertSame(
104 'Daily - '. format_date($currentDay, false, true) .' - Shaarli',
105 $assignedVariables['pagetitle']
106 );
107 static::assertEquals($currentDay, $assignedVariables['dayDate']);
108 static::assertEquals($currentDay->getTimestamp(), $assignedVariables['day']);
109 static::assertSame($previousDate->format('Ymd'), $assignedVariables['previousday']);
110 static::assertSame($nextDate->format('Ymd'), $assignedVariables['nextday']);
111 static::assertSame('day', $assignedVariables['type']);
112 static::assertSame('May 13, 2020', $assignedVariables['dayDesc']);
113 static::assertSame('Daily', $assignedVariables['localizedType']);
114 static::assertCount(3, $assignedVariables['linksToDisplay']);
115
116 $link = $assignedVariables['linksToDisplay'][0];
117
118 static::assertSame(1, $link['id']);
119 static::assertSame('http://url.tld', $link['url']);
120 static::assertNotEmpty($link['title']);
121 static::assertNotEmpty($link['description']);
122 static::assertNotEmpty($link['formatedDescription']);
123
124 $link = $assignedVariables['linksToDisplay'][1];
125
126 static::assertSame(2, $link['id']);
127 static::assertSame('http://url2.tld', $link['url']);
128 static::assertNotEmpty($link['title']);
129 static::assertNotEmpty($link['description']);
130 static::assertNotEmpty($link['formatedDescription']);
131
132 $link = $assignedVariables['linksToDisplay'][2];
133
134 static::assertSame(3, $link['id']);
135 static::assertSame('http://url3.tld', $link['url']);
136 static::assertNotEmpty($link['title']);
137 static::assertNotEmpty($link['description']);
138 static::assertNotEmpty($link['formatedDescription']);
139
140 static::assertCount(3, $assignedVariables['cols']);
141 static::assertCount(1, $assignedVariables['cols'][0]);
142 static::assertCount(1, $assignedVariables['cols'][1]);
143 static::assertCount(1, $assignedVariables['cols'][2]);
144
145 $link = $assignedVariables['cols'][0][0];
146
147 static::assertSame(1, $link['id']);
148 static::assertSame('http://url.tld', $link['url']);
149 static::assertNotEmpty($link['title']);
150 static::assertNotEmpty($link['description']);
151 static::assertNotEmpty($link['formatedDescription']);
152
153 $link = $assignedVariables['cols'][1][0];
154
155 static::assertSame(2, $link['id']);
156 static::assertSame('http://url2.tld', $link['url']);
157 static::assertNotEmpty($link['title']);
158 static::assertNotEmpty($link['description']);
159 static::assertNotEmpty($link['formatedDescription']);
160
161 $link = $assignedVariables['cols'][2][0];
162
163 static::assertSame(3, $link['id']);
164 static::assertSame('http://url3.tld', $link['url']);
165 static::assertNotEmpty($link['title']);
166 static::assertNotEmpty($link['description']);
167 static::assertNotEmpty($link['formatedDescription']);
168 }
169
170 /**
171 * Daily page - test that everything goes fine with no future or past bookmarks
172 */
173 public function testValidIndexControllerInvokeNoFutureOrPast(): void
174 {
175 $currentDay = new \DateTimeImmutable('2020-05-13');
176
177 $request = $this->createMock(Request::class);
178 $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
179 return $key === 'day' ? $currentDay->format('Ymd') : null;
180 });
181 $response = new Response();
182
183 // Save RainTPL assigned variables
184 $assignedVariables = [];
185 $this->assignTemplateVars($assignedVariables);
186
187 $this->container->bookmarkService
188 ->expects(static::once())
189 ->method('findByDate')
190 ->willReturnCallback(function () use ($currentDay): array {
191 return [
192 (new Bookmark())
193 ->setId(1)
194 ->setUrl('http://url.tld')
195 ->setTitle(static::generateString(50))
196 ->setDescription(static::generateString(500))
197 ,
198 ];
199 })
200 ;
201
202 // Make sure that PluginManager hook is triggered
203 $this->container->pluginManager
204 ->expects(static::atLeastOnce())
205 ->method('executeHooks')
206 ->withConsecutive(['render_daily'])
207 ->willReturnCallback(function (string $hook, array $data, array $param) use ($currentDay): array {
208 if ('render_daily' === $hook) {
209 static::assertArrayHasKey('linksToDisplay', $data);
210 static::assertCount(1, $data['linksToDisplay']);
211 static::assertSame(1, $data['linksToDisplay'][0]['id']);
212 static::assertSame($currentDay->getTimestamp(), $data['day']);
213 static::assertEmpty($data['previousday']);
214 static::assertEmpty($data['nextday']);
215
216 static::assertArrayHasKey('loggedin', $param);
217 }
218
219 return $data;
220 });
221
222 $result = $this->controller->index($request, $response);
223
224 static::assertSame(200, $result->getStatusCode());
225 static::assertSame('daily', (string) $result->getBody());
226 static::assertSame(
227 'Daily - '. format_date($currentDay, false, true) .' - Shaarli',
228 $assignedVariables['pagetitle']
229 );
230 static::assertCount(1, $assignedVariables['linksToDisplay']);
231
232 $link = $assignedVariables['linksToDisplay'][0];
233 static::assertSame(1, $link['id']);
234 }
235
236 /**
237 * Daily page - test that height adjustment in columns is working
238 */
239 public function testValidIndexControllerInvokeHeightAdjustment(): void
240 {
241 $currentDay = new \DateTimeImmutable('2020-05-13');
242
243 $request = $this->createMock(Request::class);
244 $response = new Response();
245
246 // Save RainTPL assigned variables
247 $assignedVariables = [];
248 $this->assignTemplateVars($assignedVariables);
249
250 $this->container->bookmarkService
251 ->expects(static::once())
252 ->method('findByDate')
253 ->willReturnCallback(function () use ($currentDay): array {
254 return [
255 (new Bookmark())->setId(1)->setUrl('http://url.tld')->setTitle('title'),
256 (new Bookmark())
257 ->setId(2)
258 ->setUrl('http://url.tld')
259 ->setTitle(static::generateString(50))
260 ->setDescription(static::generateString(5000))
261 ,
262 (new Bookmark())->setId(3)->setUrl('http://url.tld')->setTitle('title'),
263 (new Bookmark())->setId(4)->setUrl('http://url.tld')->setTitle('title'),
264 (new Bookmark())->setId(5)->setUrl('http://url.tld')->setTitle('title'),
265 (new Bookmark())->setId(6)->setUrl('http://url.tld')->setTitle('title'),
266 (new Bookmark())->setId(7)->setUrl('http://url.tld')->setTitle('title'),
267 ];
268 })
269 ;
270
271 // Make sure that PluginManager hook is triggered
272 $this->container->pluginManager
273 ->expects(static::atLeastOnce())
274 ->method('executeHooks')
275 ->willReturnCallback(function (string $hook, array $data, array $param): array {
276 return $data;
277 })
278 ;
279
280 $result = $this->controller->index($request, $response);
281
282 static::assertSame(200, $result->getStatusCode());
283 static::assertSame('daily', (string) $result->getBody());
284 static::assertCount(7, $assignedVariables['linksToDisplay']);
285
286 $columnIds = function (array $column): array {
287 return array_map(function (array $item): int { return $item['id']; }, $column);
288 };
289
290 static::assertSame([1, 4, 6], $columnIds($assignedVariables['cols'][0]));
291 static::assertSame([2], $columnIds($assignedVariables['cols'][1]));
292 static::assertSame([3, 5, 7], $columnIds($assignedVariables['cols'][2]));
293 }
294
295 /**
296 * Daily page - no bookmark
297 */
298 public function testValidIndexControllerInvokeNoBookmark(): void
299 {
300 $request = $this->createMock(Request::class);
301 $response = new Response();
302
303 // Save RainTPL assigned variables
304 $assignedVariables = [];
305 $this->assignTemplateVars($assignedVariables);
306
307 // Links dataset: 2 links with thumbnails
308 $this->container->bookmarkService
309 ->expects(static::once())
310 ->method('findByDate')
311 ->willReturnCallback(function (): array {
312 return [];
313 })
314 ;
315
316 // Make sure that PluginManager hook is triggered
317 $this->container->pluginManager
318 ->expects(static::atLeastOnce())
319 ->method('executeHooks')
320 ->willReturnCallback(function (string $hook, array $data, array $param): array {
321 return $data;
322 })
323 ;
324
325 $result = $this->controller->index($request, $response);
326
327 static::assertSame(200, $result->getStatusCode());
328 static::assertSame('daily', (string) $result->getBody());
329 static::assertCount(0, $assignedVariables['linksToDisplay']);
330 static::assertSame('Today - ' . (new \DateTime())->format('F d, Y'), $assignedVariables['dayDesc']);
331 static::assertEquals((new \DateTime())->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
332 static::assertEquals((new \DateTime())->setTime(0, 0), $assignedVariables['dayDate']);
333 }
334
335 /**
336 * Daily RSS - default behaviour
337 */
338 public function testValidRssControllerInvokeDefault(): void
339 {
340 $dates = [
341 new \DateTimeImmutable('2020-05-17'),
342 new \DateTimeImmutable('2020-05-15'),
343 new \DateTimeImmutable('2020-05-13'),
344 new \DateTimeImmutable('+1 month'),
345 ];
346
347 $request = $this->createMock(Request::class);
348 $response = new Response();
349
350 $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([
351 (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'),
352 (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
353 (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
354 (new Bookmark())->setId(4)->setCreated($dates[2])->setUrl('http://domain.tld/4'),
355 (new Bookmark())->setId(5)->setCreated($dates[3])->setUrl('http://domain.tld/5'),
356 ]);
357
358 $this->container->pageCacheManager
359 ->expects(static::once())
360 ->method('getCachePage')
361 ->willReturnCallback(function (): CachedPage {
362 $cachedPage = $this->createMock(CachedPage::class);
363 $cachedPage->expects(static::once())->method('cache')->with('dailyrss');
364
365 return $cachedPage;
366 }
367 );
368
369 // Save RainTPL assigned variables
370 $assignedVariables = [];
371 $this->assignTemplateVars($assignedVariables);
372
373 $result = $this->controller->rss($request, $response);
374
375 static::assertSame(200, $result->getStatusCode());
376 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
377 static::assertSame('dailyrss', (string) $result->getBody());
378 static::assertSame('Shaarli', $assignedVariables['title']);
379 static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
380 static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']);
381 static::assertFalse($assignedVariables['hide_timestamps']);
382 static::assertCount(3, $assignedVariables['days']);
383
384 $day = $assignedVariables['days'][$dates[0]->format('Ymd')];
385 $date = $dates[0]->setTime(23, 59, 59);
386
387 static::assertEquals($date, $day['date']);
388 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
389 static::assertSame(format_date($date, false), $day['date_human']);
390 static::assertSame('http://shaarli/subfolder/daily?day='. $dates[0]->format('Ymd'), $day['absolute_url']);
391 static::assertCount(1, $day['links']);
392 static::assertSame(1, $day['links'][0]['id']);
393 static::assertSame('http://domain.tld/1', $day['links'][0]['url']);
394 static::assertEquals($dates[0], $day['links'][0]['created']);
395
396 $day = $assignedVariables['days'][$dates[1]->format('Ymd')];
397 $date = $dates[1]->setTime(23, 59, 59);
398
399 static::assertEquals($date, $day['date']);
400 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
401 static::assertSame(format_date($date, false), $day['date_human']);
402 static::assertSame('http://shaarli/subfolder/daily?day='. $dates[1]->format('Ymd'), $day['absolute_url']);
403 static::assertCount(2, $day['links']);
404
405 static::assertSame(2, $day['links'][0]['id']);
406 static::assertSame('http://domain.tld/2', $day['links'][0]['url']);
407 static::assertEquals($dates[1], $day['links'][0]['created']);
408 static::assertSame(3, $day['links'][1]['id']);
409 static::assertSame('http://domain.tld/3', $day['links'][1]['url']);
410 static::assertEquals($dates[1], $day['links'][1]['created']);
411
412 $day = $assignedVariables['days'][$dates[2]->format('Ymd')];
413 $date = $dates[2]->setTime(23, 59, 59);
414
415 static::assertEquals($date, $day['date']);
416 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
417 static::assertSame(format_date($date, false), $day['date_human']);
418 static::assertSame('http://shaarli/subfolder/daily?day='. $dates[2]->format('Ymd'), $day['absolute_url']);
419 static::assertCount(1, $day['links']);
420 static::assertSame(4, $day['links'][0]['id']);
421 static::assertSame('http://domain.tld/4', $day['links'][0]['url']);
422 static::assertEquals($dates[2], $day['links'][0]['created']);
423 }
424
425 /**
426 * Daily RSS - trigger cache rendering
427 */
428 public function testValidRssControllerInvokeTriggerCache(): void
429 {
430 $request = $this->createMock(Request::class);
431 $response = new Response();
432
433 $this->container->pageCacheManager->method('getCachePage')->willReturnCallback(function (): CachedPage {
434 $cachedPage = $this->createMock(CachedPage::class);
435 $cachedPage->method('cachedVersion')->willReturn('this is cache!');
436
437 return $cachedPage;
438 });
439
440 $this->container->bookmarkService->expects(static::never())->method('search');
441
442 $result = $this->controller->rss($request, $response);
443
444 static::assertSame(200, $result->getStatusCode());
445 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
446 static::assertSame('this is cache!', (string) $result->getBody());
447 }
448
449 /**
450 * Daily RSS - No bookmark
451 */
452 public function testValidRssControllerInvokeNoBookmark(): void
453 {
454 $request = $this->createMock(Request::class);
455 $response = new Response();
456
457 $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([]);
458
459 // Save RainTPL assigned variables
460 $assignedVariables = [];
461 $this->assignTemplateVars($assignedVariables);
462
463 $result = $this->controller->rss($request, $response);
464
465 static::assertSame(200, $result->getStatusCode());
466 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
467 static::assertSame('dailyrss', (string) $result->getBody());
468 static::assertSame('Shaarli', $assignedVariables['title']);
469 static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
470 static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']);
471 static::assertFalse($assignedVariables['hide_timestamps']);
472 static::assertCount(0, $assignedVariables['days']);
473 }
474
475 /**
476 * Test simple display index with week parameter
477 */
478 public function testSimpleIndexWeekly(): void
479 {
480 $currentDay = new \DateTimeImmutable('2020-05-13');
481 $expectedDay = new \DateTimeImmutable('2020-05-11');
482
483 $request = $this->createMock(Request::class);
484 $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
485 return $key === 'week' ? $currentDay->format('YW') : null;
486 });
487 $response = new Response();
488
489 // Save RainTPL assigned variables
490 $assignedVariables = [];
491 $this->assignTemplateVars($assignedVariables);
492
493 $this->container->bookmarkService
494 ->expects(static::once())
495 ->method('findByDate')
496 ->willReturnCallback(
497 function (): array {
498 return [
499 (new Bookmark())
500 ->setId(1)
501 ->setUrl('http://url.tld')
502 ->setTitle(static::generateString(50))
503 ->setDescription(static::generateString(500))
504 ,
505 (new Bookmark())
506 ->setId(2)
507 ->setUrl('http://url2.tld')
508 ->setTitle(static::generateString(50))
509 ->setDescription(static::generateString(500))
510 ,
511 ];
512 }
513 )
514 ;
515
516 $result = $this->controller->index($request, $response);
517
518 static::assertSame(200, $result->getStatusCode());
519 static::assertSame('daily', (string) $result->getBody());
520 static::assertSame(
521 'Weekly - Week 20 (May 11, 2020) - Shaarli',
522 $assignedVariables['pagetitle']
523 );
524
525 static::assertCount(2, $assignedVariables['linksToDisplay']);
526 static::assertEquals($expectedDay->setTime(0, 0), $assignedVariables['dayDate']);
527 static::assertSame($expectedDay->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
528 static::assertSame('', $assignedVariables['previousday']);
529 static::assertSame('', $assignedVariables['nextday']);
530 static::assertSame('Week 20 (May 11, 2020)', $assignedVariables['dayDesc']);
531 static::assertSame('week', $assignedVariables['type']);
532 static::assertSame('Weekly', $assignedVariables['localizedType']);
533 }
534
535 /**
536 * Test simple display index with month parameter
537 */
538 public function testSimpleIndexMonthly(): void
539 {
540 $currentDay = new \DateTimeImmutable('2020-05-13');
541 $expectedDay = new \DateTimeImmutable('2020-05-01');
542
543 $request = $this->createMock(Request::class);
544 $request->method('getQueryParam')->willReturnCallback(function (string $key) use ($currentDay): ?string {
545 return $key === 'month' ? $currentDay->format('Ym') : null;
546 });
547 $response = new Response();
548
549 // Save RainTPL assigned variables
550 $assignedVariables = [];
551 $this->assignTemplateVars($assignedVariables);
552
553 $this->container->bookmarkService
554 ->expects(static::once())
555 ->method('findByDate')
556 ->willReturnCallback(
557 function (): array {
558 return [
559 (new Bookmark())
560 ->setId(1)
561 ->setUrl('http://url.tld')
562 ->setTitle(static::generateString(50))
563 ->setDescription(static::generateString(500))
564 ,
565 (new Bookmark())
566 ->setId(2)
567 ->setUrl('http://url2.tld')
568 ->setTitle(static::generateString(50))
569 ->setDescription(static::generateString(500))
570 ,
571 ];
572 }
573 )
574 ;
575
576 $result = $this->controller->index($request, $response);
577
578 static::assertSame(200, $result->getStatusCode());
579 static::assertSame('daily', (string) $result->getBody());
580 static::assertSame(
581 'Monthly - May, 2020 - Shaarli',
582 $assignedVariables['pagetitle']
583 );
584
585 static::assertCount(2, $assignedVariables['linksToDisplay']);
586 static::assertEquals($expectedDay->setTime(0, 0), $assignedVariables['dayDate']);
587 static::assertSame($expectedDay->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
588 static::assertSame('', $assignedVariables['previousday']);
589 static::assertSame('', $assignedVariables['nextday']);
590 static::assertSame('May, 2020', $assignedVariables['dayDesc']);
591 static::assertSame('month', $assignedVariables['type']);
592 static::assertSame('Monthly', $assignedVariables['localizedType']);
593 }
594
595 /**
596 * Test simple display RSS with week parameter
597 */
598 public function testSimpleRssWeekly(): void
599 {
600 $dates = [
601 new \DateTimeImmutable('2020-05-19'),
602 new \DateTimeImmutable('2020-05-13'),
603 ];
604 $expectedDates = [
605 new \DateTimeImmutable('2020-05-24 23:59:59'),
606 new \DateTimeImmutable('2020-05-17 23:59:59'),
607 ];
608
609 $this->container->environment['QUERY_STRING'] = 'week';
610 $request = $this->createMock(Request::class);
611 $request->method('getQueryParam')->willReturnCallback(function (string $key): ?string {
612 return $key === 'week' ? '' : null;
613 });
614 $response = new Response();
615
616 $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([
617 (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'),
618 (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
619 (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
620 ]);
621
622 // Save RainTPL assigned variables
623 $assignedVariables = [];
624 $this->assignTemplateVars($assignedVariables);
625
626 $result = $this->controller->rss($request, $response);
627
628 static::assertSame(200, $result->getStatusCode());
629 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
630 static::assertSame('dailyrss', (string) $result->getBody());
631 static::assertSame('Shaarli', $assignedVariables['title']);
632 static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
633 static::assertSame('http://shaarli/subfolder/daily-rss?week', $assignedVariables['page_url']);
634 static::assertFalse($assignedVariables['hide_timestamps']);
635 static::assertCount(2, $assignedVariables['days']);
636
637 $day = $assignedVariables['days'][$dates[0]->format('YW')];
638 $date = $expectedDates[0];
639
640 static::assertEquals($date, $day['date']);
641 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
642 static::assertSame('Week 21 (May 18, 2020)', $day['date_human']);
643 static::assertSame('http://shaarli/subfolder/daily?week='. $dates[0]->format('YW'), $day['absolute_url']);
644 static::assertCount(1, $day['links']);
645
646 $day = $assignedVariables['days'][$dates[1]->format('YW')];
647 $date = $expectedDates[1];
648
649 static::assertEquals($date, $day['date']);
650 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
651 static::assertSame('Week 20 (May 11, 2020)', $day['date_human']);
652 static::assertSame('http://shaarli/subfolder/daily?week='. $dates[1]->format('YW'), $day['absolute_url']);
653 static::assertCount(2, $day['links']);
654 }
655
656 /**
657 * Test simple display RSS with month parameter
658 */
659 public function testSimpleRssMonthly(): void
660 {
661 $dates = [
662 new \DateTimeImmutable('2020-05-19'),
663 new \DateTimeImmutable('2020-04-13'),
664 ];
665 $expectedDates = [
666 new \DateTimeImmutable('2020-05-31 23:59:59'),
667 new \DateTimeImmutable('2020-04-30 23:59:59'),
668 ];
669
670 $this->container->environment['QUERY_STRING'] = 'month';
671 $request = $this->createMock(Request::class);
672 $request->method('getQueryParam')->willReturnCallback(function (string $key): ?string {
673 return $key === 'month' ? '' : null;
674 });
675 $response = new Response();
676
677 $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([
678 (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'),
679 (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
680 (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
681 ]);
682
683 // Save RainTPL assigned variables
684 $assignedVariables = [];
685 $this->assignTemplateVars($assignedVariables);
686
687 $result = $this->controller->rss($request, $response);
688
689 static::assertSame(200, $result->getStatusCode());
690 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
691 static::assertSame('dailyrss', (string) $result->getBody());
692 static::assertSame('Shaarli', $assignedVariables['title']);
693 static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
694 static::assertSame('http://shaarli/subfolder/daily-rss?month', $assignedVariables['page_url']);
695 static::assertFalse($assignedVariables['hide_timestamps']);
696 static::assertCount(2, $assignedVariables['days']);
697
698 $day = $assignedVariables['days'][$dates[0]->format('Ym')];
699 $date = $expectedDates[0];
700
701 static::assertEquals($date, $day['date']);
702 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
703 static::assertSame('May, 2020', $day['date_human']);
704 static::assertSame('http://shaarli/subfolder/daily?month='. $dates[0]->format('Ym'), $day['absolute_url']);
705 static::assertCount(1, $day['links']);
706
707 $day = $assignedVariables['days'][$dates[1]->format('Ym')];
708 $date = $expectedDates[1];
709
710 static::assertEquals($date, $day['date']);
711 static::assertSame($date->format(\DateTime::RSS), $day['date_rss']);
712 static::assertSame('April, 2020', $day['date_human']);
713 static::assertSame('http://shaarli/subfolder/daily?month='. $dates[1]->format('Ym'), $day['absolute_url']);
714 static::assertCount(2, $day['links']);
715 }
716}
diff --git a/tests/front/controller/visitor/ErrorControllerTest.php b/tests/front/controller/visitor/ErrorControllerTest.php
new file mode 100644
index 00000000..75408cf4
--- /dev/null
+++ b/tests/front/controller/visitor/ErrorControllerTest.php
@@ -0,0 +1,70 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Front\Exception\ShaarliFrontException;
8use Shaarli\TestCase;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12class ErrorControllerTest extends TestCase
13{
14 use FrontControllerMockHelper;
15
16 /** @var ErrorController */
17 protected $controller;
18
19 public function setUp(): void
20 {
21 $this->createContainer();
22
23 $this->controller = new ErrorController($this->container);
24 }
25
26 /**
27 * Test displaying error with a ShaarliFrontException: display exception message and use its code for HTTTP code
28 */
29 public function testDisplayFrontExceptionError(): void
30 {
31 $request = $this->createMock(Request::class);
32 $response = new Response();
33
34 $message = 'error message';
35 $errorCode = 418;
36
37 // Save RainTPL assigned variables
38 $assignedVariables = [];
39 $this->assignTemplateVars($assignedVariables);
40
41 $result = ($this->controller)(
42 $request,
43 $response,
44 new class($message, $errorCode) extends ShaarliFrontException {}
45 );
46
47 static::assertSame($errorCode, $result->getStatusCode());
48 static::assertSame($message, $assignedVariables['message']);
49 static::assertArrayNotHasKey('stacktrace', $assignedVariables);
50 }
51
52 /**
53 * Test displaying error with any exception (no debug): only display an error occurred with HTTP 500.
54 */
55 public function testDisplayAnyExceptionErrorNoDebug(): void
56 {
57 $request = $this->createMock(Request::class);
58 $response = new Response();
59
60 // Save RainTPL assigned variables
61 $assignedVariables = [];
62 $this->assignTemplateVars($assignedVariables);
63
64 $result = ($this->controller)($request, $response, new \Exception('abc'));
65
66 static::assertSame(500, $result->getStatusCode());
67 static::assertSame('An unexpected error occurred.', $assignedVariables['message']);
68 static::assertArrayNotHasKey('stacktrace', $assignedVariables);
69 }
70}
diff --git a/tests/front/controller/visitor/ErrorNotFoundControllerTest.php b/tests/front/controller/visitor/ErrorNotFoundControllerTest.php
new file mode 100644
index 00000000..a1cbbecf
--- /dev/null
+++ b/tests/front/controller/visitor/ErrorNotFoundControllerTest.php
@@ -0,0 +1,81 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\TestCase;
8use Slim\Http\Request;
9use Slim\Http\Response;
10use Slim\Http\Uri;
11
12class ErrorNotFoundControllerTest extends TestCase
13{
14 use FrontControllerMockHelper;
15
16 /** @var ErrorNotFoundController */
17 protected $controller;
18
19 public function setUp(): void
20 {
21 $this->createContainer();
22
23 $this->controller = new ErrorNotFoundController($this->container);
24 }
25
26 /**
27 * Test displaying 404 error
28 */
29 public function testDisplayNotFoundError(): void
30 {
31 $request = $this->createMock(Request::class);
32 $request->expects(static::once())->method('getRequestTarget')->willReturn('/');
33 $request->method('getUri')->willReturnCallback(function (): Uri {
34 $uri = $this->createMock(Uri::class);
35 $uri->method('getBasePath')->willReturn('/subfolder');
36
37 return $uri;
38 });
39
40 $response = new Response();
41
42 // Save RainTPL assigned variables
43 $assignedVariables = [];
44 $this->assignTemplateVars($assignedVariables);
45
46 $result = ($this->controller)(
47 $request,
48 $response
49 );
50
51 static::assertSame(404, $result->getStatusCode());
52 static::assertSame('404', (string) $result->getBody());
53 static::assertSame('Requested page could not be found.', $assignedVariables['error_message']);
54 }
55
56 /**
57 * Test displaying 404 error from REST API
58 */
59 public function testDisplayNotFoundErrorFromAPI(): void
60 {
61 $request = $this->createMock(Request::class);
62 $request->expects(static::once())->method('getRequestTarget')->willReturn('/sufolder/api/v1/links');
63 $request->method('getUri')->willReturnCallback(function (): Uri {
64 $uri = $this->createMock(Uri::class);
65 $uri->method('getBasePath')->willReturn('/subfolder');
66
67 return $uri;
68 });
69
70 $response = new Response();
71
72 // Save RainTPL assigned variables
73 $assignedVariables = [];
74 $this->assignTemplateVars($assignedVariables);
75
76 $result = ($this->controller)($request, $response);
77
78 static::assertSame(404, $result->getStatusCode());
79 static::assertSame([], $assignedVariables);
80 }
81}
diff --git a/tests/front/controller/visitor/FeedControllerTest.php b/tests/front/controller/visitor/FeedControllerTest.php
new file mode 100644
index 00000000..4ae7c925
--- /dev/null
+++ b/tests/front/controller/visitor/FeedControllerTest.php
@@ -0,0 +1,151 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Feed\FeedBuilder;
8use Shaarli\TestCase;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12class FeedControllerTest extends TestCase
13{
14 use FrontControllerMockHelper;
15
16 /** @var FeedController */
17 protected $controller;
18
19 public function setUp(): void
20 {
21 $this->createContainer();
22
23 $this->container->feedBuilder = $this->createMock(FeedBuilder::class);
24
25 $this->controller = new FeedController($this->container);
26 }
27
28 /**
29 * Feed Controller - RSS default behaviour
30 */
31 public function testDefaultRssController(): void
32 {
33 $request = $this->createMock(Request::class);
34 $response = new Response();
35
36 $this->container->feedBuilder->expects(static::once())->method('setLocale');
37 $this->container->feedBuilder->expects(static::once())->method('setHideDates')->with(false);
38 $this->container->feedBuilder->expects(static::once())->method('setUsePermalinks')->with(true);
39
40 // Save RainTPL assigned variables
41 $assignedVariables = [];
42 $this->assignTemplateVars($assignedVariables);
43
44 $this->container->feedBuilder->method('buildData')->willReturn(['content' => 'data']);
45
46 // Make sure that PluginManager hook is triggered
47 $this->container->pluginManager
48 ->expects(static::atLeastOnce())
49 ->method('executeHooks')
50 ->withConsecutive(['render_feed'])
51 ->willReturnCallback(function (string $hook, array $data, array $param): void {
52 if ('render_feed' === $hook) {
53 static::assertSame('data', $data['content']);
54
55 static::assertArrayHasKey('loggedin', $param);
56 static::assertSame('feed.rss', $param['target']);
57 }
58 })
59 ;
60
61 $result = $this->controller->rss($request, $response);
62
63 static::assertSame(200, $result->getStatusCode());
64 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
65 static::assertSame('feed.rss', (string) $result->getBody());
66 static::assertSame('data', $assignedVariables['content']);
67 }
68
69 /**
70 * Feed Controller - ATOM default behaviour
71 */
72 public function testDefaultAtomController(): void
73 {
74 $request = $this->createMock(Request::class);
75 $response = new Response();
76
77 $this->container->feedBuilder->expects(static::once())->method('setLocale');
78 $this->container->feedBuilder->expects(static::once())->method('setHideDates')->with(false);
79 $this->container->feedBuilder->expects(static::once())->method('setUsePermalinks')->with(true);
80
81 // Save RainTPL assigned variables
82 $assignedVariables = [];
83 $this->assignTemplateVars($assignedVariables);
84
85 $this->container->feedBuilder->method('buildData')->willReturn(['content' => 'data']);
86
87 // Make sure that PluginManager hook is triggered
88 $this->container->pluginManager
89 ->expects(static::atLeastOnce())
90 ->method('executeHooks')
91 ->withConsecutive(['render_feed'])
92 ->willReturnCallback(function (string $hook, array $data, array $param): void {
93 if ('render_feed' === $hook) {
94 static::assertSame('data', $data['content']);
95
96 static::assertArrayHasKey('loggedin', $param);
97 static::assertSame('feed.atom', $param['target']);
98 }
99 })
100 ;
101
102 $result = $this->controller->atom($request, $response);
103
104 static::assertSame(200, $result->getStatusCode());
105 static::assertStringContainsString('application/atom', $result->getHeader('Content-Type')[0]);
106 static::assertSame('feed.atom', (string) $result->getBody());
107 static::assertSame('data', $assignedVariables['content']);
108 }
109
110 /**
111 * Feed Controller - ATOM with parameters
112 */
113 public function testAtomControllerWithParameters(): void
114 {
115 $request = $this->createMock(Request::class);
116 $request->method('getParams')->willReturn(['parameter' => 'value']);
117 $response = new Response();
118
119 // Save RainTPL assigned variables
120 $assignedVariables = [];
121 $this->assignTemplateVars($assignedVariables);
122
123 $this->container->feedBuilder
124 ->method('buildData')
125 ->with('atom', ['parameter' => 'value'])
126 ->willReturn(['content' => 'data'])
127 ;
128
129 // Make sure that PluginManager hook is triggered
130 $this->container->pluginManager
131 ->expects(static::atLeastOnce())
132 ->method('executeHooks')
133 ->withConsecutive(['render_feed'])
134 ->willReturnCallback(function (string $hook, array $data, array $param): void {
135 if ('render_feed' === $hook) {
136 static::assertSame('data', $data['content']);
137
138 static::assertArrayHasKey('loggedin', $param);
139 static::assertSame('feed.atom', $param['target']);
140 }
141 })
142 ;
143
144 $result = $this->controller->atom($request, $response);
145
146 static::assertSame(200, $result->getStatusCode());
147 static::assertStringContainsString('application/atom', $result->getHeader('Content-Type')[0]);
148 static::assertSame('feed.atom', (string) $result->getBody());
149 static::assertSame('data', $assignedVariables['content']);
150 }
151}
diff --git a/tests/front/controller/visitor/FrontControllerMockHelper.php b/tests/front/controller/visitor/FrontControllerMockHelper.php
new file mode 100644
index 00000000..fc0bb7d1
--- /dev/null
+++ b/tests/front/controller/visitor/FrontControllerMockHelper.php
@@ -0,0 +1,118 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Bookmark\BookmarkServiceInterface;
8use Shaarli\Config\ConfigManager;
9use Shaarli\Container\ShaarliTestContainer;
10use Shaarli\Formatter\BookmarkFormatter;
11use Shaarli\Formatter\BookmarkRawFormatter;
12use Shaarli\Formatter\FormatterFactory;
13use Shaarli\Plugin\PluginManager;
14use Shaarli\Render\PageBuilder;
15use Shaarli\Render\PageCacheManager;
16use Shaarli\Security\LoginManager;
17use Shaarli\Security\SessionManager;
18
19/**
20 * Trait FrontControllerMockHelper
21 *
22 * Helper trait used to initialize the ShaarliContainer and mock its services for controller tests.
23 *
24 * @property ShaarliTestContainer $container
25 * @package Shaarli\Front\Controller
26 */
27trait FrontControllerMockHelper
28{
29 /** @var ShaarliTestContainer */
30 protected $container;
31
32 /**
33 * Mock the container instance and initialize container's services used by tests
34 */
35 protected function createContainer(): void
36 {
37 $this->container = $this->createMock(ShaarliTestContainer::class);
38
39 $this->container->loginManager = $this->createMock(LoginManager::class);
40
41 // Config
42 $this->container->conf = $this->createMock(ConfigManager::class);
43 $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
44 return $default === null ? $parameter : $default;
45 });
46
47 // PageBuilder
48 $this->container->pageBuilder = $this->createMock(PageBuilder::class);
49 $this->container->pageBuilder
50 ->method('render')
51 ->willReturnCallback(function (string $template): string {
52 return $template;
53 })
54 ;
55
56 // Plugin Manager
57 $this->container->pluginManager = $this->createMock(PluginManager::class);
58
59 // BookmarkService
60 $this->container->bookmarkService = $this->createMock(BookmarkServiceInterface::class);
61
62 // Formatter
63 $this->container->formatterFactory = $this->createMock(FormatterFactory::class);
64 $this->container->formatterFactory
65 ->method('getFormatter')
66 ->willReturnCallback(function (): BookmarkFormatter {
67 return new BookmarkRawFormatter($this->container->conf, true);
68 })
69 ;
70
71 // CacheManager
72 $this->container->pageCacheManager = $this->createMock(PageCacheManager::class);
73
74 // SessionManager
75 $this->container->sessionManager = $this->createMock(SessionManager::class);
76
77 // $_SERVER
78 $this->container->environment = [
79 'SERVER_NAME' => 'shaarli',
80 'SERVER_PORT' => '80',
81 'REQUEST_URI' => '/subfolder/daily-rss',
82 'REMOTE_ADDR' => '1.2.3.4',
83 'SCRIPT_NAME' => '/subfolder/index.php',
84 ];
85
86 $this->container->basePath = '/subfolder';
87 }
88
89 /**
90 * Pass a reference of an array which will be populated by `pageBuilder->assign` calls during execution.
91 *
92 * @param mixed $variables Array reference to populate.
93 */
94 protected function assignTemplateVars(array &$variables): void
95 {
96 $this->container->pageBuilder
97 ->method('assign')
98 ->willReturnCallback(function ($key, $value) use (&$variables) {
99 $variables[$key] = $value;
100
101 return $this;
102 })
103 ;
104 }
105
106 protected static function generateString(int $length): string
107 {
108 // bin2hex(random_bytes) generates string twice as long as given parameter
109 $length = (int) ceil($length / 2);
110
111 return bin2hex(random_bytes($length));
112 }
113
114 /**
115 * Force to be used in PHPUnit context.
116 */
117 protected abstract function isInTestsContext(): bool;
118}
diff --git a/tests/front/controller/visitor/InstallControllerTest.php b/tests/front/controller/visitor/InstallControllerTest.php
new file mode 100644
index 00000000..2105ed77
--- /dev/null
+++ b/tests/front/controller/visitor/InstallControllerTest.php
@@ -0,0 +1,304 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Config\ConfigManager;
8use Shaarli\Front\Exception\AlreadyInstalledException;
9use Shaarli\Security\SessionManager;
10use Shaarli\TestCase;
11use Slim\Http\Request;
12use Slim\Http\Response;
13
14class InstallControllerTest extends TestCase
15{
16 use FrontControllerMockHelper;
17
18 const MOCK_FILE = '.tmp';
19
20 /** @var InstallController */
21 protected $controller;
22
23 public function setUp(): void
24 {
25 $this->createContainer();
26
27 $this->container->conf = $this->createMock(ConfigManager::class);
28 $this->container->conf->method('getConfigFileExt')->willReturn(static::MOCK_FILE);
29 $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
30 if ($key === 'resource.raintpl_tpl') {
31 return '.';
32 }
33
34 return $default ?? $key;
35 });
36
37 $this->controller = new InstallController($this->container);
38 }
39
40 protected function tearDown(): void
41 {
42 if (file_exists(static::MOCK_FILE)) {
43 unlink(static::MOCK_FILE);
44 }
45 }
46
47 /**
48 * Test displaying install page with valid session.
49 */
50 public function testInstallIndexWithValidSession(): void
51 {
52 $assignedVariables = [];
53 $this->assignTemplateVars($assignedVariables);
54
55 $request = $this->createMock(Request::class);
56 $response = new Response();
57
58 $this->container->sessionManager = $this->createMock(SessionManager::class);
59 $this->container->sessionManager
60 ->method('getSessionParameter')
61 ->willReturnCallback(function (string $key, $default) {
62 return $key === 'session_tested' ? 'Working' : $default;
63 })
64 ;
65
66 $result = $this->controller->index($request, $response);
67
68 static::assertSame(200, $result->getStatusCode());
69 static::assertSame('install', (string) $result->getBody());
70
71 static::assertIsArray($assignedVariables['continents']);
72 static::assertSame('Africa', $assignedVariables['continents'][0]);
73 static::assertSame('UTC', $assignedVariables['continents']['selected']);
74
75 static::assertIsArray($assignedVariables['cities']);
76 static::assertSame(['continent' => 'Africa', 'city' => 'Abidjan'], $assignedVariables['cities'][0]);
77 static::assertSame('UTC', $assignedVariables['continents']['selected']);
78
79 static::assertIsArray($assignedVariables['languages']);
80 static::assertSame('Automatic', $assignedVariables['languages']['auto']);
81 static::assertSame('French', $assignedVariables['languages']['fr']);
82
83 static::assertSame(PHP_VERSION, $assignedVariables['php_version']);
84 static::assertArrayHasKey('php_has_reached_eol', $assignedVariables);
85 static::assertArrayHasKey('php_eol', $assignedVariables);
86 static::assertArrayHasKey('php_extensions', $assignedVariables);
87 static::assertArrayHasKey('permissions', $assignedVariables);
88 static::assertEmpty($assignedVariables['permissions']);
89
90 static::assertSame('Install Shaarli', $assignedVariables['pagetitle']);
91 }
92
93 /**
94 * Instantiate the install controller with an existing config file: exception.
95 */
96 public function testInstallWithExistingConfigFile(): void
97 {
98 $this->expectException(AlreadyInstalledException::class);
99
100 touch(static::MOCK_FILE);
101
102 $this->controller = new InstallController($this->container);
103 }
104
105 /**
106 * Call controller without session yet defined, redirect to test session install page.
107 */
108 public function testInstallRedirectToSessionTest(): void
109 {
110 $request = $this->createMock(Request::class);
111 $response = new Response();
112
113 $this->container->sessionManager = $this->createMock(SessionManager::class);
114 $this->container->sessionManager
115 ->expects(static::once())
116 ->method('setSessionParameter')
117 ->with(InstallController::SESSION_TEST_KEY, InstallController::SESSION_TEST_VALUE)
118 ;
119
120 $result = $this->controller->index($request, $response);
121
122 static::assertSame(302, $result->getStatusCode());
123 static::assertSame('/subfolder/install/session-test', $result->getHeader('location')[0]);
124 }
125
126 /**
127 * Call controller in session test mode: valid session then redirect to install page.
128 */
129 public function testInstallSessionTestValid(): void
130 {
131 $request = $this->createMock(Request::class);
132 $response = new Response();
133
134 $this->container->sessionManager = $this->createMock(SessionManager::class);
135 $this->container->sessionManager
136 ->method('getSessionParameter')
137 ->with(InstallController::SESSION_TEST_KEY)
138 ->willReturn(InstallController::SESSION_TEST_VALUE)
139 ;
140
141 $result = $this->controller->sessionTest($request, $response);
142
143 static::assertSame(302, $result->getStatusCode());
144 static::assertSame('/subfolder/install', $result->getHeader('location')[0]);
145 }
146
147 /**
148 * Call controller in session test mode: invalid session then redirect to error page.
149 */
150 public function testInstallSessionTestError(): void
151 {
152 $assignedVars = [];
153 $this->assignTemplateVars($assignedVars);
154
155 $request = $this->createMock(Request::class);
156 $response = new Response();
157
158 $this->container->sessionManager = $this->createMock(SessionManager::class);
159 $this->container->sessionManager
160 ->method('getSessionParameter')
161 ->with(InstallController::SESSION_TEST_KEY)
162 ->willReturn('KO')
163 ;
164
165 $result = $this->controller->sessionTest($request, $response);
166
167 static::assertSame(200, $result->getStatusCode());
168 static::assertSame('error', (string) $result->getBody());
169 static::assertStringStartsWith(
170 '<pre>Sessions do not seem to work correctly on your server',
171 $assignedVars['message']
172 );
173 }
174
175 /**
176 * Test saving valid data from install form. Also initialize datastore.
177 */
178 public function testSaveInstallValid(): void
179 {
180 $providedParameters = [
181 'continent' => 'Europe',
182 'city' => 'Berlin',
183 'setlogin' => 'bob',
184 'setpassword' => 'password',
185 'title' => 'Shaarli',
186 'language' => 'fr',
187 'updateCheck' => true,
188 'enableApi' => true,
189 ];
190
191 $expectedSettings = [
192 'general.timezone' => 'Europe/Berlin',
193 'credentials.login' => 'bob',
194 'credentials.salt' => '_NOT_EMPTY',
195 'credentials.hash' => '_NOT_EMPTY',
196 'general.title' => 'Shaarli',
197 'translation.language' => 'en',
198 'updates.check_updates' => true,
199 'api.enabled' => true,
200 'api.secret' => '_NOT_EMPTY',
201 'general.header_link' => '/subfolder',
202 ];
203
204 $request = $this->createMock(Request::class);
205 $request->method('getParam')->willReturnCallback(function (string $key) use ($providedParameters) {
206 return $providedParameters[$key] ?? null;
207 });
208 $response = new Response();
209
210 $this->container->conf = $this->createMock(ConfigManager::class);
211 $this->container->conf
212 ->method('get')
213 ->willReturnCallback(function (string $key, $value) {
214 if ($key === 'credentials.login') {
215 return 'bob';
216 } elseif ($key === 'credentials.salt') {
217 return 'salt';
218 }
219
220 return $value;
221 })
222 ;
223 $this->container->conf
224 ->expects(static::exactly(count($expectedSettings)))
225 ->method('set')
226 ->willReturnCallback(function (string $key, $value) use ($expectedSettings) {
227 if ($expectedSettings[$key] ?? null === '_NOT_EMPTY') {
228 static::assertNotEmpty($value);
229 } else {
230 static::assertSame($expectedSettings[$key], $value);
231 }
232 })
233 ;
234 $this->container->conf->expects(static::once())->method('write');
235
236 $this->container->sessionManager
237 ->expects(static::once())
238 ->method('setSessionParameter')
239 ->with(SessionManager::KEY_SUCCESS_MESSAGES)
240 ;
241
242 $result = $this->controller->save($request, $response);
243
244 static::assertSame(302, $result->getStatusCode());
245 static::assertSame('/subfolder/login', $result->getHeader('location')[0]);
246 }
247
248 /**
249 * Test default settings (timezone and title).
250 * Also check that bookmarks are not initialized if
251 */
252 public function testSaveInstallDefaultValues(): void
253 {
254 $confSettings = [];
255
256 $request = $this->createMock(Request::class);
257 $response = new Response();
258
259 $this->container->conf->method('set')->willReturnCallback(function (string $key, $value) use (&$confSettings) {
260 $confSettings[$key] = $value;
261 });
262
263 $result = $this->controller->save($request, $response);
264
265 static::assertSame(302, $result->getStatusCode());
266 static::assertSame('/subfolder/login', $result->getHeader('location')[0]);
267
268 static::assertSame('UTC', $confSettings['general.timezone']);
269 static::assertSame('Shared bookmarks on http://shaarli/subfolder/', $confSettings['general.title']);
270 }
271
272 /**
273 * Same test as testSaveInstallDefaultValues() but for an instance install in root directory.
274 */
275 public function testSaveInstallDefaultValuesWithoutSubfolder(): void
276 {
277 $confSettings = [];
278
279 $this->container->environment = [
280 'SERVER_NAME' => 'shaarli',
281 'SERVER_PORT' => '80',
282 'REQUEST_URI' => '/install',
283 'REMOTE_ADDR' => '1.2.3.4',
284 'SCRIPT_NAME' => '/index.php',
285 ];
286
287 $this->container->basePath = '';
288
289 $request = $this->createMock(Request::class);
290 $response = new Response();
291
292 $this->container->conf->method('set')->willReturnCallback(function (string $key, $value) use (&$confSettings) {
293 $confSettings[$key] = $value;
294 });
295
296 $result = $this->controller->save($request, $response);
297
298 static::assertSame(302, $result->getStatusCode());
299 static::assertSame('/login', $result->getHeader('location')[0]);
300
301 static::assertSame('UTC', $confSettings['general.timezone']);
302 static::assertSame('Shared bookmarks on http://shaarli/', $confSettings['general.title']);
303 }
304}
diff --git a/tests/front/controller/visitor/LoginControllerTest.php b/tests/front/controller/visitor/LoginControllerTest.php
new file mode 100644
index 00000000..00d9eab3
--- /dev/null
+++ b/tests/front/controller/visitor/LoginControllerTest.php
@@ -0,0 +1,404 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Config\ConfigManager;
8use Shaarli\Front\Exception\LoginBannedException;
9use Shaarli\Front\Exception\WrongTokenException;
10use Shaarli\Render\TemplatePage;
11use Shaarli\Security\CookieManager;
12use Shaarli\Security\SessionManager;
13use Shaarli\TestCase;
14use Slim\Http\Request;
15use Slim\Http\Response;
16
17class LoginControllerTest extends TestCase
18{
19 use FrontControllerMockHelper;
20
21 /** @var LoginController */
22 protected $controller;
23
24 public function setUp(): void
25 {
26 $this->createContainer();
27
28 $this->container->cookieManager = $this->createMock(CookieManager::class);
29 $this->container->sessionManager->method('checkToken')->willReturn(true);
30
31 $this->controller = new LoginController($this->container);
32 }
33
34 /**
35 * Test displaying login form with valid parameters.
36 */
37 public function testValidControllerInvoke(): void
38 {
39 $request = $this->createMock(Request::class);
40 $request
41 ->expects(static::atLeastOnce())
42 ->method('getParam')
43 ->willReturnCallback(function (string $key) {
44 return 'returnurl' === $key ? '> referer' : null;
45 })
46 ;
47 $response = new Response();
48
49 $assignedVariables = [];
50 $this->container->pageBuilder
51 ->method('assign')
52 ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
53 $assignedVariables[$key] = $value;
54
55 return $this;
56 })
57 ;
58
59 $this->container->loginManager->method('canLogin')->willReturn(true);
60
61 $result = $this->controller->index($request, $response);
62
63 static::assertInstanceOf(Response::class, $result);
64 static::assertSame(200, $result->getStatusCode());
65 static::assertSame(TemplatePage::LOGIN, (string) $result->getBody());
66
67 static::assertSame('&gt; referer', $assignedVariables['returnurl']);
68 static::assertSame(true, $assignedVariables['remember_user_default']);
69 static::assertSame('Login - Shaarli', $assignedVariables['pagetitle']);
70 }
71
72 /**
73 * Test displaying login form with username defined in the request.
74 */
75 public function testValidControllerInvokeWithUserName(): void
76 {
77 $this->container->environment = ['HTTP_REFERER' => '> referer'];
78
79 $request = $this->createMock(Request::class);
80 $request
81 ->expects(static::atLeastOnce())
82 ->method('getParam')
83 ->willReturnCallback(function (string $key, $default) {
84 if ('login' === $key) {
85 return 'myUser>';
86 }
87
88 return $default;
89 })
90 ;
91 $response = new Response();
92
93 $assignedVariables = [];
94 $this->container->pageBuilder
95 ->method('assign')
96 ->willReturnCallback(function ($key, $value) use (&$assignedVariables) {
97 $assignedVariables[$key] = $value;
98
99 return $this;
100 })
101 ;
102
103 $this->container->loginManager->expects(static::once())->method('canLogin')->willReturn(true);
104
105 $result = $this->controller->index($request, $response);
106
107 static::assertInstanceOf(Response::class, $result);
108 static::assertSame(200, $result->getStatusCode());
109 static::assertSame('loginform', (string) $result->getBody());
110
111 static::assertSame('myUser&gt;', $assignedVariables['username']);
112 static::assertSame('&gt; referer', $assignedVariables['returnurl']);
113 static::assertSame(true, $assignedVariables['remember_user_default']);
114 static::assertSame('Login - Shaarli', $assignedVariables['pagetitle']);
115 }
116
117 /**
118 * Test displaying login page while being logged in.
119 */
120 public function testLoginControllerWhileLoggedIn(): void
121 {
122 $request = $this->createMock(Request::class);
123 $response = new Response();
124
125 $this->container->loginManager->expects(static::once())->method('isLoggedIn')->willReturn(true);
126
127 $result = $this->controller->index($request, $response);
128
129 static::assertInstanceOf(Response::class, $result);
130 static::assertSame(302, $result->getStatusCode());
131 static::assertSame(['/subfolder/'], $result->getHeader('Location'));
132 }
133
134 /**
135 * Test displaying login page with open shaarli configured: redirect to homepage.
136 */
137 public function testLoginControllerOpenShaarli(): void
138 {
139 $request = $this->createMock(Request::class);
140 $response = new Response();
141
142 $conf = $this->createMock(ConfigManager::class);
143 $conf->method('get')->willReturnCallback(function (string $parameter, $default) {
144 if ($parameter === 'security.open_shaarli') {
145 return true;
146 }
147 return $default;
148 });
149 $this->container->conf = $conf;
150
151 $result = $this->controller->index($request, $response);
152
153 static::assertInstanceOf(Response::class, $result);
154 static::assertSame(302, $result->getStatusCode());
155 static::assertSame(['/subfolder/'], $result->getHeader('Location'));
156 }
157
158 /**
159 * Test displaying login page while being banned.
160 */
161 public function testLoginControllerWhileBanned(): void
162 {
163 $request = $this->createMock(Request::class);
164 $response = new Response();
165
166 $this->container->loginManager->method('isLoggedIn')->willReturn(false);
167 $this->container->loginManager->method('canLogin')->willReturn(false);
168
169 $this->expectException(LoginBannedException::class);
170
171 $this->controller->index($request, $response);
172 }
173
174 /**
175 * Test processing login with valid parameters.
176 */
177 public function testProcessLoginWithValidParameters(): void
178 {
179 $parameters = [
180 'login' => 'bob',
181 'password' => 'pass',
182 ];
183 $request = $this->createMock(Request::class);
184 $request
185 ->expects(static::atLeastOnce())
186 ->method('getParam')
187 ->willReturnCallback(function (string $key) use ($parameters) {
188 return $parameters[$key] ?? null;
189 })
190 ;
191 $response = new Response();
192
193 $this->container->loginManager->method('canLogin')->willReturn(true);
194 $this->container->loginManager->expects(static::once())->method('handleSuccessfulLogin');
195 $this->container->loginManager
196 ->expects(static::once())
197 ->method('checkCredentials')
198 ->with('1.2.3.4', 'bob', 'pass')
199 ->willReturn(true)
200 ;
201 $this->container->loginManager->method('getStaySignedInToken')->willReturn(bin2hex(random_bytes(8)));
202
203 $this->container->sessionManager->expects(static::never())->method('extendSession');
204 $this->container->sessionManager->expects(static::once())->method('destroy');
205 $this->container->sessionManager
206 ->expects(static::once())
207 ->method('cookieParameters')
208 ->with(0, '/subfolder/', 'shaarli')
209 ;
210 $this->container->sessionManager->expects(static::once())->method('start');
211 $this->container->sessionManager->expects(static::once())->method('regenerateId')->with(true);
212
213 $result = $this->controller->login($request, $response);
214
215 static::assertSame(302, $result->getStatusCode());
216 static::assertSame('/subfolder/', $result->getHeader('location')[0]);
217 }
218
219 /**
220 * Test processing login with return URL.
221 */
222 public function testProcessLoginWithReturnUrl(): void
223 {
224 $parameters = [
225 'returnurl' => 'http://shaarli/subfolder/admin/shaare',
226 ];
227 $request = $this->createMock(Request::class);
228 $request
229 ->expects(static::atLeastOnce())
230 ->method('getParam')
231 ->willReturnCallback(function (string $key) use ($parameters) {
232 return $parameters[$key] ?? null;
233 })
234 ;
235 $response = new Response();
236
237 $this->container->loginManager->method('canLogin')->willReturn(true);
238 $this->container->loginManager->expects(static::once())->method('handleSuccessfulLogin');
239 $this->container->loginManager->expects(static::once())->method('checkCredentials')->willReturn(true);
240 $this->container->loginManager->method('getStaySignedInToken')->willReturn(bin2hex(random_bytes(8)));
241
242 $result = $this->controller->login($request, $response);
243
244 static::assertSame(302, $result->getStatusCode());
245 static::assertSame('/subfolder/admin/shaare', $result->getHeader('location')[0]);
246 }
247
248 /**
249 * Test processing login with remember me session enabled.
250 */
251 public function testProcessLoginLongLastingSession(): void
252 {
253 $parameters = [
254 'longlastingsession' => true,
255 ];
256 $request = $this->createMock(Request::class);
257 $request
258 ->expects(static::atLeastOnce())
259 ->method('getParam')
260 ->willReturnCallback(function (string $key) use ($parameters) {
261 return $parameters[$key] ?? null;
262 })
263 ;
264 $response = new Response();
265
266 $this->container->loginManager->method('canLogin')->willReturn(true);
267 $this->container->loginManager->expects(static::once())->method('handleSuccessfulLogin');
268 $this->container->loginManager->expects(static::once())->method('checkCredentials')->willReturn(true);
269 $this->container->loginManager->method('getStaySignedInToken')->willReturn(bin2hex(random_bytes(8)));
270
271 $this->container->sessionManager->expects(static::once())->method('destroy');
272 $this->container->sessionManager
273 ->expects(static::once())
274 ->method('cookieParameters')
275 ->with(42, '/subfolder/', 'shaarli')
276 ;
277 $this->container->sessionManager->expects(static::once())->method('start');
278 $this->container->sessionManager->expects(static::once())->method('regenerateId')->with(true);
279 $this->container->sessionManager->expects(static::once())->method('extendSession')->willReturn(42);
280
281 $this->container->cookieManager = $this->createMock(CookieManager::class);
282 $this->container->cookieManager
283 ->expects(static::once())
284 ->method('setCookieParameter')
285 ->willReturnCallback(function (string $name): CookieManager {
286 static::assertSame(CookieManager::STAY_SIGNED_IN, $name);
287
288 return $this->container->cookieManager;
289 })
290 ;
291
292 $result = $this->controller->login($request, $response);
293
294 static::assertSame(302, $result->getStatusCode());
295 static::assertSame('/subfolder/', $result->getHeader('location')[0]);
296 }
297
298 /**
299 * Test processing login with invalid credentials
300 */
301 public function testProcessLoginWrongCredentials(): void
302 {
303 $parameters = [
304 'returnurl' => 'http://shaarli/subfolder/admin/shaare',
305 ];
306 $request = $this->createMock(Request::class);
307 $request
308 ->expects(static::atLeastOnce())
309 ->method('getParam')
310 ->willReturnCallback(function (string $key) use ($parameters) {
311 return $parameters[$key] ?? null;
312 })
313 ;
314 $response = new Response();
315
316 $this->container->loginManager->method('canLogin')->willReturn(true);
317 $this->container->loginManager->expects(static::once())->method('handleFailedLogin');
318 $this->container->loginManager->expects(static::once())->method('checkCredentials')->willReturn(false);
319
320 $this->container->sessionManager
321 ->expects(static::once())
322 ->method('setSessionParameter')
323 ->with(SessionManager::KEY_ERROR_MESSAGES, ['Wrong login/password.'])
324 ;
325
326 $result = $this->controller->login($request, $response);
327
328 static::assertSame(200, $result->getStatusCode());
329 static::assertSame(TemplatePage::LOGIN, (string) $result->getBody());
330 }
331
332 /**
333 * Test processing login with wrong token
334 */
335 public function testProcessLoginWrongToken(): void
336 {
337 $request = $this->createMock(Request::class);
338 $response = new Response();
339
340 $this->container->sessionManager = $this->createMock(SessionManager::class);
341 $this->container->sessionManager->method('checkToken')->willReturn(false);
342
343 $this->expectException(WrongTokenException::class);
344
345 $this->controller->login($request, $response);
346 }
347
348 /**
349 * Test processing login with wrong token
350 */
351 public function testProcessLoginAlreadyLoggedIn(): void
352 {
353 $request = $this->createMock(Request::class);
354 $response = new Response();
355
356 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
357 $this->container->loginManager->expects(static::never())->method('handleSuccessfulLogin');
358 $this->container->loginManager->expects(static::never())->method('handleFailedLogin');
359
360 $result = $this->controller->login($request, $response);
361
362 static::assertSame(302, $result->getStatusCode());
363 static::assertSame('/subfolder/', $result->getHeader('location')[0]);
364 }
365
366 /**
367 * Test processing login with wrong token
368 */
369 public function testProcessLoginInOpenShaarli(): void
370 {
371 $request = $this->createMock(Request::class);
372 $response = new Response();
373
374 $this->container->conf = $this->createMock(ConfigManager::class);
375 $this->container->conf->method('get')->willReturnCallback(function (string $key, $value) {
376 return 'security.open_shaarli' === $key ? true : $value;
377 });
378
379 $this->container->loginManager->expects(static::never())->method('handleSuccessfulLogin');
380 $this->container->loginManager->expects(static::never())->method('handleFailedLogin');
381
382 $result = $this->controller->login($request, $response);
383
384 static::assertSame(302, $result->getStatusCode());
385 static::assertSame('/subfolder/', $result->getHeader('location')[0]);
386 }
387
388 /**
389 * Test processing login while being banned
390 */
391 public function testProcessLoginWhileBanned(): void
392 {
393 $request = $this->createMock(Request::class);
394 $response = new Response();
395
396 $this->container->loginManager->method('canLogin')->willReturn(false);
397 $this->container->loginManager->expects(static::never())->method('handleSuccessfulLogin');
398 $this->container->loginManager->expects(static::never())->method('handleFailedLogin');
399
400 $this->expectException(LoginBannedException::class);
401
402 $this->controller->login($request, $response);
403 }
404}
diff --git a/tests/front/controller/visitor/OpenSearchControllerTest.php b/tests/front/controller/visitor/OpenSearchControllerTest.php
new file mode 100644
index 00000000..42d876c3
--- /dev/null
+++ b/tests/front/controller/visitor/OpenSearchControllerTest.php
@@ -0,0 +1,44 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\TestCase;
8use Slim\Http\Request;
9use Slim\Http\Response;
10
11class OpenSearchControllerTest extends TestCase
12{
13 use FrontControllerMockHelper;
14
15 /** @var OpenSearchController */
16 protected $controller;
17
18 public function setUp(): void
19 {
20 $this->createContainer();
21
22 $this->controller = new OpenSearchController($this->container);
23 }
24
25 public function testOpenSearchController(): void
26 {
27 $request = $this->createMock(Request::class);
28 $response = new Response();
29
30 // Save RainTPL assigned variables
31 $assignedVariables = [];
32 $this->assignTemplateVars($assignedVariables);
33
34 $result = $this->controller->index($request, $response);
35
36 static::assertSame(200, $result->getStatusCode());
37 static::assertStringContainsString(
38 'application/opensearchdescription+xml',
39 $result->getHeader('Content-Type')[0]
40 );
41 static::assertSame('opensearch', (string) $result->getBody());
42 static::assertSame('http://shaarli/subfolder/', $assignedVariables['serverurl']);
43 }
44}
diff --git a/tests/front/controller/visitor/PictureWallControllerTest.php b/tests/front/controller/visitor/PictureWallControllerTest.php
new file mode 100644
index 00000000..b868231d
--- /dev/null
+++ b/tests/front/controller/visitor/PictureWallControllerTest.php
@@ -0,0 +1,123 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Config\ConfigManager;
9use Shaarli\Front\Exception\ThumbnailsDisabledException;
10use Shaarli\TestCase;
11use Shaarli\Thumbnailer;
12use Slim\Http\Request;
13use Slim\Http\Response;
14
15class PictureWallControllerTest extends TestCase
16{
17 use FrontControllerMockHelper;
18
19 /** @var PictureWallController */
20 protected $controller;
21
22 public function setUp(): void
23 {
24 $this->createContainer();
25
26 $this->controller = new PictureWallController($this->container);
27 }
28
29 public function testValidControllerInvokeDefault(): void
30 {
31 $request = $this->createMock(Request::class);
32 $request->expects(static::once())->method('getQueryParams')->willReturn([]);
33 $response = new Response();
34
35 // ConfigManager: thumbnails are enabled
36 $this->container->conf = $this->createMock(ConfigManager::class);
37 $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
38 if ($parameter === 'thumbnails.mode') {
39 return Thumbnailer::MODE_COMMON;
40 }
41
42 return $default;
43 });
44
45 // Save RainTPL assigned variables
46 $assignedVariables = [];
47 $this->assignTemplateVars($assignedVariables);
48
49 // Links dataset: 2 links with thumbnails
50 $this->container->bookmarkService
51 ->expects(static::once())
52 ->method('search')
53 ->willReturnCallback(function (array $parameters, ?string $visibility): array {
54 // Visibility is set through the container, not the call
55 static::assertNull($visibility);
56
57 // No query parameters
58 if (count($parameters) === 0) {
59 return [
60 (new Bookmark())->setId(1)->setUrl('http://url.tld')->setThumbnail('thumb1'),
61 (new Bookmark())->setId(2)->setUrl('http://url2.tld'),
62 (new Bookmark())->setId(3)->setUrl('http://url3.tld')->setThumbnail('thumb2'),
63 ];
64 }
65 })
66 ;
67
68 // Make sure that PluginManager hook is triggered
69 $this->container->pluginManager
70 ->expects(static::atLeastOnce())
71 ->method('executeHooks')
72 ->withConsecutive(['render_picwall'])
73 ->willReturnCallback(function (string $hook, array $data, array $param): array {
74 if ('render_picwall' === $hook) {
75 static::assertArrayHasKey('linksToDisplay', $data);
76 static::assertCount(2, $data['linksToDisplay']);
77 static::assertSame(1, $data['linksToDisplay'][0]['id']);
78 static::assertSame(3, $data['linksToDisplay'][1]['id']);
79 static::assertArrayHasKey('loggedin', $param);
80 }
81
82 return $data;
83 });
84
85 $result = $this->controller->index($request, $response);
86
87 static::assertSame(200, $result->getStatusCode());
88 static::assertSame('picwall', (string) $result->getBody());
89 static::assertSame('Picture wall - Shaarli', $assignedVariables['pagetitle']);
90 static::assertCount(2, $assignedVariables['linksToDisplay']);
91
92 $link = $assignedVariables['linksToDisplay'][0];
93
94 static::assertSame(1, $link['id']);
95 static::assertSame('http://url.tld', $link['url']);
96 static::assertSame('thumb1', $link['thumbnail']);
97
98 $link = $assignedVariables['linksToDisplay'][1];
99
100 static::assertSame(3, $link['id']);
101 static::assertSame('http://url3.tld', $link['url']);
102 static::assertSame('thumb2', $link['thumbnail']);
103 }
104
105 public function testControllerWithThumbnailsDisabled(): void
106 {
107 $this->expectException(ThumbnailsDisabledException::class);
108
109 $request = $this->createMock(Request::class);
110 $response = new Response();
111
112 // ConfigManager: thumbnails are disabled
113 $this->container->conf->method('get')->willReturnCallback(function (string $parameter, $default) {
114 if ($parameter === 'thumbnails.mode') {
115 return Thumbnailer::MODE_NONE;
116 }
117
118 return $default;
119 });
120
121 $this->controller->index($request, $response);
122 }
123}
diff --git a/tests/front/controller/visitor/PublicSessionFilterControllerTest.php b/tests/front/controller/visitor/PublicSessionFilterControllerTest.php
new file mode 100644
index 00000000..7e3b00af
--- /dev/null
+++ b/tests/front/controller/visitor/PublicSessionFilterControllerTest.php
@@ -0,0 +1,122 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Security\SessionManager;
8use Shaarli\TestCase;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12class PublicSessionFilterControllerTest extends TestCase
13{
14 use FrontControllerMockHelper;
15
16 /** @var PublicSessionFilterController */
17 protected $controller;
18
19 public function setUp(): void
20 {
21 $this->createContainer();
22
23 $this->controller = new PublicSessionFilterController($this->container);
24 }
25
26 /**
27 * Link per page - Default call with valid parameter and a referer.
28 */
29 public function testLinksPerPage(): void
30 {
31 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
32
33 $request = $this->createMock(Request::class);
34 $request->method('getParam')->with('nb')->willReturn('8');
35 $response = new Response();
36
37 $this->container->sessionManager
38 ->expects(static::once())
39 ->method('setSessionParameter')
40 ->with(SessionManager::KEY_LINKS_PER_PAGE, 8)
41 ;
42
43 $result = $this->controller->linksPerPage($request, $response);
44
45 static::assertInstanceOf(Response::class, $result);
46 static::assertSame(302, $result->getStatusCode());
47 static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
48 }
49
50 /**
51 * Link per page - Invalid value, should use default value (20)
52 */
53 public function testLinksPerPageNotValid(): void
54 {
55 $request = $this->createMock(Request::class);
56 $request->method('getParam')->with('nb')->willReturn('test');
57 $response = new Response();
58
59 $this->container->sessionManager
60 ->expects(static::once())
61 ->method('setSessionParameter')
62 ->with(SessionManager::KEY_LINKS_PER_PAGE, 20)
63 ;
64
65 $result = $this->controller->linksPerPage($request, $response);
66
67 static::assertInstanceOf(Response::class, $result);
68 static::assertSame(302, $result->getStatusCode());
69 static::assertSame(['/subfolder/'], $result->getHeader('location'));
70 }
71
72 /**
73 * Untagged only - valid call
74 */
75 public function testUntaggedOnly(): void
76 {
77 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
78
79 $request = $this->createMock(Request::class);
80 $response = new Response();
81
82 $this->container->sessionManager
83 ->expects(static::once())
84 ->method('setSessionParameter')
85 ->with(SessionManager::KEY_UNTAGGED_ONLY, true)
86 ;
87
88 $result = $this->controller->untaggedOnly($request, $response);
89
90 static::assertInstanceOf(Response::class, $result);
91 static::assertSame(302, $result->getStatusCode());
92 static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
93 }
94
95 /**
96 * Untagged only - toggle off
97 */
98 public function testUntaggedOnlyToggleOff(): void
99 {
100 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller/?searchtag=abc';
101
102 $request = $this->createMock(Request::class);
103 $response = new Response();
104
105 $this->container->sessionManager
106 ->method('getSessionParameter')
107 ->with(SessionManager::KEY_UNTAGGED_ONLY)
108 ->willReturn(true)
109 ;
110 $this->container->sessionManager
111 ->expects(static::once())
112 ->method('setSessionParameter')
113 ->with(SessionManager::KEY_UNTAGGED_ONLY, false)
114 ;
115
116 $result = $this->controller->untaggedOnly($request, $response);
117
118 static::assertInstanceOf(Response::class, $result);
119 static::assertSame(302, $result->getStatusCode());
120 static::assertSame(['/subfolder/controller/?searchtag=abc'], $result->getHeader('location'));
121 }
122}
diff --git a/tests/front/controller/visitor/ShaarliVisitorControllerTest.php b/tests/front/controller/visitor/ShaarliVisitorControllerTest.php
new file mode 100644
index 00000000..935ec24e
--- /dev/null
+++ b/tests/front/controller/visitor/ShaarliVisitorControllerTest.php
@@ -0,0 +1,246 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Bookmark\BookmarkFilter;
8use Shaarli\TestCase;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12/**
13 * Class ShaarliControllerTest
14 *
15 * This class is used to test default behavior of ShaarliVisitorController abstract class.
16 * It uses a dummy non abstract controller.
17 */
18class ShaarliVisitorControllerTest extends TestCase
19{
20 use FrontControllerMockHelper;
21
22 /** @var LoginController */
23 protected $controller;
24
25 /** @var mixed[] List of variable assigned to the template */
26 protected $assignedValues;
27
28 /** @var Request */
29 protected $request;
30
31 public function setUp(): void
32 {
33 $this->createContainer();
34
35 $this->controller = new class($this->container) extends ShaarliVisitorController
36 {
37 public function assignView(string $key, $value): ShaarliVisitorController
38 {
39 return parent::assignView($key, $value);
40 }
41
42 public function render(string $template): string
43 {
44 return parent::render($template);
45 }
46
47 public function redirectFromReferer(
48 Request $request,
49 Response $response,
50 array $loopTerms = [],
51 array $clearParams = [],
52 string $anchor = null
53 ): Response {
54 return parent::redirectFromReferer($request, $response, $loopTerms, $clearParams, $anchor);
55 }
56 };
57 $this->assignedValues = [];
58
59 $this->request = $this->createMock(Request::class);
60 }
61
62 public function testAssignView(): void
63 {
64 $this->assignTemplateVars($this->assignedValues);
65
66 $self = $this->controller->assignView('variableName', 'variableValue');
67
68 static::assertInstanceOf(ShaarliVisitorController::class, $self);
69 static::assertSame('variableValue', $this->assignedValues['variableName']);
70 }
71
72 public function testRender(): void
73 {
74 $this->assignTemplateVars($this->assignedValues);
75
76 $this->container->bookmarkService
77 ->method('count')
78 ->willReturnCallback(function (string $visibility): int {
79 return $visibility === BookmarkFilter::$PRIVATE ? 5 : 10;
80 })
81 ;
82
83 $this->container->pluginManager
84 ->method('executeHooks')
85 ->willReturnCallback(function (string $hook, array &$data, array $params): array {
86 return $data[$hook] = $params;
87 });
88 $this->container->pluginManager->method('getErrors')->willReturn(['error']);
89
90 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
91
92 $render = $this->controller->render('templateName');
93
94 static::assertSame('templateName', $render);
95
96 static::assertSame(10, $this->assignedValues['linkcount']);
97 static::assertSame(5, $this->assignedValues['privateLinkcount']);
98 static::assertSame(['error'], $this->assignedValues['plugin_errors']);
99
100 static::assertSame('templateName', $this->assignedValues['plugins_includes']['render_includes']['target']);
101 static::assertTrue($this->assignedValues['plugins_includes']['render_includes']['loggedin']);
102 static::assertSame('templateName', $this->assignedValues['plugins_header']['render_header']['target']);
103 static::assertTrue($this->assignedValues['plugins_header']['render_header']['loggedin']);
104 static::assertSame('templateName', $this->assignedValues['plugins_footer']['render_footer']['target']);
105 static::assertTrue($this->assignedValues['plugins_footer']['render_footer']['loggedin']);
106 }
107
108 /**
109 * Test redirectFromReferer() - Default behaviour
110 */
111 public function testRedirectFromRefererDefault(): void
112 {
113 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
114
115 $response = new Response();
116
117 $result = $this->controller->redirectFromReferer($this->request, $response);
118
119 static::assertSame(302, $result->getStatusCode());
120 static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
121 }
122
123 /**
124 * Test redirectFromReferer() - With a loop term not matched in the referer
125 */
126 public function testRedirectFromRefererWithUnmatchedLoopTerm(): void
127 {
128 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
129
130 $response = new Response();
131
132 $result = $this->controller->redirectFromReferer($this->request, $response, ['nope']);
133
134 static::assertSame(302, $result->getStatusCode());
135 static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
136 }
137
138 /**
139 * Test redirectFromReferer() - With a loop term matching the referer in its path -> redirect to default
140 */
141 public function testRedirectFromRefererWithMatchingLoopTermInPath(): void
142 {
143 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
144
145 $response = new Response();
146
147 $result = $this->controller->redirectFromReferer($this->request, $response, ['nope', 'controller']);
148
149 static::assertSame(302, $result->getStatusCode());
150 static::assertSame(['/subfolder/'], $result->getHeader('location'));
151 }
152
153 /**
154 * Test redirectFromReferer() - With a loop term matching the referer in its query parameters -> redirect to default
155 */
156 public function testRedirectFromRefererWithMatchingLoopTermInQueryParam(): void
157 {
158 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
159
160 $response = new Response();
161
162 $result = $this->controller->redirectFromReferer($this->request, $response, ['nope', 'other']);
163
164 static::assertSame(302, $result->getStatusCode());
165 static::assertSame(['/subfolder/'], $result->getHeader('location'));
166 }
167
168 /**
169 * Test redirectFromReferer() - With a loop term matching the referer in its query value
170 * -> we do not block redirection for query parameter values.
171 */
172 public function testRedirectFromRefererWithMatchingLoopTermInQueryValue(): void
173 {
174 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
175
176 $response = new Response();
177
178 $result = $this->controller->redirectFromReferer($this->request, $response, ['nope', 'param']);
179
180 static::assertSame(302, $result->getStatusCode());
181 static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
182 }
183
184 /**
185 * Test redirectFromReferer() - With a loop term matching the referer in its domain name
186 * -> we do not block redirection for shaarli's hosts
187 */
188 public function testRedirectFromRefererWithLoopTermInDomain(): void
189 {
190 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
191
192 $response = new Response();
193
194 $result = $this->controller->redirectFromReferer($this->request, $response, ['shaarli']);
195
196 static::assertSame(302, $result->getStatusCode());
197 static::assertSame(['/subfolder/controller?query=param&other=2'], $result->getHeader('location'));
198 }
199
200 /**
201 * Test redirectFromReferer() - With a loop term matching a query parameter AND clear this query param
202 * -> the param should be cleared before checking if it matches the redir loop terms
203 */
204 public function testRedirectFromRefererWithMatchingClearedParam(): void
205 {
206 $this->container->environment['HTTP_REFERER'] = 'http://shaarli/subfolder/controller?query=param&other=2';
207
208 $response = new Response();
209
210 $result = $this->controller->redirectFromReferer($this->request, $response, ['query'], ['query']);
211
212 static::assertSame(302, $result->getStatusCode());
213 static::assertSame(['/subfolder/controller?other=2'], $result->getHeader('location'));
214 }
215
216 /**
217 * Test redirectFromReferer() - From another domain -> we ignore the given referrer.
218 */
219 public function testRedirectExternalReferer(): void
220 {
221 $this->container->environment['HTTP_REFERER'] = 'http://other.domain.tld/controller?query=param&other=2';
222
223 $response = new Response();
224
225 $result = $this->controller->redirectFromReferer($this->request, $response, ['query'], ['query']);
226
227 static::assertSame(302, $result->getStatusCode());
228 static::assertSame(['/subfolder/'], $result->getHeader('location'));
229 }
230
231 /**
232 * Test redirectFromReferer() - From another domain -> we ignore the given referrer.
233 */
234 public function testRedirectExternalRefererExplicitDomainName(): void
235 {
236 $this->container->environment['SERVER_NAME'] = 'my.shaarli.tld';
237 $this->container->environment['HTTP_REFERER'] = 'http://your.shaarli.tld/controller?query=param&other=2';
238
239 $response = new Response();
240
241 $result = $this->controller->redirectFromReferer($this->request, $response, ['query'], ['query']);
242
243 static::assertSame(302, $result->getStatusCode());
244 static::assertSame(['/subfolder/'], $result->getHeader('location'));
245 }
246}
diff --git a/tests/front/controller/visitor/TagCloudControllerTest.php b/tests/front/controller/visitor/TagCloudControllerTest.php
new file mode 100644
index 00000000..9305612e
--- /dev/null
+++ b/tests/front/controller/visitor/TagCloudControllerTest.php
@@ -0,0 +1,381 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\Bookmark\BookmarkFilter;
8use Shaarli\TestCase;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12class TagCloudControllerTest extends TestCase
13{
14 use FrontControllerMockHelper;
15
16 /** @var TagCloudController */
17 protected $controller;
18
19 public function setUp(): void
20 {
21 $this->createContainer();
22
23 $this->controller = new TagCloudController($this->container);
24 }
25
26 /**
27 * Tag Cloud - default parameters
28 */
29 public function testValidCloudControllerInvokeDefault(): void
30 {
31 $allTags = [
32 'ghi' => 1,
33 'abc' => 3,
34 'def' => 12,
35 ];
36 $expectedOrder = ['abc', 'def', 'ghi'];
37
38 $request = $this->createMock(Request::class);
39 $response = new Response();
40
41 // Save RainTPL assigned variables
42 $assignedVariables = [];
43 $this->assignTemplateVars($assignedVariables);
44
45 $this->container->bookmarkService
46 ->expects(static::once())
47 ->method('bookmarksCountPerTag')
48 ->with([], null)
49 ->willReturnCallback(function () use ($allTags): array {
50 return $allTags;
51 })
52 ;
53
54 // Make sure that PluginManager hook is triggered
55 $this->container->pluginManager
56 ->expects(static::atLeastOnce())
57 ->method('executeHooks')
58 ->withConsecutive(['render_tagcloud'])
59 ->willReturnCallback(function (string $hook, array $data, array $param): array {
60 if ('render_tagcloud' === $hook) {
61 static::assertSame('', $data['search_tags']);
62 static::assertCount(3, $data['tags']);
63
64 static::assertArrayHasKey('loggedin', $param);
65 }
66
67 return $data;
68 })
69 ;
70
71 $result = $this->controller->cloud($request, $response);
72
73 static::assertSame(200, $result->getStatusCode());
74 static::assertSame('tag.cloud', (string) $result->getBody());
75 static::assertSame('Tag cloud - Shaarli', $assignedVariables['pagetitle']);
76
77 static::assertSame('', $assignedVariables['search_tags']);
78 static::assertCount(3, $assignedVariables['tags']);
79 static::assertSame($expectedOrder, array_keys($assignedVariables['tags']));
80
81 foreach ($allTags as $tag => $count) {
82 static::assertArrayHasKey($tag, $assignedVariables['tags']);
83 static::assertSame($count, $assignedVariables['tags'][$tag]['count']);
84 static::assertGreaterThan(0, $assignedVariables['tags'][$tag]['size']);
85 static::assertLessThan(5, $assignedVariables['tags'][$tag]['size']);
86 }
87 }
88
89 /**
90 * Tag Cloud - Additional parameters:
91 * - logged in
92 * - visibility private
93 * - search tags: `ghi` and `def` (note that filtered tags are not displayed anymore)
94 */
95 public function testValidCloudControllerInvokeWithParameters(): void
96 {
97 $request = $this->createMock(Request::class);
98 $request
99 ->method('getQueryParam')
100 ->with()
101 ->willReturnCallback(function (string $key): ?string {
102 if ('searchtags' === $key) {
103 return 'ghi def';
104 }
105
106 return null;
107 })
108 ;
109 $response = new Response();
110
111 // Save RainTPL assigned variables
112 $assignedVariables = [];
113 $this->assignTemplateVars($assignedVariables);
114
115 $this->container->loginManager->method('isLoggedin')->willReturn(true);
116 $this->container->sessionManager->expects(static::once())->method('getSessionParameter')->willReturn('private');
117
118 $this->container->bookmarkService
119 ->expects(static::once())
120 ->method('bookmarksCountPerTag')
121 ->with(['ghi', 'def'], BookmarkFilter::$PRIVATE)
122 ->willReturnCallback(function (): array {
123 return ['abc' => 3];
124 })
125 ;
126
127 // Make sure that PluginManager hook is triggered
128 $this->container->pluginManager
129 ->expects(static::atLeastOnce())
130 ->method('executeHooks')
131 ->withConsecutive(['render_tagcloud'])
132 ->willReturnCallback(function (string $hook, array $data, array $param): array {
133 if ('render_tagcloud' === $hook) {
134 static::assertSame('ghi def', $data['search_tags']);
135 static::assertCount(1, $data['tags']);
136
137 static::assertArrayHasKey('loggedin', $param);
138 }
139
140 return $data;
141 })
142 ;
143
144 $result = $this->controller->cloud($request, $response);
145
146 static::assertSame(200, $result->getStatusCode());
147 static::assertSame('tag.cloud', (string) $result->getBody());
148 static::assertSame('ghi def - Tag cloud - Shaarli', $assignedVariables['pagetitle']);
149
150 static::assertSame('ghi def', $assignedVariables['search_tags']);
151 static::assertCount(1, $assignedVariables['tags']);
152
153 static::assertArrayHasKey('abc', $assignedVariables['tags']);
154 static::assertSame(3, $assignedVariables['tags']['abc']['count']);
155 static::assertGreaterThan(0, $assignedVariables['tags']['abc']['size']);
156 static::assertLessThan(5, $assignedVariables['tags']['abc']['size']);
157 }
158
159 /**
160 * Tag Cloud - empty
161 */
162 public function testEmptyCloud(): void
163 {
164 $request = $this->createMock(Request::class);
165 $response = new Response();
166
167 // Save RainTPL assigned variables
168 $assignedVariables = [];
169 $this->assignTemplateVars($assignedVariables);
170
171 $this->container->bookmarkService
172 ->expects(static::once())
173 ->method('bookmarksCountPerTag')
174 ->with([], null)
175 ->willReturnCallback(function (array $parameters, ?string $visibility): array {
176 return [];
177 })
178 ;
179
180 // Make sure that PluginManager hook is triggered
181 $this->container->pluginManager
182 ->expects(static::atLeastOnce())
183 ->method('executeHooks')
184 ->withConsecutive(['render_tagcloud'])
185 ->willReturnCallback(function (string $hook, array $data, array $param): array {
186 if ('render_tagcloud' === $hook) {
187 static::assertSame('', $data['search_tags']);
188 static::assertCount(0, $data['tags']);
189
190 static::assertArrayHasKey('loggedin', $param);
191 }
192
193 return $data;
194 })
195 ;
196
197 $result = $this->controller->cloud($request, $response);
198
199 static::assertSame(200, $result->getStatusCode());
200 static::assertSame('tag.cloud', (string) $result->getBody());
201 static::assertSame('Tag cloud - Shaarli', $assignedVariables['pagetitle']);
202
203 static::assertSame('', $assignedVariables['search_tags']);
204 static::assertCount(0, $assignedVariables['tags']);
205 }
206
207 /**
208 * Tag List - Default sort is by usage DESC
209 */
210 public function testValidListControllerInvokeDefault(): void
211 {
212 $allTags = [
213 'def' => 12,
214 'abc' => 3,
215 'ghi' => 1,
216 ];
217
218 $request = $this->createMock(Request::class);
219 $response = new Response();
220
221 // Save RainTPL assigned variables
222 $assignedVariables = [];
223 $this->assignTemplateVars($assignedVariables);
224
225 $this->container->bookmarkService
226 ->expects(static::once())
227 ->method('bookmarksCountPerTag')
228 ->with([], null)
229 ->willReturnCallback(function () use ($allTags): array {
230 return $allTags;
231 })
232 ;
233
234 // Make sure that PluginManager hook is triggered
235 $this->container->pluginManager
236 ->expects(static::atLeastOnce())
237 ->method('executeHooks')
238 ->withConsecutive(['render_taglist'])
239 ->willReturnCallback(function (string $hook, array $data, array $param): array {
240 if ('render_taglist' === $hook) {
241 static::assertSame('', $data['search_tags']);
242 static::assertCount(3, $data['tags']);
243
244 static::assertArrayHasKey('loggedin', $param);
245 }
246
247 return $data;
248 })
249 ;
250
251 $result = $this->controller->list($request, $response);
252
253 static::assertSame(200, $result->getStatusCode());
254 static::assertSame('tag.list', (string) $result->getBody());
255 static::assertSame('Tag list - Shaarli', $assignedVariables['pagetitle']);
256
257 static::assertSame('', $assignedVariables['search_tags']);
258 static::assertCount(3, $assignedVariables['tags']);
259
260 foreach ($allTags as $tag => $count) {
261 static::assertSame($count, $assignedVariables['tags'][$tag]);
262 }
263 }
264
265 /**
266 * Tag List - Additional parameters:
267 * - logged in
268 * - visibility private
269 * - search tags: `ghi` and `def` (note that filtered tags are not displayed anymore)
270 * - sort alphabetically
271 */
272 public function testValidListControllerInvokeWithParameters(): void
273 {
274 $request = $this->createMock(Request::class);
275 $request
276 ->method('getQueryParam')
277 ->with()
278 ->willReturnCallback(function (string $key): ?string {
279 if ('searchtags' === $key) {
280 return 'ghi def';
281 } elseif ('sort' === $key) {
282 return 'alpha';
283 }
284
285 return null;
286 })
287 ;
288 $response = new Response();
289
290 // Save RainTPL assigned variables
291 $assignedVariables = [];
292 $this->assignTemplateVars($assignedVariables);
293
294 $this->container->loginManager->method('isLoggedin')->willReturn(true);
295 $this->container->sessionManager->expects(static::once())->method('getSessionParameter')->willReturn('private');
296
297 $this->container->bookmarkService
298 ->expects(static::once())
299 ->method('bookmarksCountPerTag')
300 ->with(['ghi', 'def'], BookmarkFilter::$PRIVATE)
301 ->willReturnCallback(function (): array {
302 return ['abc' => 3];
303 })
304 ;
305
306 // Make sure that PluginManager hook is triggered
307 $this->container->pluginManager
308 ->expects(static::atLeastOnce())
309 ->method('executeHooks')
310 ->withConsecutive(['render_taglist'])
311 ->willReturnCallback(function (string $hook, array $data, array $param): array {
312 if ('render_taglist' === $hook) {
313 static::assertSame('ghi def', $data['search_tags']);
314 static::assertCount(1, $data['tags']);
315
316 static::assertArrayHasKey('loggedin', $param);
317 }
318
319 return $data;
320 })
321 ;
322
323 $result = $this->controller->list($request, $response);
324
325 static::assertSame(200, $result->getStatusCode());
326 static::assertSame('tag.list', (string) $result->getBody());
327 static::assertSame('ghi def - Tag list - Shaarli', $assignedVariables['pagetitle']);
328
329 static::assertSame('ghi def', $assignedVariables['search_tags']);
330 static::assertCount(1, $assignedVariables['tags']);
331 static::assertSame(3, $assignedVariables['tags']['abc']);
332 }
333
334 /**
335 * Tag List - empty
336 */
337 public function testEmptyList(): void
338 {
339 $request = $this->createMock(Request::class);
340 $response = new Response();
341
342 // Save RainTPL assigned variables
343 $assignedVariables = [];
344 $this->assignTemplateVars($assignedVariables);
345
346 $this->container->bookmarkService
347 ->expects(static::once())
348 ->method('bookmarksCountPerTag')
349 ->with([], null)
350 ->willReturnCallback(function (array $parameters, ?string $visibility): array {
351 return [];
352 })
353 ;
354
355 // Make sure that PluginManager hook is triggered
356 $this->container->pluginManager
357 ->expects(static::atLeastOnce())
358 ->method('executeHooks')
359 ->withConsecutive(['render_taglist'])
360 ->willReturnCallback(function (string $hook, array $data, array $param): array {
361 if ('render_taglist' === $hook) {
362 static::assertSame('', $data['search_tags']);
363 static::assertCount(0, $data['tags']);
364
365 static::assertArrayHasKey('loggedin', $param);
366 }
367
368 return $data;
369 })
370 ;
371
372 $result = $this->controller->list($request, $response);
373
374 static::assertSame(200, $result->getStatusCode());
375 static::assertSame('tag.list', (string) $result->getBody());
376 static::assertSame('Tag list - Shaarli', $assignedVariables['pagetitle']);
377
378 static::assertSame('', $assignedVariables['search_tags']);
379 static::assertCount(0, $assignedVariables['tags']);
380 }
381}
diff --git a/tests/front/controller/visitor/TagControllerTest.php b/tests/front/controller/visitor/TagControllerTest.php
new file mode 100644
index 00000000..750ea02d
--- /dev/null
+++ b/tests/front/controller/visitor/TagControllerTest.php
@@ -0,0 +1,215 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Visitor;
6
7use Shaarli\TestCase;
8use Slim\Http\Request;
9use Slim\Http\Response;
10
11class TagControllerTest extends TestCase
12{
13 use FrontControllerMockHelper;
14
15 /** @var TagController */ protected $controller;
16
17 public function setUp(): void
18 {
19 $this->createContainer();
20
21 $this->controller = new TagController($this->container);
22 }
23
24 public function testAddTagWithReferer(): void
25 {
26 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/'];
27
28 $request = $this->createMock(Request::class);
29 $response = new Response();
30
31 $tags = ['newTag' => 'abc'];
32
33 $result = $this->controller->addTag($request, $response, $tags);
34
35 static::assertInstanceOf(Response::class, $result);
36 static::assertSame(302, $result->getStatusCode());
37 static::assertSame(['/controller/?searchtags=abc'], $result->getHeader('location'));
38 }
39
40 public function testAddTagWithRefererAndExistingSearch(): void
41 {
42 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
43
44 $request = $this->createMock(Request::class);
45 $response = new Response();
46
47 $tags = ['newTag' => 'abc'];
48
49 $result = $this->controller->addTag($request, $response, $tags);
50
51 static::assertInstanceOf(Response::class, $result);
52 static::assertSame(302, $result->getStatusCode());
53 static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
54 }
55
56 public function testAddTagWithoutRefererAndExistingSearch(): void
57 {
58 $request = $this->createMock(Request::class);
59 $response = new Response();
60
61 $tags = ['newTag' => 'abc'];
62
63 $result = $this->controller->addTag($request, $response, $tags);
64
65 static::assertInstanceOf(Response::class, $result);
66 static::assertSame(302, $result->getStatusCode());
67 static::assertSame(['/subfolder/?searchtags=abc'], $result->getHeader('location'));
68 }
69
70 public function testAddTagRemoveLegacyQueryParam(): void
71 {
72 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&addtag=abc'];
73
74 $request = $this->createMock(Request::class);
75 $response = new Response();
76
77 $tags = ['newTag' => 'abc'];
78
79 $result = $this->controller->addTag($request, $response, $tags);
80
81 static::assertInstanceOf(Response::class, $result);
82 static::assertSame(302, $result->getStatusCode());
83 static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
84 }
85
86 public function testAddTagResetPagination(): void
87 {
88 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def&page=12'];
89
90 $request = $this->createMock(Request::class);
91 $response = new Response();
92
93 $tags = ['newTag' => 'abc'];
94
95 $result = $this->controller->addTag($request, $response, $tags);
96
97 static::assertInstanceOf(Response::class, $result);
98 static::assertSame(302, $result->getStatusCode());
99 static::assertSame(['/controller/?searchtags=def+abc'], $result->getHeader('location'));
100 }
101
102 public function testAddTagWithRefererAndEmptySearch(): void
103 {
104 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags='];
105
106 $request = $this->createMock(Request::class);
107 $response = new Response();
108
109 $tags = ['newTag' => 'abc'];
110
111 $result = $this->controller->addTag($request, $response, $tags);
112
113 static::assertInstanceOf(Response::class, $result);
114 static::assertSame(302, $result->getStatusCode());
115 static::assertSame(['/controller/?searchtags=abc'], $result->getHeader('location'));
116 }
117
118 public function testAddTagWithoutNewTagWithReferer(): void
119 {
120 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
121
122 $request = $this->createMock(Request::class);
123 $response = new Response();
124
125 $result = $this->controller->addTag($request, $response, []);
126
127 static::assertInstanceOf(Response::class, $result);
128 static::assertSame(302, $result->getStatusCode());
129 static::assertSame(['/controller/?searchtags=def'], $result->getHeader('location'));
130 }
131
132 public function testAddTagWithoutNewTagWithoutReferer(): void
133 {
134 $request = $this->createMock(Request::class);
135 $response = new Response();
136
137 $result = $this->controller->addTag($request, $response, []);
138
139 static::assertInstanceOf(Response::class, $result);
140 static::assertSame(302, $result->getStatusCode());
141 static::assertSame(['/subfolder/'], $result->getHeader('location'));
142 }
143
144 public function testRemoveTagWithoutMatchingTag(): void
145 {
146 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtags=def'];
147
148 $request = $this->createMock(Request::class);
149 $response = new Response();
150
151 $tags = ['tag' => 'abc'];
152
153 $result = $this->controller->removeTag($request, $response, $tags);
154
155 static::assertInstanceOf(Response::class, $result);
156 static::assertSame(302, $result->getStatusCode());
157 static::assertSame(['/controller/?searchtags=def'], $result->getHeader('location'));
158 }
159
160 public function testRemoveTagWithoutTagsearch(): void
161 {
162 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/'];
163
164 $request = $this->createMock(Request::class);
165 $response = new Response();
166
167 $tags = ['tag' => 'abc'];
168
169 $result = $this->controller->removeTag($request, $response, $tags);
170
171 static::assertInstanceOf(Response::class, $result);
172 static::assertSame(302, $result->getStatusCode());
173 static::assertSame(['/controller/'], $result->getHeader('location'));
174 }
175
176 public function testRemoveTagWithoutReferer(): void
177 {
178 $request = $this->createMock(Request::class);
179 $response = new Response();
180
181 $tags = ['tag' => 'abc'];
182
183 $result = $this->controller->removeTag($request, $response, $tags);
184
185 static::assertInstanceOf(Response::class, $result);
186 static::assertSame(302, $result->getStatusCode());
187 static::assertSame(['/subfolder/'], $result->getHeader('location'));
188 }
189
190 public function testRemoveTagWithoutTag(): void
191 {
192 $this->container->environment = ['HTTP_REFERER' => 'http://shaarli/controller/?searchtag=abc'];
193
194 $request = $this->createMock(Request::class);
195 $response = new Response();
196
197 $result = $this->controller->removeTag($request, $response, []);
198
199 static::assertInstanceOf(Response::class, $result);
200 static::assertSame(302, $result->getStatusCode());
201 static::assertSame(['/controller/?searchtag=abc'], $result->getHeader('location'));
202 }
203
204 public function testRemoveTagWithoutTagWithoutReferer(): void
205 {
206 $request = $this->createMock(Request::class);
207 $response = new Response();
208
209 $result = $this->controller->removeTag($request, $response, []);
210
211 static::assertInstanceOf(Response::class, $result);
212 static::assertSame(302, $result->getStatusCode());
213 static::assertSame(['/subfolder/'], $result->getHeader('location'));
214 }
215}
diff --git a/tests/ApplicationUtilsTest.php b/tests/helper/ApplicationUtilsTest.php
index 15388970..654857b9 100644
--- a/tests/ApplicationUtilsTest.php
+++ b/tests/helper/ApplicationUtilsTest.php
@@ -1,14 +1,15 @@
1<?php 1<?php
2namespace Shaarli; 2namespace Shaarli\Helper;
3 3
4use Shaarli\Config\ConfigManager; 4use Shaarli\Config\ConfigManager;
5use Shaarli\FakeApplicationUtils;
5 6
6require_once 'tests/utils/FakeApplicationUtils.php'; 7require_once 'tests/utils/FakeApplicationUtils.php';
7 8
8/** 9/**
9 * Unitary tests for Shaarli utilities 10 * Unitary tests for Shaarli utilities
10 */ 11 */
11class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase 12class ApplicationUtilsTest extends \Shaarli\TestCase
12{ 13{
13 protected static $testUpdateFile = 'sandbox/update.txt'; 14 protected static $testUpdateFile = 'sandbox/update.txt';
14 protected static $testVersion = '0.5.0'; 15 protected static $testVersion = '0.5.0';
@@ -17,7 +18,7 @@ class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
17 /** 18 /**
18 * Reset test data for each test 19 * Reset test data for each test
19 */ 20 */
20 public function setUp() 21 protected function setUp(): void
21 { 22 {
22 FakeApplicationUtils::$VERSION_CODE = ''; 23 FakeApplicationUtils::$VERSION_CODE = '';
23 if (file_exists(self::$testUpdateFile)) { 24 if (file_exists(self::$testUpdateFile)) {
@@ -28,7 +29,7 @@ class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
28 /** 29 /**
29 * Remove test version file if it exists 30 * Remove test version file if it exists
30 */ 31 */
31 public function tearDown() 32 protected function tearDown(): void
32 { 33 {
33 if (is_file('sandbox/version.php')) { 34 if (is_file('sandbox/version.php')) {
34 unlink('sandbox/version.php'); 35 unlink('sandbox/version.php');
@@ -144,11 +145,12 @@ class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
144 145
145 /** 146 /**
146 * Test update checks - invalid Git branch 147 * Test update checks - invalid Git branch
147 * @expectedException Exception
148 * @expectedExceptionMessageRegExp /Invalid branch selected for updates/
149 */ 148 */
150 public function testCheckUpdateInvalidGitBranch() 149 public function testCheckUpdateInvalidGitBranch()
151 { 150 {
151 $this->expectException(\Exception::class);
152 $this->expectExceptionMessageRegExp('/Invalid branch selected for updates/');
153
152 ApplicationUtils::checkUpdate('', 'null', 0, true, true, 'unstable'); 154 ApplicationUtils::checkUpdate('', 'null', 0, true, true, 'unstable');
153 } 155 }
154 156
@@ -260,21 +262,23 @@ class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
260 262
261 /** 263 /**
262 * Check a unsupported PHP version 264 * Check a unsupported PHP version
263 * @expectedException Exception
264 * @expectedExceptionMessageRegExp /Your PHP version is obsolete/
265 */ 265 */
266 public function testCheckSupportedPHPVersion51() 266 public function testCheckSupportedPHPVersion51()
267 { 267 {
268 $this->expectException(\Exception::class);
269 $this->expectExceptionMessageRegExp('/Your PHP version is obsolete/');
270
268 $this->assertTrue(ApplicationUtils::checkPHPVersion('5.3', '5.1.0')); 271 $this->assertTrue(ApplicationUtils::checkPHPVersion('5.3', '5.1.0'));
269 } 272 }
270 273
271 /** 274 /**
272 * Check another unsupported PHP version 275 * Check another unsupported PHP version
273 * @expectedException Exception
274 * @expectedExceptionMessageRegExp /Your PHP version is obsolete/
275 */ 276 */
276 public function testCheckSupportedPHPVersion52() 277 public function testCheckSupportedPHPVersion52()
277 { 278 {
279 $this->expectException(\Exception::class);
280 $this->expectExceptionMessageRegExp('/Your PHP version is obsolete/');
281
278 $this->assertTrue(ApplicationUtils::checkPHPVersion('5.3', '5.2')); 282 $this->assertTrue(ApplicationUtils::checkPHPVersion('5.3', '5.2'));
279 } 283 }
280 284
@@ -337,6 +341,35 @@ class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
337 } 341 }
338 342
339 /** 343 /**
344 * Checks resource permissions in minimal mode.
345 */
346 public function testCheckCurrentResourcePermissionsErrorsMinimalMode(): void
347 {
348 $conf = new ConfigManager('');
349 $conf->set('resource.thumbnails_cache', 'null/cache');
350 $conf->set('resource.config', 'null/data/config.php');
351 $conf->set('resource.data_dir', 'null/data');
352 $conf->set('resource.datastore', 'null/data/store.php');
353 $conf->set('resource.ban_file', 'null/data/ipbans.php');
354 $conf->set('resource.log', 'null/data/log.txt');
355 $conf->set('resource.page_cache', 'null/pagecache');
356 $conf->set('resource.raintpl_tmp', 'null/tmp');
357 $conf->set('resource.raintpl_tpl', 'null/tpl');
358 $conf->set('resource.raintpl_theme', 'null/tpl/default');
359 $conf->set('resource.update_check', 'null/data/lastupdatecheck.txt');
360
361 static::assertSame(
362 [
363 '"null/tpl" directory is not readable',
364 '"null/tpl/default" directory is not readable',
365 '"null/tmp" directory is not readable',
366 '"null/tmp" directory is not writable'
367 ],
368 ApplicationUtils::checkResourcePermissions($conf, true)
369 );
370 }
371
372 /**
340 * Check update with 'dev' as curent version (master branch). 373 * Check update with 'dev' as curent version (master branch).
341 * It should always return false. 374 * It should always return false.
342 */ 375 */
@@ -346,4 +379,37 @@ class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
346 ApplicationUtils::checkUpdate('dev', self::$testUpdateFile, 100, true, true) 379 ApplicationUtils::checkUpdate('dev', self::$testUpdateFile, 100, true, true)
347 ); 380 );
348 } 381 }
382
383 /**
384 * Basic test of getPhpExtensionsRequirement()
385 */
386 public function testGetPhpExtensionsRequirementSimple(): void
387 {
388 static::assertCount(8, ApplicationUtils::getPhpExtensionsRequirement());
389 static::assertSame([
390 'name' => 'json',
391 'required' => true,
392 'desc' => 'Configuration parsing',
393 'loaded' => true,
394 ], ApplicationUtils::getPhpExtensionsRequirement()[0]);
395 }
396
397 /**
398 * Test getPhpEol with a known version: 7.4 -> 2022
399 */
400 public function testGetKnownPhpEol(): void
401 {
402 static::assertSame('2022-11-28', ApplicationUtils::getPhpEol('7.4.7'));
403 }
404
405 /**
406 * Test getPhpEol with an unknown version: 7.4 -> 2022
407 */
408 public function testGetUnknownPhpEol(): void
409 {
410 static::assertSame(
411 (((int) (new \DateTime())->format('Y')) + 2) . (new \DateTime())->format('-m-d'),
412 ApplicationUtils::getPhpEol('7.51.34')
413 );
414 }
349} 415}
diff --git a/tests/helper/DailyPageHelperTest.php b/tests/helper/DailyPageHelperTest.php
new file mode 100644
index 00000000..e0378491
--- /dev/null
+++ b/tests/helper/DailyPageHelperTest.php
@@ -0,0 +1,262 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Helper;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\TestCase;
9use Slim\Http\Request;
10
11class DailyPageHelperTest extends TestCase
12{
13 /**
14 * @dataProvider getRequestedTypes
15 */
16 public function testExtractRequestedType(array $queryParams, string $expectedType): void
17 {
18 $request = $this->createMock(Request::class);
19 $request->method('getQueryParam')->willReturnCallback(function ($key) use ($queryParams): ?string {
20 return $queryParams[$key] ?? null;
21 });
22
23 $type = DailyPageHelper::extractRequestedType($request);
24
25 static::assertSame($type, $expectedType);
26 }
27
28 /**
29 * @dataProvider getRequestedDateTimes
30 */
31 public function testExtractRequestedDateTime(
32 string $type,
33 string $input,
34 ?Bookmark $bookmark,
35 \DateTimeInterface $expectedDateTime,
36 string $compareFormat = 'Ymd'
37 ): void {
38 $dateTime = DailyPageHelper::extractRequestedDateTime($type, $input, $bookmark);
39
40 static::assertSame($dateTime->format($compareFormat), $expectedDateTime->format($compareFormat));
41 }
42
43 public function testExtractRequestedDateTimeExceptionUnknownType(): void
44 {
45 $this->expectException(\Exception::class);
46 $this->expectExceptionMessage('Unsupported daily format type');
47
48 DailyPageHelper::extractRequestedDateTime('nope', null, null);
49 }
50
51 /**
52 * @dataProvider getFormatsByType
53 */
54 public function testGetFormatByType(string $type, string $expectedFormat): void
55 {
56 $format = DailyPageHelper::getFormatByType($type);
57
58 static::assertSame($expectedFormat, $format);
59 }
60
61 public function testGetFormatByTypeExceptionUnknownType(): void
62 {
63 $this->expectException(\Exception::class);
64 $this->expectExceptionMessage('Unsupported daily format type');
65
66 DailyPageHelper::getFormatByType('nope');
67 }
68
69 /**
70 * @dataProvider getStartDatesByType
71 */
72 public function testGetStartDatesByType(
73 string $type,
74 \DateTimeImmutable $dateTime,
75 \DateTimeInterface $expectedDateTime
76 ): void {
77 $startDateTime = DailyPageHelper::getStartDateTimeByType($type, $dateTime);
78
79 static::assertEquals($expectedDateTime, $startDateTime);
80 }
81
82 public function testGetStartDatesByTypeExceptionUnknownType(): void
83 {
84 $this->expectException(\Exception::class);
85 $this->expectExceptionMessage('Unsupported daily format type');
86
87 DailyPageHelper::getStartDateTimeByType('nope', new \DateTimeImmutable());
88 }
89
90 /**
91 * @dataProvider getEndDatesByType
92 */
93 public function testGetEndDatesByType(
94 string $type,
95 \DateTimeImmutable $dateTime,
96 \DateTimeInterface $expectedDateTime
97 ): void {
98 $endDateTime = DailyPageHelper::getEndDateTimeByType($type, $dateTime);
99
100 static::assertEquals($expectedDateTime, $endDateTime);
101 }
102
103 public function testGetEndDatesByTypeExceptionUnknownType(): void
104 {
105 $this->expectException(\Exception::class);
106 $this->expectExceptionMessage('Unsupported daily format type');
107
108 DailyPageHelper::getEndDateTimeByType('nope', new \DateTimeImmutable());
109 }
110
111 /**
112 * @dataProvider getDescriptionsByType
113 */
114 public function testGeDescriptionsByType(
115 string $type,
116 \DateTimeImmutable $dateTime,
117 string $expectedDescription
118 ): void {
119 $description = DailyPageHelper::getDescriptionByType($type, $dateTime);
120
121 static::assertEquals($expectedDescription, $description);
122 }
123
124 public function getDescriptionByTypeExceptionUnknownType(): void
125 {
126 $this->expectException(\Exception::class);
127 $this->expectExceptionMessage('Unsupported daily format type');
128
129 DailyPageHelper::getDescriptionByType('nope', new \DateTimeImmutable());
130 }
131
132 /**
133 * @dataProvider getRssLengthsByType
134 */
135 public function testGeRssLengthsByType(string $type): void {
136 $length = DailyPageHelper::getRssLengthByType($type);
137
138 static::assertIsInt($length);
139 }
140
141 public function testGeRssLengthsByTypeExceptionUnknownType(): void
142 {
143 $this->expectException(\Exception::class);
144 $this->expectExceptionMessage('Unsupported daily format type');
145
146 DailyPageHelper::getRssLengthByType('nope');
147 }
148
149 /**
150 * Data provider for testExtractRequestedType() test method.
151 */
152 public function getRequestedTypes(): array
153 {
154 return [
155 [['month' => null], DailyPageHelper::DAY],
156 [['month' => ''], DailyPageHelper::MONTH],
157 [['month' => 'content'], DailyPageHelper::MONTH],
158 [['week' => null], DailyPageHelper::DAY],
159 [['week' => ''], DailyPageHelper::WEEK],
160 [['week' => 'content'], DailyPageHelper::WEEK],
161 [['day' => null], DailyPageHelper::DAY],
162 [['day' => ''], DailyPageHelper::DAY],
163 [['day' => 'content'], DailyPageHelper::DAY],
164 ];
165 }
166
167 /**
168 * Data provider for testExtractRequestedDateTime() test method.
169 */
170 public function getRequestedDateTimes(): array
171 {
172 return [
173 [DailyPageHelper::DAY, '20201013', null, new \DateTime('2020-10-13')],
174 [
175 DailyPageHelper::DAY,
176 '',
177 (new Bookmark())->setCreated($date = new \DateTime('2020-10-13 12:05:31')),
178 $date,
179 ],
180 [DailyPageHelper::DAY, '', null, new \DateTime()],
181 [DailyPageHelper::WEEK, '202030', null, new \DateTime('2020-07-20')],
182 [
183 DailyPageHelper::WEEK,
184 '',
185 (new Bookmark())->setCreated($date = new \DateTime('2020-10-13 12:05:31')),
186 new \DateTime('2020-10-13'),
187 ],
188 [DailyPageHelper::WEEK, '', null, new \DateTime(), 'Ym'],
189 [DailyPageHelper::MONTH, '202008', null, new \DateTime('2020-08-01'), 'Ym'],
190 [
191 DailyPageHelper::MONTH,
192 '',
193 (new Bookmark())->setCreated($date = new \DateTime('2020-10-13 12:05:31')),
194 new \DateTime('2020-10-13'),
195 'Ym'
196 ],
197 [DailyPageHelper::MONTH, '', null, new \DateTime(), 'Ym'],
198 ];
199 }
200
201 /**
202 * Data provider for testGetFormatByType() test method.
203 */
204 public function getFormatsByType(): array
205 {
206 return [
207 [DailyPageHelper::DAY, 'Ymd'],
208 [DailyPageHelper::WEEK, 'YW'],
209 [DailyPageHelper::MONTH, 'Ym'],
210 ];
211 }
212
213 /**
214 * Data provider for testGetStartDatesByType() test method.
215 */
216 public function getStartDatesByType(): array
217 {
218 return [
219 [DailyPageHelper::DAY, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-09 00:00:00')],
220 [DailyPageHelper::WEEK, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-05 00:00:00')],
221 [DailyPageHelper::MONTH, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-01 00:00:00')],
222 ];
223 }
224
225 /**
226 * Data provider for testGetEndDatesByType() test method.
227 */
228 public function getEndDatesByType(): array
229 {
230 return [
231 [DailyPageHelper::DAY, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-09 23:59:59')],
232 [DailyPageHelper::WEEK, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-11 23:59:59')],
233 [DailyPageHelper::MONTH, new \DateTimeImmutable('2020-10-09 04:05:06'), new \DateTime('2020-10-31 23:59:59')],
234 ];
235 }
236
237 /**
238 * Data provider for testGetDescriptionsByType() test method.
239 */
240 public function getDescriptionsByType(): array
241 {
242 return [
243 [DailyPageHelper::DAY, $date = new \DateTimeImmutable(), 'Today - ' . $date->format('F d, Y')],
244 [DailyPageHelper::DAY, $date = new \DateTimeImmutable('-1 day'), 'Yesterday - ' . $date->format('F d, Y')],
245 [DailyPageHelper::DAY, new \DateTimeImmutable('2020-10-09 04:05:06'), 'October 9, 2020'],
246 [DailyPageHelper::WEEK, new \DateTimeImmutable('2020-10-09 04:05:06'), 'Week 41 (October 5, 2020)'],
247 [DailyPageHelper::MONTH, new \DateTimeImmutable('2020-10-09 04:05:06'), 'October, 2020'],
248 ];
249 }
250
251 /**
252 * Data provider for testGetDescriptionsByType() test method.
253 */
254 public function getRssLengthsByType(): array
255 {
256 return [
257 [DailyPageHelper::DAY],
258 [DailyPageHelper::WEEK],
259 [DailyPageHelper::MONTH],
260 ];
261 }
262}
diff --git a/tests/helper/FileUtilsTest.php b/tests/helper/FileUtilsTest.php
new file mode 100644
index 00000000..8035f79c
--- /dev/null
+++ b/tests/helper/FileUtilsTest.php
@@ -0,0 +1,197 @@
1<?php
2
3namespace Shaarli\Helper;
4
5use Exception;
6use Shaarli\Exceptions\IOException;
7use Shaarli\TestCase;
8
9/**
10 * Class FileUtilsTest
11 *
12 * Test file utility class.
13 */
14class FileUtilsTest extends TestCase
15{
16 /**
17 * @var string Test file path.
18 */
19 protected static $file = 'sandbox/flat.db';
20
21 protected function setUp(): void
22 {
23 @mkdir('sandbox');
24 mkdir('sandbox/folder2');
25 touch('sandbox/file1');
26 touch('sandbox/file2');
27 mkdir('sandbox/folder1');
28 touch('sandbox/folder1/file1');
29 touch('sandbox/folder1/file2');
30 mkdir('sandbox/folder3');
31 mkdir('/tmp/shaarli-to-delete');
32 }
33
34 /**
35 * Delete test file after every test.
36 */
37 protected function tearDown(): void
38 {
39 @unlink(self::$file);
40
41 @unlink('sandbox/folder1/file1');
42 @unlink('sandbox/folder1/file2');
43 @rmdir('sandbox/folder1');
44 @unlink('sandbox/file1');
45 @unlink('sandbox/file2');
46 @rmdir('sandbox/folder2');
47 @rmdir('sandbox/folder3');
48 @rmdir('/tmp/shaarli-to-delete');
49 }
50
51 /**
52 * Test writeDB, then readDB with different data.
53 */
54 public function testSimpleWriteRead()
55 {
56 $data = ['blue', 'red'];
57 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
58 $this->assertTrue(startsWith(file_get_contents(self::$file), '<?php /*'));
59 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
60
61 $data = 0;
62 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
63 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
64
65 $data = null;
66 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
67 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
68
69 $data = false;
70 $this->assertTrue(FileUtils::writeFlatDB(self::$file, $data) > 0);
71 $this->assertEquals($data, FileUtils::readFlatDB(self::$file));
72 }
73
74 /**
75 * File not writable: raise an exception.
76 */
77 public function testWriteWithoutPermission()
78 {
79 $this->expectException(\Shaarli\Exceptions\IOException::class);
80 $this->expectExceptionMessage('Error accessing "sandbox/flat.db"');
81
82 touch(self::$file);
83 chmod(self::$file, 0440);
84 FileUtils::writeFlatDB(self::$file, null);
85 }
86
87 /**
88 * Folder non existent: raise an exception.
89 */
90 public function testWriteFolderDoesNotExist()
91 {
92 $this->expectException(\Shaarli\Exceptions\IOException::class);
93 $this->expectExceptionMessage('Error accessing "nopefolder"');
94
95 FileUtils::writeFlatDB('nopefolder/file', null);
96 }
97
98 /**
99 * Folder non writable: raise an exception.
100 */
101 public function testWriteFolderPermission()
102 {
103 $this->expectException(\Shaarli\Exceptions\IOException::class);
104 $this->expectExceptionMessage('Error accessing "sandbox"');
105
106 chmod(dirname(self::$file), 0555);
107 try {
108 FileUtils::writeFlatDB(self::$file, null);
109 } catch (Exception $e) {
110 chmod(dirname(self::$file), 0755);
111 throw $e;
112 }
113 }
114
115 /**
116 * Read non existent file, use default parameter.
117 */
118 public function testReadNotExistentFile()
119 {
120 $this->assertEquals(null, FileUtils::readFlatDB(self::$file));
121 $this->assertEquals(['test'], FileUtils::readFlatDB(self::$file, ['test']));
122 }
123
124 /**
125 * Read non readable file, use default parameter.
126 */
127 public function testReadNotReadable()
128 {
129 touch(self::$file);
130 chmod(self::$file, 0220);
131 $this->assertEquals(null, FileUtils::readFlatDB(self::$file));
132 $this->assertEquals(['test'], FileUtils::readFlatDB(self::$file, ['test']));
133 }
134
135 /**
136 * Test clearFolder with self delete and excluded files
137 */
138 public function testClearFolderSelfDeleteWithExclusion(): void
139 {
140 FileUtils::clearFolder('sandbox', true, ['file2']);
141
142 static::assertFileExists('sandbox/folder1/file2');
143 static::assertFileExists('sandbox/folder1');
144 static::assertFileExists('sandbox/file2');
145 static::assertFileExists('sandbox');
146
147 static::assertFileNotExists('sandbox/folder1/file1');
148 static::assertFileNotExists('sandbox/file1');
149 static::assertFileNotExists('sandbox/folder3');
150 }
151
152 /**
153 * Test clearFolder with self delete and excluded files
154 */
155 public function testClearFolderSelfDeleteWithoutExclusion(): void
156 {
157 FileUtils::clearFolder('sandbox', true);
158
159 static::assertFileNotExists('sandbox');
160 }
161
162 /**
163 * Test clearFolder with self delete and excluded files
164 */
165 public function testClearFolderNoSelfDeleteWithoutExclusion(): void
166 {
167 FileUtils::clearFolder('sandbox', false);
168
169 static::assertFileExists('sandbox');
170
171 // 2 because '.' and '..'
172 static::assertCount(2, new \DirectoryIterator('sandbox'));
173 }
174
175 /**
176 * Test clearFolder on a file instead of a folder
177 */
178 public function testClearFolderOnANonDirectory(): void
179 {
180 $this->expectException(IOException::class);
181 $this->expectExceptionMessage('Provided path is not a directory.');
182
183 FileUtils::clearFolder('sandbox/file1', false);
184 }
185
186 /**
187 * Test clearFolder on a file instead of a folder
188 */
189 public function testClearFolderOutsideOfShaarliDirectory(): void
190 {
191 $this->expectException(IOException::class);
192 $this->expectExceptionMessage('Trying to delete a folder outside of Shaarli path.');
193
194
195 FileUtils::clearFolder('/tmp/shaarli-to-delete', true);
196 }
197}
diff --git a/tests/http/HttpUtils/ClientIpIdTest.php b/tests/http/HttpUtils/ClientIpIdTest.php
index 982e57e0..3a0fcf30 100644
--- a/tests/http/HttpUtils/ClientIpIdTest.php
+++ b/tests/http/HttpUtils/ClientIpIdTest.php
@@ -10,7 +10,7 @@ require_once 'application/http/HttpUtils.php';
10/** 10/**
11 * Unitary tests for client_ip_id() 11 * Unitary tests for client_ip_id()
12 */ 12 */
13class ClientIpIdTest extends \PHPUnit\Framework\TestCase 13class ClientIpIdTest extends \Shaarli\TestCase
14{ 14{
15 /** 15 /**
16 * Get a remote client ID based on its IP 16 * Get a remote client ID based on its IP
diff --git a/tests/http/HttpUtils/GetHttpUrlTest.php b/tests/http/HttpUtils/GetHttpUrlTest.php
index 3dc5bc9b..a868ac02 100644
--- a/tests/http/HttpUtils/GetHttpUrlTest.php
+++ b/tests/http/HttpUtils/GetHttpUrlTest.php
@@ -10,7 +10,7 @@ require_once 'application/http/HttpUtils.php';
10/** 10/**
11 * Unitary tests for get_http_response() 11 * Unitary tests for get_http_response()
12 */ 12 */
13class GetHttpUrlTest extends \PHPUnit\Framework\TestCase 13class GetHttpUrlTest extends \Shaarli\TestCase
14{ 14{
15 /** 15 /**
16 * Get an invalid local URL 16 * Get an invalid local URL
diff --git a/tests/http/HttpUtils/GetIpAdressFromProxyTest.php b/tests/http/HttpUtils/GetIpAdressFromProxyTest.php
index fe3a639e..60cdb992 100644
--- a/tests/http/HttpUtils/GetIpAdressFromProxyTest.php
+++ b/tests/http/HttpUtils/GetIpAdressFromProxyTest.php
@@ -7,7 +7,7 @@ require_once 'application/http/HttpUtils.php';
7/** 7/**
8 * Unitary tests for getIpAddressFromProxy() 8 * Unitary tests for getIpAddressFromProxy()
9 */ 9 */
10class GetIpAdressFromProxyTest extends \PHPUnit\Framework\TestCase 10class GetIpAdressFromProxyTest extends \Shaarli\TestCase
11{ 11{
12 12
13 /** 13 /**
diff --git a/tests/http/HttpUtils/IndexUrlTest.php b/tests/http/HttpUtils/IndexUrlTest.php
index bcbe59cb..f283d119 100644
--- a/tests/http/HttpUtils/IndexUrlTest.php
+++ b/tests/http/HttpUtils/IndexUrlTest.php
@@ -5,12 +5,14 @@
5 5
6namespace Shaarli\Http; 6namespace Shaarli\Http;
7 7
8use Shaarli\TestCase;
9
8require_once 'application/http/HttpUtils.php'; 10require_once 'application/http/HttpUtils.php';
9 11
10/** 12/**
11 * Unitary tests for index_url() 13 * Unitary tests for index_url()
12 */ 14 */
13class IndexUrlTest extends \PHPUnit\Framework\TestCase 15class IndexUrlTest extends TestCase
14{ 16{
15 /** 17 /**
16 * If on the main page, remove "index.php" from the URL resource 18 * If on the main page, remove "index.php" from the URL resource
@@ -71,4 +73,68 @@ class IndexUrlTest extends \PHPUnit\Framework\TestCase
71 ) 73 )
72 ); 74 );
73 } 75 }
76
77 /**
78 * The route is stored in REQUEST_URI
79 */
80 public function testPageUrlWithRoute()
81 {
82 $this->assertEquals(
83 'http://host.tld/picture-wall',
84 page_url(
85 array(
86 'HTTPS' => 'Off',
87 'SERVER_NAME' => 'host.tld',
88 'SERVER_PORT' => '80',
89 'SCRIPT_NAME' => '/index.php',
90 'REQUEST_URI' => '/picture-wall',
91 )
92 )
93 );
94
95 $this->assertEquals(
96 'http://host.tld/admin/picture-wall',
97 page_url(
98 array(
99 'HTTPS' => 'Off',
100 'SERVER_NAME' => 'host.tld',
101 'SERVER_PORT' => '80',
102 'SCRIPT_NAME' => '/admin/index.php',
103 'REQUEST_URI' => '/admin/picture-wall',
104 )
105 )
106 );
107 }
108
109 /**
110 * The route is stored in REQUEST_URI and subfolder
111 */
112 public function testPageUrlWithRouteUnderSubfolder()
113 {
114 $this->assertEquals(
115 'http://host.tld/subfolder/picture-wall',
116 page_url(
117 array(
118 'HTTPS' => 'Off',
119 'SERVER_NAME' => 'host.tld',
120 'SERVER_PORT' => '80',
121 'SCRIPT_NAME' => '/subfolder/index.php',
122 'REQUEST_URI' => '/subfolder/picture-wall',
123 )
124 )
125 );
126
127 $this->assertEquals(
128 'http://host.tld/subfolder/admin/picture-wall',
129 page_url(
130 array(
131 'HTTPS' => 'Off',
132 'SERVER_NAME' => 'host.tld',
133 'SERVER_PORT' => '80',
134 'SCRIPT_NAME' => '/subfolder/admin/index.php',
135 'REQUEST_URI' => '/subfolder/admin/picture-wall',
136 )
137 )
138 );
139 }
74} 140}
diff --git a/tests/http/HttpUtils/IndexUrlTestWithConstant.php b/tests/http/HttpUtils/IndexUrlTestWithConstant.php
new file mode 100644
index 00000000..ecaea724
--- /dev/null
+++ b/tests/http/HttpUtils/IndexUrlTestWithConstant.php
@@ -0,0 +1,51 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Http;
6
7use Shaarli\TestCase;
8
9/**
10 * Test index_url with SHAARLI_ROOT_URL defined to override automatic retrieval.
11 * This should stay in its dedicated class to make sure to not alter other tests of the suite.
12 */
13class IndexUrlTestWithConstant extends TestCase
14{
15 public static function setUpBeforeClass(): void
16 {
17 define('SHAARLI_ROOT_URL', 'http://other-host.tld/subfolder/');
18 }
19
20 /**
21 * The route is stored in REQUEST_URI and subfolder
22 */
23 public function testIndexUrlWithConstantDefined()
24 {
25 $this->assertEquals(
26 'http://other-host.tld/subfolder/',
27 index_url(
28 array(
29 'HTTPS' => 'Off',
30 'SERVER_NAME' => 'host.tld',
31 'SERVER_PORT' => '80',
32 'SCRIPT_NAME' => '/index.php',
33 'REQUEST_URI' => '/picture-wall',
34 )
35 )
36 );
37
38 $this->assertEquals(
39 'http://other-host.tld/subfolder/',
40 index_url(
41 array(
42 'HTTPS' => 'Off',
43 'SERVER_NAME' => 'host.tld',
44 'SERVER_PORT' => '80',
45 'SCRIPT_NAME' => '/admin/index.php',
46 'REQUEST_URI' => '/admin/picture-wall',
47 )
48 )
49 );
50 }
51}
diff --git a/tests/http/HttpUtils/IsHttpsTest.php b/tests/http/HttpUtils/IsHttpsTest.php
index 348956c6..8b3fd93d 100644
--- a/tests/http/HttpUtils/IsHttpsTest.php
+++ b/tests/http/HttpUtils/IsHttpsTest.php
@@ -9,7 +9,7 @@ require_once 'application/http/HttpUtils.php';
9 * 9 *
10 * Test class for is_https() function. 10 * Test class for is_https() function.
11 */ 11 */
12class IsHttpsTest extends \PHPUnit\Framework\TestCase 12class IsHttpsTest extends \Shaarli\TestCase
13{ 13{
14 14
15 /** 15 /**
diff --git a/tests/http/HttpUtils/PageUrlTest.php b/tests/http/HttpUtils/PageUrlTest.php
index f1991716..ebb3e617 100644
--- a/tests/http/HttpUtils/PageUrlTest.php
+++ b/tests/http/HttpUtils/PageUrlTest.php
@@ -10,7 +10,7 @@ require_once 'application/http/HttpUtils.php';
10/** 10/**
11 * Unitary tests for page_url() 11 * Unitary tests for page_url()
12 */ 12 */
13class PageUrlTest extends \PHPUnit\Framework\TestCase 13class PageUrlTest extends \Shaarli\TestCase
14{ 14{
15 /** 15 /**
16 * If on the main page, remove "index.php" from the URL resource 16 * If on the main page, remove "index.php" from the URL resource
diff --git a/tests/http/HttpUtils/ServerUrlTest.php b/tests/http/HttpUtils/ServerUrlTest.php
index 9caf1049..339664e1 100644
--- a/tests/http/HttpUtils/ServerUrlTest.php
+++ b/tests/http/HttpUtils/ServerUrlTest.php
@@ -10,7 +10,7 @@ require_once 'application/http/HttpUtils.php';
10/** 10/**
11 * Unitary tests for server_url() 11 * Unitary tests for server_url()
12 */ 12 */
13class ServerUrlTest extends \PHPUnit\Framework\TestCase 13class ServerUrlTest extends \Shaarli\TestCase
14{ 14{
15 /** 15 /**
16 * Detect if the server uses SSL 16 * Detect if the server uses SSL
diff --git a/tests/http/MetadataRetrieverTest.php b/tests/http/MetadataRetrieverTest.php
new file mode 100644
index 00000000..3c9eaa0e
--- /dev/null
+++ b/tests/http/MetadataRetrieverTest.php
@@ -0,0 +1,154 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Http;
6
7use PHPUnit\Framework\TestCase;
8use Shaarli\Config\ConfigManager;
9
10class MetadataRetrieverTest extends TestCase
11{
12 /** @var MetadataRetriever */
13 protected $retriever;
14
15 /** @var ConfigManager */
16 protected $conf;
17
18 /** @var HttpAccess */
19 protected $httpAccess;
20
21 public function setUp(): void
22 {
23 $this->conf = $this->createMock(ConfigManager::class);
24 $this->httpAccess = $this->createMock(HttpAccess::class);
25 $this->retriever = new MetadataRetriever($this->conf, $this->httpAccess);
26
27 $this->conf->method('get')->willReturnCallback(function (string $param, $default) {
28 return $default === null ? $param : $default;
29 });
30 }
31
32 /**
33 * Test metadata retrieve() with values returned
34 */
35 public function testFullRetrieval(): void
36 {
37 $url = 'https://domain.tld/link';
38 $remoteTitle = 'Remote Title ';
39 $remoteDesc = 'Sometimes the meta description is relevant.';
40 $remoteTags = 'abc def';
41 $remoteCharset = 'utf-8';
42
43 $expectedResult = [
44 'title' => $remoteTitle,
45 'description' => $remoteDesc,
46 'tags' => $remoteTags,
47 ];
48
49 $this->httpAccess
50 ->expects(static::once())
51 ->method('getCurlHeaderCallback')
52 ->willReturnCallback(
53 function (&$charset) use (
54 $remoteCharset
55 ): callable {
56 return function () use (
57 &$charset,
58 $remoteCharset
59 ): void {
60 $charset = $remoteCharset;
61 };
62 }
63 )
64 ;
65 $this->httpAccess
66 ->expects(static::once())
67 ->method('getCurlDownloadCallback')
68 ->willReturnCallback(
69 function (&$charset, &$title, &$description, &$tags) use (
70 $remoteCharset,
71 $remoteTitle,
72 $remoteDesc,
73 $remoteTags
74 ): callable {
75 return function () use (
76 &$charset,
77 &$title,
78 &$description,
79 &$tags,
80 $remoteCharset,
81 $remoteTitle,
82 $remoteDesc,
83 $remoteTags
84 ): void {
85 static::assertSame($remoteCharset, $charset);
86
87 $title = $remoteTitle;
88 $description = $remoteDesc;
89 $tags = $remoteTags;
90 };
91 }
92 )
93 ;
94 $this->httpAccess
95 ->expects(static::once())
96 ->method('getHttpResponse')
97 ->with($url, 30, 4194304)
98 ->willReturnCallback(function($url, $timeout, $maxBytes, $headerCallback, $dlCallback): void {
99 $headerCallback();
100 $dlCallback();
101 })
102 ;
103
104 $result = $this->retriever->retrieve($url);
105
106 static::assertSame($expectedResult, $result);
107 }
108
109 /**
110 * Test metadata retrieve() without any value
111 */
112 public function testEmptyRetrieval(): void
113 {
114 $url = 'https://domain.tld/link';
115
116 $expectedResult = [
117 'title' => null,
118 'description' => null,
119 'tags' => null,
120 ];
121
122 $this->httpAccess
123 ->expects(static::once())
124 ->method('getCurlDownloadCallback')
125 ->willReturnCallback(
126 function (): callable {
127 return function (): void {};
128 }
129 )
130 ;
131 $this->httpAccess
132 ->expects(static::once())
133 ->method('getCurlHeaderCallback')
134 ->willReturnCallback(
135 function (): callable {
136 return function (): void {};
137 }
138 )
139 ;
140 $this->httpAccess
141 ->expects(static::once())
142 ->method('getHttpResponse')
143 ->with($url, 30, 4194304)
144 ->willReturnCallback(function($url, $timeout, $maxBytes, $headerCallback, $dlCallback): void {
145 $headerCallback();
146 $dlCallback();
147 })
148 ;
149
150 $result = $this->retriever->retrieve($url);
151
152 static::assertSame($expectedResult, $result);
153 }
154}
diff --git a/tests/http/UrlTest.php b/tests/http/UrlTest.php
index ae92f73a..c6b39c29 100644
--- a/tests/http/UrlTest.php
+++ b/tests/http/UrlTest.php
@@ -8,7 +8,7 @@ namespace Shaarli\Http;
8/** 8/**
9 * Unitary tests for URL utilities 9 * Unitary tests for URL utilities
10 */ 10 */
11class UrlTest extends \PHPUnit\Framework\TestCase 11class UrlTest extends \Shaarli\TestCase
12{ 12{
13 // base URL for tests 13 // base URL for tests
14 protected static $baseUrl = 'http://domain.tld:3000'; 14 protected static $baseUrl = 'http://domain.tld:3000';
diff --git a/tests/http/UrlUtils/CleanupUrlTest.php b/tests/http/UrlUtils/CleanupUrlTest.php
index 6c4d124b..45690ecf 100644
--- a/tests/http/UrlUtils/CleanupUrlTest.php
+++ b/tests/http/UrlUtils/CleanupUrlTest.php
@@ -7,7 +7,7 @@ namespace Shaarli\Http;
7 7
8require_once 'application/http/UrlUtils.php'; 8require_once 'application/http/UrlUtils.php';
9 9
10class CleanupUrlTest extends \PHPUnit\Framework\TestCase 10class CleanupUrlTest extends \Shaarli\TestCase
11{ 11{
12 /** 12 /**
13 * @var string reference URL 13 * @var string reference URL
diff --git a/tests/http/UrlUtils/GetUrlSchemeTest.php b/tests/http/UrlUtils/GetUrlSchemeTest.php
index 2b97f7be..18a9a5e5 100644
--- a/tests/http/UrlUtils/GetUrlSchemeTest.php
+++ b/tests/http/UrlUtils/GetUrlSchemeTest.php
@@ -7,7 +7,7 @@ namespace Shaarli\Http;
7 7
8require_once 'application/http/UrlUtils.php'; 8require_once 'application/http/UrlUtils.php';
9 9
10class GetUrlSchemeTest extends \PHPUnit\Framework\TestCase 10class GetUrlSchemeTest extends \Shaarli\TestCase
11{ 11{
12 /** 12 /**
13 * Get empty scheme string for empty UrlUtils 13 * Get empty scheme string for empty UrlUtils
diff --git a/tests/http/UrlUtils/UnparseUrlTest.php b/tests/http/UrlUtils/UnparseUrlTest.php
index 040d8c54..5e6246cc 100644
--- a/tests/http/UrlUtils/UnparseUrlTest.php
+++ b/tests/http/UrlUtils/UnparseUrlTest.php
@@ -10,7 +10,7 @@ require_once 'application/http/UrlUtils.php';
10/** 10/**
11 * Unitary tests for unparse_url() 11 * Unitary tests for unparse_url()
12 */ 12 */
13class UnparseUrlTest extends \PHPUnit\Framework\TestCase 13class UnparseUrlTest extends \Shaarli\TestCase
14{ 14{
15 /** 15 /**
16 * Thanks for building nothing 16 * Thanks for building nothing
diff --git a/tests/http/UrlUtils/WhitelistProtocolsTest.php b/tests/http/UrlUtils/WhitelistProtocolsTest.php
index 69512dbd..b8a6baaa 100644
--- a/tests/http/UrlUtils/WhitelistProtocolsTest.php
+++ b/tests/http/UrlUtils/WhitelistProtocolsTest.php
@@ -9,7 +9,7 @@ require_once 'application/http/UrlUtils.php';
9 * 9 *
10 * Test whitelist_protocols() function of UrlUtils. 10 * Test whitelist_protocols() function of UrlUtils.
11 */ 11 */
12class WhitelistProtocolsTest extends \PHPUnit\Framework\TestCase 12class WhitelistProtocolsTest extends \Shaarli\TestCase
13{ 13{
14 /** 14 /**
15 * Test whitelist_protocols() on a note (relative URL). 15 * Test whitelist_protocols() on a note (relative URL).
diff --git a/tests/languages/fr/LanguagesFrTest.php b/tests/languages/fr/LanguagesFrTest.php
index b8b7ca3a..d84feed1 100644
--- a/tests/languages/fr/LanguagesFrTest.php
+++ b/tests/languages/fr/LanguagesFrTest.php
@@ -12,7 +12,7 @@ use Shaarli\Config\ConfigManager;
12 * 12 *
13 * @package Shaarli 13 * @package Shaarli
14 */ 14 */
15class LanguagesFrTest extends \PHPUnit\Framework\TestCase 15class LanguagesFrTest extends \Shaarli\TestCase
16{ 16{
17 /** 17 /**
18 * @var string Config file path (without extension). 18 * @var string Config file path (without extension).
@@ -27,7 +27,7 @@ class LanguagesFrTest extends \PHPUnit\Framework\TestCase
27 /** 27 /**
28 * Init: force French 28 * Init: force French
29 */ 29 */
30 public function setUp() 30 protected function setUp(): void
31 { 31 {
32 $this->conf = new ConfigManager(self::$configFile); 32 $this->conf = new ConfigManager(self::$configFile);
33 $this->conf->set('translation.language', 'fr'); 33 $this->conf->set('translation.language', 'fr');
@@ -36,7 +36,7 @@ class LanguagesFrTest extends \PHPUnit\Framework\TestCase
36 /** 36 /**
37 * Reset the locale since gettext seems to mess with it, making it too long 37 * Reset the locale since gettext seems to mess with it, making it too long
38 */ 38 */
39 public static function tearDownAfterClass() 39 public static function tearDownAfterClass(): void
40 { 40 {
41 if (! empty(getenv('UT_LOCALE'))) { 41 if (! empty(getenv('UT_LOCALE'))) {
42 setlocale(LC_ALL, getenv('UT_LOCALE')); 42 setlocale(LC_ALL, getenv('UT_LOCALE'));
diff --git a/tests/legacy/LegacyControllerTest.php b/tests/legacy/LegacyControllerTest.php
new file mode 100644
index 00000000..1a2549a3
--- /dev/null
+++ b/tests/legacy/LegacyControllerTest.php
@@ -0,0 +1,101 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Legacy;
6
7use Shaarli\Front\Controller\Visitor\FrontControllerMockHelper;
8use Shaarli\TestCase;
9use Slim\Http\Request;
10use Slim\Http\Response;
11
12class LegacyControllerTest extends TestCase
13{
14 use FrontControllerMockHelper;
15
16 /** @var LegacyController */
17 protected $controller;
18
19 public function setUp(): void
20 {
21 $this->createContainer();
22
23 $this->controller = new LegacyController($this->container);
24 }
25
26 /**
27 * @dataProvider getProcessProvider
28 */
29 public function testProcess(string $legacyRoute, array $queryParameters, string $slimRoute, bool $isLoggedIn): void
30 {
31 $request = $this->createMock(Request::class);
32 $request->method('getQueryParams')->willReturn($queryParameters);
33 $request
34 ->method('getParam')
35 ->willReturnCallback(function (string $key) use ($queryParameters): ?string {
36 return $queryParameters[$key] ?? null;
37 })
38 ;
39 $response = new Response();
40
41 $this->container->loginManager->method('isLoggedIn')->willReturn($isLoggedIn);
42
43 $result = $this->controller->process($request, $response, $legacyRoute);
44
45 static::assertSame('/subfolder' . $slimRoute, $result->getHeader('location')[0]);
46 }
47
48 public function testProcessNotFound(): void
49 {
50 $request = $this->createMock(Request::class);
51 $response = new Response();
52
53 $this->expectException(UnknowLegacyRouteException::class);
54
55 $this->controller->process($request, $response, 'nope');
56 }
57
58 /**
59 * @return array[] Parameters:
60 * - string legacyRoute
61 * - array queryParameters
62 * - string slimRoute
63 * - bool isLoggedIn
64 */
65 public function getProcessProvider(): array
66 {
67 return [
68 ['post', [], '/admin/shaare', true],
69 ['post', [], '/login?returnurl=/subfolder/admin/shaare', false],
70 ['post', ['title' => 'test'], '/admin/shaare?title=test', true],
71 ['post', ['title' => 'test'], '/login?returnurl=/subfolder/admin/shaare?title=test', false],
72 ['addlink', [], '/admin/add-shaare', true],
73 ['addlink', [], '/login?returnurl=/subfolder/admin/add-shaare', false],
74 ['login', [], '/login', true],
75 ['login', [], '/login', false],
76 ['logout', [], '/admin/logout', true],
77 ['logout', [], '/admin/logout', false],
78 ['picwall', [], '/picture-wall', false],
79 ['picwall', [], '/picture-wall', true],
80 ['tagcloud', [], '/tags/cloud', false],
81 ['tagcloud', [], '/tags/cloud', true],
82 ['taglist', [], '/tags/list', false],
83 ['taglist', [], '/tags/list', true],
84 ['daily', [], '/daily', false],
85 ['daily', [], '/daily', true],
86 ['daily', ['day' => '123456789', 'discard' => '1'], '/daily?day=123456789', false],
87 ['rss', [], '/feed/rss', false],
88 ['rss', [], '/feed/rss', true],
89 ['rss', ['search' => 'filter123', 'other' => 'param'], '/feed/rss?search=filter123&other=param', false],
90 ['atom', [], '/feed/atom', false],
91 ['atom', [], '/feed/atom', true],
92 ['atom', ['search' => 'filter123', 'other' => 'param'], '/feed/atom?search=filter123&other=param', false],
93 ['opensearch', [], '/open-search', false],
94 ['opensearch', [], '/open-search', true],
95 ['dailyrss', [], '/daily-rss', false],
96 ['dailyrss', [], '/daily-rss', true],
97 ['configure', [], '/login?returnurl=/subfolder/admin/configure', false],
98 ['configure', [], '/admin/configure', true],
99 ];
100 }
101}
diff --git a/tests/legacy/LegacyLinkDBTest.php b/tests/legacy/LegacyLinkDBTest.php
index 17b2b0e6..5c3fd425 100644
--- a/tests/legacy/LegacyLinkDBTest.php
+++ b/tests/legacy/LegacyLinkDBTest.php
@@ -11,7 +11,6 @@ use ReflectionClass;
11use Shaarli; 11use Shaarli;
12use Shaarli\Bookmark\Bookmark; 12use Shaarli\Bookmark\Bookmark;
13 13
14require_once 'application/feed/Cache.php';
15require_once 'application/Utils.php'; 14require_once 'application/Utils.php';
16require_once 'tests/utils/ReferenceLinkDB.php'; 15require_once 'tests/utils/ReferenceLinkDB.php';
17 16
@@ -19,7 +18,7 @@ require_once 'tests/utils/ReferenceLinkDB.php';
19/** 18/**
20 * Unitary tests for LegacyLinkDBTest 19 * Unitary tests for LegacyLinkDBTest
21 */ 20 */
22class LegacyLinkDBTest extends \PHPUnit\Framework\TestCase 21class LegacyLinkDBTest extends \Shaarli\TestCase
23{ 22{
24 // datastore to test write operations 23 // datastore to test write operations
25 protected static $testDatastore = 'sandbox/datastore.php'; 24 protected static $testDatastore = 'sandbox/datastore.php';
@@ -53,7 +52,7 @@ class LegacyLinkDBTest extends \PHPUnit\Framework\TestCase
53 * 52 *
54 * Resets test data for each test 53 * Resets test data for each test
55 */ 54 */
56 protected function setUp() 55 protected function setUp(): void
57 { 56 {
58 if (file_exists(self::$testDatastore)) { 57 if (file_exists(self::$testDatastore)) {
59 unlink(self::$testDatastore); 58 unlink(self::$testDatastore);
@@ -100,12 +99,12 @@ class LegacyLinkDBTest extends \PHPUnit\Framework\TestCase
100 99
101 /** 100 /**
102 * Attempt to instantiate a LinkDB whereas the datastore is not writable 101 * Attempt to instantiate a LinkDB whereas the datastore is not writable
103 *
104 * @expectedException Shaarli\Exceptions\IOException
105 * @expectedExceptionMessageRegExp /Error accessing "null"/
106 */ 102 */
107 public function testConstructDatastoreNotWriteable() 103 public function testConstructDatastoreNotWriteable()
108 { 104 {
105 $this->expectException(\Shaarli\Exceptions\IOException::class);
106 $this->expectExceptionMessageRegExp('/Error accessing "null"/');
107
109 new LegacyLinkDB('null/store.db', false, false); 108 new LegacyLinkDB('null/store.db', false, false);
110 } 109 }
111 110
@@ -258,7 +257,7 @@ class LegacyLinkDBTest extends \PHPUnit\Framework\TestCase
258 $link = self::$publicLinkDB->getLinkFromUrl('http://mediagoblin.org/'); 257 $link = self::$publicLinkDB->getLinkFromUrl('http://mediagoblin.org/');
259 258
260 $this->assertNotEquals(false, $link); 259 $this->assertNotEquals(false, $link);
261 $this->assertContains( 260 $this->assertContainsPolyfill(
262 'A free software media publishing platform', 261 'A free software media publishing platform',
263 $link['description'] 262 $link['description']
264 ); 263 );
@@ -297,6 +296,10 @@ class LegacyLinkDBTest extends \PHPUnit\Framework\TestCase
297 // They need to be grouped with the first case found - order by date DESC: `sTuff`. 296 // They need to be grouped with the first case found - order by date DESC: `sTuff`.
298 'sTuff' => 2, 297 'sTuff' => 2,
299 'ut' => 1, 298 'ut' => 1,
299 'assurance' => 1,
300 'coding-style' => 1,
301 'quality' => 1,
302 'standards' => 1,
300 ), 303 ),
301 self::$publicLinkDB->linksCountPerTag() 304 self::$publicLinkDB->linksCountPerTag()
302 ); 305 );
@@ -325,6 +328,10 @@ class LegacyLinkDBTest extends \PHPUnit\Framework\TestCase
325 'tag3' => 1, 328 'tag3' => 1,
326 'tag4' => 1, 329 'tag4' => 1,
327 'ut' => 1, 330 'ut' => 1,
331 'assurance' => 1,
332 'coding-style' => 1,
333 'quality' => 1,
334 'standards' => 1,
328 ), 335 ),
329 self::$privateLinkDB->linksCountPerTag() 336 self::$privateLinkDB->linksCountPerTag()
330 ); 337 );
@@ -421,22 +428,22 @@ class LegacyLinkDBTest extends \PHPUnit\Framework\TestCase
421 428
422 /** 429 /**
423 * Test filterHash() with an invalid smallhash. 430 * Test filterHash() with an invalid smallhash.
424 *
425 * @expectedException \Shaarli\Bookmark\Exception\BookmarkNotFoundException
426 */ 431 */
427 public function testFilterHashInValid1() 432 public function testFilterHashInValid1()
428 { 433 {
434 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
435
429 $request = 'blabla'; 436 $request = 'blabla';
430 self::$publicLinkDB->filterHash($request); 437 self::$publicLinkDB->filterHash($request);
431 } 438 }
432 439
433 /** 440 /**
434 * Test filterHash() with an empty smallhash. 441 * Test filterHash() with an empty smallhash.
435 *
436 * @expectedException \Shaarli\Bookmark\Exception\BookmarkNotFoundException
437 */ 442 */
438 public function testFilterHashInValid() 443 public function testFilterHashInValid()
439 { 444 {
445 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
446
440 self::$publicLinkDB->filterHash(''); 447 self::$publicLinkDB->filterHash('');
441 } 448 }
442 449
@@ -471,9 +478,9 @@ class LegacyLinkDBTest extends \PHPUnit\Framework\TestCase
471 478
472 $res = $linkDB->renameTag('cartoon', 'Taz'); 479 $res = $linkDB->renameTag('cartoon', 'Taz');
473 $this->assertEquals(3, count($res)); 480 $this->assertEquals(3, count($res));
474 $this->assertContains(' Taz ', $linkDB[4]['tags']); 481 $this->assertContainsPolyfill(' Taz ', $linkDB[4]['tags']);
475 $this->assertContains(' Taz ', $linkDB[1]['tags']); 482 $this->assertContainsPolyfill(' Taz ', $linkDB[1]['tags']);
476 $this->assertContains(' Taz ', $linkDB[0]['tags']); 483 $this->assertContainsPolyfill(' Taz ', $linkDB[0]['tags']);
477 } 484 }
478 485
479 /** 486 /**
@@ -513,7 +520,7 @@ class LegacyLinkDBTest extends \PHPUnit\Framework\TestCase
513 520
514 $res = $linkDB->renameTag('cartoon', null); 521 $res = $linkDB->renameTag('cartoon', null);
515 $this->assertEquals(3, count($res)); 522 $this->assertEquals(3, count($res));
516 $this->assertNotContains('cartoon', $linkDB[4]['tags']); 523 $this->assertNotContainsPolyfill('cartoon', $linkDB[4]['tags']);
517 } 524 }
518 525
519 /** 526 /**
@@ -545,6 +552,10 @@ class LegacyLinkDBTest extends \PHPUnit\Framework\TestCase
545 'tag4' => 1, 552 'tag4' => 1,
546 'ut' => 1, 553 'ut' => 1,
547 'w3c' => 1, 554 'w3c' => 1,
555 'assurance' => 1,
556 'coding-style' => 1,
557 'quality' => 1,
558 'standards' => 1,
548 ]; 559 ];
549 $tags = self::$privateLinkDB->linksCountPerTag(); 560 $tags = self::$privateLinkDB->linksCountPerTag();
550 561
diff --git a/tests/legacy/LegacyLinkFilterTest.php b/tests/legacy/LegacyLinkFilterTest.php
index ba9ec529..45d7754d 100644
--- a/tests/legacy/LegacyLinkFilterTest.php
+++ b/tests/legacy/LegacyLinkFilterTest.php
@@ -10,7 +10,7 @@ use Shaarli\Legacy\LegacyLinkFilter;
10/** 10/**
11 * Class LegacyLinkFilterTest. 11 * Class LegacyLinkFilterTest.
12 */ 12 */
13class LegacyLinkFilterTest extends \PHPUnit\Framework\TestCase 13class LegacyLinkFilterTest extends \Shaarli\TestCase
14{ 14{
15 /** 15 /**
16 * @var string Test datastore path. 16 * @var string Test datastore path.
@@ -34,7 +34,7 @@ class LegacyLinkFilterTest extends \PHPUnit\Framework\TestCase
34 /** 34 /**
35 * Instantiate linkFilter with ReferenceLinkDB data. 35 * Instantiate linkFilter with ReferenceLinkDB data.
36 */ 36 */
37 public static function setUpBeforeClass() 37 public static function setUpBeforeClass(): void
38 { 38 {
39 self::$refDB = new ReferenceLinkDB(true); 39 self::$refDB = new ReferenceLinkDB(true);
40 self::$refDB->write(self::$testDatastore); 40 self::$refDB->write(self::$testDatastore);
@@ -197,21 +197,23 @@ class LegacyLinkFilterTest extends \PHPUnit\Framework\TestCase
197 197
198 /** 198 /**
199 * Use an invalid date format 199 * Use an invalid date format
200 * @expectedException Exception
201 * @expectedExceptionMessageRegExp /Invalid date format/
202 */ 200 */
203 public function testFilterInvalidDayWithChars() 201 public function testFilterInvalidDayWithChars()
204 { 202 {
203 $this->expectException(\Exception::class);
204 $this->expectExceptionMessageRegExp('/Invalid date format/');
205
205 self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, 'Rainy day, dream away'); 206 self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, 'Rainy day, dream away');
206 } 207 }
207 208
208 /** 209 /**
209 * Use an invalid date format 210 * Use an invalid date format
210 * @expectedException Exception
211 * @expectedExceptionMessageRegExp /Invalid date format/
212 */ 211 */
213 public function testFilterInvalidDayDigits() 212 public function testFilterInvalidDayDigits()
214 { 213 {
214 $this->expectException(\Exception::class);
215 $this->expectExceptionMessageRegExp('/Invalid date format/');
216
215 self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, '20'); 217 self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, '20');
216 } 218 }
217 219
@@ -235,11 +237,11 @@ class LegacyLinkFilterTest extends \PHPUnit\Framework\TestCase
235 237
236 /** 238 /**
237 * No link for this hash 239 * No link for this hash
238 *
239 * @expectedException \Shaarli\Bookmark\Exception\BookmarkNotFoundException
240 */ 240 */
241 public function testFilterUnknownSmallHash() 241 public function testFilterUnknownSmallHash()
242 { 242 {
243 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
244
243 self::$linkFilter->filter(LegacyLinkFilter::$FILTER_HASH, 'Iblaah'); 245 self::$linkFilter->filter(LegacyLinkFilter::$FILTER_HASH, 'Iblaah');
244 } 246 }
245 247
diff --git a/tests/legacy/LegacyUpdaterTest.php b/tests/legacy/LegacyUpdaterTest.php
index 7c429811..f7391b86 100644
--- a/tests/legacy/LegacyUpdaterTest.php
+++ b/tests/legacy/LegacyUpdaterTest.php
@@ -20,7 +20,7 @@ require_once 'inc/rain.tpl.class.php';
20 * Class UpdaterTest. 20 * Class UpdaterTest.
21 * Runs unit tests against the updater class. 21 * Runs unit tests against the updater class.
22 */ 22 */
23class LegacyUpdaterTest extends \PHPUnit\Framework\TestCase 23class LegacyUpdaterTest extends \Shaarli\TestCase
24{ 24{
25 /** 25 /**
26 * @var string Path to test datastore. 26 * @var string Path to test datastore.
@@ -40,7 +40,7 @@ class LegacyUpdaterTest extends \PHPUnit\Framework\TestCase
40 /** 40 /**
41 * Executed before each test. 41 * Executed before each test.
42 */ 42 */
43 public function setUp() 43 protected function setUp(): void
44 { 44 {
45 copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php'); 45 copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php');
46 $this->conf = new ConfigManager(self::$configFile); 46 $this->conf = new ConfigManager(self::$configFile);
@@ -80,23 +80,23 @@ class LegacyUpdaterTest extends \PHPUnit\Framework\TestCase
80 80
81 /** 81 /**
82 * Test errors in UpdaterUtils::write_updates_file(): empty updates file. 82 * Test errors in UpdaterUtils::write_updates_file(): empty updates file.
83 *
84 * @expectedException Exception
85 * @expectedExceptionMessageRegExp /Updates file path is not set(.*)/
86 */ 83 */
87 public function testWriteEmptyUpdatesFile() 84 public function testWriteEmptyUpdatesFile()
88 { 85 {
86 $this->expectException(\Exception::class);
87 $this->expectExceptionMessageRegExp('/Updates file path is not set(.*)/');
88
89 UpdaterUtils::write_updates_file('', array('test')); 89 UpdaterUtils::write_updates_file('', array('test'));
90 } 90 }
91 91
92 /** 92 /**
93 * Test errors in UpdaterUtils::write_updates_file(): not writable updates file. 93 * Test errors in UpdaterUtils::write_updates_file(): not writable updates file.
94 *
95 * @expectedException Exception
96 * @expectedExceptionMessageRegExp /Unable to write(.*)/
97 */ 94 */
98 public function testWriteUpdatesFileNotWritable() 95 public function testWriteUpdatesFileNotWritable()
99 { 96 {
97 $this->expectException(\Exception::class);
98 $this->expectExceptionMessageRegExp('/Unable to write(.*)/');
99
100 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt'; 100 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt';
101 touch($updatesFile); 101 touch($updatesFile);
102 chmod($updatesFile, 0444); 102 chmod($updatesFile, 0444);
@@ -161,11 +161,11 @@ class LegacyUpdaterTest extends \PHPUnit\Framework\TestCase
161 161
162 /** 162 /**
163 * Test Update failed. 163 * Test Update failed.
164 *
165 * @expectedException \Exception
166 */ 164 */
167 public function testUpdateFailed() 165 public function testUpdateFailed()
168 { 166 {
167 $this->expectException(\Exception::class);
168
169 $updates = array( 169 $updates = array(
170 'updateMethodDummy1', 170 'updateMethodDummy1',
171 'updateMethodDummy2', 171 'updateMethodDummy2',
@@ -723,7 +723,7 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
723 $this->assertEquals(\Shaarli\Thumbnailer::MODE_ALL, $this->conf->get('thumbnails.mode')); 723 $this->assertEquals(\Shaarli\Thumbnailer::MODE_ALL, $this->conf->get('thumbnails.mode'));
724 $this->assertEquals(125, $this->conf->get('thumbnails.width')); 724 $this->assertEquals(125, $this->conf->get('thumbnails.width'));
725 $this->assertEquals(90, $this->conf->get('thumbnails.height')); 725 $this->assertEquals(90, $this->conf->get('thumbnails.height'));
726 $this->assertContains('You have enabled or changed thumbnails', $_SESSION['warnings'][0]); 726 $this->assertContainsPolyfill('You have enabled or changed thumbnails', $_SESSION['warnings'][0]);
727 } 727 }
728 728
729 /** 729 /**
@@ -754,7 +754,7 @@ $GLOBALS[\'privateLinkByDefault\'] = true;';
754 if (isset($_SESSION['warnings'])) { 754 if (isset($_SESSION['warnings'])) {
755 unset($_SESSION['warnings']); 755 unset($_SESSION['warnings']);
756 } 756 }
757 757
758 $updater = new LegacyUpdater([], [], $this->conf, true, $_SESSION); 758 $updater = new LegacyUpdater([], [], $this->conf, true, $_SESSION);
759 $this->assertTrue($updater->updateMethodWebThumbnailer()); 759 $this->assertTrue($updater->updateMethodWebThumbnailer());
760 $this->assertFalse($this->conf->exists('thumbnail')); 760 $this->assertFalse($this->conf->exists('thumbnail'));
diff --git a/tests/netscape/BookmarkExportTest.php b/tests/netscape/BookmarkExportTest.php
index 6c948bba..ad288f78 100644
--- a/tests/netscape/BookmarkExportTest.php
+++ b/tests/netscape/BookmarkExportTest.php
@@ -1,19 +1,21 @@
1<?php 1<?php
2
2namespace Shaarli\Netscape; 3namespace Shaarli\Netscape;
3 4
5use malkusch\lock\mutex\NoMutex;
4use Shaarli\Bookmark\BookmarkFileService; 6use Shaarli\Bookmark\BookmarkFileService;
5use Shaarli\Bookmark\LinkDB;
6use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
7use Shaarli\Formatter\FormatterFactory;
8use Shaarli\Formatter\BookmarkFormatter; 8use Shaarli\Formatter\BookmarkFormatter;
9use Shaarli\Formatter\FormatterFactory;
9use Shaarli\History; 10use Shaarli\History;
11use Shaarli\TestCase;
10 12
11require_once 'tests/utils/ReferenceLinkDB.php'; 13require_once 'tests/utils/ReferenceLinkDB.php';
12 14
13/** 15/**
14 * Netscape bookmark export 16 * Netscape bookmark export
15 */ 17 */
16class BookmarkExportTest extends \PHPUnit\Framework\TestCase 18class BookmarkExportTest extends TestCase
17{ 19{
18 /** 20 /**
19 * @var string datastore to test write operations 21 * @var string datastore to test write operations
@@ -21,6 +23,11 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
21 protected static $testDatastore = 'sandbox/datastore.php'; 23 protected static $testDatastore = 'sandbox/datastore.php';
22 24
23 /** 25 /**
26 * @var ConfigManager instance.
27 */
28 protected static $conf;
29
30 /**
24 * @var \ReferenceLinkDB instance. 31 * @var \ReferenceLinkDB instance.
25 */ 32 */
26 protected static $refDb = null; 33 protected static $refDb = null;
@@ -36,29 +43,49 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
36 protected static $formatter; 43 protected static $formatter;
37 44
38 /** 45 /**
46 * @var History instance
47 */
48 protected static $history;
49
50 /**
51 * @var NetscapeBookmarkUtils
52 */
53 protected $netscapeBookmarkUtils;
54
55 /**
39 * Instantiate reference data 56 * Instantiate reference data
40 */ 57 */
41 public static function setUpBeforeClass() 58 public static function setUpBeforeClass(): void
59 {
60 $mutex = new NoMutex();
61 static::$conf = new ConfigManager('tests/utils/config/configJson');
62 static::$conf->set('resource.datastore', static::$testDatastore);
63 static::$refDb = new \ReferenceLinkDB();
64 static::$refDb->write(static::$testDatastore);
65 static::$history = new History('sandbox/history.php');
66 static::$bookmarkService = new BookmarkFileService(static::$conf, static::$history, $mutex, true);
67 $factory = new FormatterFactory(static::$conf, true);
68 static::$formatter = $factory->getFormatter('raw');
69 }
70
71 public function setUp(): void
42 { 72 {
43 $conf = new ConfigManager('tests/utils/config/configJson'); 73 $this->netscapeBookmarkUtils = new NetscapeBookmarkUtils(
44 $conf->set('resource.datastore', self::$testDatastore); 74 static::$bookmarkService,
45 self::$refDb = new \ReferenceLinkDB(); 75 static::$conf,
46 self::$refDb->write(self::$testDatastore); 76 static::$history
47 $history = new History('sandbox/history.php'); 77 );
48 self::$bookmarkService = new BookmarkFileService($conf, $history, true);
49 $factory = new FormatterFactory($conf, true);
50 self::$formatter = $factory->getFormatter('raw');
51 } 78 }
52 79
53 /** 80 /**
54 * Attempt to export an invalid link selection 81 * Attempt to export an invalid link selection
55 * @expectedException Exception
56 * @expectedExceptionMessageRegExp /Invalid export selection/
57 */ 82 */
58 public function testFilterAndFormatInvalid() 83 public function testFilterAndFormatInvalid()
59 { 84 {
60 NetscapeBookmarkUtils::filterAndFormat( 85 $this->expectException(\Exception::class);
61 self::$bookmarkService, 86 $this->expectExceptionMessageRegExp('/Invalid export selection/');
87
88 $this->netscapeBookmarkUtils->filterAndFormat(
62 self::$formatter, 89 self::$formatter,
63 'derp', 90 'derp',
64 false, 91 false,
@@ -71,8 +98,7 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
71 */ 98 */
72 public function testFilterAndFormatAll() 99 public function testFilterAndFormatAll()
73 { 100 {
74 $links = NetscapeBookmarkUtils::filterAndFormat( 101 $links = $this->netscapeBookmarkUtils->filterAndFormat(
75 self::$bookmarkService,
76 self::$formatter, 102 self::$formatter,
77 'all', 103 'all',
78 false, 104 false,
@@ -97,8 +123,7 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
97 */ 123 */
98 public function testFilterAndFormatPrivate() 124 public function testFilterAndFormatPrivate()
99 { 125 {
100 $links = NetscapeBookmarkUtils::filterAndFormat( 126 $links = $this->netscapeBookmarkUtils->filterAndFormat(
101 self::$bookmarkService,
102 self::$formatter, 127 self::$formatter,
103 'private', 128 'private',
104 false, 129 false,
@@ -123,8 +148,7 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
123 */ 148 */
124 public function testFilterAndFormatPublic() 149 public function testFilterAndFormatPublic()
125 { 150 {
126 $links = NetscapeBookmarkUtils::filterAndFormat( 151 $links = $this->netscapeBookmarkUtils->filterAndFormat(
127 self::$bookmarkService,
128 self::$formatter, 152 self::$formatter,
129 'public', 153 'public',
130 false, 154 false,
@@ -149,15 +173,14 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
149 */ 173 */
150 public function testFilterAndFormatDoNotPrependNoteUrl() 174 public function testFilterAndFormatDoNotPrependNoteUrl()
151 { 175 {
152 $links = NetscapeBookmarkUtils::filterAndFormat( 176 $links = $this->netscapeBookmarkUtils->filterAndFormat(
153 self::$bookmarkService,
154 self::$formatter, 177 self::$formatter,
155 'public', 178 'public',
156 false, 179 false,
157 '' 180 ''
158 ); 181 );
159 $this->assertEquals( 182 $this->assertEquals(
160 '?WDWyig', 183 '/shaare/WDWyig',
161 $links[2]['url'] 184 $links[2]['url']
162 ); 185 );
163 } 186 }
@@ -168,15 +191,14 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
168 public function testFilterAndFormatPrependNoteUrl() 191 public function testFilterAndFormatPrependNoteUrl()
169 { 192 {
170 $indexUrl = 'http://localhost:7469/shaarli/'; 193 $indexUrl = 'http://localhost:7469/shaarli/';
171 $links = NetscapeBookmarkUtils::filterAndFormat( 194 $links = $this->netscapeBookmarkUtils->filterAndFormat(
172 self::$bookmarkService,
173 self::$formatter, 195 self::$formatter,
174 'public', 196 'public',
175 true, 197 true,
176 $indexUrl 198 $indexUrl
177 ); 199 );
178 $this->assertEquals( 200 $this->assertEquals(
179 $indexUrl . '?WDWyig', 201 $indexUrl . 'shaare/WDWyig',
180 $links[2]['url'] 202 $links[2]['url']
181 ); 203 );
182 } 204 }
diff --git a/tests/netscape/BookmarkImportTest.php b/tests/netscape/BookmarkImportTest.php
index fef7f6d1..c526d5c8 100644
--- a/tests/netscape/BookmarkImportTest.php
+++ b/tests/netscape/BookmarkImportTest.php
@@ -1,29 +1,32 @@
1<?php 1<?php
2
2namespace Shaarli\Netscape; 3namespace Shaarli\Netscape;
3 4
4use DateTime; 5use DateTime;
6use malkusch\lock\mutex\NoMutex;
7use Psr\Http\Message\UploadedFileInterface;
5use Shaarli\Bookmark\Bookmark; 8use Shaarli\Bookmark\Bookmark;
6use Shaarli\Bookmark\BookmarkFilter;
7use Shaarli\Bookmark\BookmarkFileService; 9use Shaarli\Bookmark\BookmarkFileService;
8use Shaarli\Bookmark\LinkDB; 10use Shaarli\Bookmark\BookmarkFilter;
9use Shaarli\Config\ConfigManager; 11use Shaarli\Config\ConfigManager;
10use Shaarli\History; 12use Shaarli\History;
13use Shaarli\TestCase;
14use Slim\Http\UploadedFile;
11 15
12/** 16/**
13 * Utility function to load a file's metadata in a $_FILES-like array 17 * Utility function to load a file's metadata in a $_FILES-like array
14 * 18 *
15 * @param string $filename Basename of the file 19 * @param string $filename Basename of the file
16 * 20 *
17 * @return array A $_FILES-like array 21 * @return UploadedFileInterface Upload file in PSR-7 compatible object
18 */ 22 */
19function file2array($filename) 23function file2array($filename)
20{ 24{
21 return array( 25 return new UploadedFile(
22 'filetoupload' => array( 26 __DIR__ . '/input/' . $filename,
23 'name' => $filename, 27 $filename,
24 'tmp_name' => __DIR__ . '/input/' . $filename, 28 null,
25 'size' => filesize(__DIR__ . '/input/' . $filename) 29 filesize(__DIR__ . '/input/' . $filename)
26 )
27 ); 30 );
28} 31}
29 32
@@ -31,7 +34,7 @@ function file2array($filename)
31/** 34/**
32 * Netscape bookmark import 35 * Netscape bookmark import
33 */ 36 */
34class BookmarkImportTest extends \PHPUnit\Framework\TestCase 37class BookmarkImportTest extends TestCase
35{ 38{
36 /** 39 /**
37 * @var string datastore to test write operations 40 * @var string datastore to test write operations
@@ -64,11 +67,16 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
64 protected $history; 67 protected $history;
65 68
66 /** 69 /**
70 * @var NetscapeBookmarkUtils
71 */
72 protected $netscapeBookmarkUtils;
73
74 /**
67 * @var string Save the current timezone. 75 * @var string Save the current timezone.
68 */ 76 */
69 protected static $defaultTimeZone; 77 protected static $defaultTimeZone;
70 78
71 public static function setUpBeforeClass() 79 public static function setUpBeforeClass(): void
72 { 80 {
73 self::$defaultTimeZone = date_default_timezone_get(); 81 self::$defaultTimeZone = date_default_timezone_get();
74 // Timezone without DST for test consistency 82 // Timezone without DST for test consistency
@@ -78,8 +86,9 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
78 /** 86 /**
79 * Resets test data before each test 87 * Resets test data before each test
80 */ 88 */
81 protected function setUp() 89 protected function setUp(): void
82 { 90 {
91 $mutex = new NoMutex();
83 if (file_exists(self::$testDatastore)) { 92 if (file_exists(self::$testDatastore)) {
84 unlink(self::$testDatastore); 93 unlink(self::$testDatastore);
85 } 94 }
@@ -90,18 +99,19 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
90 $this->conf->set('resource.page_cache', $this->pagecache); 99 $this->conf->set('resource.page_cache', $this->pagecache);
91 $this->conf->set('resource.datastore', self::$testDatastore); 100 $this->conf->set('resource.datastore', self::$testDatastore);
92 $this->history = new History(self::$historyFilePath); 101 $this->history = new History(self::$historyFilePath);
93 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 102 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $mutex, true);
103 $this->netscapeBookmarkUtils = new NetscapeBookmarkUtils($this->bookmarkService, $this->conf, $this->history);
94 } 104 }
95 105
96 /** 106 /**
97 * Delete history file. 107 * Delete history file.
98 */ 108 */
99 public function tearDown() 109 protected function tearDown(): void
100 { 110 {
101 @unlink(self::$historyFilePath); 111 @unlink(self::$historyFilePath);
102 } 112 }
103 113
104 public static function tearDownAfterClass() 114 public static function tearDownAfterClass(): void
105 { 115 {
106 date_default_timezone_set(self::$defaultTimeZone); 116 date_default_timezone_set(self::$defaultTimeZone);
107 } 117 }
@@ -115,7 +125,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
115 $this->assertEquals( 125 $this->assertEquals(
116 'File empty.htm (0 bytes) has an unknown file format.' 126 'File empty.htm (0 bytes) has an unknown file format.'
117 .' Nothing was imported.', 127 .' Nothing was imported.',
118 NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history) 128 $this->netscapeBookmarkUtils->import(null, $files)
119 ); 129 );
120 $this->assertEquals(0, $this->bookmarkService->count()); 130 $this->assertEquals(0, $this->bookmarkService->count());
121 } 131 }
@@ -128,7 +138,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
128 $files = file2array('no_doctype.htm'); 138 $files = file2array('no_doctype.htm');
129 $this->assertEquals( 139 $this->assertEquals(
130 'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.', 140 'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.',
131 NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history) 141 $this->netscapeBookmarkUtils->import(null, $files)
132 ); 142 );
133 $this->assertEquals(0, $this->bookmarkService->count()); 143 $this->assertEquals(0, $this->bookmarkService->count());
134 } 144 }
@@ -142,7 +152,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
142 $this->assertStringMatchesFormat( 152 $this->assertStringMatchesFormat(
143 'File lowercase_doctype.htm (386 bytes) was successfully processed in %d seconds:' 153 'File lowercase_doctype.htm (386 bytes) was successfully processed in %d seconds:'
144 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 154 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
145 NetscapeBookmarkUtils::import(null, $files, $this->bookmarkService, $this->conf, $this->history) 155 $this->netscapeBookmarkUtils->import(null, $files)
146 ); 156 );
147 $this->assertEquals(2, $this->bookmarkService->count()); 157 $this->assertEquals(2, $this->bookmarkService->count());
148 } 158 }
@@ -157,7 +167,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
157 $this->assertStringMatchesFormat( 167 $this->assertStringMatchesFormat(
158 'File internet_explorer_encoding.htm (356 bytes) was successfully processed in %d seconds:' 168 'File internet_explorer_encoding.htm (356 bytes) was successfully processed in %d seconds:'
159 .' 1 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 169 .' 1 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
160 NetscapeBookmarkUtils::import([], $files, $this->bookmarkService, $this->conf, $this->history) 170 $this->netscapeBookmarkUtils->import([], $files)
161 ); 171 );
162 $this->assertEquals(1, $this->bookmarkService->count()); 172 $this->assertEquals(1, $this->bookmarkService->count());
163 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 173 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -185,7 +195,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
185 $this->assertStringMatchesFormat( 195 $this->assertStringMatchesFormat(
186 'File netscape_nested.htm (1337 bytes) was successfully processed in %d seconds:' 196 'File netscape_nested.htm (1337 bytes) was successfully processed in %d seconds:'
187 .' 8 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 197 .' 8 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
188 NetscapeBookmarkUtils::import([], $files, $this->bookmarkService, $this->conf, $this->history) 198 $this->netscapeBookmarkUtils->import([], $files)
189 ); 199 );
190 $this->assertEquals(8, $this->bookmarkService->count()); 200 $this->assertEquals(8, $this->bookmarkService->count());
191 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 201 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -306,7 +316,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
306 $this->assertStringMatchesFormat( 316 $this->assertStringMatchesFormat(
307 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 317 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
308 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 318 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
309 NetscapeBookmarkUtils::import([], $files, $this->bookmarkService, $this->conf, $this->history) 319 $this->netscapeBookmarkUtils->import([], $files)
310 ); 320 );
311 321
312 $this->assertEquals(2, $this->bookmarkService->count()); 322 $this->assertEquals(2, $this->bookmarkService->count());
@@ -349,7 +359,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
349 $this->assertStringMatchesFormat( 359 $this->assertStringMatchesFormat(
350 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 360 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
351 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 361 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
352 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history) 362 $this->netscapeBookmarkUtils->import($post, $files)
353 ); 363 );
354 364
355 $this->assertEquals(2, $this->bookmarkService->count()); 365 $this->assertEquals(2, $this->bookmarkService->count());
@@ -392,7 +402,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
392 $this->assertStringMatchesFormat( 402 $this->assertStringMatchesFormat(
393 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 403 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
394 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 404 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
395 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history) 405 $this->netscapeBookmarkUtils->import($post, $files)
396 ); 406 );
397 $this->assertEquals(2, $this->bookmarkService->count()); 407 $this->assertEquals(2, $this->bookmarkService->count());
398 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 408 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -410,7 +420,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
410 $this->assertStringMatchesFormat( 420 $this->assertStringMatchesFormat(
411 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 421 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
412 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 422 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
413 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history) 423 $this->netscapeBookmarkUtils->import($post, $files)
414 ); 424 );
415 $this->assertEquals(2, $this->bookmarkService->count()); 425 $this->assertEquals(2, $this->bookmarkService->count());
416 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 426 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -430,7 +440,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
430 $this->assertStringMatchesFormat( 440 $this->assertStringMatchesFormat(
431 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 441 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
432 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 442 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
433 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history) 443 $this->netscapeBookmarkUtils->import($post, $files)
434 ); 444 );
435 $this->assertEquals(2, $this->bookmarkService->count()); 445 $this->assertEquals(2, $this->bookmarkService->count());
436 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 446 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -445,7 +455,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
445 $this->assertStringMatchesFormat( 455 $this->assertStringMatchesFormat(
446 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 456 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
447 .' 2 bookmarks imported, 2 bookmarks overwritten, 0 bookmarks skipped.', 457 .' 2 bookmarks imported, 2 bookmarks overwritten, 0 bookmarks skipped.',
448 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history) 458 $this->netscapeBookmarkUtils->import($post, $files)
449 ); 459 );
450 $this->assertEquals(2, $this->bookmarkService->count()); 460 $this->assertEquals(2, $this->bookmarkService->count());
451 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 461 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -465,7 +475,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
465 $this->assertStringMatchesFormat( 475 $this->assertStringMatchesFormat(
466 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 476 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
467 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 477 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
468 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history) 478 $this->netscapeBookmarkUtils->import($post, $files)
469 ); 479 );
470 $this->assertEquals(2, $this->bookmarkService->count()); 480 $this->assertEquals(2, $this->bookmarkService->count());
471 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 481 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -480,7 +490,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
480 $this->assertStringMatchesFormat( 490 $this->assertStringMatchesFormat(
481 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 491 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
482 .' 2 bookmarks imported, 2 bookmarks overwritten, 0 bookmarks skipped.', 492 .' 2 bookmarks imported, 2 bookmarks overwritten, 0 bookmarks skipped.',
483 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history) 493 $this->netscapeBookmarkUtils->import($post, $files)
484 ); 494 );
485 $this->assertEquals(2, $this->bookmarkService->count()); 495 $this->assertEquals(2, $this->bookmarkService->count());
486 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 496 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -498,7 +508,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
498 $this->assertStringMatchesFormat( 508 $this->assertStringMatchesFormat(
499 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 509 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
500 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 510 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
501 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history) 511 $this->netscapeBookmarkUtils->import($post, $files)
502 ); 512 );
503 $this->assertEquals(2, $this->bookmarkService->count()); 513 $this->assertEquals(2, $this->bookmarkService->count());
504 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 514 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -508,7 +518,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
508 $this->assertStringMatchesFormat( 518 $this->assertStringMatchesFormat(
509 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 519 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
510 .' 0 bookmarks imported, 0 bookmarks overwritten, 2 bookmarks skipped.', 520 .' 0 bookmarks imported, 0 bookmarks overwritten, 2 bookmarks skipped.',
511 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history) 521 $this->netscapeBookmarkUtils->import($post, $files)
512 ); 522 );
513 $this->assertEquals(2, $this->bookmarkService->count()); 523 $this->assertEquals(2, $this->bookmarkService->count());
514 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 524 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -527,7 +537,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
527 $this->assertStringMatchesFormat( 537 $this->assertStringMatchesFormat(
528 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 538 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
529 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 539 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
530 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history) 540 $this->netscapeBookmarkUtils->import($post, $files)
531 ); 541 );
532 $this->assertEquals(2, $this->bookmarkService->count()); 542 $this->assertEquals(2, $this->bookmarkService->count());
533 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 543 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -548,7 +558,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
548 $this->assertStringMatchesFormat( 558 $this->assertStringMatchesFormat(
549 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 559 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
550 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 560 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
551 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history) 561 $this->netscapeBookmarkUtils->import($post, $files)
552 ); 562 );
553 $this->assertEquals(2, $this->bookmarkService->count()); 563 $this->assertEquals(2, $this->bookmarkService->count());
554 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 564 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -573,7 +583,7 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
573 $this->assertStringMatchesFormat( 583 $this->assertStringMatchesFormat(
574 'File same_date.htm (453 bytes) was successfully processed in %d seconds:' 584 'File same_date.htm (453 bytes) was successfully processed in %d seconds:'
575 .' 3 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.', 585 .' 3 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
576 NetscapeBookmarkUtils::import(array(), $files, $this->bookmarkService, $this->conf, $this->history) 586 $this->netscapeBookmarkUtils->import(array(), $files)
577 ); 587 );
578 $this->assertEquals(3, $this->bookmarkService->count()); 588 $this->assertEquals(3, $this->bookmarkService->count());
579 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE)); 589 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
@@ -589,14 +599,14 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
589 'overwrite' => 'true', 599 'overwrite' => 'true',
590 ]; 600 ];
591 $files = file2array('netscape_basic.htm'); 601 $files = file2array('netscape_basic.htm');
592 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history); 602 $this->netscapeBookmarkUtils->import($post, $files);
593 $history = $this->history->getHistory(); 603 $history = $this->history->getHistory();
594 $this->assertEquals(1, count($history)); 604 $this->assertEquals(1, count($history));
595 $this->assertEquals(History::IMPORT, $history[0]['event']); 605 $this->assertEquals(History::IMPORT, $history[0]['event']);
596 $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']); 606 $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']);
597 607
598 // re-import as private, enable overwriting 608 // re-import as private, enable overwriting
599 NetscapeBookmarkUtils::import($post, $files, $this->bookmarkService, $this->conf, $this->history); 609 $this->netscapeBookmarkUtils->import($post, $files);
600 $history = $this->history->getHistory(); 610 $history = $this->history->getHistory();
601 $this->assertEquals(2, count($history)); 611 $this->assertEquals(2, count($history));
602 $this->assertEquals(History::IMPORT, $history[0]['event']); 612 $this->assertEquals(History::IMPORT, $history[0]['event']);
diff --git a/tests/plugins/PluginAddlinkTest.php b/tests/plugins/PluginAddlinkTest.php
index d052f8b9..a3ec9fc9 100644
--- a/tests/plugins/PluginAddlinkTest.php
+++ b/tests/plugins/PluginAddlinkTest.php
@@ -2,19 +2,19 @@
2namespace Shaarli\Plugin\Addlink; 2namespace Shaarli\Plugin\Addlink;
3 3
4use Shaarli\Plugin\PluginManager; 4use Shaarli\Plugin\PluginManager;
5use Shaarli\Router; 5use Shaarli\Render\TemplatePage;
6 6
7require_once 'plugins/addlink_toolbar/addlink_toolbar.php'; 7require_once 'plugins/addlink_toolbar/addlink_toolbar.php';
8 8
9/** 9/**
10 * Unit test for the Addlink toolbar plugin 10 * Unit test for the Addlink toolbar plugin
11 */ 11 */
12class PluginAddlinkTest extends \PHPUnit\Framework\TestCase 12class PluginAddlinkTest extends \Shaarli\TestCase
13{ 13{
14 /** 14 /**
15 * Reset plugin path. 15 * Reset plugin path.
16 */ 16 */
17 public function setUp() 17 protected function setUp(): void
18 { 18 {
19 PluginManager::$PLUGINS_PATH = 'plugins'; 19 PluginManager::$PLUGINS_PATH = 'plugins';
20 } 20 }
@@ -26,8 +26,9 @@ class PluginAddlinkTest extends \PHPUnit\Framework\TestCase
26 { 26 {
27 $str = 'stuff'; 27 $str = 'stuff';
28 $data = array($str => $str); 28 $data = array($str => $str);
29 $data['_PAGE_'] = Router::$PAGE_LINKLIST; 29 $data['_PAGE_'] = TemplatePage::LINKLIST;
30 $data['_LOGGEDIN_'] = true; 30 $data['_LOGGEDIN_'] = true;
31 $data['_BASE_PATH_'] = '/subfolder';
31 32
32 $data = hook_addlink_toolbar_render_header($data); 33 $data = hook_addlink_toolbar_render_header($data);
33 $this->assertEquals($str, $data[$str]); 34 $this->assertEquals($str, $data[$str]);
@@ -36,6 +37,8 @@ class PluginAddlinkTest extends \PHPUnit\Framework\TestCase
36 $data = array($str => $str); 37 $data = array($str => $str);
37 $data['_PAGE_'] = $str; 38 $data['_PAGE_'] = $str;
38 $data['_LOGGEDIN_'] = true; 39 $data['_LOGGEDIN_'] = true;
40 $data['_BASE_PATH_'] = '/subfolder';
41
39 $data = hook_addlink_toolbar_render_header($data); 42 $data = hook_addlink_toolbar_render_header($data);
40 $this->assertEquals($str, $data[$str]); 43 $this->assertEquals($str, $data[$str]);
41 $this->assertArrayNotHasKey('fields_toolbar', $data); 44 $this->assertArrayNotHasKey('fields_toolbar', $data);
@@ -48,8 +51,9 @@ class PluginAddlinkTest extends \PHPUnit\Framework\TestCase
48 { 51 {
49 $str = 'stuff'; 52 $str = 'stuff';
50 $data = array($str => $str); 53 $data = array($str => $str);
51 $data['_PAGE_'] = Router::$PAGE_LINKLIST; 54 $data['_PAGE_'] = TemplatePage::LINKLIST;
52 $data['_LOGGEDIN_'] = false; 55 $data['_LOGGEDIN_'] = false;
56 $data['_BASE_PATH_'] = '/subfolder';
53 57
54 $data = hook_addlink_toolbar_render_header($data); 58 $data = hook_addlink_toolbar_render_header($data);
55 $this->assertEquals($str, $data[$str]); 59 $this->assertEquals($str, $data[$str]);
diff --git a/tests/plugins/PluginArchiveorgTest.php b/tests/plugins/PluginArchiveorgTest.php
index b9a67adb..467dc3d0 100644
--- a/tests/plugins/PluginArchiveorgTest.php
+++ b/tests/plugins/PluginArchiveorgTest.php
@@ -1,4 +1,5 @@
1<?php 1<?php
2
2namespace Shaarli\Plugin\Archiveorg; 3namespace Shaarli\Plugin\Archiveorg;
3 4
4/** 5/**
@@ -6,6 +7,7 @@ namespace Shaarli\Plugin\Archiveorg;
6 */ 7 */
7 8
8use Shaarli\Plugin\PluginManager; 9use Shaarli\Plugin\PluginManager;
10use Shaarli\TestCase;
9 11
10require_once 'plugins/archiveorg/archiveorg.php'; 12require_once 'plugins/archiveorg/archiveorg.php';
11 13
@@ -13,20 +15,35 @@ require_once 'plugins/archiveorg/archiveorg.php';
13 * Class PluginArchiveorgTest 15 * Class PluginArchiveorgTest
14 * Unit test for the archiveorg plugin 16 * Unit test for the archiveorg plugin
15 */ 17 */
16class PluginArchiveorgTest extends \PHPUnit\Framework\TestCase 18class PluginArchiveorgTest extends TestCase
17{ 19{
20 protected $savedScriptName;
21
18 /** 22 /**
19 * Reset plugin path 23 * Reset plugin path
20 */ 24 */
21 public function setUp() 25 public function setUp(): void
22 { 26 {
23 PluginManager::$PLUGINS_PATH = 'plugins'; 27 PluginManager::$PLUGINS_PATH = 'plugins';
28
29 // plugins manipulate global vars
30 $_SERVER['SERVER_PORT'] = '80';
31 $_SERVER['SERVER_NAME'] = 'shaarli.shaarli';
32 $this->savedScriptName = $_SERVER['SCRIPT_NAME'] ?? null;
33 $_SERVER['SCRIPT_NAME'] = '/index.php';
34 }
35
36 public function tearDown(): void
37 {
38 unset($_SERVER['SERVER_PORT']);
39 unset($_SERVER['SERVER_NAME']);
40 $_SERVER['SCRIPT_NAME'] = $this->savedScriptName;
24 } 41 }
25 42
26 /** 43 /**
27 * Test render_linklist hook on external bookmarks. 44 * Test render_linklist hook on external bookmarks.
28 */ 45 */
29 public function testArchiveorgLinklistOnExternalLinks() 46 public function testArchiveorgLinklistOnExternalLinks(): void
30 { 47 {
31 $str = 'http://randomstr.com/test'; 48 $str = 'http://randomstr.com/test';
32 49
@@ -56,16 +73,16 @@ class PluginArchiveorgTest extends \PHPUnit\Framework\TestCase
56 /** 73 /**
57 * Test render_linklist hook on internal bookmarks. 74 * Test render_linklist hook on internal bookmarks.
58 */ 75 */
59 public function testArchiveorgLinklistOnInternalLinks() 76 public function testArchiveorgLinklistOnInternalLinks(): void
60 { 77 {
61 $internalLink1 = 'http://shaarli.shaarli/?qvMAqg'; 78 $internalLink1 = 'http://shaarli.shaarli/shaare/qvMAqg';
62 $internalLinkRealURL1 = '?qvMAqg'; 79 $internalLinkRealURL1 = '/shaare/qvMAqg';
63 80
64 $internalLink2 = 'http://shaarli.shaarli/?2_7zww'; 81 $internalLink2 = 'http://shaarli.shaarli/shaare/2_7zww';
65 $internalLinkRealURL2 = '?2_7zww'; 82 $internalLinkRealURL2 = '/shaare/2_7zww';
66 83
67 $internalLink3 = 'http://shaarli.shaarli/?z7u-_Q'; 84 $internalLink3 = 'http://shaarli.shaarli/shaare/z7u-_Q';
68 $internalLinkRealURL3 = '?z7u-_Q'; 85 $internalLinkRealURL3 = '/shaare/z7u-_Q';
69 86
70 $data = array( 87 $data = array(
71 'title' => $internalLink1, 88 'title' => $internalLink1,
diff --git a/tests/plugins/PluginDefaultColorsTest.php b/tests/plugins/PluginDefaultColorsTest.php
index b9951cca..cc844c60 100644
--- a/tests/plugins/PluginDefaultColorsTest.php
+++ b/tests/plugins/PluginDefaultColorsTest.php
@@ -2,11 +2,10 @@
2 2
3namespace Shaarli\Plugin\DefaultColors; 3namespace Shaarli\Plugin\DefaultColors;
4 4
5use DateTime;
6use PHPUnit\Framework\TestCase;
7use Shaarli\Bookmark\LinkDB; 5use Shaarli\Bookmark\LinkDB;
8use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
9use Shaarli\Plugin\PluginManager; 7use Shaarli\Plugin\PluginManager;
8use Shaarli\TestCase;
10 9
11require_once 'plugins/default_colors/default_colors.php'; 10require_once 'plugins/default_colors/default_colors.php';
12 11
@@ -20,7 +19,7 @@ class PluginDefaultColorsTest extends TestCase
20 /** 19 /**
21 * Reset plugin path 20 * Reset plugin path
22 */ 21 */
23 public function setUp() 22 protected function setUp(): void
24 { 23 {
25 PluginManager::$PLUGINS_PATH = 'sandbox'; 24 PluginManager::$PLUGINS_PATH = 'sandbox';
26 mkdir(PluginManager::$PLUGINS_PATH . '/default_colors/'); 25 mkdir(PluginManager::$PLUGINS_PATH . '/default_colors/');
@@ -33,7 +32,7 @@ class PluginDefaultColorsTest extends TestCase
33 /** 32 /**
34 * Remove sandbox files and folder 33 * Remove sandbox files and folder
35 */ 34 */
36 public function tearDown() 35 protected function tearDown(): void
37 { 36 {
38 if (file_exists('sandbox/default_colors/default_colors.css.template')) { 37 if (file_exists('sandbox/default_colors/default_colors.css.template')) {
39 unlink('sandbox/default_colors/default_colors.css.template'); 38 unlink('sandbox/default_colors/default_colors.css.template');
@@ -57,6 +56,8 @@ class PluginDefaultColorsTest extends TestCase
57 $conf->set('plugins.DEFAULT_COLORS_BACKGROUND', 'value'); 56 $conf->set('plugins.DEFAULT_COLORS_BACKGROUND', 'value');
58 $errors = default_colors_init($conf); 57 $errors = default_colors_init($conf);
59 $this->assertEmpty($errors); 58 $this->assertEmpty($errors);
59
60 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css');
60 } 61 }
61 62
62 /** 63 /**
@@ -72,9 +73,9 @@ class PluginDefaultColorsTest extends TestCase
72 /** 73 /**
73 * Test the save plugin parameters hook with all colors specified. 74 * Test the save plugin parameters hook with all colors specified.
74 */ 75 */
75 public function testSavePluginParametersAll() 76 public function testGenerateCssFile()
76 { 77 {
77 $post = [ 78 $params = [
78 'other1' => true, 79 'other1' => true,
79 'DEFAULT_COLORS_MAIN' => 'blue', 80 'DEFAULT_COLORS_MAIN' => 'blue',
80 'DEFAULT_COLORS_BACKGROUND' => 'pink', 81 'DEFAULT_COLORS_BACKGROUND' => 'pink',
@@ -82,7 +83,7 @@ class PluginDefaultColorsTest extends TestCase
82 'DEFAULT_COLORS_DARK_MAIN' => 'green', 83 'DEFAULT_COLORS_DARK_MAIN' => 'green',
83 ]; 84 ];
84 85
85 hook_default_colors_save_plugin_parameters($post); 86 default_colors_generate_css_file($params);
86 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css'); 87 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css');
87 $content = file_get_contents($file); 88 $content = file_get_contents($file);
88 $expected = ':root { 89 $expected = ':root {
@@ -98,16 +99,16 @@ class PluginDefaultColorsTest extends TestCase
98 /** 99 /**
99 * Test the save plugin parameters hook with only one color specified. 100 * Test the save plugin parameters hook with only one color specified.
100 */ 101 */
101 public function testSavePluginParametersSingle() 102 public function testGenerateCssFileSingle()
102 { 103 {
103 $post = [ 104 $params = [
104 'other1' => true, 105 'other1' => true,
105 'DEFAULT_COLORS_BACKGROUND' => 'pink', 106 'DEFAULT_COLORS_BACKGROUND' => 'pink',
106 'other2' => ['yep'], 107 'other2' => ['yep'],
107 'DEFAULT_COLORS_DARK_MAIN' => '', 108 'DEFAULT_COLORS_DARK_MAIN' => '',
108 ]; 109 ];
109 110
110 hook_default_colors_save_plugin_parameters($post); 111 default_colors_generate_css_file($params);
111 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css'); 112 $this->assertFileExists($file = 'sandbox/default_colors/default_colors.css');
112 $content = file_get_contents($file); 113 $content = file_get_contents($file);
113 $expected = ':root { 114 $expected = ':root {
@@ -121,9 +122,9 @@ class PluginDefaultColorsTest extends TestCase
121 /** 122 /**
122 * Test the save plugin parameters hook with no color specified. 123 * Test the save plugin parameters hook with no color specified.
123 */ 124 */
124 public function testSavePluginParametersNone() 125 public function testGenerateCssFileNone()
125 { 126 {
126 hook_default_colors_save_plugin_parameters([]); 127 default_colors_generate_css_file([]);
127 $this->assertFileNotExists($file = 'sandbox/default_colors/default_colors.css'); 128 $this->assertFileNotExists($file = 'sandbox/default_colors/default_colors.css');
128 } 129 }
129 130
diff --git a/tests/plugins/PluginIssoTest.php b/tests/plugins/PluginIssoTest.php
index 99477205..16ecf357 100644
--- a/tests/plugins/PluginIssoTest.php
+++ b/tests/plugins/PluginIssoTest.php
@@ -5,6 +5,7 @@ use DateTime;
5use Shaarli\Bookmark\Bookmark; 5use Shaarli\Bookmark\Bookmark;
6use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
7use Shaarli\Plugin\PluginManager; 7use Shaarli\Plugin\PluginManager;
8use Shaarli\TestCase;
8 9
9require_once 'plugins/isso/isso.php'; 10require_once 'plugins/isso/isso.php';
10 11
@@ -13,12 +14,12 @@ require_once 'plugins/isso/isso.php';
13 * 14 *
14 * Test the Isso plugin (comment system). 15 * Test the Isso plugin (comment system).
15 */ 16 */
16class PluginIssoTest extends \PHPUnit\Framework\TestCase 17class PluginIssoTest extends TestCase
17{ 18{
18 /** 19 /**
19 * Reset plugin path 20 * Reset plugin path
20 */ 21 */
21 public function setUp() 22 public function setUp(): void
22 { 23 {
23 PluginManager::$PLUGINS_PATH = 'plugins'; 24 PluginManager::$PLUGINS_PATH = 'plugins';
24 } 25 }
@@ -26,7 +27,7 @@ class PluginIssoTest extends \PHPUnit\Framework\TestCase
26 /** 27 /**
27 * Test Isso init without errors. 28 * Test Isso init without errors.
28 */ 29 */
29 public function testIssoInitNoError() 30 public function testIssoInitNoError(): void
30 { 31 {
31 $conf = new ConfigManager(''); 32 $conf = new ConfigManager('');
32 $conf->set('plugins.ISSO_SERVER', 'value'); 33 $conf->set('plugins.ISSO_SERVER', 'value');
@@ -37,7 +38,7 @@ class PluginIssoTest extends \PHPUnit\Framework\TestCase
37 /** 38 /**
38 * Test Isso init with errors. 39 * Test Isso init with errors.
39 */ 40 */
40 public function testIssoInitError() 41 public function testIssoInitError(): void
41 { 42 {
42 $conf = new ConfigManager(''); 43 $conf = new ConfigManager('');
43 $errors = isso_init($conf); 44 $errors = isso_init($conf);
@@ -47,7 +48,7 @@ class PluginIssoTest extends \PHPUnit\Framework\TestCase
47 /** 48 /**
48 * Test render_linklist hook with valid settings to display the comment form. 49 * Test render_linklist hook with valid settings to display the comment form.
49 */ 50 */
50 public function testIssoDisplayed() 51 public function testIssoDisplayed(): void
51 { 52 {
52 $conf = new ConfigManager(''); 53 $conf = new ConfigManager('');
53 $conf->set('plugins.ISSO_SERVER', 'value'); 54 $conf->set('plugins.ISSO_SERVER', 'value');
@@ -87,7 +88,7 @@ class PluginIssoTest extends \PHPUnit\Framework\TestCase
87 /** 88 /**
88 * Test isso plugin when multiple bookmarks are displayed (shouldn't be displayed). 89 * Test isso plugin when multiple bookmarks are displayed (shouldn't be displayed).
89 */ 90 */
90 public function testIssoMultipleLinks() 91 public function testIssoMultipleLinks(): void
91 { 92 {
92 $conf = new ConfigManager(''); 93 $conf = new ConfigManager('');
93 $conf->set('plugins.ISSO_SERVER', 'value'); 94 $conf->set('plugins.ISSO_SERVER', 'value');
@@ -115,14 +116,14 @@ class PluginIssoTest extends \PHPUnit\Framework\TestCase
115 116
116 $processed = hook_isso_render_linklist($data, $conf); 117 $processed = hook_isso_render_linklist($data, $conf);
117 // link_plugin should be added for the icon 118 // link_plugin should be added for the icon
118 $this->assertContains('<a href="?'. $short1 .'#isso-thread">', $processed['links'][0]['link_plugin'][0]); 119 $this->assertContainsPolyfill('<a href="/shaare/'. $short1 .'#isso-thread">', $processed['links'][0]['link_plugin'][0]);
119 $this->assertContains('<a href="?'. $short2 .'#isso-thread">', $processed['links'][1]['link_plugin'][0]); 120 $this->assertContainsPolyfill('<a href="/shaare/'. $short2 .'#isso-thread">', $processed['links'][1]['link_plugin'][0]);
120 } 121 }
121 122
122 /** 123 /**
123 * Test isso plugin when using search (shouldn't be displayed). 124 * Test isso plugin when using search (shouldn't be displayed).
124 */ 125 */
125 public function testIssoNotDisplayedWhenSearch() 126 public function testIssoNotDisplayedWhenSearch(): void
126 { 127 {
127 $conf = new ConfigManager(''); 128 $conf = new ConfigManager('');
128 $conf->set('plugins.ISSO_SERVER', 'value'); 129 $conf->set('plugins.ISSO_SERVER', 'value');
@@ -145,13 +146,13 @@ class PluginIssoTest extends \PHPUnit\Framework\TestCase
145 $processed = hook_isso_render_linklist($data, $conf); 146 $processed = hook_isso_render_linklist($data, $conf);
146 147
147 // link_plugin should be added for the icon 148 // link_plugin should be added for the icon
148 $this->assertContains('<a href="?'. $short1 .'#isso-thread">', $processed['links'][0]['link_plugin'][0]); 149 $this->assertContainsPolyfill('<a href="/shaare/'. $short1 .'#isso-thread">', $processed['links'][0]['link_plugin'][0]);
149 } 150 }
150 151
151 /** 152 /**
152 * Test isso plugin without server configuration (shouldn't be displayed). 153 * Test isso plugin without server configuration (shouldn't be displayed).
153 */ 154 */
154 public function testIssoWithoutConf() 155 public function testIssoWithoutConf(): void
155 { 156 {
156 $data = 'abc'; 157 $data = 'abc';
157 $conf = new ConfigManager(''); 158 $conf = new ConfigManager('');
diff --git a/tests/plugins/PluginPlayvideosTest.php b/tests/plugins/PluginPlayvideosTest.php
index 51472617..338d2e35 100644
--- a/tests/plugins/PluginPlayvideosTest.php
+++ b/tests/plugins/PluginPlayvideosTest.php
@@ -6,7 +6,7 @@ namespace Shaarli\Plugin\Playvideos;
6 */ 6 */
7 7
8use Shaarli\Plugin\PluginManager; 8use Shaarli\Plugin\PluginManager;
9use Shaarli\Router; 9use Shaarli\Render\TemplatePage;
10 10
11require_once 'plugins/playvideos/playvideos.php'; 11require_once 'plugins/playvideos/playvideos.php';
12 12
@@ -14,12 +14,12 @@ require_once 'plugins/playvideos/playvideos.php';
14 * Class PluginPlayvideosTest 14 * Class PluginPlayvideosTest
15 * Unit test for the PlayVideos plugin 15 * Unit test for the PlayVideos plugin
16 */ 16 */
17class PluginPlayvideosTest extends \PHPUnit\Framework\TestCase 17class PluginPlayvideosTest extends \Shaarli\TestCase
18{ 18{
19 /** 19 /**
20 * Reset plugin path 20 * Reset plugin path
21 */ 21 */
22 public function setUp() 22 protected function setUp(): void
23 { 23 {
24 PluginManager::$PLUGINS_PATH = 'plugins'; 24 PluginManager::$PLUGINS_PATH = 'plugins';
25 } 25 }
@@ -31,7 +31,7 @@ class PluginPlayvideosTest extends \PHPUnit\Framework\TestCase
31 { 31 {
32 $str = 'stuff'; 32 $str = 'stuff';
33 $data = array($str => $str); 33 $data = array($str => $str);
34 $data['_PAGE_'] = Router::$PAGE_LINKLIST; 34 $data['_PAGE_'] = TemplatePage::LINKLIST;
35 35
36 $data = hook_playvideos_render_header($data); 36 $data = hook_playvideos_render_header($data);
37 $this->assertEquals($str, $data[$str]); 37 $this->assertEquals($str, $data[$str]);
@@ -50,7 +50,7 @@ class PluginPlayvideosTest extends \PHPUnit\Framework\TestCase
50 { 50 {
51 $str = 'stuff'; 51 $str = 'stuff';
52 $data = array($str => $str); 52 $data = array($str => $str);
53 $data['_PAGE_'] = Router::$PAGE_LINKLIST; 53 $data['_PAGE_'] = TemplatePage::LINKLIST;
54 54
55 $data = hook_playvideos_render_footer($data); 55 $data = hook_playvideos_render_footer($data);
56 $this->assertEquals($str, $data[$str]); 56 $this->assertEquals($str, $data[$str]);
diff --git a/tests/plugins/PluginPubsubhubbubTest.php b/tests/plugins/PluginPubsubhubbubTest.php
index a7bd8fc9..d3f7b439 100644
--- a/tests/plugins/PluginPubsubhubbubTest.php
+++ b/tests/plugins/PluginPubsubhubbubTest.php
@@ -3,7 +3,7 @@ namespace Shaarli\Plugin\Pubsubhubbub;
3 3
4use Shaarli\Config\ConfigManager; 4use Shaarli\Config\ConfigManager;
5use Shaarli\Plugin\PluginManager; 5use Shaarli\Plugin\PluginManager;
6use Shaarli\Router; 6use Shaarli\Render\TemplatePage;
7 7
8require_once 'plugins/pubsubhubbub/pubsubhubbub.php'; 8require_once 'plugins/pubsubhubbub/pubsubhubbub.php';
9 9
@@ -11,7 +11,7 @@ require_once 'plugins/pubsubhubbub/pubsubhubbub.php';
11 * Class PluginPubsubhubbubTest 11 * Class PluginPubsubhubbubTest
12 * Unit test for the pubsubhubbub plugin 12 * Unit test for the pubsubhubbub plugin
13 */ 13 */
14class PluginPubsubhubbubTest extends \PHPUnit\Framework\TestCase 14class PluginPubsubhubbubTest extends \Shaarli\TestCase
15{ 15{
16 /** 16 /**
17 * @var string Config file path (without extension). 17 * @var string Config file path (without extension).
@@ -21,7 +21,7 @@ class PluginPubsubhubbubTest extends \PHPUnit\Framework\TestCase
21 /** 21 /**
22 * Reset plugin path 22 * Reset plugin path
23 */ 23 */
24 public function setUp() 24 protected function setUp(): void
25 { 25 {
26 PluginManager::$PLUGINS_PATH = 'plugins'; 26 PluginManager::$PLUGINS_PATH = 'plugins';
27 } 27 }
@@ -34,7 +34,7 @@ class PluginPubsubhubbubTest extends \PHPUnit\Framework\TestCase
34 $hub = 'http://domain.hub'; 34 $hub = 'http://domain.hub';
35 $conf = new ConfigManager(self::$configFile); 35 $conf = new ConfigManager(self::$configFile);
36 $conf->set('plugins.PUBSUBHUB_URL', $hub); 36 $conf->set('plugins.PUBSUBHUB_URL', $hub);
37 $data['_PAGE_'] = Router::$PAGE_FEED_RSS; 37 $data['_PAGE_'] = TemplatePage::FEED_RSS;
38 38
39 $data = hook_pubsubhubbub_render_feed($data, $conf); 39 $data = hook_pubsubhubbub_render_feed($data, $conf);
40 $expected = '<atom:link rel="hub" href="'. $hub .'" />'; 40 $expected = '<atom:link rel="hub" href="'. $hub .'" />';
@@ -49,7 +49,7 @@ class PluginPubsubhubbubTest extends \PHPUnit\Framework\TestCase
49 $hub = 'http://domain.hub'; 49 $hub = 'http://domain.hub';
50 $conf = new ConfigManager(self::$configFile); 50 $conf = new ConfigManager(self::$configFile);
51 $conf->set('plugins.PUBSUBHUB_URL', $hub); 51 $conf->set('plugins.PUBSUBHUB_URL', $hub);
52 $data['_PAGE_'] = Router::$PAGE_FEED_ATOM; 52 $data['_PAGE_'] = TemplatePage::FEED_ATOM;
53 53
54 $data = hook_pubsubhubbub_render_feed($data, $conf); 54 $data = hook_pubsubhubbub_render_feed($data, $conf);
55 $expected = '<link rel="hub" href="'. $hub .'" />'; 55 $expected = '<link rel="hub" href="'. $hub .'" />';
diff --git a/tests/plugins/PluginQrcodeTest.php b/tests/plugins/PluginQrcodeTest.php
index 0c61e14a..1d85fba6 100644
--- a/tests/plugins/PluginQrcodeTest.php
+++ b/tests/plugins/PluginQrcodeTest.php
@@ -6,7 +6,7 @@ namespace Shaarli\Plugin\Qrcode;
6 */ 6 */
7 7
8use Shaarli\Plugin\PluginManager; 8use Shaarli\Plugin\PluginManager;
9use Shaarli\Router; 9use Shaarli\Render\TemplatePage;
10 10
11require_once 'plugins/qrcode/qrcode.php'; 11require_once 'plugins/qrcode/qrcode.php';
12 12
@@ -14,12 +14,12 @@ require_once 'plugins/qrcode/qrcode.php';
14 * Class PluginQrcodeTest 14 * Class PluginQrcodeTest
15 * Unit test for the QR-Code plugin 15 * Unit test for the QR-Code plugin
16 */ 16 */
17class PluginQrcodeTest extends \PHPUnit\Framework\TestCase 17class PluginQrcodeTest extends \Shaarli\TestCase
18{ 18{
19 /** 19 /**
20 * Reset plugin path 20 * Reset plugin path
21 */ 21 */
22 public function setUp() 22 protected function setUp(): void
23 { 23 {
24 PluginManager::$PLUGINS_PATH = 'plugins'; 24 PluginManager::$PLUGINS_PATH = 'plugins';
25 } 25 }
@@ -57,7 +57,7 @@ class PluginQrcodeTest extends \PHPUnit\Framework\TestCase
57 { 57 {
58 $str = 'stuff'; 58 $str = 'stuff';
59 $data = array($str => $str); 59 $data = array($str => $str);
60 $data['_PAGE_'] = Router::$PAGE_LINKLIST; 60 $data['_PAGE_'] = TemplatePage::LINKLIST;
61 61
62 $data = hook_qrcode_render_footer($data); 62 $data = hook_qrcode_render_footer($data);
63 $this->assertEquals($str, $data[$str]); 63 $this->assertEquals($str, $data[$str]);
diff --git a/tests/plugins/PluginWallabagTest.php b/tests/plugins/PluginWallabagTest.php
index 79751921..36317215 100644
--- a/tests/plugins/PluginWallabagTest.php
+++ b/tests/plugins/PluginWallabagTest.php
@@ -10,12 +10,12 @@ require_once 'plugins/wallabag/wallabag.php';
10 * Class PluginWallabagTest 10 * Class PluginWallabagTest
11 * Unit test for the Wallabag plugin 11 * Unit test for the Wallabag plugin
12 */ 12 */
13class PluginWallabagTest extends \PHPUnit\Framework\TestCase 13class PluginWallabagTest extends \Shaarli\TestCase
14{ 14{
15 /** 15 /**
16 * Reset plugin path 16 * Reset plugin path
17 */ 17 */
18 public function setUp() 18 protected function setUp(): void
19 { 19 {
20 PluginManager::$PLUGINS_PATH = 'plugins'; 20 PluginManager::$PLUGINS_PATH = 'plugins';
21 } 21 }
diff --git a/tests/plugins/WallabagInstanceTest.php b/tests/plugins/WallabagInstanceTest.php
index a3cd9076..5ef3de1a 100644
--- a/tests/plugins/WallabagInstanceTest.php
+++ b/tests/plugins/WallabagInstanceTest.php
@@ -4,7 +4,7 @@ namespace Shaarli\Plugin\Wallabag;
4/** 4/**
5 * Class WallabagInstanceTest 5 * Class WallabagInstanceTest
6 */ 6 */
7class WallabagInstanceTest extends \PHPUnit\Framework\TestCase 7class WallabagInstanceTest extends \Shaarli\TestCase
8{ 8{
9 /** 9 /**
10 * @var string wallabag url. 10 * @var string wallabag url.
@@ -14,7 +14,7 @@ class WallabagInstanceTest extends \PHPUnit\Framework\TestCase
14 /** 14 /**
15 * Reset plugin path 15 * Reset plugin path
16 */ 16 */
17 public function setUp() 17 protected function setUp(): void
18 { 18 {
19 $this->instance = 'http://some.url'; 19 $this->instance = 'http://some.url';
20 } 20 }
diff --git a/tests/plugins/resources/hashtags.md b/tests/plugins/resources/hashtags.md
deleted file mode 100644
index 46326de3..00000000
--- a/tests/plugins/resources/hashtags.md
+++ /dev/null
@@ -1,10 +0,0 @@
1[#lol](?addtag=lol)
2
3 #test
4
5`#test2`
6
7```
8bla #bli blo
9#bla
10```
diff --git a/tests/plugins/resources/hashtags.raw b/tests/plugins/resources/hashtags.raw
deleted file mode 100644
index 9d2dc98a..00000000
--- a/tests/plugins/resources/hashtags.raw
+++ /dev/null
@@ -1,10 +0,0 @@
1#lol
2
3 #test
4
5`#test2`
6
7```
8bla #bli blo
9#bla
10```
diff --git a/tests/plugins/resources/markdown.html b/tests/plugins/resources/markdown.html
deleted file mode 100644
index c3460bf7..00000000
--- a/tests/plugins/resources/markdown.html
+++ /dev/null
@@ -1,33 +0,0 @@
1<div class="markdown"><ul>
2<li>test:
3<ul>
4<li><a href="http://link.tld">zero</a></li>
5<li><a href="http://link.tld">two</a></li>
6<li><a href="http://link.tld">three</a></li>
7</ul></li>
8</ul>
9<ol>
10<li><a href="http://link.tld">zero</a>
11<ol start="2">
12<li><a href="http://link.tld">two</a></li>
13<li><a href="http://link.tld">three</a></li>
14<li><a href="http://link.tld">four</a></li>
15<li>foo <a href="?addtag=foobar">#foobar</a></li>
16</ol></li>
17</ol>
18<p><a href="?addtag=foobar">#foobar</a> foo <code>lol #foo</code> <a href="?addtag=bar">#bar</a></p>
19<p>fsdfs <a href="http://link.tld">http://link.tld</a> <a href="?addtag=foobar">#foobar</a> <code>http://link.tld</code></p>
20<pre><code>http://link.tld #foobar
21next #foo</code></pre>
22<p>Block:</p>
23<pre><code>lorem ipsum #foobar http://link.tld
24#foobar http://link.tld</code></pre>
25<p><a href="?123456">link</a><br />
26<img src="/img/train.png" alt="link" /><br />
27<a href="http://test.tld/path/?query=value#hash">link</a><br />
28<a href="http://test.tld/path/?query=value#hash">link</a><br />
29<a href="https://test.tld/path/?query=value#hash">link</a><br />
30<a href="ftp://test.tld/path/?query=value#hash">link</a><br />
31<a href="magnet:test.tld/path/?query=value#hash">link</a><br />
32<a href="http://alert(&#039;xss&#039;)">link</a><br />
33<a href="http://test.tld/path/?query=value#hash">link</a></p></div>
diff --git a/tests/plugins/resources/markdown.md b/tests/plugins/resources/markdown.md
deleted file mode 100644
index 9350a8c7..00000000
--- a/tests/plugins/resources/markdown.md
+++ /dev/null
@@ -1,34 +0,0 @@
1* test:
2 * [zero](http://link.tld)
3 + [two](http://link.tld)
4 - [three](http://link.tld)
5
61. [zero](http://link.tld)
7 2. [two](http://link.tld)
8 3. [three](http://link.tld)
9 4. [four](http://link.tld)
10 5. foo #foobar
11
12#foobar foo `lol #foo` #bar
13
14fsdfs http://link.tld #foobar `http://link.tld`
15
16 http://link.tld #foobar
17 next #foo
18
19Block:
20
21```
22lorem ipsum #foobar http://link.tld
23#foobar http://link.tld
24```
25
26[link](?123456)
27![link](/img/train.png)
28[link](test.tld/path/?query=value#hash)
29[link](http://test.tld/path/?query=value#hash)
30[link](https://test.tld/path/?query=value#hash)
31[link](ftp://test.tld/path/?query=value#hash)
32[link](magnet:test.tld/path/?query=value#hash)
33[link](javascript:alert('xss'))
34[link](other://test.tld/path/?query=value#hash)
diff --git a/tests/plugins/test/test.php b/tests/plugins/test/test.php
index 2aaf5122..03be4f4e 100644
--- a/tests/plugins/test/test.php
+++ b/tests/plugins/test/test.php
@@ -13,9 +13,17 @@ function hook_test_random($data)
13 $data[1] = 'page test'; 13 $data[1] = 'page test';
14 } elseif (isset($data['_LOGGEDIN_']) && $data['_LOGGEDIN_'] === true) { 14 } elseif (isset($data['_LOGGEDIN_']) && $data['_LOGGEDIN_'] === true) {
15 $data[1] = 'loggedin'; 15 $data[1] = 'loggedin';
16 } elseif (array_key_exists('_LOGGEDIN_', $data)) {
17 $data[1] = 'loggedin';
18 $data[2] = $data['_LOGGEDIN_'];
16 } else { 19 } else {
17 $data[1] = $data[0]; 20 $data[1] = $data[0];
18 } 21 }
19 22
20 return $data; 23 return $data;
21} 24}
25
26function hook_test_error()
27{
28 new Unknown();
29}
diff --git a/tests/feed/CacheTest.php b/tests/render/PageCacheManagerTest.php
index c0a9f26f..08d4e5ea 100644
--- a/tests/feed/CacheTest.php
+++ b/tests/render/PageCacheManagerTest.php
@@ -1,18 +1,18 @@
1<?php 1<?php
2
2/** 3/**
3 * Cache tests 4 * Cache tests
4 */ 5 */
5namespace Shaarli\Feed;
6 6
7// required to access $_SESSION array 7namespace Shaarli\Render;
8session_start();
9 8
10require_once 'application/feed/Cache.php'; 9use Shaarli\Security\SessionManager;
10use Shaarli\TestCase;
11 11
12/** 12/**
13 * Unitary tests for cached pages 13 * Unitary tests for cached pages
14 */ 14 */
15class CacheTest extends \PHPUnit\Framework\TestCase 15class PageCacheManagerTest extends TestCase
16{ 16{
17 // test cache directory 17 // test cache directory
18 protected static $testCacheDir = 'sandbox/dummycache'; 18 protected static $testCacheDir = 'sandbox/dummycache';
@@ -20,12 +20,19 @@ class CacheTest extends \PHPUnit\Framework\TestCase
20 // dummy cached file names / content 20 // dummy cached file names / content
21 protected static $pages = array('a', 'toto', 'd7b59c'); 21 protected static $pages = array('a', 'toto', 'd7b59c');
22 22
23 /** @var PageCacheManager */
24 protected $cacheManager;
25
26 /** @var SessionManager */
27 protected $sessionManager;
23 28
24 /** 29 /**
25 * Populate the cache with dummy files 30 * Populate the cache with dummy files
26 */ 31 */
27 public function setUp() 32 protected function setUp(): void
28 { 33 {
34 $this->cacheManager = new PageCacheManager(static::$testCacheDir, true);
35
29 if (!is_dir(self::$testCacheDir)) { 36 if (!is_dir(self::$testCacheDir)) {
30 mkdir(self::$testCacheDir); 37 mkdir(self::$testCacheDir);
31 } else { 38 } else {
@@ -41,7 +48,7 @@ class CacheTest extends \PHPUnit\Framework\TestCase
41 /** 48 /**
42 * Remove dummycache folder after each tests. 49 * Remove dummycache folder after each tests.
43 */ 50 */
44 public function tearDown() 51 protected function tearDown(): void
45 { 52 {
46 array_map('unlink', glob(self::$testCacheDir . '/*')); 53 array_map('unlink', glob(self::$testCacheDir . '/*'));
47 rmdir(self::$testCacheDir); 54 rmdir(self::$testCacheDir);
@@ -52,7 +59,7 @@ class CacheTest extends \PHPUnit\Framework\TestCase
52 */ 59 */
53 public function testPurgeCachedPages() 60 public function testPurgeCachedPages()
54 { 61 {
55 purgeCachedPages(self::$testCacheDir); 62 $this->cacheManager->purgeCachedPages();
56 foreach (self::$pages as $page) { 63 foreach (self::$pages as $page) {
57 $this->assertFileNotExists(self::$testCacheDir . '/' . $page . '.cache'); 64 $this->assertFileNotExists(self::$testCacheDir . '/' . $page . '.cache');
58 } 65 }
@@ -65,28 +72,14 @@ class CacheTest extends \PHPUnit\Framework\TestCase
65 */ 72 */
66 public function testPurgeCachedPagesMissingDir() 73 public function testPurgeCachedPagesMissingDir()
67 { 74 {
75 $this->cacheManager = new PageCacheManager(self::$testCacheDir . '_missing', true);
76
68 $oldlog = ini_get('error_log'); 77 $oldlog = ini_get('error_log');
69 ini_set('error_log', '/dev/null'); 78 ini_set('error_log', '/dev/null');
70 $this->assertEquals( 79 $this->assertEquals(
71 'Cannot purge sandbox/dummycache_missing: no directory', 80 'Cannot purge sandbox/dummycache_missing: no directory',
72 purgeCachedPages(self::$testCacheDir . '_missing') 81 $this->cacheManager->purgeCachedPages()
73 ); 82 );
74 ini_set('error_log', $oldlog); 83 ini_set('error_log', $oldlog);
75 } 84 }
76
77 /**
78 * Purge cached pages and session cache
79 */
80 public function testInvalidateCaches()
81 {
82 $this->assertArrayNotHasKey('tags', $_SESSION);
83 $_SESSION['tags'] = array('goodbye', 'cruel', 'world');
84
85 invalidateCaches(self::$testCacheDir);
86 foreach (self::$pages as $page) {
87 $this->assertFileNotExists(self::$testCacheDir . '/' . $page . '.cache');
88 }
89
90 $this->assertArrayNotHasKey('tags', $_SESSION);
91 }
92} 85}
diff --git a/tests/render/ThemeUtilsTest.php b/tests/render/ThemeUtilsTest.php
index 58e3426b..7d841e4d 100644
--- a/tests/render/ThemeUtilsTest.php
+++ b/tests/render/ThemeUtilsTest.php
@@ -7,7 +7,7 @@ namespace Shaarli\Render;
7 * 7 *
8 * @package Shaarli 8 * @package Shaarli
9 */ 9 */
10class ThemeUtilsTest extends \PHPUnit\Framework\TestCase 10class ThemeUtilsTest extends \Shaarli\TestCase
11{ 11{
12 /** 12 /**
13 * Test getThemes() with existing theme directories. 13 * Test getThemes() with existing theme directories.
diff --git a/tests/security/BanManagerTest.php b/tests/security/BanManagerTest.php
index bba7c8ad..29d2791b 100644
--- a/tests/security/BanManagerTest.php
+++ b/tests/security/BanManagerTest.php
@@ -3,8 +3,9 @@
3 3
4namespace Shaarli\Security; 4namespace Shaarli\Security;
5 5
6use PHPUnit\Framework\TestCase; 6use Psr\Log\LoggerInterface;
7use Shaarli\FileUtils; 7use Shaarli\Helper\FileUtils;
8use Shaarli\TestCase;
8 9
9/** 10/**
10 * Test coverage for BanManager 11 * Test coverage for BanManager
@@ -32,7 +33,7 @@ class BanManagerTest extends TestCase
32 /** 33 /**
33 * Prepare or reset test resources 34 * Prepare or reset test resources
34 */ 35 */
35 public function setUp() 36 protected function setUp(): void
36 { 37 {
37 if (file_exists($this->banFile)) { 38 if (file_exists($this->banFile)) {
38 unlink($this->banFile); 39 unlink($this->banFile);
@@ -387,7 +388,7 @@ class BanManagerTest extends TestCase
387 3, 388 3,
388 1800, 389 1800,
389 $this->banFile, 390 $this->banFile,
390 $this->logFile 391 $this->createMock(LoggerInterface::class)
391 ); 392 );
392 } 393 }
393} 394}
diff --git a/tests/security/LoginManagerTest.php b/tests/security/LoginManagerTest.php
index 8fd1698c..f7609fc6 100644
--- a/tests/security/LoginManagerTest.php
+++ b/tests/security/LoginManagerTest.php
@@ -1,16 +1,17 @@
1<?php 1<?php
2namespace Shaarli\Security;
3 2
4require_once 'tests/utils/FakeConfigManager.php'; 3namespace Shaarli\Security;
5 4
6use PHPUnit\Framework\TestCase; 5use Psr\Log\LoggerInterface;
6use Shaarli\FakeConfigManager;
7use Shaarli\TestCase;
7 8
8/** 9/**
9 * Test coverage for LoginManager 10 * Test coverage for LoginManager
10 */ 11 */
11class LoginManagerTest extends TestCase 12class LoginManagerTest extends TestCase
12{ 13{
13 /** @var \FakeConfigManager Configuration Manager instance */ 14 /** @var FakeConfigManager Configuration Manager instance */
14 protected $configManager = null; 15 protected $configManager = null;
15 16
16 /** @var LoginManager Login Manager instance */ 17 /** @var LoginManager Login Manager instance */
@@ -58,10 +59,16 @@ class LoginManagerTest extends TestCase
58 /** @var string Salt used by hash functions */ 59 /** @var string Salt used by hash functions */
59 protected $salt = '669e24fa9c5a59a613f98e8e38327384504a4af2'; 60 protected $salt = '669e24fa9c5a59a613f98e8e38327384504a4af2';
60 61
62 /** @var CookieManager */
63 protected $cookieManager;
64
65 /** @var BanManager */
66 protected $banManager;
67
61 /** 68 /**
62 * Prepare or reset test resources 69 * Prepare or reset test resources
63 */ 70 */
64 public function setUp() 71 protected function setUp(): void
65 { 72 {
66 if (file_exists($this->banFile)) { 73 if (file_exists($this->banFile)) {
67 unlink($this->banFile); 74 unlink($this->banFile);
@@ -69,7 +76,7 @@ class LoginManagerTest extends TestCase
69 76
70 $this->passwordHash = sha1($this->password . $this->login . $this->salt); 77 $this->passwordHash = sha1($this->password . $this->login . $this->salt);
71 78
72 $this->configManager = new \FakeConfigManager([ 79 $this->configManager = new FakeConfigManager([
73 'credentials.login' => $this->login, 80 'credentials.login' => $this->login,
74 'credentials.hash' => $this->passwordHash, 81 'credentials.hash' => $this->passwordHash,
75 'credentials.salt' => $this->salt, 82 'credentials.salt' => $this->salt,
@@ -84,19 +91,34 @@ class LoginManagerTest extends TestCase
84 $this->cookie = []; 91 $this->cookie = [];
85 $this->session = []; 92 $this->session = [];
86 93
87 $this->sessionManager = new SessionManager($this->session, $this->configManager); 94 $this->cookieManager = $this->createMock(CookieManager::class);
88 $this->loginManager = new LoginManager($this->configManager, $this->sessionManager); 95 $this->cookieManager->method('getCookieParameter')->willReturnCallback(function (string $key) {
96 return $this->cookie[$key] ?? null;
97 });
98 $this->sessionManager = new SessionManager($this->session, $this->configManager, 'session_path');
99 $this->banManager = $this->createMock(BanManager::class);
100 $this->loginManager = new LoginManager(
101 $this->configManager,
102 $this->sessionManager,
103 $this->cookieManager,
104 $this->banManager,
105 $this->createMock(LoggerInterface::class)
106 );
89 $this->server['REMOTE_ADDR'] = $this->ipAddr; 107 $this->server['REMOTE_ADDR'] = $this->ipAddr;
90 } 108 }
91 109
92 /** 110 /**
93 * Record a failed login attempt 111 * Record a failed login attempt
94 */ 112 */
95 public function testHandleFailedLogin() 113 public function testHandleFailedLogin(): void
96 { 114 {
115 $this->banManager->expects(static::exactly(2))->method('handleFailedAttempt');
116 $this->banManager->method('isBanned')->willReturn(true);
117
97 $this->loginManager->handleFailedLogin($this->server); 118 $this->loginManager->handleFailedLogin($this->server);
98 $this->loginManager->handleFailedLogin($this->server); 119 $this->loginManager->handleFailedLogin($this->server);
99 $this->assertFalse($this->loginManager->canLogin($this->server)); 120
121 static::assertFalse($this->loginManager->canLogin($this->server));
100 } 122 }
101 123
102 /** 124 /**
@@ -108,8 +130,13 @@ class LoginManagerTest extends TestCase
108 'REMOTE_ADDR' => $this->trustedProxy, 130 'REMOTE_ADDR' => $this->trustedProxy,
109 'HTTP_X_FORWARDED_FOR' => $this->ipAddr, 131 'HTTP_X_FORWARDED_FOR' => $this->ipAddr,
110 ]; 132 ];
133
134 $this->banManager->expects(static::exactly(2))->method('handleFailedAttempt');
135 $this->banManager->method('isBanned')->willReturn(true);
136
111 $this->loginManager->handleFailedLogin($server); 137 $this->loginManager->handleFailedLogin($server);
112 $this->loginManager->handleFailedLogin($server); 138 $this->loginManager->handleFailedLogin($server);
139
113 $this->assertFalse($this->loginManager->canLogin($server)); 140 $this->assertFalse($this->loginManager->canLogin($server));
114 } 141 }
115 142
@@ -190,11 +217,17 @@ class LoginManagerTest extends TestCase
190 */ 217 */
191 public function testCheckLoginStateNotConfigured() 218 public function testCheckLoginStateNotConfigured()
192 { 219 {
193 $configManager = new \FakeConfigManager([ 220 $configManager = new FakeConfigManager([
194 'resource.ban_file' => $this->banFile, 221 'resource.ban_file' => $this->banFile,
195 ]); 222 ]);
196 $loginManager = new LoginManager($configManager, null); 223 $loginManager = new LoginManager(
197 $loginManager->checkLoginState([], ''); 224 $configManager,
225 $this->sessionManager,
226 $this->cookieManager,
227 $this->banManager,
228 $this->createMock(LoggerInterface::class)
229 );
230 $loginManager->checkLoginState('');
198 231
199 $this->assertFalse($loginManager->isLoggedIn()); 232 $this->assertFalse($loginManager->isLoggedIn());
200 } 233 }
@@ -210,9 +243,9 @@ class LoginManagerTest extends TestCase
210 'expires_on' => time() + 100, 243 'expires_on' => time() + 100,
211 ]; 244 ];
212 $this->loginManager->generateStaySignedInToken($this->clientIpAddress); 245 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
213 $this->cookie[LoginManager::$STAY_SIGNED_IN_COOKIE] = 'nope'; 246 $this->cookie[CookieManager::STAY_SIGNED_IN] = 'nope';
214 247
215 $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress); 248 $this->loginManager->checkLoginState($this->clientIpAddress);
216 249
217 $this->assertTrue($this->loginManager->isLoggedIn()); 250 $this->assertTrue($this->loginManager->isLoggedIn());
218 $this->assertTrue(empty($this->session['username'])); 251 $this->assertTrue(empty($this->session['username']));
@@ -224,9 +257,9 @@ class LoginManagerTest extends TestCase
224 public function testCheckLoginStateStaySignedInWithValidToken() 257 public function testCheckLoginStateStaySignedInWithValidToken()
225 { 258 {
226 $this->loginManager->generateStaySignedInToken($this->clientIpAddress); 259 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
227 $this->cookie[LoginManager::$STAY_SIGNED_IN_COOKIE] = $this->loginManager->getStaySignedInToken(); 260 $this->cookie[CookieManager::STAY_SIGNED_IN] = $this->loginManager->getStaySignedInToken();
228 261
229 $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress); 262 $this->loginManager->checkLoginState($this->clientIpAddress);
230 263
231 $this->assertTrue($this->loginManager->isLoggedIn()); 264 $this->assertTrue($this->loginManager->isLoggedIn());
232 $this->assertEquals($this->login, $this->session['username']); 265 $this->assertEquals($this->login, $this->session['username']);
@@ -241,7 +274,7 @@ class LoginManagerTest extends TestCase
241 $this->loginManager->generateStaySignedInToken($this->clientIpAddress); 274 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
242 $this->session['expires_on'] = time() - 100; 275 $this->session['expires_on'] = time() - 100;
243 276
244 $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress); 277 $this->loginManager->checkLoginState($this->clientIpAddress);
245 278
246 $this->assertFalse($this->loginManager->isLoggedIn()); 279 $this->assertFalse($this->loginManager->isLoggedIn());
247 } 280 }
@@ -253,7 +286,7 @@ class LoginManagerTest extends TestCase
253 { 286 {
254 $this->loginManager->generateStaySignedInToken($this->clientIpAddress); 287 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
255 288
256 $this->loginManager->checkLoginState($this->cookie, '10.7.157.98'); 289 $this->loginManager->checkLoginState('10.7.157.98');
257 290
258 $this->assertFalse($this->loginManager->isLoggedIn()); 291 $this->assertFalse($this->loginManager->isLoggedIn());
259 } 292 }
@@ -264,7 +297,7 @@ class LoginManagerTest extends TestCase
264 public function testCheckCredentialsWrongLogin() 297 public function testCheckCredentialsWrongLogin()
265 { 298 {
266 $this->assertFalse( 299 $this->assertFalse(
267 $this->loginManager->checkCredentials('', '', 'b4dl0g1n', $this->password) 300 $this->loginManager->checkCredentials('', 'b4dl0g1n', $this->password)
268 ); 301 );
269 } 302 }
270 303
@@ -274,7 +307,7 @@ class LoginManagerTest extends TestCase
274 public function testCheckCredentialsWrongPassword() 307 public function testCheckCredentialsWrongPassword()
275 { 308 {
276 $this->assertFalse( 309 $this->assertFalse(
277 $this->loginManager->checkCredentials('', '', $this->login, 'b4dp455wd') 310 $this->loginManager->checkCredentials('', $this->login, 'b4dp455wd')
278 ); 311 );
279 } 312 }
280 313
@@ -284,7 +317,7 @@ class LoginManagerTest extends TestCase
284 public function testCheckCredentialsWrongLoginAndPassword() 317 public function testCheckCredentialsWrongLoginAndPassword()
285 { 318 {
286 $this->assertFalse( 319 $this->assertFalse(
287 $this->loginManager->checkCredentials('', '', 'b4dl0g1n', 'b4dp455wd') 320 $this->loginManager->checkCredentials('', 'b4dl0g1n', 'b4dp455wd')
288 ); 321 );
289 } 322 }
290 323
@@ -294,7 +327,7 @@ class LoginManagerTest extends TestCase
294 public function testCheckCredentialsGoodLoginAndPassword() 327 public function testCheckCredentialsGoodLoginAndPassword()
295 { 328 {
296 $this->assertTrue( 329 $this->assertTrue(
297 $this->loginManager->checkCredentials('', '', $this->login, $this->password) 330 $this->loginManager->checkCredentials('', $this->login, $this->password)
298 ); 331 );
299 } 332 }
300 333
@@ -305,7 +338,7 @@ class LoginManagerTest extends TestCase
305 { 338 {
306 $this->configManager->set('ldap.host', 'dummy'); 339 $this->configManager->set('ldap.host', 'dummy');
307 $this->assertFalse( 340 $this->assertFalse(
308 $this->loginManager->checkCredentials('', '', $this->login, $this->password) 341 $this->loginManager->checkCredentials('', $this->login, $this->password)
309 ); 342 );
310 } 343 }
311 344
diff --git a/tests/security/SessionManagerTest.php b/tests/security/SessionManagerTest.php
index f264505e..6830d714 100644
--- a/tests/security/SessionManagerTest.php
+++ b/tests/security/SessionManagerTest.php
@@ -1,12 +1,9 @@
1<?php 1<?php
2require_once 'tests/utils/FakeConfigManager.php';
3 2
4// Initialize reference data _before_ PHPUnit starts a session 3namespace Shaarli\Security;
5require_once 'tests/utils/ReferenceSessionIdHashes.php';
6ReferenceSessionIdHashes::genAllHashes();
7 4
8use PHPUnit\Framework\TestCase; 5use Shaarli\FakeConfigManager;
9use Shaarli\Security\SessionManager; 6use Shaarli\TestCase;
10 7
11/** 8/**
12 * Test coverage for SessionManager 9 * Test coverage for SessionManager
@@ -16,7 +13,7 @@ class SessionManagerTest extends TestCase
16 /** @var array Session ID hashes */ 13 /** @var array Session ID hashes */
17 protected static $sidHashes = null; 14 protected static $sidHashes = null;
18 15
19 /** @var \FakeConfigManager ConfigManager substitute for testing */ 16 /** @var FakeConfigManager ConfigManager substitute for testing */
20 protected $conf = null; 17 protected $conf = null;
21 18
22 /** @var array $_SESSION array for testing */ 19 /** @var array $_SESSION array for testing */
@@ -28,15 +25,15 @@ class SessionManagerTest extends TestCase
28 /** 25 /**
29 * Assign reference data 26 * Assign reference data
30 */ 27 */
31 public static function setUpBeforeClass() 28 public static function setUpBeforeClass(): void
32 { 29 {
33 self::$sidHashes = ReferenceSessionIdHashes::getHashes(); 30 self::$sidHashes = \ReferenceSessionIdHashes::getHashes();
34 } 31 }
35 32
36 /** 33 /**
37 * Initialize or reset test resources 34 * Initialize or reset test resources
38 */ 35 */
39 public function setUp() 36 protected function setUp(): void
40 { 37 {
41 $this->conf = new FakeConfigManager([ 38 $this->conf = new FakeConfigManager([
42 'credentials.login' => 'johndoe', 39 'credentials.login' => 'johndoe',
@@ -44,7 +41,7 @@ class SessionManagerTest extends TestCase
44 'security.session_protection_disabled' => false, 41 'security.session_protection_disabled' => false,
45 ]); 42 ]);
46 $this->session = []; 43 $this->session = [];
47 $this->sessionManager = new SessionManager($this->session, $this->conf); 44 $this->sessionManager = new SessionManager($this->session, $this->conf, 'session_path');
48 } 45 }
49 46
50 /** 47 /**
@@ -69,7 +66,7 @@ class SessionManagerTest extends TestCase
69 $token => 1, 66 $token => 1,
70 ], 67 ],
71 ]; 68 ];
72 $sessionManager = new SessionManager($session, $this->conf); 69 $sessionManager = new SessionManager($session, $this->conf, 'session_path');
73 70
74 // check and destroy the token 71 // check and destroy the token
75 $this->assertTrue($sessionManager->checkToken($token)); 72 $this->assertTrue($sessionManager->checkToken($token));
@@ -211,15 +208,16 @@ class SessionManagerTest extends TestCase
211 'expires_on' => time() + 1000, 208 'expires_on' => time() + 1000,
212 'username' => 'johndoe', 209 'username' => 'johndoe',
213 'visibility' => 'public', 210 'visibility' => 'public',
214 'untaggedonly' => false, 211 'untaggedonly' => true,
215 ]; 212 ];
216 $this->sessionManager->logout(); 213 $this->sessionManager->logout();
217 214
218 $this->assertFalse(isset($this->session['ip'])); 215 $this->assertArrayNotHasKey('ip', $this->session);
219 $this->assertFalse(isset($this->session['expires_on'])); 216 $this->assertArrayNotHasKey('expires_on', $this->session);
220 $this->assertFalse(isset($this->session['username'])); 217 $this->assertArrayNotHasKey('username', $this->session);
221 $this->assertFalse(isset($this->session['visibility'])); 218 $this->assertArrayNotHasKey('visibility', $this->session);
222 $this->assertFalse(isset($this->session['untaggedonly'])); 219 $this->assertArrayHasKey('untaggedonly', $this->session);
220 $this->assertTrue($this->session['untaggedonly']);
223 } 221 }
224 222
225 /** 223 /**
@@ -269,4 +267,61 @@ class SessionManagerTest extends TestCase
269 $this->session['ip'] = 'ip_id_one'; 267 $this->session['ip'] = 'ip_id_one';
270 $this->assertTrue($this->sessionManager->hasClientIpChanged('ip_id_two')); 268 $this->assertTrue($this->sessionManager->hasClientIpChanged('ip_id_two'));
271 } 269 }
270
271 /**
272 * Test creating an entry in the session array
273 */
274 public function testSetSessionParameterCreate(): void
275 {
276 $this->sessionManager->setSessionParameter('abc', 'def');
277
278 static::assertSame('def', $this->session['abc']);
279 }
280
281 /**
282 * Test updating an entry in the session array
283 */
284 public function testSetSessionParameterUpdate(): void
285 {
286 $this->session['abc'] = 'ghi';
287
288 $this->sessionManager->setSessionParameter('abc', 'def');
289
290 static::assertSame('def', $this->session['abc']);
291 }
292
293 /**
294 * Test updating an entry in the session array with null value
295 */
296 public function testSetSessionParameterUpdateNull(): void
297 {
298 $this->session['abc'] = 'ghi';
299
300 $this->sessionManager->setSessionParameter('abc', null);
301
302 static::assertArrayHasKey('abc', $this->session);
303 static::assertNull($this->session['abc']);
304 }
305
306 /**
307 * Test deleting an existing entry in the session array
308 */
309 public function testDeleteSessionParameter(): void
310 {
311 $this->session['abc'] = 'def';
312
313 $this->sessionManager->deleteSessionParameter('abc');
314
315 static::assertArrayNotHasKey('abc', $this->session);
316 }
317
318 /**
319 * Test deleting a non existent entry in the session array
320 */
321 public function testDeleteSessionParameterNotExisting(): void
322 {
323 $this->sessionManager->deleteSessionParameter('abc');
324
325 static::assertArrayNotHasKey('abc', $this->session);
326 }
272} 327}
diff --git a/tests/updater/DummyUpdater.php b/tests/updater/DummyUpdater.php
index 07c7f5c4..3403233f 100644
--- a/tests/updater/DummyUpdater.php
+++ b/tests/updater/DummyUpdater.php
@@ -37,7 +37,7 @@ class DummyUpdater extends Updater
37 * 37 *
38 * @return bool true. 38 * @return bool true.
39 */ 39 */
40 final private function updateMethodDummy1() 40 final protected function updateMethodDummy1()
41 { 41 {
42 return true; 42 return true;
43 } 43 }
@@ -47,7 +47,7 @@ class DummyUpdater extends Updater
47 * 47 *
48 * @return bool true. 48 * @return bool true.
49 */ 49 */
50 final private function updateMethodDummy2() 50 final protected function updateMethodDummy2()
51 { 51 {
52 return true; 52 return true;
53 } 53 }
@@ -57,7 +57,7 @@ class DummyUpdater extends Updater
57 * 57 *
58 * @return bool true. 58 * @return bool true.
59 */ 59 */
60 final private function updateMethodDummy3() 60 final protected function updateMethodDummy3()
61 { 61 {
62 return true; 62 return true;
63 } 63 }
@@ -67,7 +67,7 @@ class DummyUpdater extends Updater
67 * 67 *
68 * @throws Exception error. 68 * @throws Exception error.
69 */ 69 */
70 final private function updateMethodException() 70 final protected function updateMethodException()
71 { 71 {
72 throw new Exception('whatever'); 72 throw new Exception('whatever');
73 } 73 }
diff --git a/tests/updater/UpdaterTest.php b/tests/updater/UpdaterTest.php
index c689982b..47332544 100644
--- a/tests/updater/UpdaterTest.php
+++ b/tests/updater/UpdaterTest.php
@@ -2,17 +2,19 @@
2namespace Shaarli\Updater; 2namespace Shaarli\Updater;
3 3
4use Exception; 4use Exception;
5use malkusch\lock\mutex\NoMutex;
6use Shaarli\Bookmark\BookmarkFileService;
7use Shaarli\Bookmark\BookmarkServiceInterface;
5use Shaarli\Config\ConfigManager; 8use Shaarli\Config\ConfigManager;
9use Shaarli\History;
10use Shaarli\TestCase;
6 11
7require_once 'tests/updater/DummyUpdater.php';
8require_once 'tests/utils/ReferenceLinkDB.php';
9require_once 'inc/rain.tpl.class.php';
10 12
11/** 13/**
12 * Class UpdaterTest. 14 * Class UpdaterTest.
13 * Runs unit tests against the updater class. 15 * Runs unit tests against the updater class.
14 */ 16 */
15class UpdaterTest extends \PHPUnit\Framework\TestCase 17class UpdaterTest extends TestCase
16{ 18{
17 /** 19 /**
18 * @var string Path to test datastore. 20 * @var string Path to test datastore.
@@ -29,13 +31,28 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
29 */ 31 */
30 protected $conf; 32 protected $conf;
31 33
34 /** @var BookmarkServiceInterface */
35 protected $bookmarkService;
36
37 /** @var \ReferenceLinkDB */
38 protected $refDB;
39
40 /** @var Updater */
41 protected $updater;
42
32 /** 43 /**
33 * Executed before each test. 44 * Executed before each test.
34 */ 45 */
35 public function setUp() 46 protected function setUp(): void
36 { 47 {
48 $mutex = new NoMutex();
49 $this->refDB = new \ReferenceLinkDB();
50 $this->refDB->write(self::$testDatastore);
51
37 copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php'); 52 copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php');
38 $this->conf = new ConfigManager(self::$configFile); 53 $this->conf = new ConfigManager(self::$configFile);
54 $this->bookmarkService = new BookmarkFileService($this->conf, $this->createMock(History::class), $mutex, true);
55 $this->updater = new Updater([], $this->bookmarkService, $this->conf, true);
39 } 56 }
40 57
41 /** 58 /**
@@ -72,23 +89,23 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
72 89
73 /** 90 /**
74 * Test errors in UpdaterUtils::write_updates_file(): empty updates file. 91 * Test errors in UpdaterUtils::write_updates_file(): empty updates file.
75 *
76 * @expectedException Exception
77 * @expectedExceptionMessageRegExp /Updates file path is not set(.*)/
78 */ 92 */
79 public function testWriteEmptyUpdatesFile() 93 public function testWriteEmptyUpdatesFile()
80 { 94 {
95 $this->expectException(\Exception::class);
96 $this->expectExceptionMessageRegExp('/Updates file path is not set(.*)/');
97
81 UpdaterUtils::write_updates_file('', array('test')); 98 UpdaterUtils::write_updates_file('', array('test'));
82 } 99 }
83 100
84 /** 101 /**
85 * Test errors in UpdaterUtils::write_updates_file(): not writable updates file. 102 * Test errors in UpdaterUtils::write_updates_file(): not writable updates file.
86 *
87 * @expectedException Exception
88 * @expectedExceptionMessageRegExp /Unable to write(.*)/
89 */ 103 */
90 public function testWriteUpdatesFileNotWritable() 104 public function testWriteUpdatesFileNotWritable()
91 { 105 {
106 $this->expectException(\Exception::class);
107 $this->expectExceptionMessageRegExp('/Unable to write(.*)/');
108
92 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt'; 109 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt';
93 touch($updatesFile); 110 touch($updatesFile);
94 chmod($updatesFile, 0444); 111 chmod($updatesFile, 0444);
@@ -153,11 +170,11 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
153 170
154 /** 171 /**
155 * Test Update failed. 172 * Test Update failed.
156 *
157 * @expectedException \Exception
158 */ 173 */
159 public function testUpdateFailed() 174 public function testUpdateFailed()
160 { 175 {
176 $this->expectException(\Exception::class);
177
161 $updates = array( 178 $updates = array(
162 'updateMethodDummy1', 179 'updateMethodDummy1',
163 'updateMethodDummy2', 180 'updateMethodDummy2',
@@ -167,4 +184,40 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
167 $updater = new DummyUpdater($updates, array(), $this->conf, true); 184 $updater = new DummyUpdater($updates, array(), $this->conf, true);
168 $updater->update(); 185 $updater->update();
169 } 186 }
187
188 public function testUpdateMethodRelativeHomeLinkRename(): void
189 {
190 $this->updater->setBasePath('/subfolder');
191 $this->conf->set('general.header_link', '?');
192
193 $this->updater->updateMethodRelativeHomeLink();
194
195 static::assertSame('/subfolder/', $this->conf->get('general.header_link'));
196 }
197
198 public function testUpdateMethodRelativeHomeLinkDoNotRename(): void
199 {
200 $this->conf->set('general.header_link', '~/my-blog');
201
202 $this->updater->updateMethodRelativeHomeLink();
203
204 static::assertSame('~/my-blog', $this->conf->get('general.header_link'));
205 }
206
207 public function testUpdateMethodMigrateExistingNotesUrl(): void
208 {
209 $this->updater->updateMethodMigrateExistingNotesUrl();
210
211 static::assertSame($this->refDB->getLinks()[0]->getUrl(), $this->bookmarkService->get(0)->getUrl());
212 static::assertSame($this->refDB->getLinks()[1]->getUrl(), $this->bookmarkService->get(1)->getUrl());
213 static::assertSame($this->refDB->getLinks()[4]->getUrl(), $this->bookmarkService->get(4)->getUrl());
214 static::assertSame($this->refDB->getLinks()[6]->getUrl(), $this->bookmarkService->get(6)->getUrl());
215 static::assertSame($this->refDB->getLinks()[7]->getUrl(), $this->bookmarkService->get(7)->getUrl());
216 static::assertSame($this->refDB->getLinks()[8]->getUrl(), $this->bookmarkService->get(8)->getUrl());
217 static::assertSame($this->refDB->getLinks()[9]->getUrl(), $this->bookmarkService->get(9)->getUrl());
218 static::assertSame('/shaare/WDWyig', $this->bookmarkService->get(42)->getUrl());
219 static::assertSame('/shaare/WDWyig', $this->bookmarkService->get(41)->getUrl());
220 static::assertSame('/shaare/0gCTjQ', $this->bookmarkService->get(10)->getUrl());
221 static::assertSame('/shaare/PCRizQ', $this->bookmarkService->get(11)->getUrl());
222 }
170} 223}
diff --git a/tests/utils/FakeApplicationUtils.php b/tests/utils/FakeApplicationUtils.php
index de83d598..d5289ede 100644
--- a/tests/utils/FakeApplicationUtils.php
+++ b/tests/utils/FakeApplicationUtils.php
@@ -2,6 +2,8 @@
2 2
3namespace Shaarli; 3namespace Shaarli;
4 4
5use Shaarli\Helper\ApplicationUtils;
6
5/** 7/**
6 * Fake ApplicationUtils class to avoid HTTP requests 8 * Fake ApplicationUtils class to avoid HTTP requests
7 */ 9 */
diff --git a/tests/utils/FakeConfigManager.php b/tests/utils/FakeConfigManager.php
index 360b34a9..014c2af0 100644
--- a/tests/utils/FakeConfigManager.php
+++ b/tests/utils/FakeConfigManager.php
@@ -1,9 +1,13 @@
1<?php 1<?php
2 2
3namespace Shaarli;
4
5use Shaarli\Config\ConfigManager;
6
3/** 7/**
4 * Fake ConfigManager 8 * Fake ConfigManager
5 */ 9 */
6class FakeConfigManager 10class FakeConfigManager extends ConfigManager
7{ 11{
8 protected $values = []; 12 protected $values = [];
9 13
@@ -23,7 +27,7 @@ class FakeConfigManager
23 * @param string $key Key of the value to set 27 * @param string $key Key of the value to set
24 * @param mixed $value Value to set 28 * @param mixed $value Value to set
25 */ 29 */
26 public function set($key, $value) 30 public function set($key, $value, $write = false, $isLoggedIn = false)
27 { 31 {
28 $this->values[$key] = $value; 32 $this->values[$key] = $value;
29 } 33 }
@@ -35,7 +39,7 @@ class FakeConfigManager
35 * 39 *
36 * @return mixed The value if set, else the name of the key 40 * @return mixed The value if set, else the name of the key
37 */ 41 */
38 public function get($key) 42 public function get($key, $default = '')
39 { 43 {
40 if (isset($this->values[$key])) { 44 if (isset($this->values[$key])) {
41 return $this->values[$key]; 45 return $this->values[$key];
diff --git a/tests/utils/ReferenceHistory.php b/tests/utils/ReferenceHistory.php
index 516c9f51..aed5d2cf 100644
--- a/tests/utils/ReferenceHistory.php
+++ b/tests/utils/ReferenceHistory.php
@@ -1,6 +1,6 @@
1<?php 1<?php
2 2
3use Shaarli\FileUtils; 3use Shaarli\Helper\FileUtils;
4use Shaarli\History; 4use Shaarli\History;
5 5
6/** 6/**
diff --git a/tests/utils/ReferenceLinkDB.php b/tests/utils/ReferenceLinkDB.php
index 0095f5a1..1f53dc3c 100644
--- a/tests/utils/ReferenceLinkDB.php
+++ b/tests/utils/ReferenceLinkDB.php
@@ -30,7 +30,7 @@ class ReferenceLinkDB
30 $this->addLink( 30 $this->addLink(
31 11, 31 11,
32 'Pined older', 32 'Pined older',
33 '?PCRizQ', 33 '/shaare/PCRizQ',
34 'This is an older pinned link', 34 'This is an older pinned link',
35 0, 35 0,
36 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100309_101010'), 36 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100309_101010'),
@@ -43,7 +43,7 @@ class ReferenceLinkDB
43 $this->addLink( 43 $this->addLink(
44 10, 44 10,
45 'Pined', 45 'Pined',
46 '?0gCTjQ', 46 '/shaare/0gCTjQ',
47 'This is a pinned link', 47 'This is a pinned link',
48 0, 48 0,
49 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121207_152312'), 49 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121207_152312'),
@@ -56,7 +56,7 @@ class ReferenceLinkDB
56 $this->addLink( 56 $this->addLink(
57 41, 57 41,
58 'Link title: @website', 58 'Link title: @website',
59 '?WDWyig', 59 '/shaare/WDWyig',
60 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag', 60 'Stallman has a beard and is part of the Free Software Foundation (or not). Seriously, read this. #hashtag',
61 0, 61 0,
62 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), 62 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'),
@@ -68,7 +68,7 @@ class ReferenceLinkDB
68 $this->addLink( 68 $this->addLink(
69 42, 69 42,
70 'Note: I have a big ID but an old date', 70 'Note: I have a big ID but an old date',
71 '?WDWyig', 71 '/shaare/WDWyig',
72 'Used to test bookmarks reordering.', 72 'Used to test bookmarks reordering.',
73 0, 73 0,
74 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100310_101010'), 74 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100310_101010'),
@@ -82,7 +82,7 @@ class ReferenceLinkDB
82 'This guide extends and expands on PSR-1, the basic coding standard.', 82 'This guide extends and expands on PSR-1, the basic coding standard.',
83 0, 83 0,
84 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_152312'), 84 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_152312'),
85 '' 85 'coding-style standards quality assurance'
86 ); 86 );
87 87
88 $this->addLink( 88 $this->addLink(