aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/ApplicationUtilsTest.php31
-rw-r--r--tests/FileUtilsTest.php22
-rw-r--r--tests/HistoryTest.php255
-rw-r--r--tests/LanguagesTest.php4
-rw-r--r--tests/PluginManagerTest.php79
-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.php12
-rw-r--r--tests/api/ApiMiddlewareTest.php73
-rw-r--r--tests/api/ApiUtilsTest.php145
-rw-r--r--tests/api/controllers/history/HistoryTest.php10
-rw-r--r--tests/api/controllers/info/InfoTest.php24
-rw-r--r--tests/api/controllers/links/DeleteLinkTest.php32
-rw-r--r--tests/api/controllers/links/GetLinkIdTest.php25
-rw-r--r--tests/api/controllers/links/GetLinksTest.php23
-rw-r--r--tests/api/controllers/links/PostLinkTest.php40
-rw-r--r--tests/api/controllers/links/PutLinkTest.php40
-rw-r--r--tests/api/controllers/tags/DeleteTagTest.php41
-rw-r--r--tests/api/controllers/tags/GetTagNameTest.php20
-rw-r--r--tests/api/controllers/tags/GetTagsTest.php29
-rw-r--r--tests/api/controllers/tags/PutTagTest.php43
-rw-r--r--tests/bookmark/BookmarkArrayTest.php236
-rw-r--r--tests/bookmark/BookmarkFileServiceTest.php1111
-rw-r--r--tests/bookmark/BookmarkFilterTest.php526
-rw-r--r--tests/bookmark/BookmarkInitializerTest.php150
-rw-r--r--tests/bookmark/BookmarkTest.php388
-rw-r--r--tests/bookmark/LinkUtilsTest.php48
-rw-r--r--tests/bootstrap.php26
-rw-r--r--tests/config/ConfigJsonTest.php27
-rw-r--r--tests/config/ConfigManagerTest.php26
-rw-r--r--tests/config/ConfigPhpTest.php8
-rw-r--r--tests/config/ConfigPluginTest.php22
-rw-r--r--tests/container/ContainerBuilderTest.php88
-rw-r--r--tests/container/ShaarliTestContainer.php42
-rw-r--r--tests/feed/CachedPageTest.php13
-rw-r--r--tests/feed/FeedBuilderTest.php171
-rw-r--r--tests/formatter/BookmarkDefaultFormatterTest.php177
-rw-r--r--tests/formatter/BookmarkMarkdownFormatterTest.php160
-rw-r--r--tests/formatter/BookmarkRawFormatterTest.php97
-rw-r--r--tests/formatter/FormatterFactoryTest.php101
-rw-r--r--tests/front/ShaarliAdminMiddlewareTest.php100
-rw-r--r--tests/front/ShaarliMiddlewareTest.php221
-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/ManageShaareControllerTest/AddShaareTest.php47
-rw-r--r--tests/front/controller/admin/ManageShaareControllerTest/ChangeVisibilityBookmarkTest.php418
-rw-r--r--tests/front/controller/admin/ManageShaareControllerTest/DeleteBookmarkTest.php376
-rw-r--r--tests/front/controller/admin/ManageShaareControllerTest/DisplayCreateFormTest.php317
-rw-r--r--tests/front/controller/admin/ManageShaareControllerTest/DisplayEditFormTest.php155
-rw-r--r--tests/front/controller/admin/ManageShaareControllerTest/PinBookmarkTest.php145
-rw-r--r--tests/front/controller/admin/ManageShaareControllerTest/SaveBookmarkTest.php308
-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/SessionFilterControllerTest.php177
-rw-r--r--tests/front/controller/admin/ShaarliAdminControllerTest.php184
-rw-r--r--tests/front/controller/admin/ThumbnailsControllerTest.php154
-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.php448
-rw-r--r--tests/front/controller/visitor/DailyControllerTest.php478
-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.php295
-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/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/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/LegacyDummyUpdater.php74
-rw-r--r--tests/legacy/LegacyLinkDBTest.php (renamed from tests/bookmark/LinkDBTest.php)141
-rw-r--r--tests/legacy/LegacyLinkFilterTest.php (renamed from tests/bookmark/LinkFilterTest.php)142
-rw-r--r--tests/legacy/LegacyUpdaterTest.php886
-rw-r--r--tests/netscape/BookmarkExportTest.php110
-rw-r--r--tests/netscape/BookmarkImportTest.php608
-rw-r--r--tests/plugins/PluginAddlinkTest.php14
-rw-r--r--tests/plugins/PluginArchiveorgTest.php41
-rw-r--r--tests/plugins/PluginDefaultColorsTest.php25
-rw-r--r--tests/plugins/PluginIssoTest.php35
-rw-r--r--tests/plugins/PluginMarkdownTest.php306
-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.php4
-rw-r--r--tests/security/LoginManagerTest.php68
-rw-r--r--tests/security/SessionManagerTest.php90
-rw-r--r--tests/updater/DummyUpdater.php21
-rw-r--r--tests/updater/UpdaterTest.php727
-rw-r--r--tests/utils/FakeBookmarkService.php18
-rw-r--r--tests/utils/ReferenceHistory.php2
-rw-r--r--tests/utils/ReferenceLinkDB.php119
-rw-r--r--tests/utils/config/configJson.json.php6
125 files changed, 13269 insertions, 2789 deletions
diff --git a/tests/ApplicationUtilsTest.php b/tests/ApplicationUtilsTest.php
index 82f8804d..a232b351 100644
--- a/tests/ApplicationUtilsTest.php
+++ b/tests/ApplicationUtilsTest.php
@@ -8,7 +8,7 @@ require_once 'tests/utils/FakeApplicationUtils.php';
8/** 8/**
9 * Unitary tests for Shaarli utilities 9 * Unitary tests for Shaarli utilities
10 */ 10 */
11class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase 11class ApplicationUtilsTest extends \Shaarli\TestCase
12{ 12{
13 protected static $testUpdateFile = 'sandbox/update.txt'; 13 protected static $testUpdateFile = 'sandbox/update.txt';
14 protected static $testVersion = '0.5.0'; 14 protected static $testVersion = '0.5.0';
@@ -17,7 +17,7 @@ class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
17 /** 17 /**
18 * Reset test data for each test 18 * Reset test data for each test
19 */ 19 */
20 public function setUp() 20 protected function setUp(): void
21 { 21 {
22 FakeApplicationUtils::$VERSION_CODE = ''; 22 FakeApplicationUtils::$VERSION_CODE = '';
23 if (file_exists(self::$testUpdateFile)) { 23 if (file_exists(self::$testUpdateFile)) {
@@ -28,7 +28,7 @@ class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
28 /** 28 /**
29 * Remove test version file if it exists 29 * Remove test version file if it exists
30 */ 30 */
31 public function tearDown() 31 protected function tearDown(): void
32 { 32 {
33 if (is_file('sandbox/version.php')) { 33 if (is_file('sandbox/version.php')) {
34 unlink('sandbox/version.php'); 34 unlink('sandbox/version.php');
@@ -144,11 +144,12 @@ class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
144 144
145 /** 145 /**
146 * Test update checks - invalid Git branch 146 * Test update checks - invalid Git branch
147 * @expectedException Exception
148 * @expectedExceptionMessageRegExp /Invalid branch selected for updates/
149 */ 147 */
150 public function testCheckUpdateInvalidGitBranch() 148 public function testCheckUpdateInvalidGitBranch()
151 { 149 {
150 $this->expectException(\Exception::class);
151 $this->expectExceptionMessageRegExp('/Invalid branch selected for updates/');
152
152 ApplicationUtils::checkUpdate('', 'null', 0, true, true, 'unstable'); 153 ApplicationUtils::checkUpdate('', 'null', 0, true, true, 'unstable');
153 } 154 }
154 155
@@ -253,29 +254,31 @@ class ApplicationUtilsTest extends \PHPUnit\Framework\TestCase
253 public function testCheckSupportedPHPVersion() 254 public function testCheckSupportedPHPVersion()
254 { 255 {
255 $minVersion = '5.3'; 256 $minVersion = '5.3';
256 ApplicationUtils::checkPHPVersion($minVersion, '5.4.32'); 257 $this->assertTrue(ApplicationUtils::checkPHPVersion($minVersion, '5.4.32'));
257 ApplicationUtils::checkPHPVersion($minVersion, '5.5'); 258 $this->assertTrue(ApplicationUtils::checkPHPVersion($minVersion, '5.5'));
258 ApplicationUtils::checkPHPVersion($minVersion, '5.6.10'); 259 $this->assertTrue(ApplicationUtils::checkPHPVersion($minVersion, '5.6.10'));
259 } 260 }
260 261
261 /** 262 /**
262 * Check a unsupported PHP version 263 * Check a unsupported PHP version
263 * @expectedException Exception
264 * @expectedExceptionMessageRegExp /Your PHP version is obsolete/
265 */ 264 */
266 public function testCheckSupportedPHPVersion51() 265 public function testCheckSupportedPHPVersion51()
267 { 266 {
268 ApplicationUtils::checkPHPVersion('5.3', '5.1.0'); 267 $this->expectException(\Exception::class);
268 $this->expectExceptionMessageRegExp('/Your PHP version is obsolete/');
269
270 $this->assertTrue(ApplicationUtils::checkPHPVersion('5.3', '5.1.0'));
269 } 271 }
270 272
271 /** 273 /**
272 * Check another unsupported PHP version 274 * Check another unsupported PHP version
273 * @expectedException Exception
274 * @expectedExceptionMessageRegExp /Your PHP version is obsolete/
275 */ 275 */
276 public function testCheckSupportedPHPVersion52() 276 public function testCheckSupportedPHPVersion52()
277 { 277 {
278 ApplicationUtils::checkPHPVersion('5.3', '5.2'); 278 $this->expectException(\Exception::class);
279 $this->expectExceptionMessageRegExp('/Your PHP version is obsolete/');
280
281 $this->assertTrue(ApplicationUtils::checkPHPVersion('5.3', '5.2'));
279 } 282 }
280 283
281 /** 284 /**
diff --git a/tests/FileUtilsTest.php b/tests/FileUtilsTest.php
index 57719175..9163bdf1 100644
--- a/tests/FileUtilsTest.php
+++ b/tests/FileUtilsTest.php
@@ -9,7 +9,7 @@ use Exception;
9 * 9 *
10 * Test file utility class. 10 * Test file utility class.
11 */ 11 */
12class FileUtilsTest extends \PHPUnit\Framework\TestCase 12class FileUtilsTest extends \Shaarli\TestCase
13{ 13{
14 /** 14 /**
15 * @var string Test file path. 15 * @var string Test file path.
@@ -19,7 +19,7 @@ class FileUtilsTest extends \PHPUnit\Framework\TestCase
19 /** 19 /**
20 * Delete test file after every test. 20 * Delete test file after every test.
21 */ 21 */
22 public function tearDown() 22 protected function tearDown(): void
23 { 23 {
24 @unlink(self::$file); 24 @unlink(self::$file);
25 } 25 }
@@ -49,12 +49,12 @@ class FileUtilsTest extends \PHPUnit\Framework\TestCase
49 49
50 /** 50 /**
51 * File not writable: raise an exception. 51 * File not writable: raise an exception.
52 *
53 * @expectedException Shaarli\Exceptions\IOException
54 * @expectedExceptionMessage Error accessing "sandbox/flat.db"
55 */ 52 */
56 public function testWriteWithoutPermission() 53 public function testWriteWithoutPermission()
57 { 54 {
55 $this->expectException(\Shaarli\Exceptions\IOException::class);
56 $this->expectExceptionMessage('Error accessing "sandbox/flat.db"');
57
58 touch(self::$file); 58 touch(self::$file);
59 chmod(self::$file, 0440); 59 chmod(self::$file, 0440);
60 FileUtils::writeFlatDB(self::$file, null); 60 FileUtils::writeFlatDB(self::$file, null);
@@ -62,23 +62,23 @@ class FileUtilsTest extends \PHPUnit\Framework\TestCase
62 62
63 /** 63 /**
64 * Folder non existent: raise an exception. 64 * Folder non existent: raise an exception.
65 *
66 * @expectedException Shaarli\Exceptions\IOException
67 * @expectedExceptionMessage Error accessing "nopefolder"
68 */ 65 */
69 public function testWriteFolderDoesNotExist() 66 public function testWriteFolderDoesNotExist()
70 { 67 {
68 $this->expectException(\Shaarli\Exceptions\IOException::class);
69 $this->expectExceptionMessage('Error accessing "nopefolder"');
70
71 FileUtils::writeFlatDB('nopefolder/file', null); 71 FileUtils::writeFlatDB('nopefolder/file', null);
72 } 72 }
73 73
74 /** 74 /**
75 * Folder non writable: raise an exception. 75 * Folder non writable: raise an exception.
76 *
77 * @expectedException Shaarli\Exceptions\IOException
78 * @expectedExceptionMessage Error accessing "sandbox"
79 */ 76 */
80 public function testWriteFolderPermission() 77 public function testWriteFolderPermission()
81 { 78 {
79 $this->expectException(\Shaarli\Exceptions\IOException::class);
80 $this->expectExceptionMessage('Error accessing "sandbox"');
81
82 chmod(dirname(self::$file), 0555); 82 chmod(dirname(self::$file), 0555);
83 try { 83 try {
84 FileUtils::writeFlatDB(self::$file, null); 84 FileUtils::writeFlatDB(self::$file, null);
diff --git a/tests/HistoryTest.php b/tests/HistoryTest.php
index 8303e53a..6dc0e5b7 100644
--- a/tests/HistoryTest.php
+++ b/tests/HistoryTest.php
@@ -3,9 +3,9 @@
3namespace Shaarli; 3namespace Shaarli;
4 4
5use DateTime; 5use DateTime;
6use Exception; 6use Shaarli\Bookmark\Bookmark;
7 7
8class HistoryTest extends \PHPUnit\Framework\TestCase 8class HistoryTest extends \Shaarli\TestCase
9{ 9{
10 /** 10 /**
11 * @var string History file path 11 * @var string History file path
@@ -15,9 +15,11 @@ class HistoryTest extends \PHPUnit\Framework\TestCase
15 /** 15 /**
16 * Delete history file. 16 * Delete history file.
17 */ 17 */
18 public function tearDown() 18 protected function setUp(): void
19 { 19 {
20 @unlink(self::$historyFilePath); 20 if (file_exists(self::$historyFilePath)) {
21 unlink(self::$historyFilePath);
22 }
21 } 23 }
22 24
23 /** 25 /**
@@ -41,12 +43,12 @@ class HistoryTest extends \PHPUnit\Framework\TestCase
41 43
42 /** 44 /**
43 * Not writable history file: raise an exception. 45 * Not writable history file: raise an exception.
44 *
45 * @expectedException Exception
46 * @expectedExceptionMessage History file isn't readable or writable
47 */ 46 */
48 public function testConstructNotWritable() 47 public function testConstructNotWritable()
49 { 48 {
49 $this->expectException(\Exception::class);
50 $this->expectExceptionMessage('History file isn\'t readable or writable');
51
50 touch(self::$historyFilePath); 52 touch(self::$historyFilePath);
51 chmod(self::$historyFilePath, 0440); 53 chmod(self::$historyFilePath, 0440);
52 $history = new History(self::$historyFilePath); 54 $history = new History(self::$historyFilePath);
@@ -55,12 +57,12 @@ class HistoryTest extends \PHPUnit\Framework\TestCase
55 57
56 /** 58 /**
57 * Not parsable history file: raise an exception. 59 * Not parsable history file: raise an exception.
58 *
59 * @expectedException Exception
60 * @expectedExceptionMessageRegExp /Could not parse history file/
61 */ 60 */
62 public function testConstructNotParsable() 61 public function testConstructNotParsable()
63 { 62 {
63 $this->expectException(\Exception::class);
64 $this->expectExceptionMessageRegExp('/Could not parse history file/');
65
64 file_put_contents(self::$historyFilePath, 'not parsable'); 66 file_put_contents(self::$historyFilePath, 'not parsable');
65 $history = new History(self::$historyFilePath); 67 $history = new History(self::$historyFilePath);
66 // gzinflate generates a warning 68 // gzinflate generates a warning
@@ -73,137 +75,140 @@ class HistoryTest extends \PHPUnit\Framework\TestCase
73 public function testAddLink() 75 public function testAddLink()
74 { 76 {
75 $history = new History(self::$historyFilePath); 77 $history = new History(self::$historyFilePath);
76 $history->addLink(['id' => 0]); 78 $bookmark = (new Bookmark())->setId(0);
79 $history->addLink($bookmark);
77 $actual = $history->getHistory()[0]; 80 $actual = $history->getHistory()[0];
78 $this->assertEquals(History::CREATED, $actual['event']); 81 $this->assertEquals(History::CREATED, $actual['event']);
79 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 82 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
80 $this->assertEquals(0, $actual['id']); 83 $this->assertEquals(0, $actual['id']);
81 84
82 $history = new History(self::$historyFilePath); 85 $history = new History(self::$historyFilePath);
83 $history->addLink(['id' => 1]); 86 $bookmark = (new Bookmark())->setId(1);
87 $history->addLink($bookmark);
84 $actual = $history->getHistory()[0]; 88 $actual = $history->getHistory()[0];
85 $this->assertEquals(History::CREATED, $actual['event']); 89 $this->assertEquals(History::CREATED, $actual['event']);
86 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 90 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
87 $this->assertEquals(1, $actual['id']); 91 $this->assertEquals(1, $actual['id']);
88 92
89 $history = new History(self::$historyFilePath); 93 $history = new History(self::$historyFilePath);
90 $history->addLink(['id' => 'str']); 94 $bookmark = (new Bookmark())->setId('str');
95 $history->addLink($bookmark);
91 $actual = $history->getHistory()[0]; 96 $actual = $history->getHistory()[0];
92 $this->assertEquals(History::CREATED, $actual['event']); 97 $this->assertEquals(History::CREATED, $actual['event']);
93 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 98 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
94 $this->assertEquals('str', $actual['id']); 99 $this->assertEquals('str', $actual['id']);
95 } 100 }
96 101
97 /** 102// /**
98 * Test updated link event 103// * Test updated link event
99 */ 104// */
100 public function testUpdateLink() 105// public function testUpdateLink()
101 { 106// {
102 $history = new History(self::$historyFilePath); 107// $history = new History(self::$historyFilePath);
103 $history->updateLink(['id' => 1]); 108// $history->updateLink(['id' => 1]);
104 $actual = $history->getHistory()[0]; 109// $actual = $history->getHistory()[0];
105 $this->assertEquals(History::UPDATED, $actual['event']); 110// $this->assertEquals(History::UPDATED, $actual['event']);
106 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 111// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
107 $this->assertEquals(1, $actual['id']); 112// $this->assertEquals(1, $actual['id']);
108 } 113// }
109 114//
110 /** 115// /**
111 * Test delete link event 116// * Test delete link event
112 */ 117// */
113 public function testDeleteLink() 118// public function testDeleteLink()
114 { 119// {
115 $history = new History(self::$historyFilePath); 120// $history = new History(self::$historyFilePath);
116 $history->deleteLink(['id' => 1]); 121// $history->deleteLink(['id' => 1]);
117 $actual = $history->getHistory()[0]; 122// $actual = $history->getHistory()[0];
118 $this->assertEquals(History::DELETED, $actual['event']); 123// $this->assertEquals(History::DELETED, $actual['event']);
119 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 124// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
120 $this->assertEquals(1, $actual['id']); 125// $this->assertEquals(1, $actual['id']);
121 } 126// }
122 127//
123 /** 128// /**
124 * Test updated settings event 129// * Test updated settings event
125 */ 130// */
126 public function testUpdateSettings() 131// public function testUpdateSettings()
127 { 132// {
128 $history = new History(self::$historyFilePath); 133// $history = new History(self::$historyFilePath);
129 $history->updateSettings(); 134// $history->updateSettings();
130 $actual = $history->getHistory()[0]; 135// $actual = $history->getHistory()[0];
131 $this->assertEquals(History::SETTINGS, $actual['event']); 136// $this->assertEquals(History::SETTINGS, $actual['event']);
132 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 137// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
133 $this->assertEmpty($actual['id']); 138// $this->assertEmpty($actual['id']);
134 } 139// }
135 140//
136 /** 141// /**
137 * Make sure that new items are stored at the beginning 142// * Make sure that new items are stored at the beginning
138 */ 143// */
139 public function testHistoryOrder() 144// public function testHistoryOrder()
140 { 145// {
141 $history = new History(self::$historyFilePath); 146// $history = new History(self::$historyFilePath);
142 $history->updateLink(['id' => 1]); 147// $history->updateLink(['id' => 1]);
143 $actual = $history->getHistory()[0]; 148// $actual = $history->getHistory()[0];
144 $this->assertEquals(History::UPDATED, $actual['event']); 149// $this->assertEquals(History::UPDATED, $actual['event']);
145 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 150// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
146 $this->assertEquals(1, $actual['id']); 151// $this->assertEquals(1, $actual['id']);
147 152//
148 $history->addLink(['id' => 1]); 153// $history->addLink(['id' => 1]);
149 $actual = $history->getHistory()[0]; 154// $actual = $history->getHistory()[0];
150 $this->assertEquals(History::CREATED, $actual['event']); 155// $this->assertEquals(History::CREATED, $actual['event']);
151 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 156// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
152 $this->assertEquals(1, $actual['id']); 157// $this->assertEquals(1, $actual['id']);
153 } 158// }
154 159//
155 /** 160// /**
156 * Re-read history from file after writing an event 161// * Re-read history from file after writing an event
157 */ 162// */
158 public function testHistoryRead() 163// public function testHistoryRead()
159 { 164// {
160 $history = new History(self::$historyFilePath); 165// $history = new History(self::$historyFilePath);
161 $history->updateLink(['id' => 1]); 166// $history->updateLink(['id' => 1]);
162 $history = new History(self::$historyFilePath); 167// $history = new History(self::$historyFilePath);
163 $actual = $history->getHistory()[0]; 168// $actual = $history->getHistory()[0];
164 $this->assertEquals(History::UPDATED, $actual['event']); 169// $this->assertEquals(History::UPDATED, $actual['event']);
165 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 170// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
166 $this->assertEquals(1, $actual['id']); 171// $this->assertEquals(1, $actual['id']);
167 } 172// }
168 173//
169 /** 174// /**
170 * Re-read history from file after writing an event and make sure that the order is correct 175// * Re-read history from file after writing an event and make sure that the order is correct
171 */ 176// */
172 public function testHistoryOrderRead() 177// public function testHistoryOrderRead()
173 { 178// {
174 $history = new History(self::$historyFilePath); 179// $history = new History(self::$historyFilePath);
175 $history->updateLink(['id' => 1]); 180// $history->updateLink(['id' => 1]);
176 $history->addLink(['id' => 1]); 181// $history->addLink(['id' => 1]);
177 182//
178 $history = new History(self::$historyFilePath); 183// $history = new History(self::$historyFilePath);
179 $actual = $history->getHistory()[0]; 184// $actual = $history->getHistory()[0];
180 $this->assertEquals(History::CREATED, $actual['event']); 185// $this->assertEquals(History::CREATED, $actual['event']);
181 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 186// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
182 $this->assertEquals(1, $actual['id']); 187// $this->assertEquals(1, $actual['id']);
183 188//
184 $actual = $history->getHistory()[1]; 189// $actual = $history->getHistory()[1];
185 $this->assertEquals(History::UPDATED, $actual['event']); 190// $this->assertEquals(History::UPDATED, $actual['event']);
186 $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']); 191// $this->assertTrue(new DateTime('-2 seconds') < $actual['datetime']);
187 $this->assertEquals(1, $actual['id']); 192// $this->assertEquals(1, $actual['id']);
188 } 193// }
189 194//
190 /** 195// /**
191 * Test retention time: delete old entries. 196// * Test retention time: delete old entries.
192 */ 197// */
193 public function testHistoryRententionTime() 198// public function testHistoryRententionTime()
194 { 199// {
195 $history = new History(self::$historyFilePath, 5); 200// $history = new History(self::$historyFilePath, 5);
196 $history->updateLink(['id' => 1]); 201// $history->updateLink(['id' => 1]);
197 $this->assertEquals(1, count($history->getHistory())); 202// $this->assertEquals(1, count($history->getHistory()));
198 $arr = $history->getHistory(); 203// $arr = $history->getHistory();
199 $arr[0]['datetime'] = new DateTime('-1 hour'); 204// $arr[0]['datetime'] = new DateTime('-1 hour');
200 FileUtils::writeFlatDB(self::$historyFilePath, $arr); 205// FileUtils::writeFlatDB(self::$historyFilePath, $arr);
201 206//
202 $history = new History(self::$historyFilePath, 60); 207// $history = new History(self::$historyFilePath, 60);
203 $this->assertEquals(1, count($history->getHistory())); 208// $this->assertEquals(1, count($history->getHistory()));
204 $this->assertEquals(1, $history->getHistory()[0]['id']); 209// $this->assertEquals(1, $history->getHistory()[0]['id']);
205 $history->updateLink(['id' => 2]); 210// $history->updateLink(['id' => 2]);
206 $this->assertEquals(1, count($history->getHistory())); 211// $this->assertEquals(1, count($history->getHistory()));
207 $this->assertEquals(2, $history->getHistory()[0]['id']); 212// $this->assertEquals(2, $history->getHistory()[0]['id']);
208 } 213// }
209} 214}
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 71761ac1..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,58 +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 *
62 * @return void
63 */ 93 */
64 public function testPluginNotFound() 94 public function testPluginNotFound(): void
65 { 95 {
66 $this->pluginManager->load(array()); 96 $this->pluginManager->load([]);
67 $this->pluginManager->load(array('nope', 'renope')); 97 $this->pluginManager->load(['nope', 'renope']);
98 $this->addToAssertionCount(1);
68 } 99 }
69 100
70 /** 101 /**
71 * Test plugin metadata loading. 102 * Test plugin metadata loading.
72 */ 103 */
73 public function testGetPluginsMeta() 104 public function testGetPluginsMeta(): void
74 { 105 {
75 PluginManager::$PLUGINS_PATH = self::$pluginPath; 106 PluginManager::$PLUGINS_PATH = self::$pluginPath;
76 $this->pluginManager->load(array(self::$pluginName)); 107 $this->pluginManager->load([self::$pluginName]);
77 108
78 $expectedParameters = array( 109 $expectedParameters = [
79 'pop' => array( 110 'pop' => [
80 'value' => '', 111 'value' => '',
81 'desc' => 'pop description', 112 'desc' => 'pop description',
82 ), 113 ],
83 'hip' => array( 114 'hip' => [
84 'value' => '', 115 'value' => '',
85 'desc' => '', 116 'desc' => '',
86 ), 117 ],
87 ); 118 ];
88 $meta = $this->pluginManager->getPluginsMeta(); 119 $meta = $this->pluginManager->getPluginsMeta();
89 $this->assertEquals('test plugin', $meta[self::$pluginName]['description']); 120 $this->assertEquals('test plugin', $meta[self::$pluginName]['description']);
90 $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 8225d95a..6e787d7f 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);
@@ -203,7 +203,7 @@ class UtilsTest extends PHPUnit\Framework\TestCase
203 public function testGenerateLocationLoop() 203 public function testGenerateLocationLoop()
204 { 204 {
205 $ref = 'http://localhost/?test'; 205 $ref = 'http://localhost/?test';
206 $this->assertEquals('?', generateLocation($ref, 'localhost', array('test'))); 206 $this->assertEquals('./?', generateLocation($ref, 'localhost', array('test')));
207 } 207 }
208 208
209 /** 209 /**
@@ -212,7 +212,7 @@ class UtilsTest extends PHPUnit\Framework\TestCase
212 public function testGenerateLocationOut() 212 public function testGenerateLocationOut()
213 { 213 {
214 $ref = 'http://somewebsite.com/?test'; 214 $ref = 'http://somewebsite.com/?test';
215 $this->assertEquals('?', generateLocation($ref, 'localhost')); 215 $this->assertEquals('./?', generateLocation($ref, 'localhost'));
216 } 216 }
217 217
218 218
diff --git a/tests/api/ApiMiddlewareTest.php b/tests/api/ApiMiddlewareTest.php
index 0b9b03f2..86700840 100644
--- a/tests/api/ApiMiddlewareTest.php
+++ b/tests/api/ApiMiddlewareTest.php
@@ -2,6 +2,7 @@
2namespace Shaarli\Api; 2namespace Shaarli\Api;
3 3
4use Shaarli\Config\ConfigManager; 4use Shaarli\Config\ConfigManager;
5use Shaarli\History;
5use Slim\Container; 6use Slim\Container;
6use Slim\Http\Environment; 7use Slim\Http\Environment;
7use Slim\Http\Request; 8use Slim\Http\Request;
@@ -17,7 +18,7 @@ use Slim\Http\Response;
17 * 18 *
18 * @package Api 19 * @package Api
19 */ 20 */
20class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase 21class ApiMiddlewareTest extends \Shaarli\TestCase
21{ 22{
22 /** 23 /**
23 * @var string datastore to test write operations 24 * @var string datastore to test write operations
@@ -25,7 +26,7 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
25 protected static $testDatastore = 'sandbox/datastore.php'; 26 protected static $testDatastore = 'sandbox/datastore.php';
26 27
27 /** 28 /**
28 * @var \ConfigManager instance 29 * @var ConfigManager instance
29 */ 30 */
30 protected $conf; 31 protected $conf;
31 32
@@ -40,29 +41,79 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
40 protected $container; 41 protected $container;
41 42
42 /** 43 /**
43 * Before every test, instantiate a new Api with its config, plugins and links. 44 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
44 */ 45 */
45 public function setUp() 46 protected function setUp(): void
46 { 47 {
47 $this->conf = new ConfigManager('tests/utils/config/configJson.json.php'); 48 $this->conf = new ConfigManager('tests/utils/config/configJson');
48 $this->conf->set('api.secret', 'NapoleonWasALizard'); 49 $this->conf->set('api.secret', 'NapoleonWasALizard');
49 50
50 $this->refDB = new \ReferenceLinkDB(); 51 $this->refDB = new \ReferenceLinkDB();
51 $this->refDB->write(self::$testDatastore); 52 $this->refDB->write(self::$testDatastore);
52 53
54 $history = new History('sandbox/history.php');
55
53 $this->container = new Container(); 56 $this->container = new Container();
54 $this->container['conf'] = $this->conf; 57 $this->container['conf'] = $this->conf;
58 $this->container['history'] = $history;
55 } 59 }
56 60
57 /** 61 /**
58 * After every test, remove the test datastore. 62 * After every test, remove the test datastore.
59 */ 63 */
60 public function tearDown() 64 protected function tearDown(): void
61 { 65 {
62 @unlink(self::$testDatastore); 66 @unlink(self::$testDatastore);
63 } 67 }
64 68
65 /** 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 /**
66 * Invoke the middleware with the API disabled: 117 * Invoke the middleware with the API disabled:
67 * should return a 401 error Unauthorized. 118 * should return a 401 error Unauthorized.
68 */ 119 */
@@ -105,7 +156,7 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
105 $this->assertEquals(401, $response->getStatusCode()); 156 $this->assertEquals(401, $response->getStatusCode());
106 $body = json_decode((string) $response->getBody()); 157 $body = json_decode((string) $response->getBody());
107 $this->assertEquals('Not authorized: API is disabled', $body->message); 158 $this->assertEquals('Not authorized: API is disabled', $body->message);
108 $this->assertContains('ApiAuthorizationException', $body->stacktrace); 159 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
109 } 160 }
110 161
111 /** 162 /**
@@ -128,7 +179,7 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
128 $this->assertEquals(401, $response->getStatusCode()); 179 $this->assertEquals(401, $response->getStatusCode());
129 $body = json_decode((string) $response->getBody()); 180 $body = json_decode((string) $response->getBody());
130 $this->assertEquals('Not authorized: JWT token not provided', $body->message); 181 $this->assertEquals('Not authorized: JWT token not provided', $body->message);
131 $this->assertContains('ApiAuthorizationException', $body->stacktrace); 182 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
132 } 183 }
133 184
134 /** 185 /**
@@ -153,7 +204,7 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
153 $this->assertEquals(401, $response->getStatusCode()); 204 $this->assertEquals(401, $response->getStatusCode());
154 $body = json_decode((string) $response->getBody()); 205 $body = json_decode((string) $response->getBody());
155 $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);
156 $this->assertContains('ApiAuthorizationException', $body->stacktrace); 207 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
157 } 208 }
158 209
159 /** 210 /**
@@ -176,7 +227,7 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
176 $this->assertEquals(401, $response->getStatusCode()); 227 $this->assertEquals(401, $response->getStatusCode());
177 $body = json_decode((string) $response->getBody()); 228 $body = json_decode((string) $response->getBody());
178 $this->assertEquals('Not authorized: Invalid JWT header', $body->message); 229 $this->assertEquals('Not authorized: Invalid JWT header', $body->message);
179 $this->assertContains('ApiAuthorizationException', $body->stacktrace); 230 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
180 } 231 }
181 232
182 /** 233 /**
@@ -202,6 +253,6 @@ class ApiMiddlewareTest extends \PHPUnit\Framework\TestCase
202 $this->assertEquals(401, $response->getStatusCode()); 253 $this->assertEquals(401, $response->getStatusCode());
203 $body = json_decode((string) $response->getBody()); 254 $body = json_decode((string) $response->getBody());
204 $this->assertEquals('Not authorized: Malformed JWT token', $body->message); 255 $this->assertEquals('Not authorized: Malformed JWT token', $body->message);
205 $this->assertContains('ApiAuthorizationException', $body->stacktrace); 256 $this->assertContainsPolyfill('ApiAuthorizationException', $body->stacktrace);
206 } 257 }
207} 258}
diff --git a/tests/api/ApiUtilsTest.php b/tests/api/ApiUtilsTest.php
index ea0ae500..7a143859 100644
--- a/tests/api/ApiUtilsTest.php
+++ b/tests/api/ApiUtilsTest.php
@@ -2,17 +2,18 @@
2 2
3namespace Shaarli\Api; 3namespace Shaarli\Api;
4 4
5use Shaarli\Bookmark\Bookmark;
5use Shaarli\Http\Base64Url; 6use Shaarli\Http\Base64Url;
6 7
7/** 8/**
8 * Class ApiUtilsTest 9 * Class ApiUtilsTest
9 */ 10 */
10class ApiUtilsTest extends \PHPUnit\Framework\TestCase 11class ApiUtilsTest extends \Shaarli\TestCase
11{ 12{
12 /** 13 /**
13 * Force the timezone for ISO datetimes. 14 * Force the timezone for ISO datetimes.
14 */ 15 */
15 public static function setUpBeforeClass() 16 public static function setUpBeforeClass(): void
16 { 17 {
17 date_default_timezone_set('UTC'); 18 date_default_timezone_set('UTC');
18 } 19 }
@@ -60,148 +61,148 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
60 public function testValidateJwtTokenValid() 61 public function testValidateJwtTokenValid()
61 { 62 {
62 $secret = 'WarIsPeace'; 63 $secret = 'WarIsPeace';
63 ApiUtils::validateJwtToken(self::generateValidJwtToken($secret), $secret); 64 $this->assertTrue(ApiUtils::validateJwtToken(self::generateValidJwtToken($secret), $secret));
64 } 65 }
65 66
66 /** 67 /**
67 * Test validateJwtToken() with a malformed JWT token. 68 * Test validateJwtToken() with a malformed JWT token.
68 *
69 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
70 * @expectedExceptionMessage Malformed JWT token
71 */ 69 */
72 public function testValidateJwtTokenMalformed() 70 public function testValidateJwtTokenMalformed()
73 { 71 {
72 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
73 $this->expectExceptionMessage('Malformed JWT token');
74
74 $token = 'ABC.DEF'; 75 $token = 'ABC.DEF';
75 ApiUtils::validateJwtToken($token, 'foo'); 76 ApiUtils::validateJwtToken($token, 'foo');
76 } 77 }
77 78
78 /** 79 /**
79 * Test validateJwtToken() with an empty JWT token. 80 * Test validateJwtToken() with an empty JWT token.
80 *
81 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
82 * @expectedExceptionMessage Malformed JWT token
83 */ 81 */
84 public function testValidateJwtTokenMalformedEmpty() 82 public function testValidateJwtTokenMalformedEmpty()
85 { 83 {
84 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
85 $this->expectExceptionMessage('Malformed JWT token');
86
86 $token = false; 87 $token = false;
87 ApiUtils::validateJwtToken($token, 'foo'); 88 ApiUtils::validateJwtToken($token, 'foo');
88 } 89 }
89 90
90 /** 91 /**
91 * Test validateJwtToken() with a JWT token without header. 92 * Test validateJwtToken() with a JWT token without header.
92 *
93 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
94 * @expectedExceptionMessage Malformed JWT token
95 */ 93 */
96 public function testValidateJwtTokenMalformedEmptyHeader() 94 public function testValidateJwtTokenMalformedEmptyHeader()
97 { 95 {
96 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
97 $this->expectExceptionMessage('Malformed JWT token');
98
98 $token = '.payload.signature'; 99 $token = '.payload.signature';
99 ApiUtils::validateJwtToken($token, 'foo'); 100 ApiUtils::validateJwtToken($token, 'foo');
100 } 101 }
101 102
102 /** 103 /**
103 * Test validateJwtToken() with a JWT token without payload 104 * Test validateJwtToken() with a JWT token without payload
104 *
105 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
106 * @expectedExceptionMessage Malformed JWT token
107 */ 105 */
108 public function testValidateJwtTokenMalformedEmptyPayload() 106 public function testValidateJwtTokenMalformedEmptyPayload()
109 { 107 {
108 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
109 $this->expectExceptionMessage('Malformed JWT token');
110
110 $token = 'header..signature'; 111 $token = 'header..signature';
111 ApiUtils::validateJwtToken($token, 'foo'); 112 ApiUtils::validateJwtToken($token, 'foo');
112 } 113 }
113 114
114 /** 115 /**
115 * Test validateJwtToken() with a JWT token with an empty signature. 116 * Test validateJwtToken() with a JWT token with an empty signature.
116 *
117 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
118 * @expectedExceptionMessage Invalid JWT signature
119 */ 117 */
120 public function testValidateJwtTokenInvalidSignatureEmpty() 118 public function testValidateJwtTokenInvalidSignatureEmpty()
121 { 119 {
120 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
121 $this->expectExceptionMessage('Invalid JWT signature');
122
122 $token = 'header.payload.'; 123 $token = 'header.payload.';
123 ApiUtils::validateJwtToken($token, 'foo'); 124 ApiUtils::validateJwtToken($token, 'foo');
124 } 125 }
125 126
126 /** 127 /**
127 * Test validateJwtToken() with a JWT token with an invalid signature. 128 * Test validateJwtToken() with a JWT token with an invalid signature.
128 *
129 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
130 * @expectedExceptionMessage Invalid JWT signature
131 */ 129 */
132 public function testValidateJwtTokenInvalidSignature() 130 public function testValidateJwtTokenInvalidSignature()
133 { 131 {
132 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
133 $this->expectExceptionMessage('Invalid JWT signature');
134
134 $token = 'header.payload.nope'; 135 $token = 'header.payload.nope';
135 ApiUtils::validateJwtToken($token, 'foo'); 136 ApiUtils::validateJwtToken($token, 'foo');
136 } 137 }
137 138
138 /** 139 /**
139 * 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.
140 *
141 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
142 * @expectedExceptionMessage Invalid JWT signature
143 */ 141 */
144 public function testValidateJwtTokenInvalidSignatureSecret() 142 public function testValidateJwtTokenInvalidSignatureSecret()
145 { 143 {
144 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
145 $this->expectExceptionMessage('Invalid JWT signature');
146
146 ApiUtils::validateJwtToken(self::generateValidJwtToken('foo'), 'bar'); 147 ApiUtils::validateJwtToken(self::generateValidJwtToken('foo'), 'bar');
147 } 148 }
148 149
149 /** 150 /**
150 * 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).
151 *
152 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
153 * @expectedExceptionMessage Invalid JWT header
154 */ 152 */
155 public function testValidateJwtTokenInvalidHeader() 153 public function testValidateJwtTokenInvalidHeader()
156 { 154 {
155 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
156 $this->expectExceptionMessage('Invalid JWT header');
157
157 $token = $this->generateCustomJwtToken('notJSON', '{"JSON":1}', 'secret'); 158 $token = $this->generateCustomJwtToken('notJSON', '{"JSON":1}', 'secret');
158 ApiUtils::validateJwtToken($token, 'secret'); 159 ApiUtils::validateJwtToken($token, 'secret');
159 } 160 }
160 161
161 /** 162 /**
162 * 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).
163 *
164 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
165 * @expectedExceptionMessage Invalid JWT payload
166 */ 164 */
167 public function testValidateJwtTokenInvalidPayload() 165 public function testValidateJwtTokenInvalidPayload()
168 { 166 {
167 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
168 $this->expectExceptionMessage('Invalid JWT payload');
169
169 $token = $this->generateCustomJwtToken('{"JSON":1}', 'notJSON', 'secret'); 170 $token = $this->generateCustomJwtToken('{"JSON":1}', 'notJSON', 'secret');
170 ApiUtils::validateJwtToken($token, 'secret'); 171 ApiUtils::validateJwtToken($token, 'secret');
171 } 172 }
172 173
173 /** 174 /**
174 * Test validateJwtToken() with a JWT token without issued time. 175 * Test validateJwtToken() with a JWT token without issued time.
175 *
176 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
177 * @expectedExceptionMessage Invalid JWT issued time
178 */ 176 */
179 public function testValidateJwtTokenInvalidTimeEmpty() 177 public function testValidateJwtTokenInvalidTimeEmpty()
180 { 178 {
179 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
180 $this->expectExceptionMessage('Invalid JWT issued time');
181
181 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"JSON":1}', 'secret'); 182 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"JSON":1}', 'secret');
182 ApiUtils::validateJwtToken($token, 'secret'); 183 ApiUtils::validateJwtToken($token, 'secret');
183 } 184 }
184 185
185 /** 186 /**
186 * Test validateJwtToken() with an expired JWT token. 187 * Test validateJwtToken() with an expired JWT token.
187 *
188 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
189 * @expectedExceptionMessage Invalid JWT issued time
190 */ 188 */
191 public function testValidateJwtTokenInvalidTimeExpired() 189 public function testValidateJwtTokenInvalidTimeExpired()
192 { 190 {
191 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
192 $this->expectExceptionMessage('Invalid JWT issued time');
193
193 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() - 600) . '}', 'secret'); 194 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() - 600) . '}', 'secret');
194 ApiUtils::validateJwtToken($token, 'secret'); 195 ApiUtils::validateJwtToken($token, 'secret');
195 } 196 }
196 197
197 /** 198 /**
198 * Test validateJwtToken() with a JWT token issued in the future. 199 * Test validateJwtToken() with a JWT token issued in the future.
199 *
200 * @expectedException \Shaarli\Api\Exceptions\ApiAuthorizationException
201 * @expectedExceptionMessage Invalid JWT issued time
202 */ 200 */
203 public function testValidateJwtTokenInvalidTimeFuture() 201 public function testValidateJwtTokenInvalidTimeFuture()
204 { 202 {
203 $this->expectException(\Shaarli\Api\Exceptions\ApiAuthorizationException::class);
204 $this->expectExceptionMessage('Invalid JWT issued time');
205
205 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() + 60) . '}', 'secret'); 206 $token = $this->generateCustomJwtToken('{"JSON":1}', '{"iat":' . (time() + 60) . '}', 'secret');
206 ApiUtils::validateJwtToken($token, 'secret'); 207 ApiUtils::validateJwtToken($token, 'secret');
207 } 208 }
@@ -212,7 +213,7 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
212 public function testFormatLinkComplete() 213 public function testFormatLinkComplete()
213 { 214 {
214 $indexUrl = 'https://domain.tld/sub/'; 215 $indexUrl = 'https://domain.tld/sub/';
215 $link = [ 216 $data = [
216 'id' => 12, 217 'id' => 12,
217 'url' => 'http://lol.lol', 218 'url' => 'http://lol.lol',
218 'shorturl' => 'abc', 219 'shorturl' => 'abc',
@@ -223,6 +224,8 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
223 'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'), 224 'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'),
224 'updated' => \DateTime::createFromFormat('Ymd_His', '20170107_160612'), 225 'updated' => \DateTime::createFromFormat('Ymd_His', '20170107_160612'),
225 ]; 226 ];
227 $bookmark = new Bookmark();
228 $bookmark->fromArray($data);
226 229
227 $expected = [ 230 $expected = [
228 'id' => 12, 231 'id' => 12,
@@ -236,7 +239,7 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
236 'updated' => '2017-01-07T16:06:12+00:00', 239 'updated' => '2017-01-07T16:06:12+00:00',
237 ]; 240 ];
238 241
239 $this->assertEquals($expected, ApiUtils::formatLink($link, $indexUrl)); 242 $this->assertEquals($expected, ApiUtils::formatLink($bookmark, $indexUrl));
240 } 243 }
241 244
242 /** 245 /**
@@ -245,7 +248,7 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
245 public function testFormatLinkMinimalNote() 248 public function testFormatLinkMinimalNote()
246 { 249 {
247 $indexUrl = 'https://domain.tld/sub/'; 250 $indexUrl = 'https://domain.tld/sub/';
248 $link = [ 251 $data = [
249 'id' => 12, 252 'id' => 12,
250 'url' => '?abc', 253 'url' => '?abc',
251 'shorturl' => 'abc', 254 'shorturl' => 'abc',
@@ -255,6 +258,8 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
255 'private' => '', 258 'private' => '',
256 'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'), 259 'created' => \DateTime::createFromFormat('Ymd_His', '20170107_160102'),
257 ]; 260 ];
261 $bookmark = new Bookmark();
262 $bookmark->fromArray($data);
258 263
259 $expected = [ 264 $expected = [
260 'id' => 12, 265 'id' => 12,
@@ -268,7 +273,7 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
268 'updated' => '', 273 'updated' => '',
269 ]; 274 ];
270 275
271 $this->assertEquals($expected, ApiUtils::formatLink($link, $indexUrl)); 276 $this->assertEquals($expected, ApiUtils::formatLink($bookmark, $indexUrl));
272 } 277 }
273 278
274 /** 279 /**
@@ -277,7 +282,7 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
277 public function testUpdateLink() 282 public function testUpdateLink()
278 { 283 {
279 $created = \DateTime::createFromFormat('Ymd_His', '20170107_160102'); 284 $created = \DateTime::createFromFormat('Ymd_His', '20170107_160102');
280 $old = [ 285 $data = [
281 'id' => 12, 286 'id' => 12,
282 'url' => '?abc', 287 'url' => '?abc',
283 'shorturl' => 'abc', 288 'shorturl' => 'abc',
@@ -287,8 +292,10 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
287 'private' => '', 292 'private' => '',
288 'created' => $created, 293 'created' => $created,
289 ]; 294 ];
295 $old = new Bookmark();
296 $old->fromArray($data);
290 297
291 $new = [ 298 $data = [
292 'id' => 13, 299 'id' => 13,
293 'shorturl' => 'nope', 300 'shorturl' => 'nope',
294 'url' => 'http://somewhere.else', 301 'url' => 'http://somewhere.else',
@@ -299,17 +306,18 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
299 'created' => 'creation', 306 'created' => 'creation',
300 'updated' => 'updation', 307 'updated' => 'updation',
301 ]; 308 ];
309 $new = new Bookmark();
310 $new->fromArray($data);
302 311
303 $result = ApiUtils::updateLink($old, $new); 312 $result = ApiUtils::updateLink($old, $new);
304 $this->assertEquals(12, $result['id']); 313 $this->assertEquals(12, $result->getId());
305 $this->assertEquals('http://somewhere.else', $result['url']); 314 $this->assertEquals('http://somewhere.else', $result->getUrl());
306 $this->assertEquals('abc', $result['shorturl']); 315 $this->assertEquals('abc', $result->getShortUrl());
307 $this->assertEquals('Le Cid', $result['title']); 316 $this->assertEquals('Le Cid', $result->getTitle());
308 $this->assertEquals('Percé jusques au fond du cœur [...]', $result['description']); 317 $this->assertEquals('Percé jusques au fond du cœur [...]', $result->getDescription());
309 $this->assertEquals('corneille rodrigue', $result['tags']); 318 $this->assertEquals('corneille rodrigue', $result->getTagsString());
310 $this->assertEquals(true, $result['private']); 319 $this->assertEquals(true, $result->isPrivate());
311 $this->assertEquals($created, $result['created']); 320 $this->assertEquals($created, $result->getCreated());
312 $this->assertTrue(new \DateTime('5 seconds ago') < $result['updated']);
313 } 321 }
314 322
315 /** 323 /**
@@ -318,7 +326,7 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
318 public function testUpdateLinkMinimal() 326 public function testUpdateLinkMinimal()
319 { 327 {
320 $created = \DateTime::createFromFormat('Ymd_His', '20170107_160102'); 328 $created = \DateTime::createFromFormat('Ymd_His', '20170107_160102');
321 $old = [ 329 $data = [
322 'id' => 12, 330 'id' => 12,
323 'url' => '?abc', 331 'url' => '?abc',
324 'shorturl' => 'abc', 332 'shorturl' => 'abc',
@@ -328,24 +336,19 @@ class ApiUtilsTest extends \PHPUnit\Framework\TestCase
328 'private' => true, 336 'private' => true,
329 'created' => $created, 337 'created' => $created,
330 ]; 338 ];
339 $old = new Bookmark();
340 $old->fromArray($data);
331 341
332 $new = [ 342 $new = new Bookmark();
333 'url' => '',
334 'title' => '',
335 'description' => '',
336 'tags' => '',
337 'private' => false,
338 ];
339 343
340 $result = ApiUtils::updateLink($old, $new); 344 $result = ApiUtils::updateLink($old, $new);
341 $this->assertEquals(12, $result['id']); 345 $this->assertEquals(12, $result->getId());
342 $this->assertEquals('?abc', $result['url']); 346 $this->assertEquals('', $result->getUrl());
343 $this->assertEquals('abc', $result['shorturl']); 347 $this->assertEquals('abc', $result->getShortUrl());
344 $this->assertEquals('?abc', $result['title']); 348 $this->assertEquals('', $result->getTitle());
345 $this->assertEquals('', $result['description']); 349 $this->assertEquals('', $result->getDescription());
346 $this->assertEquals('', $result['tags']); 350 $this->assertEquals('', $result->getTagsString());
347 $this->assertEquals(false, $result['private']); 351 $this->assertEquals(false, $result->isPrivate());
348 $this->assertEquals($created, $result['created']); 352 $this->assertEquals($created, $result->getCreated());
349 $this->assertTrue(new \DateTime('5 seconds ago') < $result['updated']);
350 } 353 }
351} 354}
diff --git a/tests/api/controllers/history/HistoryTest.php b/tests/api/controllers/history/HistoryTest.php
index e287f239..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
@@ -39,11 +39,11 @@ class HistoryTest extends \PHPUnit\Framework\TestCase
39 protected $controller; 39 protected $controller;
40 40
41 /** 41 /**
42 * Before every test, instantiate a new Api with its config, plugins and links. 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.json.php'); 46 $this->conf = new ConfigManager('tests/utils/config/configJson');
47 $this->refHistory = new \ReferenceHistory(); 47 $this->refHistory = new \ReferenceHistory();
48 $this->refHistory->write(self::$testHistory); 48 $this->refHistory->write(self::$testHistory);
49 $this->container = new Container(); 49 $this->container = new Container();
@@ -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 e70d371b..1598e1e8 100644
--- a/tests/api/controllers/info/InfoTest.php
+++ b/tests/api/controllers/info/InfoTest.php
@@ -1,7 +1,10 @@
1<?php 1<?php
2namespace Shaarli\Api\Controllers; 2namespace Shaarli\Api\Controllers;
3 3
4use Shaarli\Bookmark\BookmarkFileService;
4use Shaarli\Config\ConfigManager; 5use Shaarli\Config\ConfigManager;
6use Shaarli\History;
7use Shaarli\TestCase;
5use Slim\Container; 8use Slim\Container;
6use Slim\Http\Environment; 9use Slim\Http\Environment;
7use Slim\Http\Request; 10use Slim\Http\Request;
@@ -14,7 +17,7 @@ use Slim\Http\Response;
14 * 17 *
15 * @package Api\Controllers 18 * @package Api\Controllers
16 */ 19 */
17class InfoTest extends \PHPUnit\Framework\TestCase 20class InfoTest extends TestCase
18{ 21{
19 /** 22 /**
20 * @var string datastore to test write operations 23 * @var string datastore to test write operations
@@ -42,17 +45,20 @@ class InfoTest extends \PHPUnit\Framework\TestCase
42 protected $controller; 45 protected $controller;
43 46
44 /** 47 /**
45 * Before every test, instantiate a new Api with its config, plugins and links. 48 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
46 */ 49 */
47 public function setUp() 50 protected function setUp(): void
48 { 51 {
49 $this->conf = new ConfigManager('tests/utils/config/configJson.json.php'); 52 $this->conf = new ConfigManager('tests/utils/config/configJson');
53 $this->conf->set('resource.datastore', self::$testDatastore);
50 $this->refDB = new \ReferenceLinkDB(); 54 $this->refDB = new \ReferenceLinkDB();
51 $this->refDB->write(self::$testDatastore); 55 $this->refDB->write(self::$testDatastore);
52 56
57 $history = new History('sandbox/history.php');
58
53 $this->container = new Container(); 59 $this->container = new Container();
54 $this->container['conf'] = $this->conf; 60 $this->container['conf'] = $this->conf;
55 $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); 61 $this->container['db'] = new BookmarkFileService($this->conf, $history, true);
56 $this->container['history'] = null; 62 $this->container['history'] = null;
57 63
58 $this->controller = new Info($this->container); 64 $this->controller = new Info($this->container);
@@ -61,7 +67,7 @@ class InfoTest extends \PHPUnit\Framework\TestCase
61 /** 67 /**
62 * After every test, remove the test datastore. 68 * After every test, remove the test datastore.
63 */ 69 */
64 public function tearDown() 70 protected function tearDown(): void
65 { 71 {
66 @unlink(self::$testDatastore); 72 @unlink(self::$testDatastore);
67 } 73 }
@@ -84,11 +90,11 @@ class InfoTest extends \PHPUnit\Framework\TestCase
84 $this->assertEquals(2, $data['private_counter']); 90 $this->assertEquals(2, $data['private_counter']);
85 $this->assertEquals('Shaarli', $data['settings']['title']); 91 $this->assertEquals('Shaarli', $data['settings']['title']);
86 $this->assertEquals('?', $data['settings']['header_link']); 92 $this->assertEquals('?', $data['settings']['header_link']);
87 $this->assertEquals('UTC', $data['settings']['timezone']); 93 $this->assertEquals('Europe/Paris', $data['settings']['timezone']);
88 $this->assertEquals(ConfigManager::$DEFAULT_PLUGINS, $data['settings']['enabled_plugins']); 94 $this->assertEquals(ConfigManager::$DEFAULT_PLUGINS, $data['settings']['enabled_plugins']);
89 $this->assertEquals(false, $data['settings']['default_private_links']); 95 $this->assertEquals(true, $data['settings']['default_private_links']);
90 96
91 $title = 'My links'; 97 $title = 'My bookmarks';
92 $headerLink = 'http://shaarli.tld'; 98 $headerLink = 'http://shaarli.tld';
93 $timezone = 'Europe/Paris'; 99 $timezone = 'Europe/Paris';
94 $enabledPlugins = array('foo', 'bar'); 100 $enabledPlugins = array('foo', 'bar');
diff --git a/tests/api/controllers/links/DeleteLinkTest.php b/tests/api/controllers/links/DeleteLinkTest.php
index 90193e28..cf9464f0 100644
--- a/tests/api/controllers/links/DeleteLinkTest.php
+++ b/tests/api/controllers/links/DeleteLinkTest.php
@@ -3,7 +3,7 @@
3 3
4namespace Shaarli\Api\Controllers; 4namespace Shaarli\Api\Controllers;
5 5
6use Shaarli\Bookmark\LinkDB; 6use Shaarli\Bookmark\BookmarkFileService;
7use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\History; 8use Shaarli\History;
9use Slim\Container; 9use Slim\Container;
@@ -11,7 +11,7 @@ use Slim\Http\Environment;
11use Slim\Http\Request; 11use Slim\Http\Request;
12use Slim\Http\Response; 12use Slim\Http\Response;
13 13
14class DeleteLinkTest extends \PHPUnit\Framework\TestCase 14class DeleteLinkTest extends \Shaarli\TestCase
15{ 15{
16 /** 16 /**
17 * @var string datastore to test write operations 17 * @var string datastore to test write operations
@@ -34,9 +34,9 @@ class DeleteLinkTest extends \PHPUnit\Framework\TestCase
34 protected $refDB = null; 34 protected $refDB = null;
35 35
36 /** 36 /**
37 * @var LinkDB instance. 37 * @var BookmarkFileService instance.
38 */ 38 */
39 protected $linkDB; 39 protected $bookmarkService;
40 40
41 /** 41 /**
42 * @var HistoryController instance. 42 * @var HistoryController instance.
@@ -54,20 +54,22 @@ class DeleteLinkTest extends \PHPUnit\Framework\TestCase
54 protected $controller; 54 protected $controller;
55 55
56 /** 56 /**
57 * Before each test, instantiate a new Api with its config, plugins and links. 57 * Before each test, instantiate a new Api with its config, plugins and bookmarks.
58 */ 58 */
59 public function setUp() 59 protected function setUp(): void
60 { 60 {
61 $this->conf = new ConfigManager('tests/utils/config/configJson'); 61 $this->conf = new ConfigManager('tests/utils/config/configJson');
62 $this->conf->set('resource.datastore', self::$testDatastore);
62 $this->refDB = new \ReferenceLinkDB(); 63 $this->refDB = new \ReferenceLinkDB();
63 $this->refDB->write(self::$testDatastore); 64 $this->refDB->write(self::$testDatastore);
64 $this->linkDB = new LinkDB(self::$testDatastore, true, false);
65 $refHistory = new \ReferenceHistory(); 65 $refHistory = new \ReferenceHistory();
66 $refHistory->write(self::$testHistory); 66 $refHistory->write(self::$testHistory);
67 $this->history = new History(self::$testHistory); 67 $this->history = new History(self::$testHistory);
68 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
69
68 $this->container = new Container(); 70 $this->container = new Container();
69 $this->container['conf'] = $this->conf; 71 $this->container['conf'] = $this->conf;
70 $this->container['db'] = $this->linkDB; 72 $this->container['db'] = $this->bookmarkService;
71 $this->container['history'] = $this->history; 73 $this->container['history'] = $this->history;
72 74
73 $this->controller = new Links($this->container); 75 $this->controller = new Links($this->container);
@@ -76,7 +78,7 @@ class DeleteLinkTest extends \PHPUnit\Framework\TestCase
76 /** 78 /**
77 * After each test, remove the test datastore. 79 * After each test, remove the test datastore.
78 */ 80 */
79 public function tearDown() 81 protected function tearDown(): void
80 { 82 {
81 @unlink(self::$testDatastore); 83 @unlink(self::$testDatastore);
82 @unlink(self::$testHistory); 84 @unlink(self::$testHistory);
@@ -88,7 +90,7 @@ class DeleteLinkTest extends \PHPUnit\Framework\TestCase
88 public function testDeleteLinkValid() 90 public function testDeleteLinkValid()
89 { 91 {
90 $id = '41'; 92 $id = '41';
91 $this->assertTrue(isset($this->linkDB[$id])); 93 $this->assertTrue($this->bookmarkService->exists($id));
92 $env = Environment::mock([ 94 $env = Environment::mock([
93 'REQUEST_METHOD' => 'DELETE', 95 'REQUEST_METHOD' => 'DELETE',
94 ]); 96 ]);
@@ -98,8 +100,8 @@ class DeleteLinkTest extends \PHPUnit\Framework\TestCase
98 $this->assertEquals(204, $response->getStatusCode()); 100 $this->assertEquals(204, $response->getStatusCode());
99 $this->assertEmpty((string) $response->getBody()); 101 $this->assertEmpty((string) $response->getBody());
100 102
101 $this->linkDB = new LinkDB(self::$testDatastore, true, false); 103 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
102 $this->assertFalse(isset($this->linkDB[$id])); 104 $this->assertFalse($this->bookmarkService->exists($id));
103 105
104 $historyEntry = $this->history->getHistory()[0]; 106 $historyEntry = $this->history->getHistory()[0];
105 $this->assertEquals(History::DELETED, $historyEntry['event']); 107 $this->assertEquals(History::DELETED, $historyEntry['event']);
@@ -111,13 +113,13 @@ class DeleteLinkTest extends \PHPUnit\Framework\TestCase
111 113
112 /** 114 /**
113 * Test DELETE link endpoint: reach not existing ID. 115 * Test DELETE link endpoint: reach not existing ID.
114 *
115 * @expectedException \Shaarli\Api\Exceptions\ApiLinkNotFoundException
116 */ 116 */
117 public function testDeleteLink404() 117 public function testDeleteLink404()
118 { 118 {
119 $this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class);
120
119 $id = -1; 121 $id = -1;
120 $this->assertFalse(isset($this->linkDB[$id])); 122 $this->assertFalse($this->bookmarkService->exists($id));
121 $env = Environment::mock([ 123 $env = Environment::mock([
122 'REQUEST_METHOD' => 'DELETE', 124 'REQUEST_METHOD' => 'DELETE',
123 ]); 125 ]);
diff --git a/tests/api/controllers/links/GetLinkIdTest.php b/tests/api/controllers/links/GetLinkIdTest.php
index cb9b7f6a..99dc606f 100644
--- a/tests/api/controllers/links/GetLinkIdTest.php
+++ b/tests/api/controllers/links/GetLinkIdTest.php
@@ -2,7 +2,10 @@
2 2
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5use Shaarli\Bookmark\Bookmark;
6use Shaarli\Bookmark\BookmarkFileService;
5use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\History;
6use Slim\Container; 9use Slim\Container;
7use Slim\Http\Environment; 10use Slim\Http\Environment;
8use Slim\Http\Request; 11use Slim\Http\Request;
@@ -17,7 +20,7 @@ use Slim\Http\Response;
17 * 20 *
18 * @package Shaarli\Api\Controllers 21 * @package Shaarli\Api\Controllers
19 */ 22 */
20class GetLinkIdTest extends \PHPUnit\Framework\TestCase 23class GetLinkIdTest extends \Shaarli\TestCase
21{ 24{
22 /** 25 /**
23 * @var string datastore to test write operations 26 * @var string datastore to test write operations
@@ -50,17 +53,19 @@ class GetLinkIdTest extends \PHPUnit\Framework\TestCase
50 const NB_FIELDS_LINK = 9; 53 const NB_FIELDS_LINK = 9;
51 54
52 /** 55 /**
53 * Before each test, instantiate a new Api with its config, plugins and links. 56 * Before each test, instantiate a new Api with its config, plugins and bookmarks.
54 */ 57 */
55 public function setUp() 58 protected function setUp(): void
56 { 59 {
57 $this->conf = new ConfigManager('tests/utils/config/configJson'); 60 $this->conf = new ConfigManager('tests/utils/config/configJson');
61 $this->conf->set('resource.datastore', self::$testDatastore);
58 $this->refDB = new \ReferenceLinkDB(); 62 $this->refDB = new \ReferenceLinkDB();
59 $this->refDB->write(self::$testDatastore); 63 $this->refDB->write(self::$testDatastore);
64 $history = new History('sandbox/history.php');
60 65
61 $this->container = new Container(); 66 $this->container = new Container();
62 $this->container['conf'] = $this->conf; 67 $this->container['conf'] = $this->conf;
63 $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); 68 $this->container['db'] = new BookmarkFileService($this->conf, $history, true);
64 $this->container['history'] = null; 69 $this->container['history'] = null;
65 70
66 $this->controller = new Links($this->container); 71 $this->controller = new Links($this->container);
@@ -69,7 +74,7 @@ class GetLinkIdTest extends \PHPUnit\Framework\TestCase
69 /** 74 /**
70 * After each test, remove the test datastore. 75 * After each test, remove the test datastore.
71 */ 76 */
72 public function tearDown() 77 protected function tearDown(): void
73 { 78 {
74 @unlink(self::$testDatastore); 79 @unlink(self::$testDatastore);
75 } 80 }
@@ -97,7 +102,7 @@ class GetLinkIdTest extends \PHPUnit\Framework\TestCase
97 $this->assertEquals($id, $data['id']); 102 $this->assertEquals($id, $data['id']);
98 103
99 // Check link elements 104 // Check link elements
100 $this->assertEquals('http://domain.tld/?WDWyig', $data['url']); 105 $this->assertEquals('http://domain.tld/shaare/WDWyig', $data['url']);
101 $this->assertEquals('WDWyig', $data['shorturl']); 106 $this->assertEquals('WDWyig', $data['shorturl']);
102 $this->assertEquals('Link title: @website', $data['title']); 107 $this->assertEquals('Link title: @website', $data['title']);
103 $this->assertEquals( 108 $this->assertEquals(
@@ -107,7 +112,7 @@ class GetLinkIdTest extends \PHPUnit\Framework\TestCase
107 $this->assertEquals('sTuff', $data['tags'][0]); 112 $this->assertEquals('sTuff', $data['tags'][0]);
108 $this->assertEquals(false, $data['private']); 113 $this->assertEquals(false, $data['private']);
109 $this->assertEquals( 114 $this->assertEquals(
110 \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM), 115 \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
111 $data['created'] 116 $data['created']
112 ); 117 );
113 $this->assertEmpty($data['updated']); 118 $this->assertEmpty($data['updated']);
@@ -115,12 +120,12 @@ class GetLinkIdTest extends \PHPUnit\Framework\TestCase
115 120
116 /** 121 /**
117 * Test basic getLink service: get non existent link => ApiLinkNotFoundException. 122 * Test basic getLink service: get non existent link => ApiLinkNotFoundException.
118 *
119 * @expectedException Shaarli\Api\Exceptions\ApiLinkNotFoundException
120 * @expectedExceptionMessage Link not found
121 */ 123 */
122 public function testGetLink404() 124 public function testGetLink404()
123 { 125 {
126 $this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class);
127 $this->expectExceptionMessage('Link not found');
128
124 $env = Environment::mock([ 129 $env = Environment::mock([
125 'REQUEST_METHOD' => 'GET', 130 'REQUEST_METHOD' => 'GET',
126 ]); 131 ]);
diff --git a/tests/api/controllers/links/GetLinksTest.php b/tests/api/controllers/links/GetLinksTest.php
index 711a3152..ca1bfc63 100644
--- a/tests/api/controllers/links/GetLinksTest.php
+++ b/tests/api/controllers/links/GetLinksTest.php
@@ -1,8 +1,11 @@
1<?php 1<?php
2namespace Shaarli\Api\Controllers; 2namespace Shaarli\Api\Controllers;
3 3
4use Shaarli\Bookmark\Bookmark;
5use Shaarli\Bookmark\BookmarkFileService;
4use Shaarli\Bookmark\LinkDB; 6use Shaarli\Bookmark\LinkDB;
5use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\History;
6use Slim\Container; 9use Slim\Container;
7use Slim\Http\Environment; 10use Slim\Http\Environment;
8use Slim\Http\Request; 11use Slim\Http\Request;
@@ -17,7 +20,7 @@ use Slim\Http\Response;
17 * 20 *
18 * @package Shaarli\Api\Controllers 21 * @package Shaarli\Api\Controllers
19 */ 22 */
20class GetLinksTest extends \PHPUnit\Framework\TestCase 23class GetLinksTest extends \Shaarli\TestCase
21{ 24{
22 /** 25 /**
23 * @var string datastore to test write operations 26 * @var string datastore to test write operations
@@ -50,17 +53,19 @@ class GetLinksTest extends \PHPUnit\Framework\TestCase
50 const NB_FIELDS_LINK = 9; 53 const NB_FIELDS_LINK = 9;
51 54
52 /** 55 /**
53 * Before every test, instantiate a new Api with its config, plugins and links. 56 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
54 */ 57 */
55 public function setUp() 58 protected function setUp(): void
56 { 59 {
57 $this->conf = new ConfigManager('tests/utils/config/configJson'); 60 $this->conf = new ConfigManager('tests/utils/config/configJson');
61 $this->conf->set('resource.datastore', self::$testDatastore);
58 $this->refDB = new \ReferenceLinkDB(); 62 $this->refDB = new \ReferenceLinkDB();
59 $this->refDB->write(self::$testDatastore); 63 $this->refDB->write(self::$testDatastore);
64 $history = new History('sandbox/history.php');
60 65
61 $this->container = new Container(); 66 $this->container = new Container();
62 $this->container['conf'] = $this->conf; 67 $this->container['conf'] = $this->conf;
63 $this->container['db'] = new LinkDB(self::$testDatastore, true, false); 68 $this->container['db'] = new BookmarkFileService($this->conf, $history, true);
64 $this->container['history'] = null; 69 $this->container['history'] = null;
65 70
66 $this->controller = new Links($this->container); 71 $this->controller = new Links($this->container);
@@ -69,13 +74,13 @@ class GetLinksTest extends \PHPUnit\Framework\TestCase
69 /** 74 /**
70 * After every test, remove the test datastore. 75 * After every test, remove the test datastore.
71 */ 76 */
72 public function tearDown() 77 protected function tearDown(): void
73 { 78 {
74 @unlink(self::$testDatastore); 79 @unlink(self::$testDatastore);
75 } 80 }
76 81
77 /** 82 /**
78 * Test basic getLinks service: returns all links. 83 * Test basic getLinks service: returns all bookmarks.
79 */ 84 */
80 public function testGetLinks() 85 public function testGetLinks()
81 { 86 {
@@ -104,7 +109,7 @@ class GetLinksTest extends \PHPUnit\Framework\TestCase
104 109
105 // Check first element fields 110 // Check first element fields
106 $first = $data[2]; 111 $first = $data[2];
107 $this->assertEquals('http://domain.tld/?WDWyig', $first['url']); 112 $this->assertEquals('http://domain.tld/shaare/WDWyig', $first['url']);
108 $this->assertEquals('WDWyig', $first['shorturl']); 113 $this->assertEquals('WDWyig', $first['shorturl']);
109 $this->assertEquals('Link title: @website', $first['title']); 114 $this->assertEquals('Link title: @website', $first['title']);
110 $this->assertEquals( 115 $this->assertEquals(
@@ -114,7 +119,7 @@ class GetLinksTest extends \PHPUnit\Framework\TestCase
114 $this->assertEquals('sTuff', $first['tags'][0]); 119 $this->assertEquals('sTuff', $first['tags'][0]);
115 $this->assertEquals(false, $first['private']); 120 $this->assertEquals(false, $first['private']);
116 $this->assertEquals( 121 $this->assertEquals(
117 \DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM), 122 \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651')->format(\DateTime::ATOM),
118 $first['created'] 123 $first['created']
119 ); 124 );
120 $this->assertEmpty($first['updated']); 125 $this->assertEmpty($first['updated']);
@@ -125,7 +130,7 @@ class GetLinksTest extends \PHPUnit\Framework\TestCase
125 130
126 // Update date 131 // Update date
127 $this->assertEquals( 132 $this->assertEquals(
128 \DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160803_093033')->format(\DateTime::ATOM), 133 \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160803_093033')->format(\DateTime::ATOM),
129 $link['updated'] 134 $link['updated']
130 ); 135 );
131 } 136 }
diff --git a/tests/api/controllers/links/PostLinkTest.php b/tests/api/controllers/links/PostLinkTest.php
index d683a984..fe3de66f 100644
--- a/tests/api/controllers/links/PostLinkTest.php
+++ b/tests/api/controllers/links/PostLinkTest.php
@@ -2,9 +2,11 @@
2 2
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5use PHPUnit\Framework\TestCase; 5use Shaarli\Bookmark\Bookmark;
6use Shaarli\Bookmark\BookmarkFileService;
6use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
7use Shaarli\History; 8use Shaarli\History;
9use Shaarli\TestCase;
8use Slim\Container; 10use Slim\Container;
9use Slim\Http\Environment; 11use Slim\Http\Environment;
10use Slim\Http\Request; 12use Slim\Http\Request;
@@ -41,6 +43,11 @@ class PostLinkTest extends TestCase
41 protected $refDB = null; 43 protected $refDB = null;
42 44
43 /** 45 /**
46 * @var BookmarkFileService instance.
47 */
48 protected $bookmarkService;
49
50 /**
44 * @var HistoryController instance. 51 * @var HistoryController instance.
45 */ 52 */
46 protected $history; 53 protected $history;
@@ -61,29 +68,30 @@ class PostLinkTest extends TestCase
61 const NB_FIELDS_LINK = 9; 68 const NB_FIELDS_LINK = 9;
62 69
63 /** 70 /**
64 * Before every test, instantiate a new Api with its config, plugins and links. 71 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
65 */ 72 */
66 public function setUp() 73 protected function setUp(): void
67 { 74 {
68 $this->conf = new ConfigManager('tests/utils/config/configJson.json.php'); 75 $this->conf = new ConfigManager('tests/utils/config/configJson');
76 $this->conf->set('resource.datastore', self::$testDatastore);
69 $this->refDB = new \ReferenceLinkDB(); 77 $this->refDB = new \ReferenceLinkDB();
70 $this->refDB->write(self::$testDatastore); 78 $this->refDB->write(self::$testDatastore);
71
72 $refHistory = new \ReferenceHistory(); 79 $refHistory = new \ReferenceHistory();
73 $refHistory->write(self::$testHistory); 80 $refHistory->write(self::$testHistory);
74 $this->history = new History(self::$testHistory); 81 $this->history = new History(self::$testHistory);
82 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
75 83
76 $this->container = new Container(); 84 $this->container = new Container();
77 $this->container['conf'] = $this->conf; 85 $this->container['conf'] = $this->conf;
78 $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); 86 $this->container['db'] = $this->bookmarkService;
79 $this->container['history'] = new History(self::$testHistory); 87 $this->container['history'] = $this->history;
80 88
81 $this->controller = new Links($this->container); 89 $this->controller = new Links($this->container);
82 90
83 $mock = $this->createMock(Router::class); 91 $mock = $this->createMock(Router::class);
84 $mock->expects($this->any()) 92 $mock->expects($this->any())
85 ->method('relativePathFor') 93 ->method('relativePathFor')
86 ->willReturn('api/v1/links/1'); 94 ->willReturn('api/v1/bookmarks/1');
87 95
88 // affect @property-read... seems to work 96 // affect @property-read... seems to work
89 $this->controller->getCi()->router = $mock; 97 $this->controller->getCi()->router = $mock;
@@ -99,7 +107,7 @@ class PostLinkTest extends TestCase
99 /** 107 /**
100 * After every test, remove the test datastore. 108 * After every test, remove the test datastore.
101 */ 109 */
102 public function tearDown() 110 protected function tearDown(): void
103 { 111 {
104 @unlink(self::$testDatastore); 112 @unlink(self::$testDatastore);
105 @unlink(self::$testHistory); 113 @unlink(self::$testHistory);
@@ -118,16 +126,16 @@ class PostLinkTest extends TestCase
118 126
119 $response = $this->controller->postLink($request, new Response()); 127 $response = $this->controller->postLink($request, new Response());
120 $this->assertEquals(201, $response->getStatusCode()); 128 $this->assertEquals(201, $response->getStatusCode());
121 $this->assertEquals('api/v1/links/1', $response->getHeader('Location')[0]); 129 $this->assertEquals('api/v1/bookmarks/1', $response->getHeader('Location')[0]);
122 $data = json_decode((string) $response->getBody(), true); 130 $data = json_decode((string) $response->getBody(), true);
123 $this->assertEquals(self::NB_FIELDS_LINK, count($data)); 131 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
124 $this->assertEquals(43, $data['id']); 132 $this->assertEquals(43, $data['id']);
125 $this->assertRegExp('/[\w_-]{6}/', $data['shorturl']); 133 $this->assertRegExp('/[\w_-]{6}/', $data['shorturl']);
126 $this->assertEquals('http://domain.tld/?' . $data['shorturl'], $data['url']); 134 $this->assertEquals('http://domain.tld/shaare/' . $data['shorturl'], $data['url']);
127 $this->assertEquals('?' . $data['shorturl'], $data['title']); 135 $this->assertEquals('/shaare/' . $data['shorturl'], $data['title']);
128 $this->assertEquals('', $data['description']); 136 $this->assertEquals('', $data['description']);
129 $this->assertEquals([], $data['tags']); 137 $this->assertEquals([], $data['tags']);
130 $this->assertEquals(false, $data['private']); 138 $this->assertEquals(true, $data['private']);
131 $this->assertTrue( 139 $this->assertTrue(
132 new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) 140 new \DateTime('5 seconds ago') < \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
133 ); 141 );
@@ -163,7 +171,7 @@ class PostLinkTest extends TestCase
163 $response = $this->controller->postLink($request, new Response()); 171 $response = $this->controller->postLink($request, new Response());
164 172
165 $this->assertEquals(201, $response->getStatusCode()); 173 $this->assertEquals(201, $response->getStatusCode());
166 $this->assertEquals('api/v1/links/1', $response->getHeader('Location')[0]); 174 $this->assertEquals('api/v1/bookmarks/1', $response->getHeader('Location')[0]);
167 $data = json_decode((string) $response->getBody(), true); 175 $data = json_decode((string) $response->getBody(), true);
168 $this->assertEquals(self::NB_FIELDS_LINK, count($data)); 176 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
169 $this->assertEquals(43, $data['id']); 177 $this->assertEquals(43, $data['id']);
@@ -211,11 +219,11 @@ class PostLinkTest extends TestCase
211 $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']); 219 $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']);
212 $this->assertEquals(false, $data['private']); 220 $this->assertEquals(false, $data['private']);
213 $this->assertEquals( 221 $this->assertEquals(
214 \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130614_184135'), 222 \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130614_184135'),
215 \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) 223 \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
216 ); 224 );
217 $this->assertEquals( 225 $this->assertEquals(
218 \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130615_184230'), 226 \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130615_184230'),
219 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) 227 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
220 ); 228 );
221 } 229 }
diff --git a/tests/api/controllers/links/PutLinkTest.php b/tests/api/controllers/links/PutLinkTest.php
index cd815b66..a2e87c59 100644
--- a/tests/api/controllers/links/PutLinkTest.php
+++ b/tests/api/controllers/links/PutLinkTest.php
@@ -3,6 +3,8 @@
3 3
4namespace Shaarli\Api\Controllers; 4namespace Shaarli\Api\Controllers;
5 5
6use Shaarli\Bookmark\Bookmark;
7use Shaarli\Bookmark\BookmarkFileService;
6use Shaarli\Config\ConfigManager; 8use Shaarli\Config\ConfigManager;
7use Shaarli\History; 9use Shaarli\History;
8use Slim\Container; 10use Slim\Container;
@@ -10,7 +12,7 @@ use Slim\Http\Environment;
10use Slim\Http\Request; 12use Slim\Http\Request;
11use Slim\Http\Response; 13use Slim\Http\Response;
12 14
13class PutLinkTest extends \PHPUnit\Framework\TestCase 15class PutLinkTest extends \Shaarli\TestCase
14{ 16{
15 /** 17 /**
16 * @var string datastore to test write operations 18 * @var string datastore to test write operations
@@ -33,6 +35,11 @@ class PutLinkTest extends \PHPUnit\Framework\TestCase
33 protected $refDB = null; 35 protected $refDB = null;
34 36
35 /** 37 /**
38 * @var BookmarkFileService instance.
39 */
40 protected $bookmarkService;
41
42 /**
36 * @var HistoryController instance. 43 * @var HistoryController instance.
37 */ 44 */
38 protected $history; 45 protected $history;
@@ -53,22 +60,23 @@ class PutLinkTest extends \PHPUnit\Framework\TestCase
53 const NB_FIELDS_LINK = 9; 60 const NB_FIELDS_LINK = 9;
54 61
55 /** 62 /**
56 * Before every test, instantiate a new Api with its config, plugins and links. 63 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
57 */ 64 */
58 public function setUp() 65 protected function setUp(): void
59 { 66 {
60 $this->conf = new ConfigManager('tests/utils/config/configJson.json.php'); 67 $this->conf = new ConfigManager('tests/utils/config/configJson');
68 $this->conf->set('resource.datastore', self::$testDatastore);
61 $this->refDB = new \ReferenceLinkDB(); 69 $this->refDB = new \ReferenceLinkDB();
62 $this->refDB->write(self::$testDatastore); 70 $this->refDB->write(self::$testDatastore);
63
64 $refHistory = new \ReferenceHistory(); 71 $refHistory = new \ReferenceHistory();
65 $refHistory->write(self::$testHistory); 72 $refHistory->write(self::$testHistory);
66 $this->history = new History(self::$testHistory); 73 $this->history = new History(self::$testHistory);
74 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
67 75
68 $this->container = new Container(); 76 $this->container = new Container();
69 $this->container['conf'] = $this->conf; 77 $this->container['conf'] = $this->conf;
70 $this->container['db'] = new \Shaarli\Bookmark\LinkDB(self::$testDatastore, true, false); 78 $this->container['db'] = $this->bookmarkService;
71 $this->container['history'] = new History(self::$testHistory); 79 $this->container['history'] = $this->history;
72 80
73 $this->controller = new Links($this->container); 81 $this->controller = new Links($this->container);
74 82
@@ -83,7 +91,7 @@ class PutLinkTest extends \PHPUnit\Framework\TestCase
83 /** 91 /**
84 * After every test, remove the test datastore. 92 * After every test, remove the test datastore.
85 */ 93 */
86 public function tearDown() 94 protected function tearDown(): void
87 { 95 {
88 @unlink(self::$testDatastore); 96 @unlink(self::$testDatastore);
89 @unlink(self::$testHistory); 97 @unlink(self::$testHistory);
@@ -106,11 +114,11 @@ class PutLinkTest extends \PHPUnit\Framework\TestCase
106 $this->assertEquals(self::NB_FIELDS_LINK, count($data)); 114 $this->assertEquals(self::NB_FIELDS_LINK, count($data));
107 $this->assertEquals($id, $data['id']); 115 $this->assertEquals($id, $data['id']);
108 $this->assertEquals('WDWyig', $data['shorturl']); 116 $this->assertEquals('WDWyig', $data['shorturl']);
109 $this->assertEquals('http://domain.tld/?WDWyig', $data['url']); 117 $this->assertEquals('http://domain.tld/shaare/WDWyig', $data['url']);
110 $this->assertEquals('?WDWyig', $data['title']); 118 $this->assertEquals('/shaare/WDWyig', $data['title']);
111 $this->assertEquals('', $data['description']); 119 $this->assertEquals('', $data['description']);
112 $this->assertEquals([], $data['tags']); 120 $this->assertEquals([], $data['tags']);
113 $this->assertEquals(false, $data['private']); 121 $this->assertEquals(true, $data['private']);
114 $this->assertEquals( 122 $this->assertEquals(
115 \DateTime::createFromFormat('Ymd_His', '20150310_114651'), 123 \DateTime::createFromFormat('Ymd_His', '20150310_114651'),
116 \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) 124 \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
@@ -199,23 +207,23 @@ class PutLinkTest extends \PHPUnit\Framework\TestCase
199 $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']); 207 $this->assertEquals(['gnu', 'media', 'web', '.hidden', 'hashtag'], $data['tags']);
200 $this->assertEquals(false, $data['private']); 208 $this->assertEquals(false, $data['private']);
201 $this->assertEquals( 209 $this->assertEquals(
202 \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130614_184135'), 210 \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130614_184135'),
203 \DateTime::createFromFormat(\DateTime::ATOM, $data['created']) 211 \DateTime::createFromFormat(\DateTime::ATOM, $data['created'])
204 ); 212 );
205 $this->assertEquals( 213 $this->assertEquals(
206 \DateTime::createFromFormat(\Shaarli\Bookmark\LinkDB::LINK_DATE_FORMAT, '20130615_184230'), 214 \DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130615_184230'),
207 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated']) 215 \DateTime::createFromFormat(\DateTime::ATOM, $data['updated'])
208 ); 216 );
209 } 217 }
210 218
211 /** 219 /**
212 * Test link update on non existent link => ApiLinkNotFoundException. 220 * Test link update on non existent link => ApiLinkNotFoundException.
213 *
214 * @expectedException Shaarli\Api\Exceptions\ApiLinkNotFoundException
215 * @expectedExceptionMessage Link not found
216 */ 221 */
217 public function testGetLink404() 222 public function testGetLink404()
218 { 223 {
224 $this->expectException(\Shaarli\Api\Exceptions\ApiLinkNotFoundException::class);
225 $this->expectExceptionMessage('Link not found');
226
219 $env = Environment::mock([ 227 $env = Environment::mock([
220 'REQUEST_METHOD' => 'PUT', 228 'REQUEST_METHOD' => 'PUT',
221 ]); 229 ]);
diff --git a/tests/api/controllers/tags/DeleteTagTest.php b/tests/api/controllers/tags/DeleteTagTest.php
index 84e1d56e..1326eb47 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 Shaarli\Bookmark\BookmarkFileService;
6use Shaarli\Bookmark\LinkDB; 7use Shaarli\Bookmark\LinkDB;
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 DeleteTagTest extends \PHPUnit\Framework\TestCase 15class DeleteTagTest extends \Shaarli\TestCase
15{ 16{
16 /** 17 /**
17 * @var string datastore to test write operations 18 * @var string datastore to test write operations
@@ -34,9 +35,9 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
34 protected $refDB = null; 35 protected $refDB = null;
35 36
36 /** 37 /**
37 * @var LinkDB instance. 38 * @var BookmarkFileService instance.
38 */ 39 */
39 protected $linkDB; 40 protected $bookmarkService;
40 41
41 /** 42 /**
42 * @var HistoryController instance. 43 * @var HistoryController instance.
@@ -54,20 +55,22 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
54 protected $controller; 55 protected $controller;
55 56
56 /** 57 /**
57 * Before each test, instantiate a new Api with its config, plugins and links. 58 * Before each test, instantiate a new Api with its config, plugins and bookmarks.
58 */ 59 */
59 public function setUp() 60 protected function setUp(): void
60 { 61 {
61 $this->conf = new ConfigManager('tests/utils/config/configJson'); 62 $this->conf = new ConfigManager('tests/utils/config/configJson');
63 $this->conf->set('resource.datastore', self::$testDatastore);
62 $this->refDB = new \ReferenceLinkDB(); 64 $this->refDB = new \ReferenceLinkDB();
63 $this->refDB->write(self::$testDatastore); 65 $this->refDB->write(self::$testDatastore);
64 $this->linkDB = new LinkDB(self::$testDatastore, true, false);
65 $refHistory = new \ReferenceHistory(); 66 $refHistory = new \ReferenceHistory();
66 $refHistory->write(self::$testHistory); 67 $refHistory->write(self::$testHistory);
67 $this->history = new History(self::$testHistory); 68 $this->history = new History(self::$testHistory);
69 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
70
68 $this->container = new Container(); 71 $this->container = new Container();
69 $this->container['conf'] = $this->conf; 72 $this->container['conf'] = $this->conf;
70 $this->container['db'] = $this->linkDB; 73 $this->container['db'] = $this->bookmarkService;
71 $this->container['history'] = $this->history; 74 $this->container['history'] = $this->history;
72 75
73 $this->controller = new Tags($this->container); 76 $this->controller = new Tags($this->container);
@@ -76,7 +79,7 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
76 /** 79 /**
77 * After each test, remove the test datastore. 80 * After each test, remove the test datastore.
78 */ 81 */
79 public function tearDown() 82 protected function tearDown(): void
80 { 83 {
81 @unlink(self::$testDatastore); 84 @unlink(self::$testDatastore);
82 @unlink(self::$testHistory); 85 @unlink(self::$testHistory);
@@ -88,7 +91,7 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
88 public function testDeleteTagValid() 91 public function testDeleteTagValid()
89 { 92 {
90 $tagName = 'gnu'; 93 $tagName = 'gnu';
91 $tags = $this->linkDB->linksCountPerTag(); 94 $tags = $this->bookmarkService->bookmarksCountPerTag();
92 $this->assertTrue($tags[$tagName] > 0); 95 $this->assertTrue($tags[$tagName] > 0);
93 $env = Environment::mock([ 96 $env = Environment::mock([
94 'REQUEST_METHOD' => 'DELETE', 97 'REQUEST_METHOD' => 'DELETE',
@@ -99,11 +102,11 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
99 $this->assertEquals(204, $response->getStatusCode()); 102 $this->assertEquals(204, $response->getStatusCode());
100 $this->assertEmpty((string) $response->getBody()); 103 $this->assertEmpty((string) $response->getBody());
101 104
102 $this->linkDB = new LinkDB(self::$testDatastore, true, false); 105 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
103 $tags = $this->linkDB->linksCountPerTag(); 106 $tags = $this->bookmarkService->bookmarksCountPerTag();
104 $this->assertFalse(isset($tags[$tagName])); 107 $this->assertFalse(isset($tags[$tagName]));
105 108
106 // 2 links affected 109 // 2 bookmarks affected
107 $historyEntry = $this->history->getHistory()[0]; 110 $historyEntry = $this->history->getHistory()[0];
108 $this->assertEquals(History::UPDATED, $historyEntry['event']); 111 $this->assertEquals(History::UPDATED, $historyEntry['event']);
109 $this->assertTrue( 112 $this->assertTrue(
@@ -122,7 +125,7 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
122 public function testDeleteTagCaseSensitivity() 125 public function testDeleteTagCaseSensitivity()
123 { 126 {
124 $tagName = 'sTuff'; 127 $tagName = 'sTuff';
125 $tags = $this->linkDB->linksCountPerTag(); 128 $tags = $this->bookmarkService->bookmarksCountPerTag();
126 $this->assertTrue($tags[$tagName] > 0); 129 $this->assertTrue($tags[$tagName] > 0);
127 $env = Environment::mock([ 130 $env = Environment::mock([
128 'REQUEST_METHOD' => 'DELETE', 131 'REQUEST_METHOD' => 'DELETE',
@@ -133,8 +136,8 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
133 $this->assertEquals(204, $response->getStatusCode()); 136 $this->assertEquals(204, $response->getStatusCode());
134 $this->assertEmpty((string) $response->getBody()); 137 $this->assertEmpty((string) $response->getBody());
135 138
136 $this->linkDB = new LinkDB(self::$testDatastore, true, false); 139 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
137 $tags = $this->linkDB->linksCountPerTag(); 140 $tags = $this->bookmarkService->bookmarksCountPerTag();
138 $this->assertFalse(isset($tags[$tagName])); 141 $this->assertFalse(isset($tags[$tagName]));
139 $this->assertTrue($tags[strtolower($tagName)] > 0); 142 $this->assertTrue($tags[strtolower($tagName)] > 0);
140 143
@@ -147,14 +150,14 @@ class DeleteTagTest extends \PHPUnit\Framework\TestCase
147 150
148 /** 151 /**
149 * Test DELETE tag endpoint: reach not existing tag. 152 * Test DELETE tag endpoint: reach not existing tag.
150 *
151 * @expectedException Shaarli\Api\Exceptions\ApiTagNotFoundException
152 * @expectedExceptionMessage Tag not found
153 */ 153 */
154 public function testDeleteLink404() 154 public function testDeleteLink404()
155 { 155 {
156 $this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class);
157 $this->expectExceptionMessage('Tag not found');
158
156 $tagName = 'nopenope'; 159 $tagName = 'nopenope';
157 $tags = $this->linkDB->linksCountPerTag(); 160 $tags = $this->bookmarkService->bookmarksCountPerTag();
158 $this->assertFalse(isset($tags[$tagName])); 161 $this->assertFalse(isset($tags[$tagName]));
159 $env = Environment::mock([ 162 $env = Environment::mock([
160 'REQUEST_METHOD' => 'DELETE', 163 'REQUEST_METHOD' => 'DELETE',
diff --git a/tests/api/controllers/tags/GetTagNameTest.php b/tests/api/controllers/tags/GetTagNameTest.php
index a2525c17..9c05954b 100644
--- a/tests/api/controllers/tags/GetTagNameTest.php
+++ b/tests/api/controllers/tags/GetTagNameTest.php
@@ -2,8 +2,10 @@
2 2
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5use Shaarli\Bookmark\BookmarkFileService;
5use Shaarli\Bookmark\LinkDB; 6use Shaarli\Bookmark\LinkDB;
6use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\History;
7use Slim\Container; 9use Slim\Container;
8use Slim\Http\Environment; 10use Slim\Http\Environment;
9use Slim\Http\Request; 11use Slim\Http\Request;
@@ -16,7 +18,7 @@ use Slim\Http\Response;
16 * 18 *
17 * @package Shaarli\Api\Controllers 19 * @package Shaarli\Api\Controllers
18 */ 20 */
19class GetTagNameTest extends \PHPUnit\Framework\TestCase 21class GetTagNameTest extends \Shaarli\TestCase
20{ 22{
21 /** 23 /**
22 * @var string datastore to test write operations 24 * @var string datastore to test write operations
@@ -49,17 +51,19 @@ class GetTagNameTest extends \PHPUnit\Framework\TestCase
49 const NB_FIELDS_TAG = 2; 51 const NB_FIELDS_TAG = 2;
50 52
51 /** 53 /**
52 * Before each test, instantiate a new Api with its config, plugins and links. 54 * Before each test, instantiate a new Api with its config, plugins and bookmarks.
53 */ 55 */
54 public function setUp() 56 protected function setUp(): void
55 { 57 {
56 $this->conf = new ConfigManager('tests/utils/config/configJson'); 58 $this->conf = new ConfigManager('tests/utils/config/configJson');
59 $this->conf->set('resource.datastore', self::$testDatastore);
57 $this->refDB = new \ReferenceLinkDB(); 60 $this->refDB = new \ReferenceLinkDB();
58 $this->refDB->write(self::$testDatastore); 61 $this->refDB->write(self::$testDatastore);
62 $history = new History('sandbox/history.php');
59 63
60 $this->container = new Container(); 64 $this->container = new Container();
61 $this->container['conf'] = $this->conf; 65 $this->container['conf'] = $this->conf;
62 $this->container['db'] = new LinkDB(self::$testDatastore, true, false); 66 $this->container['db'] = new BookmarkFileService($this->conf, $history, true);
63 $this->container['history'] = null; 67 $this->container['history'] = null;
64 68
65 $this->controller = new Tags($this->container); 69 $this->controller = new Tags($this->container);
@@ -68,7 +72,7 @@ class GetTagNameTest extends \PHPUnit\Framework\TestCase
68 /** 72 /**
69 * After each test, remove the test datastore. 73 * After each test, remove the test datastore.
70 */ 74 */
71 public function tearDown() 75 protected function tearDown(): void
72 { 76 {
73 @unlink(self::$testDatastore); 77 @unlink(self::$testDatastore);
74 } 78 }
@@ -113,12 +117,12 @@ class GetTagNameTest extends \PHPUnit\Framework\TestCase
113 117
114 /** 118 /**
115 * Test basic getTag service: get non existent tag => ApiTagNotFoundException. 119 * Test basic getTag service: get non existent tag => ApiTagNotFoundException.
116 *
117 * @expectedException Shaarli\Api\Exceptions\ApiTagNotFoundException
118 * @expectedExceptionMessage Tag not found
119 */ 120 */
120 public function testGetTag404() 121 public function testGetTag404()
121 { 122 {
123 $this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class);
124 $this->expectExceptionMessage('Tag not found');
125
122 $env = Environment::mock([ 126 $env = Environment::mock([
123 'REQUEST_METHOD' => 'GET', 127 'REQUEST_METHOD' => 'GET',
124 ]); 128 ]);
diff --git a/tests/api/controllers/tags/GetTagsTest.php b/tests/api/controllers/tags/GetTagsTest.php
index 98628c98..3459fdfa 100644
--- a/tests/api/controllers/tags/GetTagsTest.php
+++ b/tests/api/controllers/tags/GetTagsTest.php
@@ -1,8 +1,10 @@
1<?php 1<?php
2namespace Shaarli\Api\Controllers; 2namespace Shaarli\Api\Controllers;
3 3
4use Shaarli\Bookmark\BookmarkFileService;
4use Shaarli\Bookmark\LinkDB; 5use Shaarli\Bookmark\LinkDB;
5use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
7use Shaarli\History;
6use Slim\Container; 8use Slim\Container;
7use Slim\Http\Environment; 9use Slim\Http\Environment;
8use Slim\Http\Request; 10use Slim\Http\Request;
@@ -15,7 +17,7 @@ use Slim\Http\Response;
15 * 17 *
16 * @package Shaarli\Api\Controllers 18 * @package Shaarli\Api\Controllers
17 */ 19 */
18class GetTagsTest extends \PHPUnit\Framework\TestCase 20class GetTagsTest extends \Shaarli\TestCase
19{ 21{
20 /** 22 /**
21 * @var string datastore to test write operations 23 * @var string datastore to test write operations
@@ -38,9 +40,9 @@ class GetTagsTest extends \PHPUnit\Framework\TestCase
38 protected $container; 40 protected $container;
39 41
40 /** 42 /**
41 * @var LinkDB instance. 43 * @var BookmarkFileService instance.
42 */ 44 */
43 protected $linkDB; 45 protected $bookmarkService;
44 46
45 /** 47 /**
46 * @var Tags controller instance. 48 * @var Tags controller instance.
@@ -53,18 +55,21 @@ class GetTagsTest extends \PHPUnit\Framework\TestCase
53 const NB_FIELDS_TAG = 2; 55 const NB_FIELDS_TAG = 2;
54 56
55 /** 57 /**
56 * Before every test, instantiate a new Api with its config, plugins and links. 58 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
57 */ 59 */
58 public function setUp() 60 protected function setUp(): void
59 { 61 {
60 $this->conf = new ConfigManager('tests/utils/config/configJson'); 62 $this->conf = new ConfigManager('tests/utils/config/configJson');
63 $this->conf->set('resource.datastore', self::$testDatastore);
61 $this->refDB = new \ReferenceLinkDB(); 64 $this->refDB = new \ReferenceLinkDB();
62 $this->refDB->write(self::$testDatastore); 65 $this->refDB->write(self::$testDatastore);
66 $history = new History('sandbox/history.php');
67
68 $this->bookmarkService = new BookmarkFileService($this->conf, $history, true);
63 69
64 $this->container = new Container(); 70 $this->container = new Container();
65 $this->container['conf'] = $this->conf; 71 $this->container['conf'] = $this->conf;
66 $this->linkDB = new LinkDB(self::$testDatastore, true, false); 72 $this->container['db'] = $this->bookmarkService;
67 $this->container['db'] = $this->linkDB;
68 $this->container['history'] = null; 73 $this->container['history'] = null;
69 74
70 $this->controller = new Tags($this->container); 75 $this->controller = new Tags($this->container);
@@ -73,7 +78,7 @@ class GetTagsTest extends \PHPUnit\Framework\TestCase
73 /** 78 /**
74 * After every test, remove the test datastore. 79 * After every test, remove the test datastore.
75 */ 80 */
76 public function tearDown() 81 protected function tearDown(): void
77 { 82 {
78 @unlink(self::$testDatastore); 83 @unlink(self::$testDatastore);
79 } 84 }
@@ -83,7 +88,7 @@ class GetTagsTest extends \PHPUnit\Framework\TestCase
83 */ 88 */
84 public function testGetTagsAll() 89 public function testGetTagsAll()
85 { 90 {
86 $tags = $this->linkDB->linksCountPerTag(); 91 $tags = $this->bookmarkService->bookmarksCountPerTag();
87 $env = Environment::mock([ 92 $env = Environment::mock([
88 'REQUEST_METHOD' => 'GET', 93 'REQUEST_METHOD' => 'GET',
89 ]); 94 ]);
@@ -136,7 +141,7 @@ class GetTagsTest extends \PHPUnit\Framework\TestCase
136 */ 141 */
137 public function testGetTagsLimitAll() 142 public function testGetTagsLimitAll()
138 { 143 {
139 $tags = $this->linkDB->linksCountPerTag(); 144 $tags = $this->bookmarkService->bookmarksCountPerTag();
140 $env = Environment::mock([ 145 $env = Environment::mock([
141 'REQUEST_METHOD' => 'GET', 146 'REQUEST_METHOD' => 'GET',
142 'QUERY_STRING' => 'limit=all' 147 'QUERY_STRING' => 'limit=all'
@@ -170,7 +175,7 @@ class GetTagsTest extends \PHPUnit\Framework\TestCase
170 */ 175 */
171 public function testGetTagsVisibilityPrivate() 176 public function testGetTagsVisibilityPrivate()
172 { 177 {
173 $tags = $this->linkDB->linksCountPerTag([], 'private'); 178 $tags = $this->bookmarkService->bookmarksCountPerTag([], 'private');
174 $env = Environment::mock([ 179 $env = Environment::mock([
175 'REQUEST_METHOD' => 'GET', 180 'REQUEST_METHOD' => 'GET',
176 'QUERY_STRING' => 'visibility=private' 181 'QUERY_STRING' => 'visibility=private'
@@ -190,7 +195,7 @@ class GetTagsTest extends \PHPUnit\Framework\TestCase
190 */ 195 */
191 public function testGetTagsVisibilityPublic() 196 public function testGetTagsVisibilityPublic()
192 { 197 {
193 $tags = $this->linkDB->linksCountPerTag([], 'public'); 198 $tags = $this->bookmarkService->bookmarksCountPerTag([], 'public');
194 $env = Environment::mock( 199 $env = Environment::mock(
195 [ 200 [
196 'REQUEST_METHOD' => 'GET', 201 'REQUEST_METHOD' => 'GET',
diff --git a/tests/api/controllers/tags/PutTagTest.php b/tests/api/controllers/tags/PutTagTest.php
index 86106fc7..74edde78 100644
--- a/tests/api/controllers/tags/PutTagTest.php
+++ b/tests/api/controllers/tags/PutTagTest.php
@@ -3,6 +3,7 @@
3namespace Shaarli\Api\Controllers; 3namespace Shaarli\Api\Controllers;
4 4
5use Shaarli\Api\Exceptions\ApiBadParametersException; 5use Shaarli\Api\Exceptions\ApiBadParametersException;
6use Shaarli\Bookmark\BookmarkFileService;
6use Shaarli\Bookmark\LinkDB; 7use Shaarli\Bookmark\LinkDB;
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 PutTagTest extends \PHPUnit\Framework\TestCase 15class PutTagTest extends \Shaarli\TestCase
15{ 16{
16 /** 17 /**
17 * @var string datastore to test write operations 18 * @var string datastore to test write operations
@@ -44,9 +45,9 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
44 protected $container; 45 protected $container;
45 46
46 /** 47 /**
47 * @var LinkDB instance. 48 * @var BookmarkFileService instance.
48 */ 49 */
49 protected $linkDB; 50 protected $bookmarkService;
50 51
51 /** 52 /**
52 * @var Tags controller instance. 53 * @var Tags controller instance.
@@ -59,22 +60,22 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
59 const NB_FIELDS_TAG = 2; 60 const NB_FIELDS_TAG = 2;
60 61
61 /** 62 /**
62 * Before every test, instantiate a new Api with its config, plugins and links. 63 * Before every test, instantiate a new Api with its config, plugins and bookmarks.
63 */ 64 */
64 public function setUp() 65 protected function setUp(): void
65 { 66 {
66 $this->conf = new ConfigManager('tests/utils/config/configJson.json.php'); 67 $this->conf = new ConfigManager('tests/utils/config/configJson');
68 $this->conf->set('resource.datastore', self::$testDatastore);
67 $this->refDB = new \ReferenceLinkDB(); 69 $this->refDB = new \ReferenceLinkDB();
68 $this->refDB->write(self::$testDatastore); 70 $this->refDB->write(self::$testDatastore);
69
70 $refHistory = new \ReferenceHistory(); 71 $refHistory = new \ReferenceHistory();
71 $refHistory->write(self::$testHistory); 72 $refHistory->write(self::$testHistory);
72 $this->history = new History(self::$testHistory); 73 $this->history = new History(self::$testHistory);
74 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
73 75
74 $this->container = new Container(); 76 $this->container = new Container();
75 $this->container['conf'] = $this->conf; 77 $this->container['conf'] = $this->conf;
76 $this->linkDB = new LinkDB(self::$testDatastore, true, false); 78 $this->container['db'] = $this->bookmarkService;
77 $this->container['db'] = $this->linkDB;
78 $this->container['history'] = $this->history; 79 $this->container['history'] = $this->history;
79 80
80 $this->controller = new Tags($this->container); 81 $this->controller = new Tags($this->container);
@@ -83,7 +84,7 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
83 /** 84 /**
84 * After every test, remove the test datastore. 85 * After every test, remove the test datastore.
85 */ 86 */
86 public function tearDown() 87 protected function tearDown(): void
87 { 88 {
88 @unlink(self::$testDatastore); 89 @unlink(self::$testDatastore);
89 @unlink(self::$testHistory); 90 @unlink(self::$testHistory);
@@ -109,7 +110,7 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
109 $this->assertEquals($newName, $data['name']); 110 $this->assertEquals($newName, $data['name']);
110 $this->assertEquals(2, $data['occurrences']); 111 $this->assertEquals(2, $data['occurrences']);
111 112
112 $tags = $this->linkDB->linksCountPerTag(); 113 $tags = $this->bookmarkService->bookmarksCountPerTag();
113 $this->assertNotTrue(isset($tags[$tagName])); 114 $this->assertNotTrue(isset($tags[$tagName]));
114 $this->assertEquals(2, $tags[$newName]); 115 $this->assertEquals(2, $tags[$newName]);
115 116
@@ -133,7 +134,7 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
133 $tagName = 'gnu'; 134 $tagName = 'gnu';
134 $newName = 'w3c'; 135 $newName = 'w3c';
135 136
136 $tags = $this->linkDB->linksCountPerTag(); 137 $tags = $this->bookmarkService->bookmarksCountPerTag();
137 $this->assertEquals(1, $tags[$newName]); 138 $this->assertEquals(1, $tags[$newName]);
138 $this->assertEquals(2, $tags[$tagName]); 139 $this->assertEquals(2, $tags[$tagName]);
139 140
@@ -151,23 +152,23 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
151 $this->assertEquals($newName, $data['name']); 152 $this->assertEquals($newName, $data['name']);
152 $this->assertEquals(3, $data['occurrences']); 153 $this->assertEquals(3, $data['occurrences']);
153 154
154 $tags = $this->linkDB->linksCountPerTag(); 155 $tags = $this->bookmarkService->bookmarksCountPerTag();
155 $this->assertNotTrue(isset($tags[$tagName])); 156 $this->assertNotTrue(isset($tags[$tagName]));
156 $this->assertEquals(3, $tags[$newName]); 157 $this->assertEquals(3, $tags[$newName]);
157 } 158 }
158 159
159 /** 160 /**
160 * Test tag update with an empty new tag name => ApiBadParametersException 161 * Test tag update with an empty new tag name => ApiBadParametersException
161 *
162 * @expectedException Shaarli\Api\Exceptions\ApiBadParametersException
163 * @expectedExceptionMessage New tag name is required in the request body
164 */ 162 */
165 public function testPutTagEmpty() 163 public function testPutTagEmpty()
166 { 164 {
165 $this->expectException(\Shaarli\Api\Exceptions\ApiBadParametersException::class);
166 $this->expectExceptionMessage('New tag name is required in the request body');
167
167 $tagName = 'gnu'; 168 $tagName = 'gnu';
168 $newName = ''; 169 $newName = '';
169 170
170 $tags = $this->linkDB->linksCountPerTag(); 171 $tags = $this->bookmarkService->bookmarksCountPerTag();
171 $this->assertEquals(2, $tags[$tagName]); 172 $this->assertEquals(2, $tags[$tagName]);
172 173
173 $env = Environment::mock([ 174 $env = Environment::mock([
@@ -185,7 +186,7 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
185 try { 186 try {
186 $this->controller->putTag($request, new Response(), ['tagName' => $tagName]); 187 $this->controller->putTag($request, new Response(), ['tagName' => $tagName]);
187 } catch (ApiBadParametersException $e) { 188 } catch (ApiBadParametersException $e) {
188 $tags = $this->linkDB->linksCountPerTag(); 189 $tags = $this->bookmarkService->bookmarksCountPerTag();
189 $this->assertEquals(2, $tags[$tagName]); 190 $this->assertEquals(2, $tags[$tagName]);
190 throw $e; 191 throw $e;
191 } 192 }
@@ -193,12 +194,12 @@ class PutTagTest extends \PHPUnit\Framework\TestCase
193 194
194 /** 195 /**
195 * Test tag update on non existent tag => ApiTagNotFoundException. 196 * Test tag update on non existent tag => ApiTagNotFoundException.
196 *
197 * @expectedException Shaarli\Api\Exceptions\ApiTagNotFoundException
198 * @expectedExceptionMessage Tag not found
199 */ 197 */
200 public function testPutTag404() 198 public function testPutTag404()
201 { 199 {
200 $this->expectException(\Shaarli\Api\Exceptions\ApiTagNotFoundException::class);
201 $this->expectExceptionMessage('Tag not found');
202
202 $env = Environment::mock([ 203 $env = Environment::mock([
203 'REQUEST_METHOD' => 'PUT', 204 'REQUEST_METHOD' => 'PUT',
204 ]); 205 ]);
diff --git a/tests/bookmark/BookmarkArrayTest.php b/tests/bookmark/BookmarkArrayTest.php
new file mode 100644
index 00000000..ebed9bfc
--- /dev/null
+++ b/tests/bookmark/BookmarkArrayTest.php
@@ -0,0 +1,236 @@
1<?php
2
3namespace Shaarli\Bookmark;
4
5use Shaarli\TestCase;
6
7/**
8 * Class BookmarkArrayTest
9 */
10class BookmarkArrayTest extends TestCase
11{
12 /**
13 * Test the constructor and make sure that the instance is properly initialized
14 */
15 public function testArrayConstructorEmpty()
16 {
17 $array = new BookmarkArray();
18 $this->assertTrue(is_iterable($array));
19 $this->assertEmpty($array);
20 }
21
22 /**
23 * Test adding entries to the array, specifying the key offset or not.
24 */
25 public function testArrayAccessAddEntries()
26 {
27 $array = new BookmarkArray();
28 $bookmark = new Bookmark();
29 $bookmark->setId(11)->validate();
30 $array[] = $bookmark;
31 $this->assertCount(1, $array);
32 $this->assertTrue(isset($array[11]));
33 $this->assertNull($array[0]);
34 $this->assertEquals($bookmark, $array[11]);
35
36 $bookmark = new Bookmark();
37 $bookmark->setId(14)->validate();
38 $array[14] = $bookmark;
39 $this->assertCount(2, $array);
40 $this->assertTrue(isset($array[14]));
41 $this->assertNull($array[0]);
42 $this->assertEquals($bookmark, $array[14]);
43 }
44
45 /**
46 * Test adding a bad entry: wrong type
47 */
48 public function testArrayAccessAddBadEntryInstance()
49 {
50 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
51
52 $array = new BookmarkArray();
53 $array[] = 'nope';
54 }
55
56 /**
57 * Test adding a bad entry: no id
58 */
59 public function testArrayAccessAddBadEntryNoId()
60 {
61 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
62
63 $array = new BookmarkArray();
64 $bookmark = new Bookmark();
65 $array[] = $bookmark;
66 }
67
68 /**
69 * Test adding a bad entry: no url
70 */
71 public function testArrayAccessAddBadEntryNoUrl()
72 {
73 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
74
75 $array = new BookmarkArray();
76 $bookmark = (new Bookmark())->setId(11);
77 $array[] = $bookmark;
78 }
79
80 /**
81 * Test adding a bad entry: invalid offset
82 */
83 public function testArrayAccessAddBadEntryOffset()
84 {
85 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
86
87 $array = new BookmarkArray();
88 $bookmark = (new Bookmark())->setId(11);
89 $bookmark->validate();
90 $array['nope'] = $bookmark;
91 }
92
93 /**
94 * Test adding a bad entry: invalid ID type
95 */
96 public function testArrayAccessAddBadEntryIdType()
97 {
98 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
99
100 $array = new BookmarkArray();
101 $bookmark = (new Bookmark())->setId('nope');
102 $bookmark->validate();
103 $array[] = $bookmark;
104 }
105
106 /**
107 * Test adding a bad entry: ID/offset not consistent
108 */
109 public function testArrayAccessAddBadEntryIdOffset()
110 {
111 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
112
113 $array = new BookmarkArray();
114 $bookmark = (new Bookmark())->setId(11);
115 $bookmark->validate();
116 $array[14] = $bookmark;
117 }
118
119 /**
120 * Test update entries through array access.
121 */
122 public function testArrayAccessUpdateEntries()
123 {
124 $array = new BookmarkArray();
125 $bookmark = new Bookmark();
126 $bookmark->setId(11)->validate();
127 $bookmark->setTitle('old');
128 $array[] = $bookmark;
129 $bookmark = new Bookmark();
130 $bookmark->setId(11)->validate();
131 $bookmark->setTitle('test');
132 $array[] = $bookmark;
133 $this->assertCount(1, $array);
134 $this->assertEquals('test', $array[11]->getTitle());
135
136 $bookmark = new Bookmark();
137 $bookmark->setId(11)->validate();
138 $bookmark->setTitle('test2');
139 $array[11] = $bookmark;
140 $this->assertCount(1, $array);
141 $this->assertEquals('test2', $array[11]->getTitle());
142 }
143
144 /**
145 * Test delete entries through array access.
146 */
147 public function testArrayAccessDeleteEntries()
148 {
149 $array = new BookmarkArray();
150 $bookmark11 = new Bookmark();
151 $bookmark11->setId(11)->validate();
152 $array[] = $bookmark11;
153 $bookmark14 = new Bookmark();
154 $bookmark14->setId(14)->validate();
155 $array[] = $bookmark14;
156 $bookmark23 = new Bookmark();
157 $bookmark23->setId(23)->validate();
158 $array[] = $bookmark23;
159 $bookmark0 = new Bookmark();
160 $bookmark0->setId(0)->validate();
161 $array[] = $bookmark0;
162 $this->assertCount(4, $array);
163
164 unset($array[14]);
165 $this->assertCount(3, $array);
166 $this->assertEquals($bookmark11, $array[11]);
167 $this->assertEquals($bookmark23, $array[23]);
168 $this->assertEquals($bookmark0, $array[0]);
169
170 unset($array[23]);
171 $this->assertCount(2, $array);
172 $this->assertEquals($bookmark11, $array[11]);
173 $this->assertEquals($bookmark0, $array[0]);
174
175 unset($array[11]);
176 $this->assertCount(1, $array);
177 $this->assertEquals($bookmark0, $array[0]);
178
179 unset($array[0]);
180 $this->assertCount(0, $array);
181 }
182
183 /**
184 * Test iterating through array access.
185 */
186 public function testArrayAccessIterate()
187 {
188 $array = new BookmarkArray();
189 $bookmark11 = new Bookmark();
190 $bookmark11->setId(11)->validate();
191 $array[] = $bookmark11;
192 $bookmark14 = new Bookmark();
193 $bookmark14->setId(14)->validate();
194 $array[] = $bookmark14;
195 $bookmark23 = new Bookmark();
196 $bookmark23->setId(23)->validate();
197 $array[] = $bookmark23;
198 $this->assertCount(3, $array);
199
200 foreach ($array as $id => $bookmark) {
201 $this->assertEquals(${'bookmark'. $id}, $bookmark);
202 }
203 }
204
205 /**
206 * Test reordering the array.
207 */
208 public function testReorder()
209 {
210 $refDB = new \ReferenceLinkDB();
211 $refDB->write('sandbox/datastore.php');
212
213
214 $bookmarks = $refDB->getLinks();
215 $bookmarks->reorder('ASC');
216 $this->assertInstanceOf(BookmarkArray::class, $bookmarks);
217
218 $stickyIds = [11, 10];
219 $standardIds = [42, 4, 9, 1, 0, 7, 6, 8, 41];
220 $linkIds = array_merge($stickyIds, $standardIds);
221 $cpt = 0;
222 foreach ($bookmarks as $key => $value) {
223 $this->assertEquals($linkIds[$cpt++], $key);
224 }
225
226 $bookmarks = $refDB->getLinks();
227 $bookmarks->reorder('DESC');
228 $this->assertInstanceOf(BookmarkArray::class, $bookmarks);
229
230 $linkIds = array_merge(array_reverse($stickyIds), array_reverse($standardIds));
231 $cpt = 0;
232 foreach ($bookmarks as $key => $value) {
233 $this->assertEquals($linkIds[$cpt++], $key);
234 }
235 }
236}
diff --git a/tests/bookmark/BookmarkFileServiceTest.php b/tests/bookmark/BookmarkFileServiceTest.php
new file mode 100644
index 00000000..c399822b
--- /dev/null
+++ b/tests/bookmark/BookmarkFileServiceTest.php
@@ -0,0 +1,1111 @@
1<?php
2/**
3 * Link datastore tests
4 */
5
6namespace Shaarli\Bookmark;
7
8use DateTime;
9use ReferenceLinkDB;
10use ReflectionClass;
11use Shaarli;
12use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
13use Shaarli\Config\ConfigManager;
14use Shaarli\Formatter\BookmarkMarkdownFormatter;
15use Shaarli\History;
16use Shaarli\TestCase;
17
18/**
19 * Unitary tests for LegacyLinkDBTest
20 */
21class BookmarkFileServiceTest extends TestCase
22{
23 // datastore to test write operations
24 protected static $testDatastore = 'sandbox/datastore.php';
25
26 protected static $testConf = 'sandbox/config';
27
28 protected static $testUpdates = 'sandbox/updates.txt';
29
30 /**
31 * @var ConfigManager instance.
32 */
33 protected $conf;
34
35 /**
36 * @var History instance.
37 */
38 protected $history;
39
40 /**
41 * @var ReferenceLinkDB instance.
42 */
43 protected $refDB = null;
44
45 /**
46 * @var BookmarkFileService public LinkDB instance.
47 */
48 protected $publicLinkDB = null;
49
50 /**
51 * @var BookmarkFileService private LinkDB instance.
52 */
53 protected $privateLinkDB = null;
54
55 /**
56 * Instantiates public and private LinkDBs with test data
57 *
58 * The reference datastore contains public and private bookmarks that
59 * will be used to test LinkDB's methods:
60 * - access filtering (public/private),
61 * - link searches:
62 * - by day,
63 * - by tag,
64 * - by text,
65 * - etc.
66 *
67 * Resets test data for each test
68 */
69 protected function setUp(): void
70 {
71 if (file_exists(self::$testDatastore)) {
72 unlink(self::$testDatastore);
73 }
74
75 if (file_exists(self::$testConf .'.json.php')) {
76 unlink(self::$testConf .'.json.php');
77 }
78
79 if (file_exists(self::$testUpdates)) {
80 unlink(self::$testUpdates);
81 }
82
83 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php');
84 $this->conf = new ConfigManager(self::$testConf);
85 $this->conf->set('resource.datastore', self::$testDatastore);
86 $this->conf->set('resource.updates', self::$testUpdates);
87 $this->refDB = new \ReferenceLinkDB();
88 $this->refDB->write(self::$testDatastore);
89 $this->history = new History('sandbox/history.php');
90 $this->publicLinkDB = new BookmarkFileService($this->conf, $this->history, false);
91 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true);
92 }
93
94 /**
95 * Test migrate() method with a legacy datastore.
96 */
97 public function testDatabaseMigration()
98 {
99 if (!defined('SHAARLI_VERSION')) {
100 define('SHAARLI_VERSION', 'dev');
101 }
102
103 $this->refDB = new \ReferenceLinkDB(true);
104 $this->refDB->write(self::$testDatastore);
105 $db = self::getMethod('migrate');
106 $db->invokeArgs($this->privateLinkDB, []);
107
108 $db = new \FakeBookmarkService($this->conf, $this->history, true);
109 $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks());
110 $this->assertEquals($this->refDB->countLinks(), $db->count());
111 }
112
113 /**
114 * Test get() method for a defined and saved bookmark
115 */
116 public function testGetDefinedSaved()
117 {
118 $bookmark = $this->privateLinkDB->get(42);
119 $this->assertEquals(42, $bookmark->getId());
120 $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle());
121 }
122
123 /**
124 * Test get() method for a defined and not saved bookmark
125 */
126 public function testGetDefinedNotSaved()
127 {
128 $bookmark = new Bookmark();
129 $this->privateLinkDB->add($bookmark);
130 $createdBookmark = $this->privateLinkDB->get(43);
131 $this->assertEquals(43, $createdBookmark->getId());
132 $this->assertEmpty($createdBookmark->getDescription());
133 }
134
135 /**
136 * Test get() method for an undefined bookmark
137 */
138 public function testGetUndefined()
139 {
140 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
141
142 $this->privateLinkDB->get(666);
143 }
144
145 /**
146 * Test add() method for a bookmark fully built
147 */
148 public function testAddFull()
149 {
150 $bookmark = new Bookmark();
151 $bookmark->setUrl($url = 'https://domain.tld/index.php');
152 $bookmark->setShortUrl('abc');
153 $bookmark->setTitle($title = 'This a brand new bookmark');
154 $bookmark->setDescription($desc = 'It should be created and written');
155 $bookmark->setTags($tags = ['tag1', 'tagssss']);
156 $bookmark->setThumbnail($thumb = 'http://thumb.tld/dle.png');
157 $bookmark->setPrivate(true);
158 $bookmark->setSticky(true);
159 $bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190518_140354'));
160 $bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190518_150354'));
161
162 $this->privateLinkDB->add($bookmark);
163 $bookmark = $this->privateLinkDB->get(43);
164 $this->assertEquals(43, $bookmark->getId());
165 $this->assertEquals($url, $bookmark->getUrl());
166 $this->assertEquals('abc', $bookmark->getShortUrl());
167 $this->assertEquals($title, $bookmark->getTitle());
168 $this->assertEquals($desc, $bookmark->getDescription());
169 $this->assertEquals($tags, $bookmark->getTags());
170 $this->assertEquals($thumb, $bookmark->getThumbnail());
171 $this->assertTrue($bookmark->isPrivate());
172 $this->assertTrue($bookmark->isSticky());
173 $this->assertEquals($created, $bookmark->getCreated());
174 $this->assertEquals($updated, $bookmark->getUpdated());
175
176 // reload from file
177 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true);
178
179 $bookmark = $this->privateLinkDB->get(43);
180 $this->assertEquals(43, $bookmark->getId());
181 $this->assertEquals($url, $bookmark->getUrl());
182 $this->assertEquals('abc', $bookmark->getShortUrl());
183 $this->assertEquals($title, $bookmark->getTitle());
184 $this->assertEquals($desc, $bookmark->getDescription());
185 $this->assertEquals($tags, $bookmark->getTags());
186 $this->assertEquals($thumb, $bookmark->getThumbnail());
187 $this->assertTrue($bookmark->isPrivate());
188 $this->assertTrue($bookmark->isSticky());
189 $this->assertEquals($created, $bookmark->getCreated());
190 $this->assertEquals($updated, $bookmark->getUpdated());
191 }
192
193 /**
194 * Test add() method for a bookmark without any field set
195 */
196 public function testAddMinimal()
197 {
198 $bookmark = new Bookmark();
199 $this->privateLinkDB->add($bookmark);
200
201 $bookmark = $this->privateLinkDB->get(43);
202 $this->assertEquals(43, $bookmark->getId());
203 $this->assertRegExp('#/shaare/[\w\-]{6}#', $bookmark->getUrl());
204 $this->assertRegExp('/[\w\-]{6}/', $bookmark->getShortUrl());
205 $this->assertEquals($bookmark->getUrl(), $bookmark->getTitle());
206 $this->assertEmpty($bookmark->getDescription());
207 $this->assertEmpty($bookmark->getTags());
208 $this->assertEmpty($bookmark->getThumbnail());
209 $this->assertFalse($bookmark->isPrivate());
210 $this->assertFalse($bookmark->isSticky());
211 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getCreated());
212 $this->assertNull($bookmark->getUpdated());
213
214 // reload from file
215 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true);
216
217 $bookmark = $this->privateLinkDB->get(43);
218 $this->assertEquals(43, $bookmark->getId());
219 $this->assertRegExp('#/shaare/[\w\-]{6}#', $bookmark->getUrl());
220 $this->assertRegExp('/[\w\-]{6}/', $bookmark->getShortUrl());
221 $this->assertEquals($bookmark->getUrl(), $bookmark->getTitle());
222 $this->assertEmpty($bookmark->getDescription());
223 $this->assertEmpty($bookmark->getTags());
224 $this->assertEmpty($bookmark->getThumbnail());
225 $this->assertFalse($bookmark->isPrivate());
226 $this->assertFalse($bookmark->isSticky());
227 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getCreated());
228 $this->assertNull($bookmark->getUpdated());
229 }
230
231 /**
232 * Test add() method for a bookmark without any field set and without writing the data store
233 */
234 public function testAddMinimalNoWrite()
235 {
236 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
237
238 $bookmark = new Bookmark();
239 $this->privateLinkDB->add($bookmark, false);
240
241 $bookmark = $this->privateLinkDB->get(43);
242 $this->assertEquals(43, $bookmark->getId());
243
244 // reload from file
245 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true);
246
247 $this->privateLinkDB->get(43);
248 }
249
250 /**
251 * Test add() method while logged out
252 */
253 public function testAddLoggedOut()
254 {
255 $this->expectException(\Exception::class);
256 $this->expectExceptionMessage('You\'re not authorized to alter the datastore');
257
258 $this->publicLinkDB->add(new Bookmark());
259 }
260
261 /**
262 * Test add() method with an entry which is not a bookmark instance
263 */
264 public function testAddNotABookmark()
265 {
266 $this->expectException(\Exception::class);
267 $this->expectExceptionMessage('Provided data is invalid');
268
269 $this->privateLinkDB->add(['title' => 'hi!']);
270 }
271
272 /**
273 * Test add() method with a Bookmark already containing an ID
274 */
275 public function testAddWithId()
276 {
277 $this->expectException(\Exception::class);
278 $this->expectExceptionMessage('This bookmarks already exists');
279
280 $bookmark = new Bookmark();
281 $bookmark->setId(43);
282 $this->privateLinkDB->add($bookmark);
283 }
284
285 /**
286 * Test set() method for a bookmark fully built
287 */
288 public function testSetFull()
289 {
290 $bookmark = $this->privateLinkDB->get(42);
291 $bookmark->setUrl($url = 'https://domain.tld/index.php');
292 $bookmark->setShortUrl('abc');
293 $bookmark->setTitle($title = 'This a brand new bookmark');
294 $bookmark->setDescription($desc = 'It should be created and written');
295 $bookmark->setTags($tags = ['tag1', 'tagssss']);
296 $bookmark->setThumbnail($thumb = 'http://thumb.tld/dle.png');
297 $bookmark->setPrivate(true);
298 $bookmark->setSticky(true);
299 $bookmark->setCreated($created = DateTime::createFromFormat('Ymd_His', '20190518_140354'));
300 $bookmark->setUpdated($updated = DateTime::createFromFormat('Ymd_His', '20190518_150354'));
301
302 $this->privateLinkDB->set($bookmark);
303 $bookmark = $this->privateLinkDB->get(42);
304 $this->assertEquals(42, $bookmark->getId());
305 $this->assertEquals($url, $bookmark->getUrl());
306 $this->assertEquals('abc', $bookmark->getShortUrl());
307 $this->assertEquals($title, $bookmark->getTitle());
308 $this->assertEquals($desc, $bookmark->getDescription());
309 $this->assertEquals($tags, $bookmark->getTags());
310 $this->assertEquals($thumb, $bookmark->getThumbnail());
311 $this->assertTrue($bookmark->isPrivate());
312 $this->assertTrue($bookmark->isSticky());
313 $this->assertEquals($created, $bookmark->getCreated());
314 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated());
315
316 // reload from file
317 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true);
318
319 $bookmark = $this->privateLinkDB->get(42);
320 $this->assertEquals(42, $bookmark->getId());
321 $this->assertEquals($url, $bookmark->getUrl());
322 $this->assertEquals('abc', $bookmark->getShortUrl());
323 $this->assertEquals($title, $bookmark->getTitle());
324 $this->assertEquals($desc, $bookmark->getDescription());
325 $this->assertEquals($tags, $bookmark->getTags());
326 $this->assertEquals($thumb, $bookmark->getThumbnail());
327 $this->assertTrue($bookmark->isPrivate());
328 $this->assertTrue($bookmark->isSticky());
329 $this->assertEquals($created, $bookmark->getCreated());
330 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated());
331 }
332
333 /**
334 * Test set() method for a bookmark without any field set
335 */
336 public function testSetMinimal()
337 {
338 $bookmark = $this->privateLinkDB->get(42);
339 $this->privateLinkDB->set($bookmark);
340
341 $bookmark = $this->privateLinkDB->get(42);
342 $this->assertEquals(42, $bookmark->getId());
343 $this->assertEquals('/shaare/WDWyig', $bookmark->getUrl());
344 $this->assertEquals('1eYJ1Q', $bookmark->getShortUrl());
345 $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle());
346 $this->assertEquals('Used to test bookmarks reordering.', $bookmark->getDescription());
347 $this->assertEquals(['ut'], $bookmark->getTags());
348 $this->assertFalse($bookmark->getThumbnail());
349 $this->assertFalse($bookmark->isPrivate());
350 $this->assertFalse($bookmark->isSticky());
351 $this->assertEquals(
352 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100310_101010'),
353 $bookmark->getCreated()
354 );
355 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated());
356
357 // reload from file
358 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true);
359
360 $bookmark = $this->privateLinkDB->get(42);
361 $this->assertEquals(42, $bookmark->getId());
362 $this->assertEquals('/shaare/WDWyig', $bookmark->getUrl());
363 $this->assertEquals('1eYJ1Q', $bookmark->getShortUrl());
364 $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle());
365 $this->assertEquals('Used to test bookmarks reordering.', $bookmark->getDescription());
366 $this->assertEquals(['ut'], $bookmark->getTags());
367 $this->assertFalse($bookmark->getThumbnail());
368 $this->assertFalse($bookmark->isPrivate());
369 $this->assertFalse($bookmark->isSticky());
370 $this->assertEquals(
371 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100310_101010'),
372 $bookmark->getCreated()
373 );
374 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated());
375 }
376
377 /**
378 * Test set() method for a bookmark without any field set and without writing the data store
379 */
380 public function testSetMinimalNoWrite()
381 {
382 $bookmark = $this->privateLinkDB->get(42);
383 $bookmark->setTitle($title = 'hi!');
384 $this->privateLinkDB->set($bookmark, false);
385
386 $bookmark = $this->privateLinkDB->get(42);
387 $this->assertEquals(42, $bookmark->getId());
388 $this->assertEquals($title, $bookmark->getTitle());
389
390 // reload from file
391 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true);
392
393 $bookmark = $this->privateLinkDB->get(42);
394 $this->assertEquals(42, $bookmark->getId());
395 $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle());
396 }
397
398 /**
399 * Test set() method while logged out
400 */
401 public function testSetLoggedOut()
402 {
403 $this->expectException(\Exception::class);
404 $this->expectExceptionMessage('You\'re not authorized to alter the datastore');
405
406 $this->publicLinkDB->set(new Bookmark());
407 }
408
409 /**
410 * Test set() method with an entry which is not a bookmark instance
411 */
412 public function testSetNotABookmark()
413 {
414 $this->expectException(\Exception::class);
415 $this->expectExceptionMessage('Provided data is invalid');
416
417 $this->privateLinkDB->set(['title' => 'hi!']);
418 }
419
420 /**
421 * Test set() method with a Bookmark without an ID defined.
422 */
423 public function testSetWithoutId()
424 {
425 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
426
427 $bookmark = new Bookmark();
428 $this->privateLinkDB->set($bookmark);
429 }
430
431 /**
432 * Test set() method with a Bookmark with an unknow ID
433 */
434 public function testSetWithUnknownId()
435 {
436 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
437
438 $bookmark = new Bookmark();
439 $bookmark->setId(666);
440 $this->privateLinkDB->set($bookmark);
441 }
442
443 /**
444 * Test addOrSet() method with a new ID
445 */
446 public function testAddOrSetNew()
447 {
448 $bookmark = new Bookmark();
449 $this->privateLinkDB->addOrSet($bookmark);
450
451 $bookmark = $this->privateLinkDB->get(43);
452 $this->assertEquals(43, $bookmark->getId());
453
454 // reload from file
455 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true);
456
457 $bookmark = $this->privateLinkDB->get(43);
458 $this->assertEquals(43, $bookmark->getId());
459 }
460
461 /**
462 * Test addOrSet() method with an existing ID
463 */
464 public function testAddOrSetExisting()
465 {
466 $bookmark = $this->privateLinkDB->get(42);
467 $bookmark->setTitle($title = 'hi!');
468 $this->privateLinkDB->addOrSet($bookmark);
469
470 $bookmark = $this->privateLinkDB->get(42);
471 $this->assertEquals(42, $bookmark->getId());
472 $this->assertEquals($title, $bookmark->getTitle());
473
474 // reload from file
475 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true);
476
477 $bookmark = $this->privateLinkDB->get(42);
478 $this->assertEquals(42, $bookmark->getId());
479 $this->assertEquals($title, $bookmark->getTitle());
480 }
481
482 /**
483 * Test addOrSet() method while logged out
484 */
485 public function testAddOrSetLoggedOut()
486 {
487 $this->expectException(\Exception::class);
488 $this->expectExceptionMessage('You\'re not authorized to alter the datastore');
489
490 $this->publicLinkDB->addOrSet(new Bookmark());
491 }
492
493 /**
494 * Test addOrSet() method with an entry which is not a bookmark instance
495 */
496 public function testAddOrSetNotABookmark()
497 {
498 $this->expectException(\Exception::class);
499 $this->expectExceptionMessage('Provided data is invalid');
500
501 $this->privateLinkDB->addOrSet(['title' => 'hi!']);
502 }
503
504 /**
505 * Test addOrSet() method for a bookmark without any field set and without writing the data store
506 */
507 public function testAddOrSetMinimalNoWrite()
508 {
509 $bookmark = $this->privateLinkDB->get(42);
510 $bookmark->setTitle($title = 'hi!');
511 $this->privateLinkDB->addOrSet($bookmark, false);
512
513 $bookmark = $this->privateLinkDB->get(42);
514 $this->assertEquals(42, $bookmark->getId());
515 $this->assertEquals($title, $bookmark->getTitle());
516
517 // reload from file
518 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true);
519
520 $bookmark = $this->privateLinkDB->get(42);
521 $this->assertEquals(42, $bookmark->getId());
522 $this->assertEquals('Note: I have a big ID but an old date', $bookmark->getTitle());
523 }
524
525 /**
526 * Test remove() method with an existing Bookmark
527 */
528 public function testRemoveExisting()
529 {
530 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
531
532 $bookmark = $this->privateLinkDB->get(42);
533 $this->privateLinkDB->remove($bookmark);
534
535 $exception = null;
536 try {
537 $this->privateLinkDB->get(42);
538 } catch (BookmarkNotFoundException $e) {
539 $exception = $e;
540 }
541 $this->assertInstanceOf(BookmarkNotFoundException::class, $exception);
542
543 // reload from file
544 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true);
545
546 $this->privateLinkDB->get(42);
547 }
548
549 /**
550 * Test remove() method while logged out
551 */
552 public function testRemoveLoggedOut()
553 {
554 $this->expectException(\Exception::class);
555 $this->expectExceptionMessage('You\'re not authorized to alter the datastore');
556
557 $bookmark = $this->privateLinkDB->get(42);
558 $this->publicLinkDB->remove($bookmark);
559 }
560
561 /**
562 * Test remove() method with an entry which is not a bookmark instance
563 */
564 public function testRemoveNotABookmark()
565 {
566 $this->expectException(\Exception::class);
567 $this->expectExceptionMessage('Provided data is invalid');
568
569 $this->privateLinkDB->remove(['title' => 'hi!']);
570 }
571
572 /**
573 * Test remove() method with a Bookmark with an unknown ID
574 */
575 public function testRemoveWithUnknownId()
576 {
577 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
578
579 $bookmark = new Bookmark();
580 $bookmark->setId(666);
581 $this->privateLinkDB->remove($bookmark);
582 }
583
584 /**
585 * Test exists() method
586 */
587 public function testExists()
588 {
589 $this->assertTrue($this->privateLinkDB->exists(42)); // public
590 $this->assertTrue($this->privateLinkDB->exists(6)); // private
591
592 $this->assertTrue($this->privateLinkDB->exists(42, BookmarkFilter::$ALL));
593 $this->assertTrue($this->privateLinkDB->exists(6, BookmarkFilter::$ALL));
594
595 $this->assertTrue($this->privateLinkDB->exists(42, BookmarkFilter::$PUBLIC));
596 $this->assertFalse($this->privateLinkDB->exists(6, BookmarkFilter::$PUBLIC));
597
598 $this->assertFalse($this->privateLinkDB->exists(42, BookmarkFilter::$PRIVATE));
599 $this->assertTrue($this->privateLinkDB->exists(6, BookmarkFilter::$PRIVATE));
600
601 $this->assertTrue($this->publicLinkDB->exists(42));
602 $this->assertFalse($this->publicLinkDB->exists(6));
603
604 $this->assertTrue($this->publicLinkDB->exists(42, BookmarkFilter::$PUBLIC));
605 $this->assertFalse($this->publicLinkDB->exists(6, BookmarkFilter::$PUBLIC));
606
607 $this->assertFalse($this->publicLinkDB->exists(42, BookmarkFilter::$PRIVATE));
608 $this->assertTrue($this->publicLinkDB->exists(6, BookmarkFilter::$PRIVATE));
609 }
610
611 /**
612 * Test initialize() method
613 */
614 public function testInitialize()
615 {
616 $dbSize = $this->privateLinkDB->count();
617 $this->privateLinkDB->initialize();
618 $this->assertEquals($dbSize + 3, $this->privateLinkDB->count());
619 $this->assertStringStartsWith(
620 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.',
621 $this->privateLinkDB->get(43)->getDescription()
622 );
623 $this->assertStringStartsWith(
624 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.',
625 $this->privateLinkDB->get(44)->getDescription()
626 );
627 $this->assertStringStartsWith(
628 'Welcome to Shaarli!',
629 $this->privateLinkDB->get(45)->getDescription()
630 );
631 }
632
633 /*
634 * The following tests have been taken from the legacy LinkDB test and adapted
635 * to make sure that nothing have been broken in the migration process.
636 * They mostly cover search/filters. Some of them might be redundant with the previous ones.
637 */
638 /**
639 * Attempt to instantiate a LinkDB whereas the datastore is not writable
640 */
641 public function testConstructDatastoreNotWriteable()
642 {
643 $this->expectException(\Shaarli\Bookmark\Exception\NotWritableDataStoreException::class);
644 $this->expectExceptionMessageRegExp('#Couldn\'t load data from the data store file "null".*#');
645
646 $conf = new ConfigManager('tests/utils/config/configJson');
647 $conf->set('resource.datastore', 'null/store.db');
648 new BookmarkFileService($conf, $this->history, true);
649 }
650
651 /**
652 * The DB doesn't exist, ensure it is created with an empty datastore
653 */
654 public function testCheckDBNewLoggedIn()
655 {
656 unlink(self::$testDatastore);
657 $this->assertFileNotExists(self::$testDatastore);
658 new BookmarkFileService($this->conf, $this->history, true);
659 $this->assertFileExists(self::$testDatastore);
660
661 // ensure the correct data has been written
662 $this->assertGreaterThan(0, filesize(self::$testDatastore));
663 }
664
665 /**
666 * The DB doesn't exist, but not logged in, ensure it initialized, but the file is not written
667 */
668 public function testCheckDBNewLoggedOut()
669 {
670 unlink(self::$testDatastore);
671 $this->assertFileNotExists(self::$testDatastore);
672 $db = new \FakeBookmarkService($this->conf, $this->history, false);
673 $this->assertFileNotExists(self::$testDatastore);
674 $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks());
675 $this->assertCount(0, $db->getBookmarks());
676 }
677
678 /**
679 * Load public bookmarks from the DB
680 */
681 public function testReadPublicDB()
682 {
683 $this->assertEquals(
684 $this->refDB->countPublicLinks(),
685 $this->publicLinkDB->count()
686 );
687 }
688
689 /**
690 * Load public and private bookmarks from the DB
691 */
692 public function testReadPrivateDB()
693 {
694 $this->assertEquals(
695 $this->refDB->countLinks(),
696 $this->privateLinkDB->count()
697 );
698 }
699
700 /**
701 * Save the bookmarks to the DB
702 */
703 public function testSave()
704 {
705 $testDB = new BookmarkFileService($this->conf, $this->history, true);
706 $dbSize = $testDB->count();
707
708 $bookmark = new Bookmark();
709 $testDB->add($bookmark);
710
711 $testDB = new BookmarkFileService($this->conf, $this->history, true);
712 $this->assertEquals($dbSize + 1, $testDB->count());
713 }
714
715 /**
716 * Count existing bookmarks - public bookmarks hidden
717 */
718 public function testCountHiddenPublic()
719 {
720 $this->conf->set('privacy.hide_public_links', true);
721 $linkDB = new BookmarkFileService($this->conf, $this->history, false);
722
723 $this->assertEquals(0, $linkDB->count());
724 }
725
726 /**
727 * List the days for which bookmarks have been posted
728 */
729 public function testDays()
730 {
731 $this->assertEquals(
732 ['20100309', '20100310', '20121206', '20121207', '20130614', '20150310'],
733 $this->publicLinkDB->days()
734 );
735
736 $this->assertEquals(
737 ['20100309', '20100310', '20121206', '20121207', '20130614', '20141125', '20150310'],
738 $this->privateLinkDB->days()
739 );
740 }
741
742 /**
743 * The URL corresponds to an existing entry in the DB
744 */
745 public function testGetKnownLinkFromURL()
746 {
747 $link = $this->publicLinkDB->findByUrl('http://mediagoblin.org/');
748
749 $this->assertNotEquals(false, $link);
750 $this->assertContainsPolyfill(
751 'A free software media publishing platform',
752 $link->getDescription()
753 );
754 }
755
756 /**
757 * The URL is not in the DB
758 */
759 public function testGetUnknownLinkFromURL()
760 {
761 $this->assertEquals(
762 false,
763 $this->publicLinkDB->findByUrl('http://dev.null')
764 );
765 }
766
767 /**
768 * Lists all tags
769 */
770 public function testAllTags()
771 {
772 $this->assertEquals(
773 [
774 'web' => 3,
775 'cartoon' => 2,
776 'gnu' => 2,
777 'dev' => 1,
778 'samba' => 1,
779 'media' => 1,
780 'software' => 1,
781 'stallman' => 1,
782 'free' => 1,
783 '-exclude' => 1,
784 'hashtag' => 2,
785 // The DB contains a link with `sTuff` and another one with `stuff` tag.
786 // They need to be grouped with the first case found - order by date DESC: `sTuff`.
787 'sTuff' => 2,
788 'ut' => 1,
789 ],
790 $this->publicLinkDB->bookmarksCountPerTag()
791 );
792
793 $this->assertEquals(
794 [
795 'web' => 4,
796 'cartoon' => 3,
797 'gnu' => 2,
798 'dev' => 2,
799 'samba' => 1,
800 'media' => 1,
801 'software' => 1,
802 'stallman' => 1,
803 'free' => 1,
804 'html' => 1,
805 'w3c' => 1,
806 'css' => 1,
807 'Mercurial' => 1,
808 'sTuff' => 2,
809 '-exclude' => 1,
810 '.hidden' => 1,
811 'hashtag' => 2,
812 'tag1' => 1,
813 'tag2' => 1,
814 'tag3' => 1,
815 'tag4' => 1,
816 'ut' => 1,
817 ],
818 $this->privateLinkDB->bookmarksCountPerTag()
819 );
820 $this->assertEquals(
821 [
822 'cartoon' => 2,
823 'gnu' => 1,
824 'dev' => 1,
825 'samba' => 1,
826 'media' => 1,
827 'html' => 1,
828 'w3c' => 1,
829 'css' => 1,
830 'Mercurial' => 1,
831 '.hidden' => 1,
832 'hashtag' => 1,
833 ],
834 $this->privateLinkDB->bookmarksCountPerTag(['web'])
835 );
836 $this->assertEquals(
837 [
838 'html' => 1,
839 'w3c' => 1,
840 'css' => 1,
841 'Mercurial' => 1,
842 ],
843 $this->privateLinkDB->bookmarksCountPerTag(['web'], 'private')
844 );
845 }
846
847 /**
848 * Test filter with string.
849 */
850 public function testFilterString()
851 {
852 $tags = 'dev cartoon';
853 $request = ['searchtags' => $tags];
854 $this->assertEquals(
855 2,
856 count($this->privateLinkDB->search($request, null, true))
857 );
858 }
859
860 /**
861 * Test filter with array.
862 */
863 public function testFilterArray()
864 {
865 $tags = ['dev', 'cartoon'];
866 $request = ['searchtags' => $tags];
867 $this->assertEquals(
868 2,
869 count($this->privateLinkDB->search($request, null, true))
870 );
871 }
872
873 /**
874 * Test hidden tags feature:
875 * tags starting with a dot '.' are only visible when logged in.
876 */
877 public function testHiddenTags()
878 {
879 $tags = '.hidden';
880 $request = ['searchtags' => $tags];
881 $this->assertEquals(
882 1,
883 count($this->privateLinkDB->search($request, 'all', true))
884 );
885
886 $this->assertEquals(
887 0,
888 count($this->publicLinkDB->search($request, 'public', true))
889 );
890 }
891
892 /**
893 * Test filterHash() with a valid smallhash.
894 */
895 public function testFilterHashValid()
896 {
897 $request = smallHash('20150310_114651');
898 $this->assertSame(
899 $request,
900 $this->publicLinkDB->findByHash($request)->getShortUrl()
901 );
902 $request = smallHash('20150310_114633' . 8);
903 $this->assertSame(
904 $request,
905 $this->publicLinkDB->findByHash($request)->getShortUrl()
906 );
907 }
908
909 /**
910 * Test filterHash() with an invalid smallhash.
911 */
912 public function testFilterHashInValid1()
913 {
914 $this->expectException(BookmarkNotFoundException::class);
915
916 $request = 'blabla';
917 $this->publicLinkDB->findByHash($request);
918 }
919
920 /**
921 * Test filterHash() with an empty smallhash.
922 */
923 public function testFilterHashInValid()
924 {
925 $this->expectException(BookmarkNotFoundException::class);
926
927 $this->publicLinkDB->findByHash('');
928 }
929
930 /**
931 * Test linksCountPerTag all tags without filter.
932 * Equal occurrences should be sorted alphabetically.
933 */
934 public function testCountLinkPerTagAllNoFilter()
935 {
936 $expected = [
937 'web' => 4,
938 'cartoon' => 3,
939 'dev' => 2,
940 'gnu' => 2,
941 'hashtag' => 2,
942 'sTuff' => 2,
943 '-exclude' => 1,
944 '.hidden' => 1,
945 'Mercurial' => 1,
946 'css' => 1,
947 'free' => 1,
948 'html' => 1,
949 'media' => 1,
950 'samba' => 1,
951 'software' => 1,
952 'stallman' => 1,
953 'tag1' => 1,
954 'tag2' => 1,
955 'tag3' => 1,
956 'tag4' => 1,
957 'ut' => 1,
958 'w3c' => 1,
959 ];
960 $tags = $this->privateLinkDB->bookmarksCountPerTag();
961
962 $this->assertEquals($expected, $tags, var_export($tags, true));
963 }
964
965 /**
966 * Test linksCountPerTag all tags with filter.
967 * Equal occurrences should be sorted alphabetically.
968 */
969 public function testCountLinkPerTagAllWithFilter()
970 {
971 $expected = [
972 'hashtag' => 2,
973 '-exclude' => 1,
974 '.hidden' => 1,
975 'free' => 1,
976 'media' => 1,
977 'software' => 1,
978 'stallman' => 1,
979 'stuff' => 1,
980 'web' => 1,
981 ];
982 $tags = $this->privateLinkDB->bookmarksCountPerTag(['gnu']);
983
984 $this->assertEquals($expected, $tags, var_export($tags, true));
985 }
986
987 /**
988 * Test linksCountPerTag public tags with filter.
989 * Equal occurrences should be sorted alphabetically.
990 */
991 public function testCountLinkPerTagPublicWithFilter()
992 {
993 $expected = [
994 'hashtag' => 2,
995 '-exclude' => 1,
996 '.hidden' => 1,
997 'free' => 1,
998 'media' => 1,
999 'software' => 1,
1000 'stallman' => 1,
1001 'stuff' => 1,
1002 'web' => 1,
1003 ];
1004 $tags = $this->privateLinkDB->bookmarksCountPerTag(['gnu'], 'public');
1005
1006 $this->assertEquals($expected, $tags, var_export($tags, true));
1007 }
1008
1009 /**
1010 * Test linksCountPerTag public tags with filter.
1011 * Equal occurrences should be sorted alphabetically.
1012 */
1013 public function testCountLinkPerTagPrivateWithFilter()
1014 {
1015 $expected = [
1016 'cartoon' => 1,
1017 'tag1' => 1,
1018 'tag2' => 1,
1019 'tag3' => 1,
1020 'tag4' => 1,
1021 ];
1022 $tags = $this->privateLinkDB->bookmarksCountPerTag(['dev'], 'private');
1023
1024 $this->assertEquals($expected, $tags, var_export($tags, true));
1025 }
1026
1027 /**
1028 * Test linksCountPerTag public tags with filter.
1029 * Equal occurrences should be sorted alphabetically.
1030 */
1031 public function testCountTagsNoMarkdown()
1032 {
1033 $expected = [
1034 'cartoon' => 3,
1035 'dev' => 2,
1036 'tag1' => 1,
1037 'tag2' => 1,
1038 'tag3' => 1,
1039 'tag4' => 1,
1040 'web' => 4,
1041 'gnu' => 2,
1042 'hashtag' => 2,
1043 'sTuff' => 2,
1044 '-exclude' => 1,
1045 '.hidden' => 1,
1046 'Mercurial' => 1,
1047 'css' => 1,
1048 'free' => 1,
1049 'html' => 1,
1050 'media' => 1,
1051 'newTagToCount' => 1,
1052 'samba' => 1,
1053 'software' => 1,
1054 'stallman' => 1,
1055 'ut' => 1,
1056 'w3c' => 1,
1057 ];
1058 $bookmark = new Bookmark();
1059 $bookmark->setTags(['newTagToCount', BookmarkMarkdownFormatter::NO_MD_TAG]);
1060 $this->privateLinkDB->add($bookmark);
1061
1062 $tags = $this->privateLinkDB->bookmarksCountPerTag();
1063
1064 $this->assertEquals($expected, $tags, var_export($tags, true));
1065 }
1066
1067 /**
1068 * Test filterDay while logged in
1069 */
1070 public function testFilterDayLoggedIn(): void
1071 {
1072 $bookmarks = $this->privateLinkDB->filterDay('20121206');
1073 $expectedIds = [4, 9, 1, 0];
1074
1075 static::assertCount(4, $bookmarks);
1076 foreach ($bookmarks as $bookmark) {
1077 $i = ($i ?? -1) + 1;
1078 static::assertSame($expectedIds[$i], $bookmark->getId());
1079 }
1080 }
1081
1082 /**
1083 * Test filterDay while logged out
1084 */
1085 public function testFilterDayLoggedOut(): void
1086 {
1087 $bookmarks = $this->publicLinkDB->filterDay('20121206');
1088 $expectedIds = [4, 9, 1];
1089
1090 static::assertCount(3, $bookmarks);
1091 foreach ($bookmarks as $bookmark) {
1092 $i = ($i ?? -1) + 1;
1093 static::assertSame($expectedIds[$i], $bookmark->getId());
1094 }
1095 }
1096
1097 /**
1098 * Allows to test LinkDB's private methods
1099 *
1100 * @see
1101 * https://sebastian-bergmann.de/archives/881-Testing-Your-Privates.html
1102 * http://stackoverflow.com/a/2798203
1103 */
1104 protected static function getMethod($name)
1105 {
1106 $class = new ReflectionClass('Shaarli\Bookmark\BookmarkFileService');
1107 $method = $class->getMethod($name);
1108 $method->setAccessible(true);
1109 return $method;
1110 }
1111}
diff --git a/tests/bookmark/BookmarkFilterTest.php b/tests/bookmark/BookmarkFilterTest.php
new file mode 100644
index 00000000..48c7f824
--- /dev/null
+++ b/tests/bookmark/BookmarkFilterTest.php
@@ -0,0 +1,526 @@
1<?php
2
3namespace Shaarli\Bookmark;
4
5use Exception;
6use ReferenceLinkDB;
7use Shaarli\Config\ConfigManager;
8use Shaarli\History;
9use Shaarli\TestCase;
10
11/**
12 * Class BookmarkFilterTest.
13 */
14class BookmarkFilterTest extends TestCase
15{
16 /**
17 * @var string Test datastore path.
18 */
19 protected static $testDatastore = 'sandbox/datastore.php';
20 /**
21 * @var BookmarkFilter instance.
22 */
23 protected static $linkFilter;
24
25 /**
26 * @var ReferenceLinkDB instance
27 */
28 protected static $refDB;
29
30 /**
31 * @var BookmarkFileService instance
32 */
33 protected static $bookmarkService;
34
35 /**
36 * Instantiate linkFilter with ReferenceLinkDB data.
37 */
38 public static function setUpBeforeClass(): void
39 {
40 $conf = new ConfigManager('tests/utils/config/configJson');
41 $conf->set('resource.datastore', self::$testDatastore);
42 self::$refDB = new \ReferenceLinkDB();
43 self::$refDB->write(self::$testDatastore);
44 $history = new History('sandbox/history.php');
45 self::$bookmarkService = new \FakeBookmarkService($conf, $history, true);
46 self::$linkFilter = new BookmarkFilter(self::$bookmarkService->getBookmarks());
47 }
48
49 /**
50 * Blank filter.
51 */
52 public function testFilter()
53 {
54 $this->assertEquals(
55 self::$refDB->countLinks(),
56 count(self::$linkFilter->filter('', ''))
57 );
58
59 $this->assertEquals(
60 self::$refDB->countLinks(),
61 count(self::$linkFilter->filter('', '', 'all'))
62 );
63
64 $this->assertEquals(
65 self::$refDB->countLinks(),
66 count(self::$linkFilter->filter('', '', 'randomstr'))
67 );
68
69 // Private only.
70 $this->assertEquals(
71 self::$refDB->countPrivateLinks(),
72 count(self::$linkFilter->filter('', '', false, 'private'))
73 );
74
75 // Public only.
76 $this->assertEquals(
77 self::$refDB->countPublicLinks(),
78 count(self::$linkFilter->filter('', '', false, 'public'))
79 );
80
81 $this->assertEquals(
82 ReferenceLinkDB::$NB_LINKS_TOTAL,
83 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, ''))
84 );
85
86 $this->assertEquals(
87 self::$refDB->countUntaggedLinks(),
88 count(
89 self::$linkFilter->filter(
90 BookmarkFilter::$FILTER_TAG,
91 /*$request=*/
92 '',
93 /*$casesensitive=*/
94 false,
95 /*$visibility=*/
96 'all',
97 /*$untaggedonly=*/
98 true
99 )
100 )
101 );
102
103 $this->assertEquals(
104 ReferenceLinkDB::$NB_LINKS_TOTAL,
105 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, ''))
106 );
107 }
108
109 /**
110 * Filter bookmarks using a tag
111 */
112 public function testFilterOneTag()
113 {
114 $this->assertEquals(
115 4,
116 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false))
117 );
118
119 $this->assertEquals(
120 4,
121 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'all'))
122 );
123
124 $this->assertEquals(
125 4,
126 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'default-blabla'))
127 );
128
129 // Private only.
130 $this->assertEquals(
131 1,
132 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'private'))
133 );
134
135 // Public only.
136 $this->assertEquals(
137 3,
138 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'public'))
139 );
140 }
141
142 /**
143 * Filter bookmarks using a tag - case-sensitive
144 */
145 public function testFilterCaseSensitiveTag()
146 {
147 $this->assertEquals(
148 0,
149 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'mercurial', true))
150 );
151
152 $this->assertEquals(
153 1,
154 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'Mercurial', true))
155 );
156 }
157
158 /**
159 * Filter bookmarks using a tag combination
160 */
161 public function testFilterMultipleTags()
162 {
163 $this->assertEquals(
164 2,
165 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'dev cartoon', false))
166 );
167 }
168
169 /**
170 * Filter bookmarks using a non-existent tag
171 */
172 public function testFilterUnknownTag()
173 {
174 $this->assertEquals(
175 0,
176 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'null', false))
177 );
178 }
179
180 /**
181 * Return bookmarks for a given day
182 */
183 public function testFilterDay()
184 {
185 $this->assertEquals(
186 4,
187 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, '20121206'))
188 );
189 }
190
191 /**
192 * Return bookmarks for a given day
193 */
194 public function testFilterDayRestrictedVisibility(): void
195 {
196 $this->assertEquals(
197 3,
198 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, '20121206', false, BookmarkFilter::$PUBLIC))
199 );
200 }
201
202 /**
203 * 404 - day not found
204 */
205 public function testFilterUnknownDay()
206 {
207 $this->assertEquals(
208 0,
209 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, '19700101'))
210 );
211 }
212
213 /**
214 * Use an invalid date format
215 */
216 public function testFilterInvalidDayWithChars()
217 {
218 $this->expectException(\Exception::class);
219 $this->expectExceptionMessageRegExp('/Invalid date format/');
220
221 self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, 'Rainy day, dream away');
222 }
223
224 /**
225 * Use an invalid date format
226 */
227 public function testFilterInvalidDayDigits()
228 {
229 $this->expectException(\Exception::class);
230 $this->expectExceptionMessageRegExp('/Invalid date format/');
231
232 self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, '20');
233 }
234
235 /**
236 * Retrieve a link entry with its hash
237 */
238 public function testFilterSmallHash()
239 {
240 $links = self::$linkFilter->filter(BookmarkFilter::$FILTER_HASH, 'IuWvgA');
241
242 $this->assertEquals(
243 1,
244 count($links)
245 );
246
247 $this->assertEquals(
248 'MediaGoblin',
249 $links[7]->getTitle()
250 );
251 }
252
253 /**
254 * No link for this hash
255 */
256 public function testFilterUnknownSmallHash()
257 {
258 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
259
260 self::$linkFilter->filter(BookmarkFilter::$FILTER_HASH, 'Iblaah');
261 }
262
263 /**
264 * Full-text search - no result found.
265 */
266 public function testFilterFullTextNoResult()
267 {
268 $this->assertEquals(
269 0,
270 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'azertyuiop'))
271 );
272 }
273
274 /**
275 * Full-text search - result from a link's URL
276 */
277 public function testFilterFullTextURL()
278 {
279 $this->assertEquals(
280 2,
281 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'ars.userfriendly.org'))
282 );
283
284 $this->assertEquals(
285 2,
286 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'ars org'))
287 );
288 }
289
290 /**
291 * Full-text search - result from a link's title only
292 */
293 public function testFilterFullTextTitle()
294 {
295 // use miscellaneous cases
296 $this->assertEquals(
297 2,
298 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'userfriendly -'))
299 );
300 $this->assertEquals(
301 2,
302 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'UserFriendly -'))
303 );
304 $this->assertEquals(
305 2,
306 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'uSeRFrIendlY -'))
307 );
308
309 // use miscellaneous case and offset
310 $this->assertEquals(
311 2,
312 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'RFrIendL'))
313 );
314 }
315
316 /**
317 * Full-text search - result from the link's description only
318 */
319 public function testFilterFullTextDescription()
320 {
321 $this->assertEquals(
322 1,
323 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'publishing media'))
324 );
325
326 $this->assertEquals(
327 1,
328 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'mercurial w3c'))
329 );
330
331 $this->assertEquals(
332 3,
333 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, '"free software"'))
334 );
335 }
336
337 /**
338 * Full-text search - result from the link's tags only
339 */
340 public function testFilterFullTextTags()
341 {
342 $this->assertEquals(
343 6,
344 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web'))
345 );
346
347 $this->assertEquals(
348 6,
349 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', 'all'))
350 );
351
352 $this->assertEquals(
353 6,
354 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', 'bla'))
355 );
356
357 // Private only.
358 $this->assertEquals(
359 1,
360 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', false, 'private'))
361 );
362
363 // Public only.
364 $this->assertEquals(
365 5,
366 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', false, 'public'))
367 );
368 }
369
370 /**
371 * Full-text search - result set from mixed sources
372 */
373 public function testFilterFullTextMixed()
374 {
375 $this->assertEquals(
376 3,
377 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'free software'))
378 );
379 }
380
381 /**
382 * Full-text search - test exclusion with '-'.
383 */
384 public function testExcludeSearch()
385 {
386 $this->assertEquals(
387 1,
388 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'free -gnu'))
389 );
390
391 $this->assertEquals(
392 ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
393 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, '-revolution'))
394 );
395 }
396
397 /**
398 * Full-text search - test AND, exact terms and exclusion combined, across fields.
399 */
400 public function testMultiSearch()
401 {
402 $this->assertEquals(
403 2,
404 count(self::$linkFilter->filter(
405 BookmarkFilter::$FILTER_TEXT,
406 '"Free Software " stallman "read this" @website stuff'
407 ))
408 );
409
410 $this->assertEquals(
411 1,
412 count(self::$linkFilter->filter(
413 BookmarkFilter::$FILTER_TEXT,
414 '"free software " stallman "read this" -beard @website stuff'
415 ))
416 );
417 }
418
419 /**
420 * Full-text search - make sure that exact search won't work across fields.
421 */
422 public function testSearchExactTermMultiFieldsKo()
423 {
424 $this->assertEquals(
425 0,
426 count(self::$linkFilter->filter(
427 BookmarkFilter::$FILTER_TEXT,
428 '"designer naming"'
429 ))
430 );
431
432 $this->assertEquals(
433 0,
434 count(self::$linkFilter->filter(
435 BookmarkFilter::$FILTER_TEXT,
436 '"designernaming"'
437 ))
438 );
439 }
440
441 /**
442 * Tag search with exclusion.
443 */
444 public function testTagFilterWithExclusion()
445 {
446 $this->assertEquals(
447 1,
448 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'gnu -free'))
449 );
450
451 $this->assertEquals(
452 ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
453 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '-free'))
454 );
455 }
456
457 /**
458 * Test crossed search (terms + tags).
459 */
460 public function testFilterCrossedSearch()
461 {
462 $terms = '"Free Software " stallman "read this" @website stuff';
463 $tags = 'free';
464 $this->assertEquals(
465 1,
466 count(self::$linkFilter->filter(
467 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
468 array($tags, $terms)
469 ))
470 );
471 $this->assertEquals(
472 2,
473 count(self::$linkFilter->filter(
474 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
475 array('', $terms)
476 ))
477 );
478 $this->assertEquals(
479 1,
480 count(self::$linkFilter->filter(
481 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
482 array(false, 'PSR-2')
483 ))
484 );
485 $this->assertEquals(
486 1,
487 count(self::$linkFilter->filter(
488 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
489 array($tags, '')
490 ))
491 );
492 $this->assertEquals(
493 ReferenceLinkDB::$NB_LINKS_TOTAL,
494 count(self::$linkFilter->filter(
495 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
496 ''
497 ))
498 );
499 }
500
501 /**
502 * Filter bookmarks by #hashtag.
503 */
504 public function testFilterByHashtag()
505 {
506 $hashtag = 'hashtag';
507 $this->assertEquals(
508 3,
509 count(self::$linkFilter->filter(
510 BookmarkFilter::$FILTER_TAG,
511 $hashtag
512 ))
513 );
514
515 $hashtag = 'private';
516 $this->assertEquals(
517 1,
518 count(self::$linkFilter->filter(
519 BookmarkFilter::$FILTER_TAG,
520 $hashtag,
521 false,
522 'private'
523 ))
524 );
525 }
526}
diff --git a/tests/bookmark/BookmarkInitializerTest.php b/tests/bookmark/BookmarkInitializerTest.php
new file mode 100644
index 00000000..25704004
--- /dev/null
+++ b/tests/bookmark/BookmarkInitializerTest.php
@@ -0,0 +1,150 @@
1<?php
2
3namespace Shaarli\Bookmark;
4
5use Shaarli\Config\ConfigManager;
6use Shaarli\History;
7use Shaarli\TestCase;
8
9/**
10 * Class BookmarkInitializerTest
11 * @package Shaarli\Bookmark
12 */
13class BookmarkInitializerTest extends TestCase
14{
15 /** @var string Path of test data store */
16 protected static $testDatastore = 'sandbox/datastore.php';
17
18 /** @var string Path of test config file */
19 protected static $testConf = 'sandbox/config';
20
21 /**
22 * @var ConfigManager instance.
23 */
24 protected $conf;
25
26 /**
27 * @var History instance.
28 */
29 protected $history;
30
31 /** @var BookmarkServiceInterface instance */
32 protected $bookmarkService;
33
34 /** @var BookmarkInitializer instance */
35 protected $initializer;
36
37 /**
38 * Initialize an empty BookmarkFileService
39 */
40 public function setUp(): void
41 {
42 if (file_exists(self::$testDatastore)) {
43 unlink(self::$testDatastore);
44 }
45
46 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php');
47 $this->conf = new ConfigManager(self::$testConf);
48 $this->conf->set('resource.datastore', self::$testDatastore);
49 $this->history = new History('sandbox/history.php');
50 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
51
52 $this->initializer = new BookmarkInitializer($this->bookmarkService);
53 }
54
55 /**
56 * Test initialize() with a data store containing bookmarks.
57 */
58 public function testInitializeNotEmptyDataStore(): void
59 {
60 $refDB = new \ReferenceLinkDB();
61 $refDB->write(self::$testDatastore);
62 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
63 $this->initializer = new BookmarkInitializer($this->bookmarkService);
64
65 $this->initializer->initialize();
66
67 $this->assertEquals($refDB->countLinks() + 3, $this->bookmarkService->count());
68
69 $bookmark = $this->bookmarkService->get(43);
70 $this->assertStringStartsWith(
71 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.',
72 $bookmark->getDescription()
73 );
74 $this->assertTrue($bookmark->isPrivate());
75
76 $bookmark = $this->bookmarkService->get(44);
77 $this->assertStringStartsWith(
78 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.',
79 $bookmark->getDescription()
80 );
81 $this->assertTrue($bookmark->isPrivate());
82
83 $bookmark = $this->bookmarkService->get(45);
84 $this->assertStringStartsWith(
85 'Welcome to Shaarli!',
86 $bookmark->getDescription()
87 );
88 $this->assertFalse($bookmark->isPrivate());
89
90 $this->bookmarkService->save();
91
92 // Reload from file
93 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
94 $this->assertEquals($refDB->countLinks() + 3, $this->bookmarkService->count());
95
96 $bookmark = $this->bookmarkService->get(43);
97 $this->assertStringStartsWith(
98 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.',
99 $bookmark->getDescription()
100 );
101 $this->assertTrue($bookmark->isPrivate());
102
103 $bookmark = $this->bookmarkService->get(44);
104 $this->assertStringStartsWith(
105 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.',
106 $bookmark->getDescription()
107 );
108 $this->assertTrue($bookmark->isPrivate());
109
110 $bookmark = $this->bookmarkService->get(45);
111 $this->assertStringStartsWith(
112 'Welcome to Shaarli!',
113 $bookmark->getDescription()
114 );
115 $this->assertFalse($bookmark->isPrivate());
116 }
117
118 /**
119 * Test initialize() with an a non existent datastore file .
120 */
121 public function testInitializeNonExistentDataStore(): void
122 {
123 $this->conf->set('resource.datastore', static::$testDatastore . '_empty');
124 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
125
126 $this->initializer->initialize();
127
128 $this->assertEquals(3, $this->bookmarkService->count());
129 $bookmark = $this->bookmarkService->get(0);
130 $this->assertStringStartsWith(
131 'Shaarli will automatically pick up the thumbnail for links to a variety of websites.',
132 $bookmark->getDescription()
133 );
134 $this->assertTrue($bookmark->isPrivate());
135
136 $bookmark = $this->bookmarkService->get(1);
137 $this->assertStringStartsWith(
138 'Adding a shaare without entering a URL creates a text-only "note" post such as this one.',
139 $bookmark->getDescription()
140 );
141 $this->assertTrue($bookmark->isPrivate());
142
143 $bookmark = $this->bookmarkService->get(2);
144 $this->assertStringStartsWith(
145 'Welcome to Shaarli!',
146 $bookmark->getDescription()
147 );
148 $this->assertFalse($bookmark->isPrivate());
149 }
150}
diff --git a/tests/bookmark/BookmarkTest.php b/tests/bookmark/BookmarkTest.php
new file mode 100644
index 00000000..afec2440
--- /dev/null
+++ b/tests/bookmark/BookmarkTest.php
@@ -0,0 +1,388 @@
1<?php
2
3namespace Shaarli\Bookmark;
4
5use Shaarli\Bookmark\Exception\InvalidBookmarkException;
6use Shaarli\TestCase;
7
8/**
9 * Class BookmarkTest
10 */
11class BookmarkTest extends TestCase
12{
13 /**
14 * Test fromArray() with a link with full data
15 */
16 public function testFromArrayFull()
17 {
18 $data = [
19 'id' => 1,
20 'shorturl' => 'abc',
21 'url' => 'https://domain.tld/oof.html?param=value#anchor',
22 'title' => 'This is an array link',
23 'description' => 'HTML desc<br><p>hi!</p>',
24 'thumbnail' => 'https://domain.tld/pic.png',
25 'sticky' => true,
26 'created' => new \DateTime('-1 minute'),
27 'tags' => ['tag1', 'tag2', 'chair'],
28 'updated' => new \DateTime(),
29 'private' => true,
30 ];
31
32 $bookmark = (new Bookmark())->fromArray($data);
33 $this->assertEquals($data['id'], $bookmark->getId());
34 $this->assertEquals($data['shorturl'], $bookmark->getShortUrl());
35 $this->assertEquals($data['url'], $bookmark->getUrl());
36 $this->assertEquals($data['title'], $bookmark->getTitle());
37 $this->assertEquals($data['description'], $bookmark->getDescription());
38 $this->assertEquals($data['thumbnail'], $bookmark->getThumbnail());
39 $this->assertEquals($data['sticky'], $bookmark->isSticky());
40 $this->assertEquals($data['created'], $bookmark->getCreated());
41 $this->assertEquals($data['tags'], $bookmark->getTags());
42 $this->assertEquals('tag1 tag2 chair', $bookmark->getTagsString());
43 $this->assertEquals($data['updated'], $bookmark->getUpdated());
44 $this->assertEquals($data['private'], $bookmark->isPrivate());
45 $this->assertFalse($bookmark->isNote());
46 }
47
48 /**
49 * Test fromArray() with a link with minimal data.
50 * Note that I use null values everywhere but this should not happen in the real world.
51 */
52 public function testFromArrayMinimal()
53 {
54 $data = [
55 'id' => null,
56 'shorturl' => null,
57 'url' => null,
58 'title' => null,
59 'description' => null,
60 'created' => null,
61 'tags' => null,
62 'private' => null,
63 ];
64
65 $bookmark = (new Bookmark())->fromArray($data);
66 $this->assertNull($bookmark->getId());
67 $this->assertNull($bookmark->getShortUrl());
68 $this->assertNull($bookmark->getUrl());
69 $this->assertNull($bookmark->getTitle());
70 $this->assertEquals('', $bookmark->getDescription());
71 $this->assertNull($bookmark->getCreated());
72 $this->assertEquals([], $bookmark->getTags());
73 $this->assertEquals('', $bookmark->getTagsString());
74 $this->assertNull($bookmark->getUpdated());
75 $this->assertFalse($bookmark->getThumbnail());
76 $this->assertFalse($bookmark->isSticky());
77 $this->assertFalse($bookmark->isPrivate());
78 $this->assertTrue($bookmark->isNote());
79 }
80
81 /**
82 * Test validate() with a valid minimal bookmark
83 */
84 public function testValidateValidFullBookmark()
85 {
86 $bookmark = new Bookmark();
87 $bookmark->setId(2);
88 $bookmark->setShortUrl('abc');
89 $bookmark->setCreated($date = \DateTime::createFromFormat('Ymd_His', '20190514_200102'));
90 $bookmark->setUpdated($dateUp = \DateTime::createFromFormat('Ymd_His', '20190514_210203'));
91 $bookmark->setUrl($url = 'https://domain.tld/oof.html?param=value#anchor');
92 $bookmark->setTitle($title = 'This is an array link');
93 $bookmark->setDescription($desc = 'HTML desc<br><p>hi!</p>');
94 $bookmark->setTags($tags = ['tag1', 'tag2', 'chair']);
95 $bookmark->setThumbnail($thumb = 'https://domain.tld/pic.png');
96 $bookmark->setPrivate(true);
97 $bookmark->validate();
98
99 $this->assertEquals(2, $bookmark->getId());
100 $this->assertEquals('abc', $bookmark->getShortUrl());
101 $this->assertEquals($date, $bookmark->getCreated());
102 $this->assertEquals($dateUp, $bookmark->getUpdated());
103 $this->assertEquals($url, $bookmark->getUrl());
104 $this->assertEquals($title, $bookmark->getTitle());
105 $this->assertEquals($desc, $bookmark->getDescription());
106 $this->assertEquals($tags, $bookmark->getTags());
107 $this->assertEquals(implode(' ', $tags), $bookmark->getTagsString());
108 $this->assertEquals($thumb, $bookmark->getThumbnail());
109 $this->assertTrue($bookmark->isPrivate());
110 $this->assertFalse($bookmark->isNote());
111 }
112
113 /**
114 * Test validate() with a valid minimal bookmark
115 */
116 public function testValidateValidMinimalBookmark()
117 {
118 $bookmark = new Bookmark();
119 $bookmark->setId(1);
120 $bookmark->setShortUrl('abc');
121 $bookmark->setCreated($date = \DateTime::createFromFormat('Ymd_His', '20190514_200102'));
122 $bookmark->validate();
123
124 $this->assertEquals(1, $bookmark->getId());
125 $this->assertEquals('abc', $bookmark->getShortUrl());
126 $this->assertEquals($date, $bookmark->getCreated());
127 $this->assertEquals('/shaare/abc', $bookmark->getUrl());
128 $this->assertEquals('/shaare/abc', $bookmark->getTitle());
129 $this->assertEquals('', $bookmark->getDescription());
130 $this->assertEquals([], $bookmark->getTags());
131 $this->assertEquals('', $bookmark->getTagsString());
132 $this->assertFalse($bookmark->getThumbnail());
133 $this->assertFalse($bookmark->isPrivate());
134 $this->assertTrue($bookmark->isNote());
135 $this->assertNull($bookmark->getUpdated());
136 }
137
138 /**
139 * Test validate() with a a bookmark without ID.
140 */
141 public function testValidateNotValidNoId()
142 {
143 $bookmark = new Bookmark();
144 $bookmark->setShortUrl('abc');
145 $bookmark->setCreated(\DateTime::createFromFormat('Ymd_His', '20190514_200102'));
146 $exception = null;
147 try {
148 $bookmark->validate();
149 } catch (InvalidBookmarkException $e) {
150 $exception = $e;
151 }
152 $this->assertNotNull($exception);
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->assertContainsPolyfill('- ID: str'. PHP_EOL, $exception->getMessage());
173 }
174
175 /**
176 * Test validate() with a a bookmark without short url.
177 */
178 public function testValidateNotValidNoShortUrl()
179 {
180 $bookmark = new Bookmark();
181 $bookmark->setId(1);
182 $bookmark->setCreated(\DateTime::createFromFormat('Ymd_His', '20190514_200102'));
183 $bookmark->setShortUrl(null);
184 $exception = null;
185 try {
186 $bookmark->validate();
187 } catch (InvalidBookmarkException $e) {
188 $exception = $e;
189 }
190 $this->assertNotNull($exception);
191 $this->assertContainsPolyfill('- ShortUrl: '. PHP_EOL, $exception->getMessage());
192 }
193
194 /**
195 * Test validate() with a a bookmark without created datetime.
196 */
197 public function testValidateNotValidNoCreated()
198 {
199 $bookmark = new Bookmark();
200 $bookmark->setId(1);
201 $bookmark->setShortUrl('abc');
202 $bookmark->setCreated(null);
203 $exception = null;
204 try {
205 $bookmark->validate();
206 } catch (InvalidBookmarkException $e) {
207 $exception = $e;
208 }
209 $this->assertNotNull($exception);
210 $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->assertContainsPolyfill('- Created: Not a DateTime object'. PHP_EOL, $exception->getMessage());
230 }
231
232 /**
233 * Test setId() and make sure that default fields are generated.
234 */
235 public function testSetIdEmptyGeneratedFields()
236 {
237 $bookmark = new Bookmark();
238 $bookmark->setId(2);
239
240 $this->assertEquals(2, $bookmark->getId());
241 $this->assertRegExp('/[\w\-]{6}/', $bookmark->getShortUrl());
242 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getCreated());
243 }
244
245 /**
246 * Test setId() and with generated fields already set.
247 */
248 public function testSetIdSetGeneratedFields()
249 {
250 $bookmark = new Bookmark();
251 $bookmark->setShortUrl('abc');
252 $bookmark->setCreated($date = \DateTime::createFromFormat('Ymd_His', '20190514_200102'));
253 $bookmark->setId(2);
254
255 $this->assertEquals(2, $bookmark->getId());
256 $this->assertEquals('abc', $bookmark->getShortUrl());
257 $this->assertEquals($date, $bookmark->getCreated());
258 }
259
260 /**
261 * Test setUrl() and make sure it accepts custom protocols
262 */
263 public function testGetUrlWithValidProtocols()
264 {
265 $bookmark = new Bookmark();
266 $bookmark->setUrl($url = 'myprotocol://helloworld', ['myprotocol']);
267 $this->assertEquals($url, $bookmark->getUrl());
268
269 $bookmark->setUrl($url = 'https://helloworld.tld', ['myprotocol']);
270 $this->assertEquals($url, $bookmark->getUrl());
271 }
272
273 /**
274 * Test setUrl() and make sure it accepts custom protocols
275 */
276 public function testGetUrlWithNotValidProtocols()
277 {
278 $bookmark = new Bookmark();
279 $bookmark->setUrl('myprotocol://helloworld', []);
280 $this->assertEquals('http://helloworld', $bookmark->getUrl());
281
282 $bookmark->setUrl($url = 'https://helloworld.tld', []);
283 $this->assertEquals($url, $bookmark->getUrl());
284 }
285
286 /**
287 * Test setTagsString() with exotic data
288 */
289 public function testSetTagsString()
290 {
291 $bookmark = new Bookmark();
292
293 $str = 'tag1 tag2 tag3.tag3-2, tag4 , -tag5 ';
294 $bookmark->setTagsString($str);
295 $this->assertEquals(
296 [
297 'tag1',
298 'tag2',
299 'tag3.tag3-2',
300 'tag4',
301 'tag5',
302 ],
303 $bookmark->getTags()
304 );
305 }
306
307 /**
308 * Test setTags() with exotic data
309 */
310 public function testSetTags()
311 {
312 $bookmark = new Bookmark();
313
314 $array = [
315 'tag1 ',
316 ' tag2',
317 'tag3.tag3-2,',
318 ', tag4',
319 ', ',
320 '-tag5 ',
321 ];
322 $bookmark->setTags($array);
323 $this->assertEquals(
324 [
325 'tag1',
326 'tag2',
327 'tag3.tag3-2',
328 'tag4',
329 'tag5',
330 ],
331 $bookmark->getTags()
332 );
333 }
334
335 /**
336 * Test renameTag()
337 */
338 public function testRenameTag()
339 {
340 $bookmark = new Bookmark();
341 $bookmark->setTags(['tag1', 'tag2', 'chair']);
342 $bookmark->renameTag('chair', 'table');
343 $this->assertEquals(['tag1', 'tag2', 'table'], $bookmark->getTags());
344 $bookmark->renameTag('tag1', 'tag42');
345 $this->assertEquals(['tag42', 'tag2', 'table'], $bookmark->getTags());
346 $bookmark->renameTag('tag42', 'tag43');
347 $this->assertEquals(['tag43', 'tag2', 'table'], $bookmark->getTags());
348 $bookmark->renameTag('table', 'desk');
349 $this->assertEquals(['tag43', 'tag2', 'desk'], $bookmark->getTags());
350 }
351
352 /**
353 * Test renameTag() with a tag that is not present in the bookmark
354 */
355 public function testRenameTagNotExists()
356 {
357 $bookmark = new Bookmark();
358 $bookmark->setTags(['tag1', 'tag2', 'chair']);
359 $bookmark->renameTag('nope', 'table');
360 $this->assertEquals(['tag1', 'tag2', 'chair'], $bookmark->getTags());
361 }
362
363 /**
364 * Test deleteTag()
365 */
366 public function testDeleteTag()
367 {
368 $bookmark = new Bookmark();
369 $bookmark->setTags(['tag1', 'tag2', 'chair']);
370 $bookmark->deleteTag('chair');
371 $this->assertEquals(['tag1', 'tag2'], $bookmark->getTags());
372 $bookmark->deleteTag('tag1');
373 $this->assertEquals(['tag2'], $bookmark->getTags());
374 $bookmark->deleteTag('tag2');
375 $this->assertEquals([], $bookmark->getTags());
376 }
377
378 /**
379 * Test deleteTag() with a tag that is not present in the bookmark
380 */
381 public function testDeleteTagNotExists()
382 {
383 $bookmark = new Bookmark();
384 $bookmark->setTags(['tag1', 'tag2', 'chair']);
385 $bookmark->deleteTag('nope');
386 $this->assertEquals(['tag1', 'tag2', 'chair'], $bookmark->getTags());
387 }
388}
diff --git a/tests/bookmark/LinkUtilsTest.php b/tests/bookmark/LinkUtilsTest.php
index 78cb8f2a..ef00b92f 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()
@@ -389,15 +400,6 @@ class LinkUtilsTest extends TestCase
389 } 400 }
390 401
391 /** 402 /**
392 * Test count_private.
393 */
394 public function testCountPrivateLinks()
395 {
396 $refDB = new ReferenceLinkDB();
397 $this->assertEquals($refDB->countPrivateLinks(), count_private($refDB->getLinks()));
398 }
399
400 /**
401 * Test text2clickable. 403 * Test text2clickable.
402 */ 404 */
403 public function testText2clickable() 405 public function testText2clickable()
@@ -448,13 +450,13 @@ class LinkUtilsTest extends TestCase
448 カタカナ #カタカナ」カタカナ\n'; 450 カタカナ #カタカナ」カタカナ\n';
449 $autolinkedDescription = hashtag_autolink($rawDescription, $index); 451 $autolinkedDescription = hashtag_autolink($rawDescription, $index);
450 452
451 $this->assertContains($this->getHashtagLink('hashtag', $index), $autolinkedDescription); 453 $this->assertContainsPolyfill($this->getHashtagLink('hashtag', $index), $autolinkedDescription);
452 $this->assertNotContains(' #hashtag', $autolinkedDescription); 454 $this->assertNotContainsPolyfill(' #hashtag', $autolinkedDescription);
453 $this->assertNotContains('>#nothashtag', $autolinkedDescription); 455 $this->assertNotContainsPolyfill('>#nothashtag', $autolinkedDescription);
454 $this->assertContains($this->getHashtagLink('ашок', $index), $autolinkedDescription); 456 $this->assertContainsPolyfill($this->getHashtagLink('ашок', $index), $autolinkedDescription);
455 $this->assertContains($this->getHashtagLink('カタカナ', $index), $autolinkedDescription); 457 $this->assertContainsPolyfill($this->getHashtagLink('カタカナ', $index), $autolinkedDescription);
456 $this->assertContains($this->getHashtagLink('hashtag_hashtag', $index), $autolinkedDescription); 458 $this->assertContainsPolyfill($this->getHashtagLink('hashtag_hashtag', $index), $autolinkedDescription);
457 $this->assertNotContains($this->getHashtagLink('hashtag-nothashtag', $index), $autolinkedDescription); 459 $this->assertNotContainsPolyfill($this->getHashtagLink('hashtag-nothashtag', $index), $autolinkedDescription);
458 } 460 }
459 461
460 /** 462 /**
@@ -465,9 +467,9 @@ class LinkUtilsTest extends TestCase
465 $rawDescription = 'blabla #hashtag x#nothashtag'; 467 $rawDescription = 'blabla #hashtag x#nothashtag';
466 $autolinkedDescription = hashtag_autolink($rawDescription); 468 $autolinkedDescription = hashtag_autolink($rawDescription);
467 469
468 $this->assertContains($this->getHashtagLink('hashtag'), $autolinkedDescription); 470 $this->assertContainsPolyfill($this->getHashtagLink('hashtag'), $autolinkedDescription);
469 $this->assertNotContains(' #hashtag', $autolinkedDescription); 471 $this->assertNotContainsPolyfill(' #hashtag', $autolinkedDescription);
470 $this->assertNotContains('>#nothashtag', $autolinkedDescription); 472 $this->assertNotContainsPolyfill('>#nothashtag', $autolinkedDescription);
471 } 473 }
472 474
473 /** 475 /**
@@ -500,7 +502,7 @@ class LinkUtilsTest extends TestCase
500 */ 502 */
501 private function getHashtagLink($hashtag, $index = '') 503 private function getHashtagLink($hashtag, $index = '')
502 { 504 {
503 $hashtagLink = '<a href="' . $index . '?addtag=$1" title="Hashtag $1">#$1</a>'; 505 $hashtagLink = '<a href="' . $index . './add-tag/$1" title="Hashtag $1">#$1</a>';
504 return str_replace('$1', $hashtag, $hashtagLink); 506 return str_replace('$1', $hashtag, $hashtagLink);
505 } 507 }
506} 508}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index d36d73cd..2d675c9a 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -4,3 +4,29 @@ require_once 'vendor/autoload.php';
4 4
5$conf = new \Shaarli\Config\ConfigManager('tests/utils/config/configJson'); 5$conf = new \Shaarli\Config\ConfigManager('tests/utils/config/configJson');
6new \Shaarli\Languages('en', $conf); 6new \Shaarli\Languages('en', $conf);
7
8// is_iterable is only compatible with PHP 7.1+
9if (!function_exists('is_iterable')) {
10 function is_iterable($var)
11 {
12 return is_array($var) || $var instanceof \Traversable;
13 }
14}
15
16// TODO: remove this after fixing UT
17require_once 'application/bookmark/LinkUtils.php';
18require_once 'application/Utils.php';
19require_once 'application/http/UrlUtils.php';
20require_once 'application/http/HttpUtils.php';
21require_once 'tests/TestCase.php';
22require_once 'tests/container/ShaarliTestContainer.php';
23require_once 'tests/front/controller/visitor/FrontControllerMockHelper.php';
24require_once 'tests/front/controller/admin/FrontAdminControllerMockHelper.php';
25require_once 'tests/updater/DummyUpdater.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();
diff --git a/tests/config/ConfigJsonTest.php b/tests/config/ConfigJsonTest.php
index 95ad060b..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 }
@@ -24,7 +24,7 @@ class ConfigJsonTest extends \PHPUnit\Framework\TestCase
24 $conf = $this->configIO->read('tests/utils/config/configJson.json.php'); 24 $conf = $this->configIO->read('tests/utils/config/configJson.json.php');
25 $this->assertEquals('root', $conf['credentials']['login']); 25 $this->assertEquals('root', $conf['credentials']['login']);
26 $this->assertEquals('lala', $conf['redirector']['url']); 26 $this->assertEquals('lala', $conf['redirector']['url']);
27 $this->assertEquals('tests/utils/config/datastore.php', $conf['resource']['datastore']); 27 $this->assertEquals('sandbox/datastore.php', $conf['resource']['datastore']);
28 $this->assertEquals('1', $conf['plugins']['WALLABAG_VERSION']); 28 $this->assertEquals('1', $conf['plugins']['WALLABAG_VERSION']);
29 } 29 }
30 30
@@ -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 67d878ce..7bf9fe64 100644
--- a/tests/config/ConfigPhpTest.php
+++ b/tests/config/ConfigPhpTest.php
@@ -3,15 +3,19 @@ namespace Shaarli\Config;
3 3
4/** 4/**
5 * Class ConfigPhpTest 5 * Class ConfigPhpTest
6 *
7 * We run tests in separate processes due to the usage for $GLOBALS
8 * which are kept between tests.
9 * @runTestsInSeparateProcesses
6 */ 10 */
7class ConfigPhpTest extends \PHPUnit\Framework\TestCase 11class ConfigPhpTest extends \Shaarli\TestCase
8{ 12{
9 /** 13 /**
10 * @var ConfigPhp 14 * @var ConfigPhp
11 */ 15 */
12 protected $configIO; 16 protected $configIO;
13 17
14 public function setUp() 18 protected function setUp(): void
15 { 19 {
16 $this->configIO = new ConfigPhp(); 20 $this->configIO = new ConfigPhp();
17 } 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
new file mode 100644
index 00000000..5d52daef
--- /dev/null
+++ b/tests/container/ContainerBuilderTest.php
@@ -0,0 +1,88 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Container;
6
7use Shaarli\Bookmark\BookmarkServiceInterface;
8use Shaarli\Config\ConfigManager;
9use Shaarli\Feed\FeedBuilder;
10use Shaarli\Formatter\FormatterFactory;
11use Shaarli\Front\Controller\Visitor\ErrorController;
12use Shaarli\Front\Controller\Visitor\ErrorNotFoundController;
13use Shaarli\History;
14use Shaarli\Http\HttpAccess;
15use Shaarli\Netscape\NetscapeBookmarkUtils;
16use Shaarli\Plugin\PluginManager;
17use Shaarli\Render\PageBuilder;
18use Shaarli\Render\PageCacheManager;
19use Shaarli\Security\CookieManager;
20use Shaarli\Security\LoginManager;
21use Shaarli\Security\SessionManager;
22use Shaarli\TestCase;
23use Shaarli\Thumbnailer;
24use Shaarli\Updater\Updater;
25use Slim\Http\Environment;
26
27class ContainerBuilderTest extends TestCase
28{
29 /** @var ConfigManager */
30 protected $conf;
31
32 /** @var SessionManager */
33 protected $sessionManager;
34
35 /** @var LoginManager */
36 protected $loginManager;
37
38 /** @var ContainerBuilder */
39 protected $containerBuilder;
40
41 /** @var CookieManager */
42 protected $cookieManager;
43
44 public function setUp(): void
45 {
46 $this->conf = new ConfigManager('tests/utils/config/configJson');
47 $this->sessionManager = $this->createMock(SessionManager::class);
48 $this->cookieManager = $this->createMock(CookieManager::class);
49
50 $this->loginManager = $this->createMock(LoginManager::class);
51 $this->loginManager->method('isLoggedIn')->willReturn(true);
52
53 $this->containerBuilder = new ContainerBuilder(
54 $this->conf,
55 $this->sessionManager,
56 $this->cookieManager,
57 $this->loginManager
58 );
59 }
60
61 public function testBuildContainer(): void
62 {
63 $container = $this->containerBuilder->build();
64
65 static::assertInstanceOf(BookmarkServiceInterface::class, $container->bookmarkService);
66 static::assertInstanceOf(CookieManager::class, $container->cookieManager);
67 static::assertInstanceOf(ConfigManager::class, $container->conf);
68 static::assertInstanceOf(ErrorController::class, $container->errorHandler);
69 static::assertInstanceOf(Environment::class, $container->environment);
70 static::assertInstanceOf(FeedBuilder::class, $container->feedBuilder);
71 static::assertInstanceOf(FormatterFactory::class, $container->formatterFactory);
72 static::assertInstanceOf(History::class, $container->history);
73 static::assertInstanceOf(HttpAccess::class, $container->httpAccess);
74 static::assertInstanceOf(LoginManager::class, $container->loginManager);
75 static::assertInstanceOf(NetscapeBookmarkUtils::class, $container->netscapeBookmarkUtils);
76 static::assertInstanceOf(PageBuilder::class, $container->pageBuilder);
77 static::assertInstanceOf(PageCacheManager::class, $container->pageCacheManager);
78 static::assertInstanceOf(ErrorController::class, $container->phpErrorHandler);
79 static::assertInstanceOf(ErrorNotFoundController::class, $container->notFoundHandler);
80 static::assertInstanceOf(PluginManager::class, $container->pluginManager);
81 static::assertInstanceOf(SessionManager::class, $container->sessionManager);
82 static::assertInstanceOf(Thumbnailer::class, $container->thumbnailer);
83 static::assertInstanceOf(Updater::class, $container->updater);
84
85 // Set by the middleware
86 static::assertNull($container->basePath);
87 }
88}
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 0bcc1442..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,9 @@ 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 } 48 }
48 49
49 /** 50 /**
diff --git a/tests/feed/FeedBuilderTest.php b/tests/feed/FeedBuilderTest.php
index b496cb4c..c29e8ef3 100644
--- a/tests/feed/FeedBuilderTest.php
+++ b/tests/feed/FeedBuilderTest.php
@@ -4,14 +4,20 @@ namespace Shaarli\Feed;
4 4
5use DateTime; 5use DateTime;
6use ReferenceLinkDB; 6use ReferenceLinkDB;
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\BookmarkFileService;
7use Shaarli\Bookmark\LinkDB; 9use Shaarli\Bookmark\LinkDB;
10use Shaarli\Config\ConfigManager;
11use Shaarli\Formatter\FormatterFactory;
12use Shaarli\History;
13use Shaarli\TestCase;
8 14
9/** 15/**
10 * FeedBuilderTest class. 16 * FeedBuilderTest class.
11 * 17 *
12 * Unit tests for FeedBuilder. 18 * Unit tests for FeedBuilder.
13 */ 19 */
14class FeedBuilderTest extends \PHPUnit\Framework\TestCase 20class FeedBuilderTest extends TestCase
15{ 21{
16 /** 22 /**
17 * @var string locale Basque (Spain). 23 * @var string locale Basque (Spain).
@@ -30,74 +36,70 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
30 36
31 protected static $testDatastore = 'sandbox/datastore.php'; 37 protected static $testDatastore = 'sandbox/datastore.php';
32 38
33 public static $linkDB; 39 public static $bookmarkService;
40
41 public static $formatter;
34 42
35 public static $serverInfo; 43 public static $serverInfo;
36 44
37 /** 45 /**
38 * Called before every test method. 46 * Called before every test method.
39 */ 47 */
40 public static function setUpBeforeClass() 48 public static function setUpBeforeClass(): void
41 { 49 {
42 $refLinkDB = new ReferenceLinkDB(); 50 $conf = new ConfigManager('tests/utils/config/configJson');
51 $conf->set('resource.datastore', self::$testDatastore);
52 $refLinkDB = new \ReferenceLinkDB();
43 $refLinkDB->write(self::$testDatastore); 53 $refLinkDB->write(self::$testDatastore);
44 self::$linkDB = new LinkDB(self::$testDatastore, true, false); 54 $history = new History('sandbox/history.php');
55 $factory = new FormatterFactory($conf, true);
56 self::$formatter = $factory->getFormatter();
57 self::$bookmarkService = new BookmarkFileService($conf, $history, true);
58
45 self::$serverInfo = array( 59 self::$serverInfo = array(
46 'HTTPS' => 'Off', 60 'HTTPS' => 'Off',
47 'SERVER_NAME' => 'host.tld', 61 'SERVER_NAME' => 'host.tld',
48 'SERVER_PORT' => '80', 62 'SERVER_PORT' => '80',
49 'SCRIPT_NAME' => '/index.php', 63 'SCRIPT_NAME' => '/index.php',
50 'REQUEST_URI' => '/index.php?do=feed', 64 'REQUEST_URI' => '/feed/atom',
51 ); 65 );
52 } 66 }
53 67
54 /** 68 /**
55 * Test GetTypeLanguage().
56 */
57 public function testGetTypeLanguage()
58 {
59 $feedBuilder = new FeedBuilder(null, FeedBuilder::$FEED_ATOM, null, null, false);
60 $feedBuilder->setLocale(self::$LOCALE);
61 $this->assertEquals(self::$ATOM_LANGUAGUE, $feedBuilder->getTypeLanguage());
62 $feedBuilder = new FeedBuilder(null, FeedBuilder::$FEED_RSS, null, null, false);
63 $feedBuilder->setLocale(self::$LOCALE);
64 $this->assertEquals(self::$RSS_LANGUAGE, $feedBuilder->getTypeLanguage());
65 $feedBuilder = new FeedBuilder(null, FeedBuilder::$FEED_ATOM, null, null, false);
66 $this->assertEquals('en', $feedBuilder->getTypeLanguage());
67 $feedBuilder = new FeedBuilder(null, FeedBuilder::$FEED_RSS, null, null, false);
68 $this->assertEquals('en-en', $feedBuilder->getTypeLanguage());
69 }
70
71 /**
72 * Test buildData with RSS feed. 69 * Test buildData with RSS feed.
73 */ 70 */
74 public function testRSSBuildData() 71 public function testRSSBuildData()
75 { 72 {
76 $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_RSS, self::$serverInfo, null, false); 73 $feedBuilder = new FeedBuilder(
74 self::$bookmarkService,
75 self::$formatter,
76 static::$serverInfo,
77 false
78 );
77 $feedBuilder->setLocale(self::$LOCALE); 79 $feedBuilder->setLocale(self::$LOCALE);
78 $data = $feedBuilder->buildData(); 80 $data = $feedBuilder->buildData(FeedBuilder::$FEED_RSS, null);
79 // Test headers (RSS) 81 // Test headers (RSS)
80 $this->assertEquals(self::$RSS_LANGUAGE, $data['language']); 82 $this->assertEquals(self::$RSS_LANGUAGE, $data['language']);
81 $this->assertRegExp('/Wed, 03 Aug 2016 09:30:33 \+\d{4}/', $data['last_update']); 83 $this->assertRegExp('/Wed, 03 Aug 2016 09:30:33 \+\d{4}/', $data['last_update']);
82 $this->assertEquals(true, $data['show_dates']); 84 $this->assertEquals(true, $data['show_dates']);
83 $this->assertEquals('http://host.tld/index.php?do=feed', $data['self_link']); 85 $this->assertEquals('http://host.tld/feed/atom', $data['self_link']);
84 $this->assertEquals('http://host.tld/', $data['index_url']); 86 $this->assertEquals('http://host.tld/', $data['index_url']);
85 $this->assertFalse($data['usepermalinks']); 87 $this->assertFalse($data['usepermalinks']);
86 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 88 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
87 89
88 // Test first not pinned link (note link) 90 // Test first not pinned link (note link)
89 $link = $data['links'][array_keys($data['links'])[2]]; 91 $link = $data['links'][array_keys($data['links'])[0]];
90 $this->assertEquals(41, $link['id']); 92 $this->assertEquals(41, $link['id']);
91 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 93 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
92 $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); 94 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']);
93 $this->assertEquals('http://host.tld/?WDWyig', $link['url']); 95 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['url']);
94 $this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']); 96 $this->assertRegExp('/Tue, 10 Mar 2015 11:46:51 \+\d{4}/', $link['pub_iso_date']);
95 $pub = DateTime::createFromFormat(DateTime::RSS, $link['pub_iso_date']); 97 $pub = DateTime::createFromFormat(DateTime::RSS, $link['pub_iso_date']);
96 $up = DateTime::createFromFormat(DateTime::ATOM, $link['up_iso_date']); 98 $up = DateTime::createFromFormat(DateTime::ATOM, $link['up_iso_date']);
97 $this->assertEquals($pub, $up); 99 $this->assertEquals($pub, $up);
98 $this->assertContains('Stallman has a beard', $link['description']); 100 $this->assertContainsPolyfill('Stallman has a beard', $link['description']);
99 $this->assertContains('Permalink', $link['description']); 101 $this->assertContainsPolyfill('Permalink', $link['description']);
100 $this->assertContains('http://host.tld/?WDWyig', $link['description']); 102 $this->assertContainsPolyfill('http://host.tld/shaare/WDWyig', $link['description']);
101 $this->assertEquals(1, count($link['taglist'])); 103 $this->assertEquals(1, count($link['taglist']));
102 $this->assertEquals('sTuff', $link['taglist'][0]); 104 $this->assertEquals('sTuff', $link['taglist'][0]);
103 105
@@ -117,12 +119,17 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
117 */ 119 */
118 public function testAtomBuildData() 120 public function testAtomBuildData()
119 { 121 {
120 $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, null, false); 122 $feedBuilder = new FeedBuilder(
123 self::$bookmarkService,
124 self::$formatter,
125 static::$serverInfo,
126 false
127 );
121 $feedBuilder->setLocale(self::$LOCALE); 128 $feedBuilder->setLocale(self::$LOCALE);
122 $data = $feedBuilder->buildData(); 129 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
123 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 130 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
124 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']); 131 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['last_update']);
125 $link = $data['links'][array_keys($data['links'])[2]]; 132 $link = $data['links'][array_keys($data['links'])[0]];
126 $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']); 133 $this->assertRegExp('/2015-03-10T11:46:51\+\d{2}:\d{2}/', $link['pub_iso_date']);
127 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']); 134 $this->assertRegExp('/2016-08-03T09:30:33\+\d{2}:\d{2}/', $data['links'][8]['up_iso_date']);
128 } 135 }
@@ -136,13 +143,18 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
136 'searchtags' => 'stuff', 143 'searchtags' => 'stuff',
137 'searchterm' => 'beard', 144 'searchterm' => 'beard',
138 ); 145 );
139 $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, $criteria, false); 146 $feedBuilder = new FeedBuilder(
147 self::$bookmarkService,
148 self::$formatter,
149 static::$serverInfo,
150 false
151 );
140 $feedBuilder->setLocale(self::$LOCALE); 152 $feedBuilder->setLocale(self::$LOCALE);
141 $data = $feedBuilder->buildData(); 153 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, $criteria);
142 $this->assertEquals(1, count($data['links'])); 154 $this->assertEquals(1, count($data['links']));
143 $link = array_shift($data['links']); 155 $link = array_shift($data['links']);
144 $this->assertEquals(41, $link['id']); 156 $this->assertEquals(41, $link['id']);
145 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 157 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
146 } 158 }
147 159
148 /** 160 /**
@@ -153,13 +165,18 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
153 $criteria = array( 165 $criteria = array(
154 'nb' => '3', 166 'nb' => '3',
155 ); 167 );
156 $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, $criteria, false); 168 $feedBuilder = new FeedBuilder(
169 self::$bookmarkService,
170 self::$formatter,
171 static::$serverInfo,
172 false
173 );
157 $feedBuilder->setLocale(self::$LOCALE); 174 $feedBuilder->setLocale(self::$LOCALE);
158 $data = $feedBuilder->buildData(); 175 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, $criteria);
159 $this->assertEquals(3, count($data['links'])); 176 $this->assertEquals(3, count($data['links']));
160 $link = $data['links'][array_keys($data['links'])[2]]; 177 $link = $data['links'][array_keys($data['links'])[0]];
161 $this->assertEquals(41, $link['id']); 178 $this->assertEquals(41, $link['id']);
162 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 179 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
163 } 180 }
164 181
165 /** 182 /**
@@ -167,28 +184,33 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
167 */ 184 */
168 public function testBuildDataPermalinks() 185 public function testBuildDataPermalinks()
169 { 186 {
170 $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, null, false); 187 $feedBuilder = new FeedBuilder(
188 self::$bookmarkService,
189 self::$formatter,
190 static::$serverInfo,
191 false
192 );
171 $feedBuilder->setLocale(self::$LOCALE); 193 $feedBuilder->setLocale(self::$LOCALE);
172 $feedBuilder->setUsePermalinks(true); 194 $feedBuilder->setUsePermalinks(true);
173 $data = $feedBuilder->buildData(); 195 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
174 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 196 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
175 $this->assertTrue($data['usepermalinks']); 197 $this->assertTrue($data['usepermalinks']);
176 // First link is a permalink 198 // First link is a permalink
177 $link = $data['links'][array_keys($data['links'])[2]]; 199 $link = $data['links'][array_keys($data['links'])[0]];
178 $this->assertEquals(41, $link['id']); 200 $this->assertEquals(41, $link['id']);
179 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), $link['created']); 201 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'), $link['created']);
180 $this->assertEquals('http://host.tld/?WDWyig', $link['guid']); 202 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['guid']);
181 $this->assertEquals('http://host.tld/?WDWyig', $link['url']); 203 $this->assertEquals('http://host.tld/shaare/WDWyig', $link['url']);
182 $this->assertContains('Direct link', $link['description']); 204 $this->assertContainsPolyfill('Direct link', $link['description']);
183 $this->assertContains('http://host.tld/?WDWyig', $link['description']); 205 $this->assertContainsPolyfill('http://host.tld/shaare/WDWyig', $link['description']);
184 // Second link is a direct link 206 // Second link is a direct link
185 $link = $data['links'][array_keys($data['links'])[3]]; 207 $link = $data['links'][array_keys($data['links'])[1]];
186 $this->assertEquals(8, $link['id']); 208 $this->assertEquals(8, $link['id']);
187 $this->assertEquals(DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114633'), $link['created']); 209 $this->assertEquals(DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114633'), $link['created']);
188 $this->assertEquals('http://host.tld/?RttfEw', $link['guid']); 210 $this->assertEquals('http://host.tld/shaare/RttfEw', $link['guid']);
189 $this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['url']); 211 $this->assertEquals('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['url']);
190 $this->assertContains('Direct link', $link['description']); 212 $this->assertContainsPolyfill('Direct link', $link['description']);
191 $this->assertContains('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['description']); 213 $this->assertContainsPolyfill('https://static.fsf.org/nosvn/faif-2.0.pdf', $link['description']);
192 } 214 }
193 215
194 /** 216 /**
@@ -196,18 +218,28 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
196 */ 218 */
197 public function testBuildDataHideDates() 219 public function testBuildDataHideDates()
198 { 220 {
199 $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, null, false); 221 $feedBuilder = new FeedBuilder(
222 self::$bookmarkService,
223 self::$formatter,
224 static::$serverInfo,
225 false
226 );
200 $feedBuilder->setLocale(self::$LOCALE); 227 $feedBuilder->setLocale(self::$LOCALE);
201 $feedBuilder->setHideDates(true); 228 $feedBuilder->setHideDates(true);
202 $data = $feedBuilder->buildData(); 229 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
203 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 230 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
204 $this->assertFalse($data['show_dates']); 231 $this->assertFalse($data['show_dates']);
205 232
206 // Show dates while logged in 233 // Show dates while logged in
207 $feedBuilder = new FeedBuilder(self::$linkDB, FeedBuilder::$FEED_ATOM, self::$serverInfo, null, true); 234 $feedBuilder = new FeedBuilder(
235 self::$bookmarkService,
236 self::$formatter,
237 static::$serverInfo,
238 true
239 );
208 $feedBuilder->setLocale(self::$LOCALE); 240 $feedBuilder->setLocale(self::$LOCALE);
209 $feedBuilder->setHideDates(true); 241 $feedBuilder->setHideDates(true);
210 $data = $feedBuilder->buildData(); 242 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
211 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links'])); 243 $this->assertEquals(ReferenceLinkDB::$NB_LINKS_TOTAL, count($data['links']));
212 $this->assertTrue($data['show_dates']); 244 $this->assertTrue($data['show_dates']);
213 } 245 }
@@ -222,27 +254,26 @@ class FeedBuilderTest extends \PHPUnit\Framework\TestCase
222 'SERVER_NAME' => 'host.tld', 254 'SERVER_NAME' => 'host.tld',
223 'SERVER_PORT' => '8080', 255 'SERVER_PORT' => '8080',
224 'SCRIPT_NAME' => '/~user/shaarli/index.php', 256 'SCRIPT_NAME' => '/~user/shaarli/index.php',
225 'REQUEST_URI' => '/~user/shaarli/index.php?do=feed', 257 'REQUEST_URI' => '/~user/shaarli/feed/atom',
226 ); 258 );
227 $feedBuilder = new FeedBuilder( 259 $feedBuilder = new FeedBuilder(
228 self::$linkDB, 260 self::$bookmarkService,
229 FeedBuilder::$FEED_ATOM, 261 self::$formatter,
230 $serverInfo, 262 $serverInfo,
231 null,
232 false 263 false
233 ); 264 );
234 $feedBuilder->setLocale(self::$LOCALE); 265 $feedBuilder->setLocale(self::$LOCALE);
235 $data = $feedBuilder->buildData(); 266 $data = $feedBuilder->buildData(FeedBuilder::$FEED_ATOM, null);
236 267
237 $this->assertEquals( 268 $this->assertEquals(
238 'http://host.tld:8080/~user/shaarli/index.php?do=feed', 269 'http://host.tld:8080/~user/shaarli/feed/atom',
239 $data['self_link'] 270 $data['self_link']
240 ); 271 );
241 272
242 // Test first link (note link) 273 // Test first link (note link)
243 $link = $data['links'][array_keys($data['links'])[2]]; 274 $link = $data['links'][array_keys($data['links'])[0]];
244 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['guid']); 275 $this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['guid']);
245 $this->assertEquals('http://host.tld:8080/~user/shaarli/?WDWyig', $link['url']); 276 $this->assertEquals('http://host.tld:8080/~user/shaarli/shaare/WDWyig', $link['url']);
246 $this->assertContains('http://host.tld:8080/~user/shaarli/?addtag=hashtag', $link['description']); 277 $this->assertContainsPolyfill('http://host.tld:8080/~user/shaarli/./add-tag/hashtag', $link['description']);
247 } 278 }
248} 279}
diff --git a/tests/formatter/BookmarkDefaultFormatterTest.php b/tests/formatter/BookmarkDefaultFormatterTest.php
new file mode 100644
index 00000000..9534436e
--- /dev/null
+++ b/tests/formatter/BookmarkDefaultFormatterTest.php
@@ -0,0 +1,177 @@
1<?php
2
3namespace Shaarli\Formatter;
4
5use DateTime;
6use Shaarli\Bookmark\Bookmark;
7use Shaarli\Config\ConfigManager;
8use Shaarli\TestCase;
9
10/**
11 * Class BookmarkDefaultFormatterTest
12 * @package Shaarli\Formatter
13 */
14class BookmarkDefaultFormatterTest 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 protected 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 BookmarkDefaultFormatter($this->conf, true);
33 }
34
35 /**
36 * Test formatting a bookmark with all its attribute filled.
37 */
38 public function testFormatFull()
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($desc = '<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 '&lt;h2&gt;Content&lt;/h2&gt;&lt;p&gt;`Here is some content&lt;/p&gt;',
64 $link['description']
65 );
66 $tags[3] = '&lt;script&gt;alert(&quot;xss&quot;);&lt;/script&gt;';
67 $this->assertEquals($tags, $link['taglist']);
68 $this->assertEquals(implode(' ', $tags), $link['tags']);
69 $this->assertEquals(
70 'http://domain2.tdl2/?type=img&amp;name=file.png',
71 $link['thumbnail']
72 );
73 $this->assertEquals($created, $link['created']);
74 $this->assertEquals($created->getTimestamp(), $link['timestamp']);
75 $this->assertEquals($updated, $link['updated']);
76 $this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']);
77 $this->assertTrue($link['private']);
78 $this->assertTrue($link['sticky']);
79 $this->assertEquals('private', $link['class']);
80 }
81
82 /**
83 * Test formatting a bookmark with all its attribute filled.
84 */
85 public function testFormatMinimal()
86 {
87 $bookmark = new Bookmark();
88
89 $link = $this->formatter->format($bookmark);
90 $this->assertEmpty($link['id']);
91 $this->assertEmpty($link['shorturl']);
92 $this->assertEmpty($link['url']);
93 $this->assertEmpty($link['real_url']);
94 $this->assertEmpty($link['title']);
95 $this->assertEmpty($link['description']);
96 $this->assertEmpty($link['taglist']);
97 $this->assertEmpty($link['tags']);
98 $this->assertEmpty($link['thumbnail']);
99 $this->assertEmpty($link['created']);
100 $this->assertEmpty($link['timestamp']);
101 $this->assertEmpty($link['updated']);
102 $this->assertEmpty($link['updated_timestamp']);
103 $this->assertFalse($link['private']);
104 $this->assertFalse($link['sticky']);
105 $this->assertEmpty($link['class']);
106 }
107
108 /**
109 * Make sure that the description is properly formatted by the default formatter.
110 */
111 public function testFormatDescription()
112 {
113 $description = [];
114 $description[] = 'This a <strong>description</strong>' . PHP_EOL;
115 $description[] = 'text https://sub.domain.tld?query=here&for=real#hash more text'. PHP_EOL;
116 $description[] = 'Also, there is an #hashtag added'. PHP_EOL;
117 $description[] = ' A N D KEEP SPACES ! '. PHP_EOL;
118
119 $bookmark = new Bookmark();
120 $bookmark->setDescription(implode('', $description));
121 $link = $this->formatter->format($bookmark);
122
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';
125 $description[1] = 'text <a href="'. $url .'">'. $url .'</a> more text<br />';
126 $description[2] = 'Also, there is an <a href="./add-tag/hashtag" '.
127 'title="Hashtag hashtag">#hashtag</a> added<br />';
128 $description[3] = '&nbsp; &nbsp; A &nbsp;N &nbsp;D KEEP &nbsp; &nbsp; '.
129 'SPACES &nbsp; &nbsp;! &nbsp; <br />';
130
131 $this->assertEquals(implode(PHP_EOL, $description) . PHP_EOL, $link['description']);
132 }
133
134 /**
135 * Test formatting URL with an index_url set
136 * It should prepend relative links.
137 */
138 public function testFormatNoteWithIndexUrl()
139 {
140 $bookmark = new Bookmark();
141 $bookmark->setUrl($short = '?abcdef');
142 $description = 'Text #hashtag more text';
143 $bookmark->setDescription($description);
144
145 $this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/');
146
147 $link = $this->formatter->format($bookmark);
148 $this->assertEquals($root . $short, $link['url']);
149 $this->assertEquals($root . $short, $link['real_url']);
150 $this->assertEquals(
151 'Text <a href="'. $root .'./add-tag/hashtag" title="Hashtag hashtag">'.
152 '#hashtag</a> more text',
153 $link['description']
154 );
155 }
156
157 /**
158 * Make sure that private tags are properly filtered out when the user is logged out.
159 */
160 public function testFormatTagListRemovePrivate(): void
161 {
162 $this->formatter = new BookmarkDefaultFormatter($this->conf, false);
163
164 $bookmark = new Bookmark();
165 $bookmark->setId($id = 11);
166 $bookmark->setTags($tags = ['bookmark', '.private', 'othertag']);
167
168 $link = $this->formatter->format($bookmark);
169
170 unset($tags[1]);
171 $tags = array_values($tags);
172
173 $this->assertSame(11, $link['id']);
174 $this->assertSame($tags, $link['taglist']);
175 $this->assertSame(implode(' ', $tags), $link['tags']);
176 }
177}
diff --git a/tests/formatter/BookmarkMarkdownFormatterTest.php b/tests/formatter/BookmarkMarkdownFormatterTest.php
new file mode 100644
index 00000000..ab6b4080
--- /dev/null
+++ b/tests/formatter/BookmarkMarkdownFormatterTest.php
@@ -0,0 +1,160 @@
1<?php
2
3namespace Shaarli\Formatter;
4
5use DateTime;
6use Shaarli\Bookmark\Bookmark;
7use Shaarli\Config\ConfigManager;
8use Shaarli\TestCase;
9
10/**
11 * Class BookmarkMarkdownFormatterTest
12 * @package Shaarli\Formatter
13 */
14class BookmarkMarkdownFormatterTest 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 protected 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 BookmarkMarkdownFormatter($this->conf, true);
33 }
34
35 /**
36 * Test formatting a bookmark with all its attribute filled.
37 */
38 public function testFormatFull()
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 testFormatMinimal()
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 testFormatDescription()
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
120 $bookmark = new Bookmark();
121 $bookmark->setDescription($description);
122 $link = $this->formatter->format($bookmark);
123
124 $description = '<div class="markdown"><p>';
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';
127 $description .= 'text <a href="'. $url .'">'. $url .'</a> more text<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 ! ';
130 $description .= '</p></div>';
131
132 $this->assertEquals($description, $link['description']);
133 }
134
135 /**
136 * Test formatting URL with an index_url set
137 * It should prepend relative links.
138 */
139 public function testFormatNoteWithIndexUrl()
140 {
141 $bookmark = new Bookmark();
142 $bookmark->setUrl($short = '?abcdef');
143 $description = 'Text #hashtag more text';
144 $bookmark->setDescription($description);
145
146 $this->formatter->addContextData('index_url', $root = 'https://domain.tld/hithere/');
147
148 $description = '<div class="markdown"><p>';
149 $description .= 'Text <a href="'. $root .'./add-tag/hashtag">#hashtag</a> more text';
150 $description .= '</p></div>';
151
152 $link = $this->formatter->format($bookmark);
153 $this->assertEquals($root . $short, $link['url']);
154 $this->assertEquals($root . $short, $link['real_url']);
155 $this->assertEquals(
156 $description,
157 $link['description']
158 );
159 }
160}
diff --git a/tests/formatter/BookmarkRawFormatterTest.php b/tests/formatter/BookmarkRawFormatterTest.php
new file mode 100644
index 00000000..c76bb7b9
--- /dev/null
+++ b/tests/formatter/BookmarkRawFormatterTest.php
@@ -0,0 +1,97 @@
1<?php
2
3namespace Shaarli\Formatter;
4
5use DateTime;
6use Shaarli\Bookmark\Bookmark;
7use Shaarli\Config\ConfigManager;
8use Shaarli\TestCase;
9
10/**
11 * Class BookmarkRawFormatterTest
12 * @package Shaarli\Formatter
13 */
14class BookmarkRawFormatterTest 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 protected 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 BookmarkRawFormatter($this->conf, true);
33 }
34
35 /**
36 * Test formatting a bookmark with all its attribute filled.
37 */
38 public function testFormatFull()
39 {
40 $bookmark = new Bookmark();
41 $bookmark->setId($id = 11);
42 $bookmark->setShortUrl($short = 'abcdef');
43 $bookmark->setUrl($url = 'https://sub.domain.tld?query=here&for=real#hash');
44 $bookmark->setTitle($title = 'This is a <strong>bookmark</strong>');
45 $bookmark->setDescription($desc = '<h2>Content</h2><p>`Here is some content</p>');
46 $bookmark->setTags($tags = ['tag1', 'bookmark', 'other', '<script>alert("xss");</script>']);
47 $bookmark->setThumbnail($thumb = 'http://domain2.tdl2/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($url, $link['url']);
57 $this->assertEquals($url, $link['real_url']);
58 $this->assertEquals($title, $link['title']);
59 $this->assertEquals($desc, $link['description']);
60 $this->assertEquals($tags, $link['taglist']);
61 $this->assertEquals(implode(' ', $tags), $link['tags']);
62 $this->assertEquals($thumb, $link['thumbnail']);
63 $this->assertEquals($created, $link['created']);
64 $this->assertEquals($created->getTimestamp(), $link['timestamp']);
65 $this->assertEquals($updated, $link['updated']);
66 $this->assertEquals($updated->getTimestamp(), $link['updated_timestamp']);
67 $this->assertTrue($link['private']);
68 $this->assertTrue($link['sticky']);
69 $this->assertEquals('private', $link['class']);
70 }
71
72 /**
73 * Test formatting a bookmark with all its attribute filled.
74 */
75 public function testFormatMinimal()
76 {
77 $bookmark = new Bookmark();
78
79 $link = $this->formatter->format($bookmark);
80 $this->assertEmpty($link['id']);
81 $this->assertEmpty($link['shorturl']);
82 $this->assertEmpty($link['url']);
83 $this->assertEmpty($link['real_url']);
84 $this->assertEmpty($link['title']);
85 $this->assertEmpty($link['description']);
86 $this->assertEmpty($link['taglist']);
87 $this->assertEmpty($link['tags']);
88 $this->assertEmpty($link['thumbnail']);
89 $this->assertEmpty($link['created']);
90 $this->assertEmpty($link['timestamp']);
91 $this->assertEmpty($link['updated']);
92 $this->assertEmpty($link['updated_timestamp']);
93 $this->assertFalse($link['private']);
94 $this->assertFalse($link['sticky']);
95 $this->assertEmpty($link['class']);
96 }
97}
diff --git a/tests/formatter/FormatterFactoryTest.php b/tests/formatter/FormatterFactoryTest.php
new file mode 100644
index 00000000..ae476cb5
--- /dev/null
+++ b/tests/formatter/FormatterFactoryTest.php
@@ -0,0 +1,101 @@
1<?php
2
3namespace Shaarli\Formatter;
4
5use Shaarli\Config\ConfigManager;
6use Shaarli\TestCase;
7
8/**
9 * Class FormatterFactoryTest
10 *
11 * @package Shaarli\Formatter
12 */
13class FormatterFactoryTest extends TestCase
14{
15 /** @var string Path of test config file */
16 protected static $testConf = 'sandbox/config';
17
18 /** @var FormatterFactory instance */
19 protected $factory;
20
21 /** @var ConfigManager instance */
22 protected $conf;
23
24 /**
25 * Initialize FormatterFactory instance
26 */
27 protected function setUp(): void
28 {
29 copy('tests/utils/config/configJson.json.php', self::$testConf .'.json.php');
30 $this->conf = new ConfigManager(self::$testConf);
31 $this->factory = new FormatterFactory($this->conf, true);
32 }
33
34 /**
35 * Test creating an instance of BookmarkFormatter without any setting -> default formatter
36 */
37 public function testCreateInstanceDefault()
38 {
39 $this->assertInstanceOf(BookmarkDefaultFormatter::class, $this->factory->getFormatter());
40 }
41
42 /**
43 * Test creating an instance of BookmarkDefaultFormatter from settings
44 */
45 public function testCreateInstanceDefaultSetting()
46 {
47 $this->conf->set('formatter', 'default');
48 $this->assertInstanceOf(BookmarkDefaultFormatter::class, $this->factory->getFormatter());
49 }
50
51 /**
52 * Test creating an instance of BookmarkDefaultFormatter from parameter
53 */
54 public function testCreateInstanceDefaultParameter()
55 {
56 $this->assertInstanceOf(
57 BookmarkDefaultFormatter::class,
58 $this->factory->getFormatter('default')
59 );
60 }
61
62 /**
63 * Test creating an instance of BookmarkRawFormatter from settings
64 */
65 public function testCreateInstanceRawSetting()
66 {
67 $this->conf->set('formatter', 'raw');
68 $this->assertInstanceOf(BookmarkRawFormatter::class, $this->factory->getFormatter());
69 }
70
71 /**
72 * Test creating an instance of BookmarkRawFormatter from parameter
73 */
74 public function testCreateInstanceRawParameter()
75 {
76 $this->assertInstanceOf(
77 BookmarkRawFormatter::class,
78 $this->factory->getFormatter('raw')
79 );
80 }
81
82 /**
83 * Test creating an instance of BookmarkMarkdownFormatter from settings
84 */
85 public function testCreateInstanceMarkdownSetting()
86 {
87 $this->conf->set('formatter', 'markdown');
88 $this->assertInstanceOf(BookmarkMarkdownFormatter::class, $this->factory->getFormatter());
89 }
90
91 /**
92 * Test creating an instance of BookmarkMarkdownFormatter from parameter
93 */
94 public function testCreateInstanceMarkdownParameter()
95 {
96 $this->assertInstanceOf(
97 BookmarkMarkdownFormatter::class,
98 $this->factory->getFormatter('markdown')
99 );
100 }
101}
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
new file mode 100644
index 00000000..655c5bba
--- /dev/null
+++ b/tests/front/ShaarliMiddlewareTest.php
@@ -0,0 +1,221 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front;
6
7use Shaarli\Config\ConfigManager;
8use Shaarli\Container\ShaarliContainer;
9use Shaarli\Front\Exception\LoginBannedException;
10use Shaarli\Front\Exception\UnauthorizedException;
11use Shaarli\Render\PageBuilder;
12use Shaarli\Render\PageCacheManager;
13use Shaarli\Security\LoginManager;
14use Shaarli\TestCase;
15use Shaarli\Updater\Updater;
16use Slim\Http\Request;
17use Slim\Http\Response;
18use Slim\Http\Uri;
19
20class ShaarliMiddlewareTest extends TestCase
21{
22 protected const TMP_MOCK_FILE = '.tmp';
23
24 /** @var ShaarliContainer */
25 protected $container;
26
27 /** @var ShaarliMiddleware */
28 protected $middleware;
29
30 public function setUp(): void
31 {
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
43 $this->middleware = new ShaarliMiddleware($this->container);
44 }
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 */
54 public function testMiddlewareExecution(): void
55 {
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
64 $response = new Response();
65 $controller = function (Request $request, Response $response): Response {
66 return $response->withStatus(418); // I'm a tea pot
67 };
68
69 /** @var Response $result */
70 $result = $this->middleware->__invoke($request, $response, $controller);
71
72 static::assertInstanceOf(Response::class, $result);
73 static::assertSame(418, $result->getStatusCode());
74 }
75
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
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 (): void {
92 $exception = new LoginBannedException();
93
94 throw new $exception;
95 };
96
97 $pageBuilder = $this->createMock(PageBuilder::class);
98 $pageBuilder->method('render')->willReturnCallback(function (string $message): string {
99 return $message;
100 });
101 $this->container->pageBuilder = $pageBuilder;
102
103 $this->expectException(LoginBannedException::class);
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 ;
214
215 /** @var Response $result */
216 $result = $this->middleware->__invoke($request, $response, $controller);
217
218 static::assertInstanceOf(Response::class, $result);
219 static::assertSame(418, $result->getStatusCode());
220 }
221}
diff --git a/tests/front/controller/admin/ConfigureControllerTest.php b/tests/front/controller/admin/ConfigureControllerTest.php
new file mode 100644
index 00000000..aca6cff3
--- /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'], $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/ManageShaareControllerTest/AddShaareTest.php b/tests/front/controller/admin/ManageShaareControllerTest/AddShaareTest.php
new file mode 100644
index 00000000..0f27ec2f
--- /dev/null
+++ b/tests/front/controller/admin/ManageShaareControllerTest/AddShaareTest.php
@@ -0,0 +1,47 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ManageShaareControllerTest;
6
7use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
8use Shaarli\Front\Controller\Admin\ManageShaareController;
9use Shaarli\Http\HttpAccess;
10use Shaarli\TestCase;
11use Slim\Http\Request;
12use Slim\Http\Response;
13
14class AddShaareTest extends TestCase
15{
16 use FrontAdminControllerMockHelper;
17
18 /** @var ManageShaareController */
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 ManageShaareController($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 $result = $this->controller->addShaare($request, $response);
41
42 static::assertSame(200, $result->getStatusCode());
43 static::assertSame('addlink', (string) $result->getBody());
44
45 static::assertSame('Shaare a new link - Shaarli', $assignedVariables['pagetitle']);
46 }
47}
diff --git a/tests/front/controller/admin/ManageShaareControllerTest/ChangeVisibilityBookmarkTest.php b/tests/front/controller/admin/ManageShaareControllerTest/ChangeVisibilityBookmarkTest.php
new file mode 100644
index 00000000..096d0774
--- /dev/null
+++ b/tests/front/controller/admin/ManageShaareControllerTest/ChangeVisibilityBookmarkTest.php
@@ -0,0 +1,418 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ManageShaareControllerTest;
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\ManageShaareController;
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 ManageShaareController */
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 ManageShaareController($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/ManageShaareControllerTest/DeleteBookmarkTest.php b/tests/front/controller/admin/ManageShaareControllerTest/DeleteBookmarkTest.php
new file mode 100644
index 00000000..ba774e21
--- /dev/null
+++ b/tests/front/controller/admin/ManageShaareControllerTest/DeleteBookmarkTest.php
@@ -0,0 +1,376 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ManageShaareControllerTest;
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\ManageShaareController;
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 ManageShaareController */
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 ManageShaareController($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->formatterFactory = $this->createMock(FormatterFactory::class);
360 $this->container->formatterFactory
361 ->expects(static::once())
362 ->method('getFormatter')
363 ->willReturnCallback(function (): BookmarkFormatter {
364 $formatter = $this->createMock(BookmarkFormatter::class);
365 $formatter->method('format')->willReturn(['formatted']);
366
367 return $formatter;
368 })
369 ;
370
371 $result = $this->controller->deleteBookmark($request, $response);
372
373 static::assertSame(200, $result->getStatusCode());
374 static::assertSame('<script>self.close();</script>', (string) $result->getBody('location'));
375 }
376}
diff --git a/tests/front/controller/admin/ManageShaareControllerTest/DisplayCreateFormTest.php b/tests/front/controller/admin/ManageShaareControllerTest/DisplayCreateFormTest.php
new file mode 100644
index 00000000..2eb95251
--- /dev/null
+++ b/tests/front/controller/admin/ManageShaareControllerTest/DisplayCreateFormTest.php
@@ -0,0 +1,317 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ManageShaareControllerTest;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Config\ConfigManager;
9use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
10use Shaarli\Front\Controller\Admin\ManageShaareController;
11use Shaarli\Http\HttpAccess;
12use Shaarli\TestCase;
13use Slim\Http\Request;
14use Slim\Http\Response;
15
16class DisplayCreateFormTest extends TestCase
17{
18 use FrontAdminControllerMockHelper;
19
20 /** @var ManageShaareController */
21 protected $controller;
22
23 public function setUp(): void
24 {
25 $this->createContainer();
26
27 $this->container->httpAccess = $this->createMock(HttpAccess::class);
28 $this->controller = new ManageShaareController($this->container);
29 }
30
31 /**
32 * Test displaying bookmark create form
33 * Ensure that every step of the standard workflow works properly.
34 */
35 public function testDisplayCreateFormWithUrl(): void
36 {
37 $this->container->environment = [
38 'HTTP_REFERER' => $referer = 'http://shaarli/subfolder/controller/?searchtag=abc'
39 ];
40
41 $assignedVariables = [];
42 $this->assignTemplateVars($assignedVariables);
43
44 $url = 'http://url.tld/other?part=3&utm_ad=pay#hash';
45 $expectedUrl = str_replace('&utm_ad=pay', '', $url);
46 $remoteTitle = 'Remote Title';
47 $remoteDesc = 'Sometimes the meta description is relevant.';
48 $remoteTags = 'abc def';
49
50 $request = $this->createMock(Request::class);
51 $request->method('getParam')->willReturnCallback(function (string $key) use ($url): ?string {
52 return $key === 'post' ? $url : null;
53 });
54 $response = new Response();
55
56 $this->container->httpAccess
57 ->expects(static::once())
58 ->method('getCurlDownloadCallback')
59 ->willReturnCallback(
60 function (&$charset, &$title, &$description, &$tags) use (
61 $remoteTitle,
62 $remoteDesc,
63 $remoteTags
64 ): callable {
65 return function () use (
66 &$charset,
67 &$title,
68 &$description,
69 &$tags,
70 $remoteTitle,
71 $remoteDesc,
72 $remoteTags
73 ): void {
74 $charset = 'ISO-8859-1';
75 $title = $remoteTitle;
76 $description = $remoteDesc;
77 $tags = $remoteTags;
78 };
79 }
80 )
81 ;
82 $this->container->httpAccess
83 ->expects(static::once())
84 ->method('getHttpResponse')
85 ->with($expectedUrl, 30, 4194304)
86 ->willReturnCallback(function($url, $timeout, $maxBytes, $callback): void {
87 $callback();
88 })
89 ;
90
91 $this->container->bookmarkService
92 ->expects(static::once())
93 ->method('bookmarksCountPerTag')
94 ->willReturn($tags = ['tag1' => 2, 'tag2' => 1])
95 ;
96
97 // Make sure that PluginManager hook is triggered
98 $this->container->pluginManager
99 ->expects(static::atLeastOnce())
100 ->method('executeHooks')
101 ->withConsecutive(['render_editlink'], ['render_includes'])
102 ->willReturnCallback(function (string $hook, array $data) use ($remoteTitle, $remoteDesc): array {
103 if ('render_editlink' === $hook) {
104 static::assertSame($remoteTitle, $data['link']['title']);
105 static::assertSame($remoteDesc, $data['link']['description']);
106 }
107
108 return $data;
109 })
110 ;
111
112 $result = $this->controller->displayCreateForm($request, $response);
113
114 static::assertSame(200, $result->getStatusCode());
115 static::assertSame('editlink', (string) $result->getBody());
116
117 static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']);
118
119 static::assertSame($expectedUrl, $assignedVariables['link']['url']);
120 static::assertSame($remoteTitle, $assignedVariables['link']['title']);
121 static::assertSame($remoteDesc, $assignedVariables['link']['description']);
122 static::assertSame($remoteTags, $assignedVariables['link']['tags']);
123 static::assertFalse($assignedVariables['link']['private']);
124
125 static::assertTrue($assignedVariables['link_is_new']);
126 static::assertSame($referer, $assignedVariables['http_referer']);
127 static::assertSame($tags, $assignedVariables['tags']);
128 static::assertArrayHasKey('source', $assignedVariables);
129 static::assertArrayHasKey('default_private_links', $assignedVariables);
130 }
131
132 /**
133 * Test displaying bookmark create form
134 * Ensure all available query parameters are handled properly.
135 */
136 public function testDisplayCreateFormWithFullParameters(): void
137 {
138 $assignedVariables = [];
139 $this->assignTemplateVars($assignedVariables);
140
141 $parameters = [
142 'post' => 'http://url.tld/other?part=3&utm_ad=pay#hash',
143 'title' => 'Provided Title',
144 'description' => 'Provided description.',
145 'tags' => 'abc def',
146 'private' => '1',
147 'source' => 'apps',
148 ];
149 $expectedUrl = str_replace('&utm_ad=pay', '', $parameters['post']);
150
151 $request = $this->createMock(Request::class);
152 $request
153 ->method('getParam')
154 ->willReturnCallback(function (string $key) use ($parameters): ?string {
155 return $parameters[$key] ?? null;
156 });
157 $response = new Response();
158
159 $result = $this->controller->displayCreateForm($request, $response);
160
161 static::assertSame(200, $result->getStatusCode());
162 static::assertSame('editlink', (string) $result->getBody());
163
164 static::assertSame('Shaare - Shaarli', $assignedVariables['pagetitle']);
165
166 static::assertSame($expectedUrl, $assignedVariables['link']['url']);
167 static::assertSame($parameters['title'], $assignedVariables['link']['title']);
168 static::assertSame($parameters['description'], $assignedVariables['link']['description']);
169 static::assertSame($parameters['tags'], $assignedVariables['link']['tags']);
170 static::assertTrue($assignedVariables['link']['private']);
171 static::assertTrue($assignedVariables['link_is_new']);
172 static::assertSame($parameters['source'], $assignedVariables['source']);
173 }
174
175 /**
176 * Test displaying bookmark create form
177 * Without any parameter.
178 */
179 public function testDisplayCreateFormEmpty(): void
180 {
181 $assignedVariables = [];
182 $this->assignTemplateVars($assignedVariables);
183
184 $request = $this->createMock(Request::class);
185 $response = new Response();
186
187 $this->container->httpAccess->expects(static::never())->method('getHttpResponse');
188 $this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback');
189
190 $result = $this->controller->displayCreateForm($request, $response);
191
192 static::assertSame(200, $result->getStatusCode());
193 static::assertSame('editlink', (string) $result->getBody());
194 static::assertSame('', $assignedVariables['link']['url']);
195 static::assertSame('Note: ', $assignedVariables['link']['title']);
196 static::assertSame('', $assignedVariables['link']['description']);
197 static::assertSame('', $assignedVariables['link']['tags']);
198 static::assertFalse($assignedVariables['link']['private']);
199 static::assertTrue($assignedVariables['link_is_new']);
200 }
201
202 /**
203 * Test displaying bookmark create form
204 * URL not using HTTP protocol: do not try to retrieve the title
205 */
206 public function testDisplayCreateFormNotHttp(): void
207 {
208 $assignedVariables = [];
209 $this->assignTemplateVars($assignedVariables);
210
211 $url = 'magnet://kubuntu.torrent';
212 $request = $this->createMock(Request::class);
213 $request
214 ->method('getParam')
215 ->willReturnCallback(function (string $key) use ($url): ?string {
216 return $key === 'post' ? $url : null;
217 });
218 $response = new Response();
219
220 $this->container->httpAccess->expects(static::never())->method('getHttpResponse');
221 $this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback');
222
223 $result = $this->controller->displayCreateForm($request, $response);
224
225 static::assertSame(200, $result->getStatusCode());
226 static::assertSame('editlink', (string) $result->getBody());
227 static::assertSame($url, $assignedVariables['link']['url']);
228 static::assertTrue($assignedVariables['link_is_new']);
229 }
230
231 /**
232 * Test displaying bookmark create form
233 * When markdown formatter is enabled, the no markdown tag should be added to existing tags.
234 */
235 public function testDisplayCreateFormWithMarkdownEnabled(): void
236 {
237 $assignedVariables = [];
238 $this->assignTemplateVars($assignedVariables);
239
240 $this->container->conf = $this->createMock(ConfigManager::class);
241 $this->container->conf
242 ->expects(static::atLeastOnce())
243 ->method('get')->willReturnCallback(function (string $key): ?string {
244 if ($key === 'formatter') {
245 return 'markdown';
246 }
247
248 return $key;
249 })
250 ;
251
252 $request = $this->createMock(Request::class);
253 $response = new Response();
254
255 $result = $this->controller->displayCreateForm($request, $response);
256
257 static::assertSame(200, $result->getStatusCode());
258 static::assertSame('editlink', (string) $result->getBody());
259 static::assertSame(['nomarkdown' => 1], $assignedVariables['tags']);
260 }
261
262 /**
263 * Test displaying bookmark create form
264 * When an existing URL is submitted, we want to edit the existing link.
265 */
266 public function testDisplayCreateFormWithExistingUrl(): void
267 {
268 $assignedVariables = [];
269 $this->assignTemplateVars($assignedVariables);
270
271 $url = 'http://url.tld/other?part=3&utm_ad=pay#hash';
272 $expectedUrl = str_replace('&utm_ad=pay', '', $url);
273
274 $request = $this->createMock(Request::class);
275 $request
276 ->method('getParam')
277 ->willReturnCallback(function (string $key) use ($url): ?string {
278 return $key === 'post' ? $url : null;
279 });
280 $response = new Response();
281
282 $this->container->httpAccess->expects(static::never())->method('getHttpResponse');
283 $this->container->httpAccess->expects(static::never())->method('getCurlDownloadCallback');
284
285 $this->container->bookmarkService
286 ->expects(static::once())
287 ->method('findByUrl')
288 ->with($expectedUrl)
289 ->willReturn(
290 (new Bookmark())
291 ->setId($id = 23)
292 ->setUrl($expectedUrl)
293 ->setTitle($title = 'Bookmark Title')
294 ->setDescription($description = 'Bookmark description.')
295 ->setTags($tags = ['abc', 'def'])
296 ->setPrivate(true)
297 ->setCreated($createdAt = new \DateTime('2020-06-10 18:45:44'))
298 )
299 ;
300
301 $result = $this->controller->displayCreateForm($request, $response);
302
303 static::assertSame(200, $result->getStatusCode());
304 static::assertSame('editlink', (string) $result->getBody());
305
306 static::assertSame('Edit Shaare - Shaarli', $assignedVariables['pagetitle']);
307 static::assertFalse($assignedVariables['link_is_new']);
308
309 static::assertSame($id, $assignedVariables['link']['id']);
310 static::assertSame($expectedUrl, $assignedVariables['link']['url']);
311 static::assertSame($title, $assignedVariables['link']['title']);
312 static::assertSame($description, $assignedVariables['link']['description']);
313 static::assertSame(implode(' ', $tags), $assignedVariables['link']['tags']);
314 static::assertTrue($assignedVariables['link']['private']);
315 static::assertSame($createdAt, $assignedVariables['link']['created']);
316 }
317}
diff --git a/tests/front/controller/admin/ManageShaareControllerTest/DisplayEditFormTest.php b/tests/front/controller/admin/ManageShaareControllerTest/DisplayEditFormTest.php
new file mode 100644
index 00000000..2dc3f41c
--- /dev/null
+++ b/tests/front/controller/admin/ManageShaareControllerTest/DisplayEditFormTest.php
@@ -0,0 +1,155 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ManageShaareControllerTest;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
9use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
10use Shaarli\Front\Controller\Admin\ManageShaareController;
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 ManageShaareController */
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 ManageShaareController($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/ManageShaareControllerTest/PinBookmarkTest.php b/tests/front/controller/admin/ManageShaareControllerTest/PinBookmarkTest.php
new file mode 100644
index 00000000..50ce7df1
--- /dev/null
+++ b/tests/front/controller/admin/ManageShaareControllerTest/PinBookmarkTest.php
@@ -0,0 +1,145 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ManageShaareControllerTest;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\Exception\BookmarkNotFoundException;
9use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
10use Shaarli\Front\Controller\Admin\ManageShaareController;
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 ManageShaareController */
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 ManageShaareController($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/ManageShaareControllerTest/SaveBookmarkTest.php b/tests/front/controller/admin/ManageShaareControllerTest/SaveBookmarkTest.php
new file mode 100644
index 00000000..f7a68226
--- /dev/null
+++ b/tests/front/controller/admin/ManageShaareControllerTest/SaveBookmarkTest.php
@@ -0,0 +1,308 @@
1<?php
2
3declare(strict_types=1);
4
5namespace Shaarli\Front\Controller\Admin\ManageShaareControllerTest;
6
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Config\ConfigManager;
9use Shaarli\Front\Controller\Admin\FrontAdminControllerMockHelper;
10use Shaarli\Front\Controller\Admin\ManageShaareController;
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 ManageShaareController */
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 ManageShaareController($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): void {
70 static::assertFalse($save);
71
72 $checkBookmark($bookmark);
73
74 $bookmark->setId($id);
75 })
76 ;
77 $this->container->bookmarkService
78 ->expects(static::once())
79 ->method('set')
80 ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): void {
81 static::assertTrue($save);
82
83 $checkBookmark($bookmark);
84
85 static::assertSame($id, $bookmark->getId());
86 })
87 ;
88
89 // Make sure that PluginManager hook is triggered
90 $this->container->pluginManager
91 ->expects(static::atLeastOnce())
92 ->method('executeHooks')
93 ->withConsecutive(['save_link'])
94 ->willReturnCallback(function (string $hook, array $data) use ($parameters, $id): array {
95 if ('save_link' === $hook) {
96 static::assertSame($id, $data['id']);
97 static::assertSame($parameters['lf_url'], $data['url']);
98 static::assertSame($parameters['lf_title'], $data['title']);
99 static::assertSame($parameters['lf_description'], $data['description']);
100 static::assertSame($parameters['lf_tags'], $data['tags']);
101 static::assertTrue($data['private']);
102 }
103
104 return $data;
105 })
106 ;
107
108 $result = $this->controller->save($request, $response);
109
110 static::assertSame(302, $result->getStatusCode());
111 static::assertRegExp('@/subfolder/#[\w\-]{6}@', $result->getHeader('location')[0]);
112 }
113
114
115 /**
116 * Test save an existing bookmark
117 */
118 public function testSaveExistingBookmark(): void
119 {
120 $id = 21;
121 $parameters = [
122 'lf_id' => (string) $id,
123 'lf_url' => 'http://url.tld/other?part=3#hash',
124 'lf_title' => 'Provided Title',
125 'lf_description' => 'Provided description.',
126 'lf_tags' => 'abc def',
127 'lf_private' => '1',
128 'returnurl' => 'http://shaarli/subfolder/?page=2'
129 ];
130
131 $request = $this->createMock(Request::class);
132 $request
133 ->method('getParam')
134 ->willReturnCallback(function (string $key) use ($parameters): ?string {
135 return $parameters[$key] ?? null;
136 })
137 ;
138 $response = new Response();
139
140 $checkBookmark = function (Bookmark $bookmark) use ($parameters, $id) {
141 static::assertSame($id, $bookmark->getId());
142 static::assertSame($parameters['lf_url'], $bookmark->getUrl());
143 static::assertSame($parameters['lf_title'], $bookmark->getTitle());
144 static::assertSame($parameters['lf_description'], $bookmark->getDescription());
145 static::assertSame($parameters['lf_tags'], $bookmark->getTagsString());
146 static::assertTrue($bookmark->isPrivate());
147 };
148
149 $this->container->bookmarkService->expects(static::atLeastOnce())->method('exists')->willReturn(true);
150 $this->container->bookmarkService
151 ->expects(static::once())
152 ->method('get')
153 ->willReturn((new Bookmark())->setId($id)->setUrl('http://other.url'))
154 ;
155 $this->container->bookmarkService
156 ->expects(static::once())
157 ->method('addOrSet')
158 ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): void {
159 static::assertFalse($save);
160
161 $checkBookmark($bookmark);
162 })
163 ;
164 $this->container->bookmarkService
165 ->expects(static::once())
166 ->method('set')
167 ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($checkBookmark, $id): void {
168 static::assertTrue($save);
169
170 $checkBookmark($bookmark);
171
172 static::assertSame($id, $bookmark->getId());
173 })
174 ;
175
176 // Make sure that PluginManager hook is triggered
177 $this->container->pluginManager
178 ->expects(static::atLeastOnce())
179 ->method('executeHooks')
180 ->withConsecutive(['save_link'])
181 ->willReturnCallback(function (string $hook, array $data) use ($parameters, $id): array {
182 if ('save_link' === $hook) {
183 static::assertSame($id, $data['id']);
184 static::assertSame($parameters['lf_url'], $data['url']);
185 static::assertSame($parameters['lf_title'], $data['title']);
186 static::assertSame($parameters['lf_description'], $data['description']);
187 static::assertSame($parameters['lf_tags'], $data['tags']);
188 static::assertTrue($data['private']);
189 }
190
191 return $data;
192 })
193 ;
194
195 $result = $this->controller->save($request, $response);
196
197 static::assertSame(302, $result->getStatusCode());
198 static::assertRegExp('@/subfolder/\?page=2#[\w\-]{6}@', $result->getHeader('location')[0]);
199 }
200
201 /**
202 * Test save a bookmark - try to retrieve the thumbnail
203 */
204 public function testSaveBookmarkWithThumbnail(): void
205 {
206 $parameters = ['lf_url' => 'http://url.tld/other?part=3#hash'];
207
208 $request = $this->createMock(Request::class);
209 $request
210 ->method('getParam')
211 ->willReturnCallback(function (string $key) use ($parameters): ?string {
212 return $parameters[$key] ?? null;
213 })
214 ;
215 $response = new Response();
216
217 $this->container->conf = $this->createMock(ConfigManager::class);
218 $this->container->conf->method('get')->willReturnCallback(function (string $key, $default) {
219 return $key === 'thumbnails.mode' ? Thumbnailer::MODE_ALL : $default;
220 });
221
222 $this->container->thumbnailer = $this->createMock(Thumbnailer::class);
223 $this->container->thumbnailer
224 ->expects(static::once())
225 ->method('get')
226 ->with($parameters['lf_url'])
227 ->willReturn($thumb = 'http://thumb.url')
228 ;
229
230 $this->container->bookmarkService
231 ->expects(static::once())
232 ->method('addOrSet')
233 ->willReturnCallback(function (Bookmark $bookmark, bool $save) use ($thumb): void {
234 static::assertSame($thumb, $bookmark->getThumbnail());
235 })
236 ;
237
238 $result = $this->controller->save($request, $response);
239
240 static::assertSame(302, $result->getStatusCode());
241 }
242
243 /**
244 * Test save a bookmark - with ID #0
245 */
246 public function testSaveBookmarkWithIdZero(): void
247 {
248 $parameters = ['lf_id' => '0'];
249
250 $request = $this->createMock(Request::class);
251 $request
252 ->method('getParam')
253 ->willReturnCallback(function (string $key) use ($parameters): ?string {
254 return $parameters[$key] ?? null;
255 })
256 ;
257 $response = new Response();
258
259 $this->container->bookmarkService->expects(static::once())->method('exists')->with(0)->willReturn(true);
260 $this->container->bookmarkService->expects(static::once())->method('get')->with(0)->willReturn(new Bookmark());
261
262 $result = $this->controller->save($request, $response);
263
264 static::assertSame(302, $result->getStatusCode());
265 }
266
267 /**
268 * Change the password with a wrong existing password
269 */
270 public function testSaveBookmarkFromBookmarklet(): void
271 {
272 $parameters = ['source' => 'bookmarklet'];
273
274 $request = $this->createMock(Request::class);
275 $request
276 ->method('getParam')
277 ->willReturnCallback(function (string $key) use ($parameters): ?string {
278 return $parameters[$key] ?? null;
279 })
280 ;
281 $response = new Response();
282
283 $result = $this->controller->save($request, $response);
284
285 static::assertSame(200, $result->getStatusCode());
286 static::assertSame('<script>self.close();</script>', (string) $result->getBody());
287 }
288
289 /**
290 * Change the password with a wrong existing password
291 */
292 public function testSaveBookmarkWrongToken(): void
293 {
294 $this->container->sessionManager = $this->createMock(SessionManager::class);
295 $this->container->sessionManager->method('checkToken')->willReturn(false);
296
297 $this->container->bookmarkService->expects(static::never())->method('addOrSet');
298 $this->container->bookmarkService->expects(static::never())->method('set');
299
300 $request = $this->createMock(Request::class);
301 $response = new Response();
302
303 $this->expectException(WrongTokenException::class);
304
305 $this->controller->save($request, $response);
306 }
307
308}
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/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/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..f4a8acff
--- /dev/null
+++ b/tests/front/controller/admin/ThumbnailsControllerTest.php
@@ -0,0 +1,154 @@
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) {
93 static::assertSame($thumb, $bookmark->getThumbnail());
94 })
95 ;
96
97 $result = $this->controller->ajaxUpdate($request, $response, ['id' => (string) $id]);
98
99 static::assertSame(200, $result->getStatusCode());
100
101 $payload = json_decode((string) $result->getBody(), true);
102
103 static::assertSame($id, $payload['id']);
104 static::assertSame($url, $payload['url']);
105 static::assertSame($thumb, $payload['thumbnail']);
106 }
107
108 /**
109 * Test updating a bookmark thumbnail - Invalid ID
110 */
111 public function testAjaxUpdateInvalidId(): void
112 {
113 $request = $this->createMock(Request::class);
114 $response = new Response();
115
116 $result = $this->controller->ajaxUpdate($request, $response, ['id' => 'nope']);
117
118 static::assertSame(400, $result->getStatusCode());
119 }
120
121 /**
122 * Test updating a bookmark thumbnail - No ID
123 */
124 public function testAjaxUpdateNoId(): void
125 {
126 $request = $this->createMock(Request::class);
127 $response = new Response();
128
129 $result = $this->controller->ajaxUpdate($request, $response, []);
130
131 static::assertSame(400, $result->getStatusCode());
132 }
133
134 /**
135 * Test updating a bookmark thumbnail with valid parameters
136 */
137 public function testAjaxUpdateBookmarkNotFound(): void
138 {
139 $id = 123;
140 $request = $this->createMock(Request::class);
141 $response = new Response();
142
143 $this->container->bookmarkService
144 ->expects(static::once())
145 ->method('get')
146 ->with($id)
147 ->willThrowException(new BookmarkNotFoundException())
148 ;
149
150 $result = $this->controller->ajaxUpdate($request, $response, ['id' => (string) $id]);
151
152 static::assertSame(404, $result->getStatusCode());
153 }
154}
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..0c95df97
--- /dev/null
+++ b/tests/front/controller/visitor/BookmarkListControllerTest.php
@@ -0,0 +1,448 @@
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 getting link list with thumbnail updates.
296 * -> 2 thumbnails update, only 1 datastore write
297 */
298 public function testThumbnailUpdateFromLinkList(): void
299 {
300 $request = $this->createMock(Request::class);
301 $response = new Response();
302
303 $this->container->loginManager = $this->createMock(LoginManager::class);
304 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
305
306 $this->container->conf = $this->createMock(ConfigManager::class);
307 $this->container->conf
308 ->method('get')
309 ->willReturnCallback(function (string $key, $default) {
310 return $key === 'thumbnails.mode' ? Thumbnailer::MODE_ALL : $default;
311 })
312 ;
313
314 $this->container->thumbnailer = $this->createMock(Thumbnailer::class);
315 $this->container->thumbnailer
316 ->expects(static::exactly(2))
317 ->method('get')
318 ->withConsecutive(['https://url2.tld'], ['https://url4.tld'])
319 ;
320
321 $this->container->bookmarkService
322 ->expects(static::once())
323 ->method('search')
324 ->willReturn([
325 (new Bookmark())->setId(1)->setUrl('https://url1.tld')->setTitle('Title 1')->setThumbnail(false),
326 $b1 = (new Bookmark())->setId(2)->setUrl('https://url2.tld')->setTitle('Title 2'),
327 (new Bookmark())->setId(3)->setUrl('https://url3.tld')->setTitle('Title 3')->setThumbnail(false),
328 $b2 = (new Bookmark())->setId(2)->setUrl('https://url4.tld')->setTitle('Title 4'),
329 (new Bookmark())->setId(2)->setUrl('ftp://url5.tld', ['ftp'])->setTitle('Title 5'),
330 ])
331 ;
332 $this->container->bookmarkService
333 ->expects(static::exactly(2))
334 ->method('set')
335 ->withConsecutive([$b1, false], [$b2, false])
336 ;
337 $this->container->bookmarkService->expects(static::once())->method('save');
338
339 $result = $this->controller->index($request, $response);
340
341 static::assertSame(200, $result->getStatusCode());
342 static::assertSame('linklist', (string) $result->getBody());
343 }
344
345 /**
346 * Test getting a permalink with thumbnail update.
347 */
348 public function testThumbnailUpdateFromPermalink(): void
349 {
350 $request = $this->createMock(Request::class);
351 $response = new Response();
352
353 $this->container->loginManager = $this->createMock(LoginManager::class);
354 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
355
356 $this->container->conf = $this->createMock(ConfigManager::class);
357 $this->container->conf
358 ->method('get')
359 ->willReturnCallback(function (string $key, $default) {
360 return $key === 'thumbnails.mode' ? Thumbnailer::MODE_ALL : $default;
361 })
362 ;
363
364 $this->container->thumbnailer = $this->createMock(Thumbnailer::class);
365 $this->container->thumbnailer->expects(static::once())->method('get')->withConsecutive(['https://url.tld']);
366
367 $this->container->bookmarkService
368 ->expects(static::once())
369 ->method('findByHash')
370 ->willReturn($bookmark = (new Bookmark())->setId(2)->setUrl('https://url.tld')->setTitle('Title 1'))
371 ;
372 $this->container->bookmarkService->expects(static::once())->method('set')->with($bookmark, true);
373 $this->container->bookmarkService->expects(static::never())->method('save');
374
375 $result = $this->controller->permalink($request, $response, ['hash' => 'abc']);
376
377 static::assertSame(200, $result->getStatusCode());
378 static::assertSame('linklist', (string) $result->getBody());
379 }
380
381 /**
382 * Trigger legacy controller in link list controller: permalink
383 */
384 public function testLegacyControllerPermalink(): void
385 {
386 $hash = 'abcdef';
387 $this->container->environment['QUERY_STRING'] = $hash;
388
389 $request = $this->createMock(Request::class);
390 $response = new Response();
391
392 $result = $this->controller->index($request, $response);
393
394 static::assertSame(302, $result->getStatusCode());
395 static::assertSame('/subfolder/shaare/' . $hash, $result->getHeader('location')[0]);
396 }
397
398 /**
399 * Trigger legacy controller in link list controller: ?do= query parameter
400 */
401 public function testLegacyControllerDoPage(): void
402 {
403 $request = $this->createMock(Request::class);
404 $request->method('getQueryParam')->with('do')->willReturn('picwall');
405 $response = new Response();
406
407 $result = $this->controller->index($request, $response);
408
409 static::assertSame(302, $result->getStatusCode());
410 static::assertSame('/subfolder/picture-wall', $result->getHeader('location')[0]);
411 }
412
413 /**
414 * Trigger legacy controller in link list controller: ?do= query parameter with unknown legacy route
415 */
416 public function testLegacyControllerUnknownDoPage(): void
417 {
418 $request = $this->createMock(Request::class);
419 $request->method('getQueryParam')->with('do')->willReturn('nope');
420 $response = new Response();
421
422 $result = $this->controller->index($request, $response);
423
424 static::assertSame(200, $result->getStatusCode());
425 static::assertSame('linklist', (string) $result->getBody());
426 }
427
428 /**
429 * Trigger legacy controller in link list controller: other GET route (e.g. ?post)
430 */
431 public function testLegacyControllerGetParameter(): void
432 {
433 $request = $this->createMock(Request::class);
434 $request->method('getQueryParams')->willReturn(['post' => $url = 'http://url.tld']);
435 $response = new Response();
436
437 $this->container->loginManager = $this->createMock(LoginManager::class);
438 $this->container->loginManager->method('isLoggedIn')->willReturn(true);
439
440 $result = $this->controller->index($request, $response);
441
442 static::assertSame(302, $result->getStatusCode());
443 static::assertSame(
444 '/subfolder/admin/shaare?post=' . urlencode($url),
445 $result->getHeader('location')[0]
446 );
447 }
448}
diff --git a/tests/front/controller/visitor/DailyControllerTest.php b/tests/front/controller/visitor/DailyControllerTest.php
new file mode 100644
index 00000000..fc78bc13
--- /dev/null
+++ b/tests/front/controller/visitor/DailyControllerTest.php
@@ -0,0 +1,478 @@
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
32 $request = $this->createMock(Request::class);
33 $request->method('getQueryParam')->willReturn($currentDay->format('Ymd'));
34 $response = new Response();
35
36 // Save RainTPL assigned variables
37 $assignedVariables = [];
38 $this->assignTemplateVars($assignedVariables);
39
40 // Links dataset: 2 links with thumbnails
41 $this->container->bookmarkService
42 ->expects(static::once())
43 ->method('days')
44 ->willReturnCallback(function () use ($currentDay): array {
45 return [
46 '20200510',
47 $currentDay->format('Ymd'),
48 '20200516',
49 ];
50 })
51 ;
52 $this->container->bookmarkService
53 ->expects(static::once())
54 ->method('filterDay')
55 ->willReturnCallback(function (): array {
56 return [
57 (new Bookmark())
58 ->setId(1)
59 ->setUrl('http://url.tld')
60 ->setTitle(static::generateString(50))
61 ->setDescription(static::generateString(500))
62 ,
63 (new Bookmark())
64 ->setId(2)
65 ->setUrl('http://url2.tld')
66 ->setTitle(static::generateString(50))
67 ->setDescription(static::generateString(500))
68 ,
69 (new Bookmark())
70 ->setId(3)
71 ->setUrl('http://url3.tld')
72 ->setTitle(static::generateString(50))
73 ->setDescription(static::generateString(500))
74 ,
75 ];
76 })
77 ;
78
79 // Make sure that PluginManager hook is triggered
80 $this->container->pluginManager
81 ->expects(static::atLeastOnce())
82 ->method('executeHooks')
83 ->withConsecutive(['render_daily'])
84 ->willReturnCallback(function (string $hook, array $data, array $param) use ($currentDay): array {
85 if ('render_daily' === $hook) {
86 static::assertArrayHasKey('linksToDisplay', $data);
87 static::assertCount(3, $data['linksToDisplay']);
88 static::assertSame(1, $data['linksToDisplay'][0]['id']);
89 static::assertSame($currentDay->getTimestamp(), $data['day']);
90 static::assertSame('20200510', $data['previousday']);
91 static::assertSame('20200516', $data['nextday']);
92
93 static::assertArrayHasKey('loggedin', $param);
94 }
95
96 return $data;
97 })
98 ;
99
100 $result = $this->controller->index($request, $response);
101
102 static::assertSame(200, $result->getStatusCode());
103 static::assertSame('daily', (string) $result->getBody());
104 static::assertSame(
105 'Daily - '. format_date($currentDay, false, true) .' - Shaarli',
106 $assignedVariables['pagetitle']
107 );
108 static::assertEquals($currentDay, $assignedVariables['dayDate']);
109 static::assertEquals($currentDay->getTimestamp(), $assignedVariables['day']);
110 static::assertCount(3, $assignedVariables['linksToDisplay']);
111
112 $link = $assignedVariables['linksToDisplay'][0];
113
114 static::assertSame(1, $link['id']);
115 static::assertSame('http://url.tld', $link['url']);
116 static::assertNotEmpty($link['title']);
117 static::assertNotEmpty($link['description']);
118 static::assertNotEmpty($link['formatedDescription']);
119
120 $link = $assignedVariables['linksToDisplay'][1];
121
122 static::assertSame(2, $link['id']);
123 static::assertSame('http://url2.tld', $link['url']);
124 static::assertNotEmpty($link['title']);
125 static::assertNotEmpty($link['description']);
126 static::assertNotEmpty($link['formatedDescription']);
127
128 $link = $assignedVariables['linksToDisplay'][2];
129
130 static::assertSame(3, $link['id']);
131 static::assertSame('http://url3.tld', $link['url']);
132 static::assertNotEmpty($link['title']);
133 static::assertNotEmpty($link['description']);
134 static::assertNotEmpty($link['formatedDescription']);
135
136 static::assertCount(3, $assignedVariables['cols']);
137 static::assertCount(1, $assignedVariables['cols'][0]);
138 static::assertCount(1, $assignedVariables['cols'][1]);
139 static::assertCount(1, $assignedVariables['cols'][2]);
140
141 $link = $assignedVariables['cols'][0][0];
142
143 static::assertSame(1, $link['id']);
144 static::assertSame('http://url.tld', $link['url']);
145 static::assertNotEmpty($link['title']);
146 static::assertNotEmpty($link['description']);
147 static::assertNotEmpty($link['formatedDescription']);
148
149 $link = $assignedVariables['cols'][1][0];
150
151 static::assertSame(2, $link['id']);
152 static::assertSame('http://url2.tld', $link['url']);
153 static::assertNotEmpty($link['title']);
154 static::assertNotEmpty($link['description']);
155 static::assertNotEmpty($link['formatedDescription']);
156
157 $link = $assignedVariables['cols'][2][0];
158
159 static::assertSame(3, $link['id']);
160 static::assertSame('http://url3.tld', $link['url']);
161 static::assertNotEmpty($link['title']);
162 static::assertNotEmpty($link['description']);
163 static::assertNotEmpty($link['formatedDescription']);
164 }
165
166 /**
167 * Daily page - test that everything goes fine with no future or past bookmarks
168 */
169 public function testValidIndexControllerInvokeNoFutureOrPast(): void
170 {
171 $currentDay = new \DateTimeImmutable('2020-05-13');
172
173 $request = $this->createMock(Request::class);
174 $response = new Response();
175
176 // Save RainTPL assigned variables
177 $assignedVariables = [];
178 $this->assignTemplateVars($assignedVariables);
179
180 // Links dataset: 2 links with thumbnails
181 $this->container->bookmarkService
182 ->expects(static::once())
183 ->method('days')
184 ->willReturnCallback(function () use ($currentDay): array {
185 return [
186 $currentDay->format($currentDay->format('Ymd')),
187 ];
188 })
189 ;
190 $this->container->bookmarkService
191 ->expects(static::once())
192 ->method('filterDay')
193 ->willReturnCallback(function (): array {
194 return [
195 (new Bookmark())
196 ->setId(1)
197 ->setUrl('http://url.tld')
198 ->setTitle(static::generateString(50))
199 ->setDescription(static::generateString(500))
200 ,
201 ];
202 })
203 ;
204
205 // Make sure that PluginManager hook is triggered
206 $this->container->pluginManager
207 ->expects(static::atLeastOnce())
208 ->method('executeHooks')
209 ->withConsecutive(['render_daily'])
210 ->willReturnCallback(function (string $hook, array $data, array $param) use ($currentDay): array {
211 if ('render_daily' === $hook) {
212 static::assertArrayHasKey('linksToDisplay', $data);
213 static::assertCount(1, $data['linksToDisplay']);
214 static::assertSame(1, $data['linksToDisplay'][0]['id']);
215 static::assertSame($currentDay->getTimestamp(), $data['day']);
216 static::assertEmpty($data['previousday']);
217 static::assertEmpty($data['nextday']);
218
219 static::assertArrayHasKey('loggedin', $param);
220 }
221
222 return $data;
223 });
224
225 $result = $this->controller->index($request, $response);
226
227 static::assertSame(200, $result->getStatusCode());
228 static::assertSame('daily', (string) $result->getBody());
229 static::assertSame(
230 'Daily - '. format_date($currentDay, false, true) .' - Shaarli',
231 $assignedVariables['pagetitle']
232 );
233 static::assertCount(1, $assignedVariables['linksToDisplay']);
234
235 $link = $assignedVariables['linksToDisplay'][0];
236 static::assertSame(1, $link['id']);
237 }
238
239 /**
240 * Daily page - test that height adjustment in columns is working
241 */
242 public function testValidIndexControllerInvokeHeightAdjustment(): void
243 {
244 $currentDay = new \DateTimeImmutable('2020-05-13');
245
246 $request = $this->createMock(Request::class);
247 $response = new Response();
248
249 // Save RainTPL assigned variables
250 $assignedVariables = [];
251 $this->assignTemplateVars($assignedVariables);
252
253 // Links dataset: 2 links with thumbnails
254 $this->container->bookmarkService
255 ->expects(static::once())
256 ->method('days')
257 ->willReturnCallback(function () use ($currentDay): array {
258 return [
259 $currentDay->format($currentDay->format('Ymd')),
260 ];
261 })
262 ;
263 $this->container->bookmarkService
264 ->expects(static::once())
265 ->method('filterDay')
266 ->willReturnCallback(function (): array {
267 return [
268 (new Bookmark())->setId(1)->setUrl('http://url.tld')->setTitle('title'),
269 (new Bookmark())
270 ->setId(2)
271 ->setUrl('http://url.tld')
272 ->setTitle(static::generateString(50))
273 ->setDescription(static::generateString(5000))
274 ,
275 (new Bookmark())->setId(3)->setUrl('http://url.tld')->setTitle('title'),
276 (new Bookmark())->setId(4)->setUrl('http://url.tld')->setTitle('title'),
277 (new Bookmark())->setId(5)->setUrl('http://url.tld')->setTitle('title'),
278 (new Bookmark())->setId(6)->setUrl('http://url.tld')->setTitle('title'),
279 (new Bookmark())->setId(7)->setUrl('http://url.tld')->setTitle('title'),
280 ];
281 })
282 ;
283
284 // Make sure that PluginManager hook is triggered
285 $this->container->pluginManager
286 ->expects(static::atLeastOnce())
287 ->method('executeHooks')
288 ->willReturnCallback(function (string $hook, array $data, array $param): array {
289 return $data;
290 })
291 ;
292
293 $result = $this->controller->index($request, $response);
294
295 static::assertSame(200, $result->getStatusCode());
296 static::assertSame('daily', (string) $result->getBody());
297 static::assertCount(7, $assignedVariables['linksToDisplay']);
298
299 $columnIds = function (array $column): array {
300 return array_map(function (array $item): int { return $item['id']; }, $column);
301 };
302
303 static::assertSame([1, 4, 6], $columnIds($assignedVariables['cols'][0]));
304 static::assertSame([2], $columnIds($assignedVariables['cols'][1]));
305 static::assertSame([3, 5, 7], $columnIds($assignedVariables['cols'][2]));
306 }
307
308 /**
309 * Daily page - no bookmark
310 */
311 public function testValidIndexControllerInvokeNoBookmark(): void
312 {
313 $request = $this->createMock(Request::class);
314 $response = new Response();
315
316 // Save RainTPL assigned variables
317 $assignedVariables = [];
318 $this->assignTemplateVars($assignedVariables);
319
320 // Links dataset: 2 links with thumbnails
321 $this->container->bookmarkService
322 ->expects(static::once())
323 ->method('days')
324 ->willReturnCallback(function (): array {
325 return [];
326 })
327 ;
328 $this->container->bookmarkService
329 ->expects(static::once())
330 ->method('filterDay')
331 ->willReturnCallback(function (): array {
332 return [];
333 })
334 ;
335
336 // Make sure that PluginManager hook is triggered
337 $this->container->pluginManager
338 ->expects(static::atLeastOnce())
339 ->method('executeHooks')
340 ->willReturnCallback(function (string $hook, array $data, array $param): array {
341 return $data;
342 })
343 ;
344
345 $result = $this->controller->index($request, $response);
346
347 static::assertSame(200, $result->getStatusCode());
348 static::assertSame('daily', (string) $result->getBody());
349 static::assertCount(0, $assignedVariables['linksToDisplay']);
350 static::assertSame('Today', $assignedVariables['dayDesc']);
351 static::assertEquals((new \DateTime())->setTime(0, 0)->getTimestamp(), $assignedVariables['day']);
352 static::assertEquals((new \DateTime())->setTime(0, 0), $assignedVariables['dayDate']);
353 }
354
355 /**
356 * Daily RSS - default behaviour
357 */
358 public function testValidRssControllerInvokeDefault(): void
359 {
360 $dates = [
361 new \DateTimeImmutable('2020-05-17'),
362 new \DateTimeImmutable('2020-05-15'),
363 new \DateTimeImmutable('2020-05-13'),
364 ];
365
366 $request = $this->createMock(Request::class);
367 $response = new Response();
368
369 $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([
370 (new Bookmark())->setId(1)->setCreated($dates[0])->setUrl('http://domain.tld/1'),
371 (new Bookmark())->setId(2)->setCreated($dates[1])->setUrl('http://domain.tld/2'),
372 (new Bookmark())->setId(3)->setCreated($dates[1])->setUrl('http://domain.tld/3'),
373 (new Bookmark())->setId(4)->setCreated($dates[2])->setUrl('http://domain.tld/4'),
374 ]);
375
376 $this->container->pageCacheManager
377 ->expects(static::once())
378 ->method('getCachePage')
379 ->willReturnCallback(function (): CachedPage {
380 $cachedPage = $this->createMock(CachedPage::class);
381 $cachedPage->expects(static::once())->method('cache')->with('dailyrss');
382
383 return $cachedPage;
384 }
385 );
386
387 // Save RainTPL assigned variables
388 $assignedVariables = [];
389 $this->assignTemplateVars($assignedVariables);
390
391 $result = $this->controller->rss($request, $response);
392
393 static::assertSame(200, $result->getStatusCode());
394 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
395 static::assertSame('dailyrss', (string) $result->getBody());
396 static::assertSame('Shaarli', $assignedVariables['title']);
397 static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
398 static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']);
399 static::assertFalse($assignedVariables['hide_timestamps']);
400 static::assertCount(2, $assignedVariables['days']);
401
402 $day = $assignedVariables['days'][$dates[0]->format('Ymd')];
403
404 static::assertEquals($dates[0], $day['date']);
405 static::assertSame($dates[0]->format(\DateTime::RSS), $day['date_rss']);
406 static::assertSame(format_date($dates[0], false), $day['date_human']);
407 static::assertSame('http://shaarli/subfolder/daily?day='. $dates[0]->format('Ymd'), $day['absolute_url']);
408 static::assertCount(1, $day['links']);
409 static::assertSame(1, $day['links'][0]['id']);
410 static::assertSame('http://domain.tld/1', $day['links'][0]['url']);
411 static::assertEquals($dates[0], $day['links'][0]['created']);
412
413 $day = $assignedVariables['days'][$dates[1]->format('Ymd')];
414
415 static::assertEquals($dates[1], $day['date']);
416 static::assertSame($dates[1]->format(\DateTime::RSS), $day['date_rss']);
417 static::assertSame(format_date($dates[1], false), $day['date_human']);
418 static::assertSame('http://shaarli/subfolder/daily?day='. $dates[1]->format('Ymd'), $day['absolute_url']);
419 static::assertCount(2, $day['links']);
420
421 static::assertSame(2, $day['links'][0]['id']);
422 static::assertSame('http://domain.tld/2', $day['links'][0]['url']);
423 static::assertEquals($dates[1], $day['links'][0]['created']);
424 static::assertSame(3, $day['links'][1]['id']);
425 static::assertSame('http://domain.tld/3', $day['links'][1]['url']);
426 static::assertEquals($dates[1], $day['links'][1]['created']);
427 }
428
429 /**
430 * Daily RSS - trigger cache rendering
431 */
432 public function testValidRssControllerInvokeTriggerCache(): void
433 {
434 $request = $this->createMock(Request::class);
435 $response = new Response();
436
437 $this->container->pageCacheManager->method('getCachePage')->willReturnCallback(function (): CachedPage {
438 $cachedPage = $this->createMock(CachedPage::class);
439 $cachedPage->method('cachedVersion')->willReturn('this is cache!');
440
441 return $cachedPage;
442 });
443
444 $this->container->bookmarkService->expects(static::never())->method('search');
445
446 $result = $this->controller->rss($request, $response);
447
448 static::assertSame(200, $result->getStatusCode());
449 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
450 static::assertSame('this is cache!', (string) $result->getBody());
451 }
452
453 /**
454 * Daily RSS - No bookmark
455 */
456 public function testValidRssControllerInvokeNoBookmark(): void
457 {
458 $request = $this->createMock(Request::class);
459 $response = new Response();
460
461 $this->container->bookmarkService->expects(static::once())->method('search')->willReturn([]);
462
463 // Save RainTPL assigned variables
464 $assignedVariables = [];
465 $this->assignTemplateVars($assignedVariables);
466
467 $result = $this->controller->rss($request, $response);
468
469 static::assertSame(200, $result->getStatusCode());
470 static::assertStringContainsString('application/rss', $result->getHeader('Content-Type')[0]);
471 static::assertSame('dailyrss', (string) $result->getBody());
472 static::assertSame('Shaarli', $assignedVariables['title']);
473 static::assertSame('http://shaarli/subfolder/', $assignedVariables['index_url']);
474 static::assertSame('http://shaarli/subfolder/daily-rss', $assignedVariables['page_url']);
475 static::assertFalse($assignedVariables['hide_timestamps']);
476 static::assertCount(0, $assignedVariables['days']);
477 }
478}
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..345ad544
--- /dev/null
+++ b/tests/front/controller/visitor/InstallControllerTest.php
@@ -0,0 +1,295 @@
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
84 /**
85 * Instantiate the install controller with an existing config file: exception.
86 */
87 public function testInstallWithExistingConfigFile(): void
88 {
89 $this->expectException(AlreadyInstalledException::class);
90
91 touch(static::MOCK_FILE);
92
93 $this->controller = new InstallController($this->container);
94 }
95
96 /**
97 * Call controller without session yet defined, redirect to test session install page.
98 */
99 public function testInstallRedirectToSessionTest(): void
100 {
101 $request = $this->createMock(Request::class);
102 $response = new Response();
103
104 $this->container->sessionManager = $this->createMock(SessionManager::class);
105 $this->container->sessionManager
106 ->expects(static::once())
107 ->method('setSessionParameter')
108 ->with(InstallController::SESSION_TEST_KEY, InstallController::SESSION_TEST_VALUE)
109 ;
110
111 $result = $this->controller->index($request, $response);
112
113 static::assertSame(302, $result->getStatusCode());
114 static::assertSame('/subfolder/install/session-test', $result->getHeader('location')[0]);
115 }
116
117 /**
118 * Call controller in session test mode: valid session then redirect to install page.
119 */
120 public function testInstallSessionTestValid(): void
121 {
122 $request = $this->createMock(Request::class);
123 $response = new Response();
124
125 $this->container->sessionManager = $this->createMock(SessionManager::class);
126 $this->container->sessionManager
127 ->method('getSessionParameter')
128 ->with(InstallController::SESSION_TEST_KEY)
129 ->willReturn(InstallController::SESSION_TEST_VALUE)
130 ;
131
132 $result = $this->controller->sessionTest($request, $response);
133
134 static::assertSame(302, $result->getStatusCode());
135 static::assertSame('/subfolder/install', $result->getHeader('location')[0]);
136 }
137
138 /**
139 * Call controller in session test mode: invalid session then redirect to error page.
140 */
141 public function testInstallSessionTestError(): void
142 {
143 $assignedVars = [];
144 $this->assignTemplateVars($assignedVars);
145
146 $request = $this->createMock(Request::class);
147 $response = new Response();
148
149 $this->container->sessionManager = $this->createMock(SessionManager::class);
150 $this->container->sessionManager
151 ->method('getSessionParameter')
152 ->with(InstallController::SESSION_TEST_KEY)
153 ->willReturn('KO')
154 ;
155
156 $result = $this->controller->sessionTest($request, $response);
157
158 static::assertSame(200, $result->getStatusCode());
159 static::assertSame('error', (string) $result->getBody());
160 static::assertStringStartsWith(
161 '<pre>Sessions do not seem to work correctly on your server',
162 $assignedVars['message']
163 );
164 }
165
166 /**
167 * Test saving valid data from install form. Also initialize datastore.
168 */
169 public function testSaveInstallValid(): void
170 {
171 $providedParameters = [
172 'continent' => 'Europe',
173 'city' => 'Berlin',
174 'setlogin' => 'bob',
175 'setpassword' => 'password',
176 'title' => 'Shaarli',
177 'language' => 'fr',
178 'updateCheck' => true,
179 'enableApi' => true,
180 ];
181
182 $expectedSettings = [
183 'general.timezone' => 'Europe/Berlin',
184 'credentials.login' => 'bob',
185 'credentials.salt' => '_NOT_EMPTY',
186 'credentials.hash' => '_NOT_EMPTY',
187 'general.title' => 'Shaarli',
188 'translation.language' => 'en',
189 'updates.check_updates' => true,
190 'api.enabled' => true,
191 'api.secret' => '_NOT_EMPTY',
192 'general.header_link' => '/subfolder',
193 ];
194
195 $request = $this->createMock(Request::class);
196 $request->method('getParam')->willReturnCallback(function (string $key) use ($providedParameters) {
197 return $providedParameters[$key] ?? null;
198 });
199 $response = new Response();
200
201 $this->container->conf = $this->createMock(ConfigManager::class);
202 $this->container->conf
203 ->method('get')
204 ->willReturnCallback(function (string $key, $value) {
205 if ($key === 'credentials.login') {
206 return 'bob';
207 } elseif ($key === 'credentials.salt') {
208 return 'salt';
209 }
210
211 return $value;
212 })
213 ;
214 $this->container->conf
215 ->expects(static::exactly(count($expectedSettings)))
216 ->method('set')
217 ->willReturnCallback(function (string $key, $value) use ($expectedSettings) {
218 if ($expectedSettings[$key] ?? null === '_NOT_EMPTY') {
219 static::assertNotEmpty($value);
220 } else {
221 static::assertSame($expectedSettings[$key], $value);
222 }
223 })
224 ;
225 $this->container->conf->expects(static::once())->method('write');
226
227 $this->container->sessionManager
228 ->expects(static::once())
229 ->method('setSessionParameter')
230 ->with(SessionManager::KEY_SUCCESS_MESSAGES)
231 ;
232
233 $result = $this->controller->save($request, $response);
234
235 static::assertSame(302, $result->getStatusCode());
236 static::assertSame('/subfolder/login', $result->getHeader('location')[0]);
237 }
238
239 /**
240 * Test default settings (timezone and title).
241 * Also check that bookmarks are not initialized if
242 */
243 public function testSaveInstallDefaultValues(): void
244 {
245 $confSettings = [];
246
247 $request = $this->createMock(Request::class);
248 $response = new Response();
249
250 $this->container->conf->method('set')->willReturnCallback(function (string $key, $value) use (&$confSettings) {
251 $confSettings[$key] = $value;
252 });
253
254 $result = $this->controller->save($request, $response);
255
256 static::assertSame(302, $result->getStatusCode());
257 static::assertSame('/subfolder/login', $result->getHeader('location')[0]);
258
259 static::assertSame('UTC', $confSettings['general.timezone']);
260 static::assertSame('Shared bookmarks on http://shaarli/subfolder/', $confSettings['general.title']);
261 }
262
263 /**
264 * Same test as testSaveInstallDefaultValues() but for an instance install in root directory.
265 */
266 public function testSaveInstallDefaultValuesWithoutSubfolder(): void
267 {
268 $confSettings = [];
269
270 $this->container->environment = [
271 'SERVER_NAME' => 'shaarli',
272 'SERVER_PORT' => '80',
273 'REQUEST_URI' => '/install',
274 'REMOTE_ADDR' => '1.2.3.4',
275 'SCRIPT_NAME' => '/index.php',
276 ];
277
278 $this->container->basePath = '';
279
280 $request = $this->createMock(Request::class);
281 $response = new Response();
282
283 $this->container->conf->method('set')->willReturnCallback(function (string $key, $value) use (&$confSettings) {
284 $confSettings[$key] = $value;
285 });
286
287 $result = $this->controller->save($request, $response);
288
289 static::assertSame(302, $result->getStatusCode());
290 static::assertSame('/login', $result->getHeader('location')[0]);
291
292 static::assertSame('UTC', $confSettings['general.timezone']);
293 static::assertSame('Shared bookmarks on http://shaarli/', $confSettings['general.title']);
294 }
295}
diff --git a/tests/front/controller/visitor/LoginControllerTest.php b/tests/front/controller/visitor/LoginControllerTest.php
new file mode 100644
index 00000000..1312ccb7
--- /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', '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/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/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/LegacyDummyUpdater.php b/tests/legacy/LegacyDummyUpdater.php
new file mode 100644
index 00000000..10e0a5b7
--- /dev/null
+++ b/tests/legacy/LegacyDummyUpdater.php
@@ -0,0 +1,74 @@
1<?php
2namespace Shaarli\Updater;
3
4use Exception;
5use ReflectionClass;
6use ReflectionMethod;
7use Shaarli\Config\ConfigManager;
8use Shaarli\Legacy\LegacyLinkDB;
9use Shaarli\Legacy\LegacyUpdater;
10
11/**
12 * Class LegacyDummyUpdater.
13 * Extends updater to add update method designed for unit tests.
14 */
15class LegacyDummyUpdater extends LegacyUpdater
16{
17 /**
18 * Object constructor.
19 *
20 * @param array $doneUpdates Updates which are already done.
21 * @param LegacyLinkDB $linkDB LinkDB instance.
22 * @param ConfigManager $conf Configuration Manager instance.
23 * @param boolean $isLoggedIn True if the user is logged in.
24 */
25 public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn)
26 {
27 parent::__construct($doneUpdates, $linkDB, $conf, $isLoggedIn);
28
29 // Retrieve all update methods.
30 // For unit test, only retrieve final methods,
31 $class = new ReflectionClass($this);
32 $this->methods = $class->getMethods(ReflectionMethod::IS_FINAL);
33 }
34
35 /**
36 * Update method 1.
37 *
38 * @return bool true.
39 */
40 final private function updateMethodDummy1()
41 {
42 return true;
43 }
44
45 /**
46 * Update method 2.
47 *
48 * @return bool true.
49 */
50 final private function updateMethodDummy2()
51 {
52 return true;
53 }
54
55 /**
56 * Update method 3.
57 *
58 * @return bool true.
59 */
60 final private function updateMethodDummy3()
61 {
62 return true;
63 }
64
65 /**
66 * Update method 4, raise an exception.
67 *
68 * @throws Exception error.
69 */
70 final private function updateMethodException()
71 {
72 throw new Exception('whatever');
73 }
74}
diff --git a/tests/bookmark/LinkDBTest.php b/tests/legacy/LegacyLinkDBTest.php
index 2990a6b5..df2cad62 100644
--- a/tests/bookmark/LinkDBTest.php
+++ b/tests/legacy/LegacyLinkDBTest.php
@@ -3,22 +3,22 @@
3 * Link datastore tests 3 * Link datastore tests
4 */ 4 */
5 5
6namespace Shaarli\Bookmark; 6namespace Shaarli\Legacy;
7 7
8use DateTime; 8use DateTime;
9use ReferenceLinkDB; 9use ReferenceLinkDB;
10use ReflectionClass; 10use ReflectionClass;
11use Shaarli; 11use Shaarli;
12use Shaarli\Bookmark\Bookmark;
12 13
13require_once 'application/feed/Cache.php';
14require_once 'application/Utils.php'; 14require_once 'application/Utils.php';
15require_once 'tests/utils/ReferenceLinkDB.php'; 15require_once 'tests/utils/ReferenceLinkDB.php';
16 16
17 17
18/** 18/**
19 * Unitary tests for LinkDB 19 * Unitary tests for LegacyLinkDBTest
20 */ 20 */
21class LinkDBTest extends \PHPUnit\Framework\TestCase 21class LegacyLinkDBTest extends \Shaarli\TestCase
22{ 22{
23 // datastore to test write operations 23 // datastore to test write operations
24 protected static $testDatastore = 'sandbox/datastore.php'; 24 protected static $testDatastore = 'sandbox/datastore.php';
@@ -29,19 +29,19 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
29 protected static $refDB = null; 29 protected static $refDB = null;
30 30
31 /** 31 /**
32 * @var LinkDB public LinkDB instance. 32 * @var LegacyLinkDB public LinkDB instance.
33 */ 33 */
34 protected static $publicLinkDB = null; 34 protected static $publicLinkDB = null;
35 35
36 /** 36 /**
37 * @var LinkDB private LinkDB instance. 37 * @var LegacyLinkDB private LinkDB instance.
38 */ 38 */
39 protected static $privateLinkDB = null; 39 protected static $privateLinkDB = null;
40 40
41 /** 41 /**
42 * Instantiates public and private LinkDBs with test data 42 * Instantiates public and private LinkDBs with test data
43 * 43 *
44 * The reference datastore contains public and private links that 44 * The reference datastore contains public and private bookmarks that
45 * will be used to test LinkDB's methods: 45 * will be used to test LinkDB's methods:
46 * - access filtering (public/private), 46 * - access filtering (public/private),
47 * - link searches: 47 * - link searches:
@@ -49,24 +49,19 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
49 * - by tag, 49 * - by tag,
50 * - by text, 50 * - by text,
51 * - etc. 51 * - etc.
52 */ 52 *
53 public static function setUpBeforeClass()
54 {
55 self::$refDB = new ReferenceLinkDB();
56 self::$refDB->write(self::$testDatastore);
57
58 self::$publicLinkDB = new LinkDB(self::$testDatastore, false, false);
59 self::$privateLinkDB = new LinkDB(self::$testDatastore, true, false);
60 }
61
62 /**
63 * Resets test data for each test 53 * Resets test data for each test
64 */ 54 */
65 protected function setUp() 55 protected function setUp(): void
66 { 56 {
67 if (file_exists(self::$testDatastore)) { 57 if (file_exists(self::$testDatastore)) {
68 unlink(self::$testDatastore); 58 unlink(self::$testDatastore);
69 } 59 }
60
61 self::$refDB = new ReferenceLinkDB(true);
62 self::$refDB->write(self::$testDatastore);
63 self::$publicLinkDB = new LegacyLinkDB(self::$testDatastore, false, false);
64 self::$privateLinkDB = new LegacyLinkDB(self::$testDatastore, true, false);
70 } 65 }
71 66
72 /** 67 /**
@@ -78,7 +73,7 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
78 */ 73 */
79 protected static function getMethod($name) 74 protected static function getMethod($name)
80 { 75 {
81 $class = new ReflectionClass('Shaarli\Bookmark\LinkDB'); 76 $class = new ReflectionClass('Shaarli\Legacy\LegacyLinkDB');
82 $method = $class->getMethod($name); 77 $method = $class->getMethod($name);
83 $method->setAccessible(true); 78 $method->setAccessible(true);
84 return $method; 79 return $method;
@@ -89,7 +84,7 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
89 */ 84 */
90 public function testConstructLoggedIn() 85 public function testConstructLoggedIn()
91 { 86 {
92 new LinkDB(self::$testDatastore, true, false); 87 new LegacyLinkDB(self::$testDatastore, true, false);
93 $this->assertFileExists(self::$testDatastore); 88 $this->assertFileExists(self::$testDatastore);
94 } 89 }
95 90
@@ -98,19 +93,19 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
98 */ 93 */
99 public function testConstructLoggedOut() 94 public function testConstructLoggedOut()
100 { 95 {
101 new LinkDB(self::$testDatastore, false, false); 96 new LegacyLinkDB(self::$testDatastore, false, false);
102 $this->assertFileExists(self::$testDatastore); 97 $this->assertFileExists(self::$testDatastore);
103 } 98 }
104 99
105 /** 100 /**
106 * Attempt to instantiate a LinkDB whereas the datastore is not writable 101 * Attempt to instantiate a LinkDB whereas the datastore is not writable
107 *
108 * @expectedException Shaarli\Exceptions\IOException
109 * @expectedExceptionMessageRegExp /Error accessing "null"/
110 */ 102 */
111 public function testConstructDatastoreNotWriteable() 103 public function testConstructDatastoreNotWriteable()
112 { 104 {
113 new LinkDB('null/store.db', false, false); 105 $this->expectException(\Shaarli\Exceptions\IOException::class);
106 $this->expectExceptionMessageRegExp('/Error accessing "null"/');
107
108 new LegacyLinkDB('null/store.db', false, false);
114 } 109 }
115 110
116 /** 111 /**
@@ -118,7 +113,7 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
118 */ 113 */
119 public function testCheckDBNew() 114 public function testCheckDBNew()
120 { 115 {
121 $linkDB = new LinkDB(self::$testDatastore, false, false); 116 $linkDB = new LegacyLinkDB(self::$testDatastore, false, false);
122 unlink(self::$testDatastore); 117 unlink(self::$testDatastore);
123 $this->assertFileNotExists(self::$testDatastore); 118 $this->assertFileNotExists(self::$testDatastore);
124 119
@@ -135,7 +130,7 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
135 */ 130 */
136 public function testCheckDBLoad() 131 public function testCheckDBLoad()
137 { 132 {
138 $linkDB = new LinkDB(self::$testDatastore, false, false); 133 $linkDB = new LegacyLinkDB(self::$testDatastore, false, false);
139 $datastoreSize = filesize(self::$testDatastore); 134 $datastoreSize = filesize(self::$testDatastore);
140 $this->assertGreaterThan(0, $datastoreSize); 135 $this->assertGreaterThan(0, $datastoreSize);
141 136
@@ -155,13 +150,13 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
155 public function testReadEmptyDB() 150 public function testReadEmptyDB()
156 { 151 {
157 file_put_contents(self::$testDatastore, '<?php /* S7QysKquBQA= */ ?>'); 152 file_put_contents(self::$testDatastore, '<?php /* S7QysKquBQA= */ ?>');
158 $emptyDB = new LinkDB(self::$testDatastore, false, false); 153 $emptyDB = new LegacyLinkDB(self::$testDatastore, false, false);
159 $this->assertEquals(0, sizeof($emptyDB)); 154 $this->assertEquals(0, sizeof($emptyDB));
160 $this->assertEquals(0, count($emptyDB)); 155 $this->assertEquals(0, count($emptyDB));
161 } 156 }
162 157
163 /** 158 /**
164 * Load public links from the DB 159 * Load public bookmarks from the DB
165 */ 160 */
166 public function testReadPublicDB() 161 public function testReadPublicDB()
167 { 162 {
@@ -172,7 +167,7 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
172 } 167 }
173 168
174 /** 169 /**
175 * Load public and private links from the DB 170 * Load public and private bookmarks from the DB
176 */ 171 */
177 public function testReadPrivateDB() 172 public function testReadPrivateDB()
178 { 173 {
@@ -183,31 +178,31 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
183 } 178 }
184 179
185 /** 180 /**
186 * Save the links to the DB 181 * Save the bookmarks to the DB
187 */ 182 */
188 public function testSave() 183 public function testSave()
189 { 184 {
190 $testDB = new LinkDB(self::$testDatastore, true, false); 185 $testDB = new LegacyLinkDB(self::$testDatastore, true, false);
191 $dbSize = sizeof($testDB); 186 $dbSize = sizeof($testDB);
192 187
193 $link = array( 188 $link = array(
194 'id' => 42, 189 'id' => 43,
195 'title' => 'an additional link', 190 'title' => 'an additional link',
196 'url' => 'http://dum.my', 191 'url' => 'http://dum.my',
197 'description' => 'One more', 192 'description' => 'One more',
198 'private' => 0, 193 'private' => 0,
199 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150518_190000'), 194 'created' => DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150518_190000'),
200 'tags' => 'unit test' 195 'tags' => 'unit test'
201 ); 196 );
202 $testDB[$link['id']] = $link; 197 $testDB[$link['id']] = $link;
203 $testDB->save('tests'); 198 $testDB->save('tests');
204 199
205 $testDB = new LinkDB(self::$testDatastore, true, false); 200 $testDB = new LegacyLinkDB(self::$testDatastore, true, false);
206 $this->assertEquals($dbSize + 1, sizeof($testDB)); 201 $this->assertEquals($dbSize + 1, sizeof($testDB));
207 } 202 }
208 203
209 /** 204 /**
210 * Count existing links 205 * Count existing bookmarks
211 */ 206 */
212 public function testCount() 207 public function testCount()
213 { 208 {
@@ -222,11 +217,11 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
222 } 217 }
223 218
224 /** 219 /**
225 * Count existing links - public links hidden 220 * Count existing bookmarks - public bookmarks hidden
226 */ 221 */
227 public function testCountHiddenPublic() 222 public function testCountHiddenPublic()
228 { 223 {
229 $linkDB = new LinkDB(self::$testDatastore, false, true); 224 $linkDB = new LegacyLinkDB(self::$testDatastore, false, true);
230 225
231 $this->assertEquals( 226 $this->assertEquals(
232 0, 227 0,
@@ -239,7 +234,7 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
239 } 234 }
240 235
241 /** 236 /**
242 * List the days for which links have been posted 237 * List the days for which bookmarks have been posted
243 */ 238 */
244 public function testDays() 239 public function testDays()
245 { 240 {
@@ -262,7 +257,7 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
262 $link = self::$publicLinkDB->getLinkFromUrl('http://mediagoblin.org/'); 257 $link = self::$publicLinkDB->getLinkFromUrl('http://mediagoblin.org/');
263 258
264 $this->assertNotEquals(false, $link); 259 $this->assertNotEquals(false, $link);
265 $this->assertContains( 260 $this->assertContainsPolyfill(
266 'A free software media publishing platform', 261 'A free software media publishing platform',
267 $link['description'] 262 $link['description']
268 ); 263 );
@@ -425,22 +420,22 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
425 420
426 /** 421 /**
427 * Test filterHash() with an invalid smallhash. 422 * Test filterHash() with an invalid smallhash.
428 *
429 * @expectedException \Shaarli\Bookmark\Exception\LinkNotFoundException
430 */ 423 */
431 public function testFilterHashInValid1() 424 public function testFilterHashInValid1()
432 { 425 {
426 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
427
433 $request = 'blabla'; 428 $request = 'blabla';
434 self::$publicLinkDB->filterHash($request); 429 self::$publicLinkDB->filterHash($request);
435 } 430 }
436 431
437 /** 432 /**
438 * Test filterHash() with an empty smallhash. 433 * Test filterHash() with an empty smallhash.
439 *
440 * @expectedException \Shaarli\Bookmark\Exception\LinkNotFoundException
441 */ 434 */
442 public function testFilterHashInValid() 435 public function testFilterHashInValid()
443 { 436 {
437 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
438
444 self::$publicLinkDB->filterHash(''); 439 self::$publicLinkDB->filterHash('');
445 } 440 }
446 441
@@ -466,18 +461,18 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
466 } 461 }
467 462
468 /** 463 /**
469 * Test rename tag with a valid value present in multiple links 464 * Test rename tag with a valid value present in multiple bookmarks
470 */ 465 */
471 public function testRenameTagMultiple() 466 public function testRenameTagMultiple()
472 { 467 {
473 self::$refDB->write(self::$testDatastore); 468 self::$refDB->write(self::$testDatastore);
474 $linkDB = new LinkDB(self::$testDatastore, true, false); 469 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
475 470
476 $res = $linkDB->renameTag('cartoon', 'Taz'); 471 $res = $linkDB->renameTag('cartoon', 'Taz');
477 $this->assertEquals(3, count($res)); 472 $this->assertEquals(3, count($res));
478 $this->assertContains(' Taz ', $linkDB[4]['tags']); 473 $this->assertContainsPolyfill(' Taz ', $linkDB[4]['tags']);
479 $this->assertContains(' Taz ', $linkDB[1]['tags']); 474 $this->assertContainsPolyfill(' Taz ', $linkDB[1]['tags']);
480 $this->assertContains(' Taz ', $linkDB[0]['tags']); 475 $this->assertContainsPolyfill(' Taz ', $linkDB[0]['tags']);
481 } 476 }
482 477
483 /** 478 /**
@@ -486,7 +481,7 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
486 public function testRenameTagCaseSensitive() 481 public function testRenameTagCaseSensitive()
487 { 482 {
488 self::$refDB->write(self::$testDatastore); 483 self::$refDB->write(self::$testDatastore);
489 $linkDB = new LinkDB(self::$testDatastore, true, false); 484 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
490 485
491 $res = $linkDB->renameTag('sTuff', 'Taz'); 486 $res = $linkDB->renameTag('sTuff', 'Taz');
492 $this->assertEquals(1, count($res)); 487 $this->assertEquals(1, count($res));
@@ -498,7 +493,7 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
498 */ 493 */
499 public function testRenameTagInvalid() 494 public function testRenameTagInvalid()
500 { 495 {
501 $linkDB = new LinkDB(self::$testDatastore, false, false); 496 $linkDB = new LegacyLinkDB(self::$testDatastore, false, false);
502 497
503 $this->assertFalse($linkDB->renameTag('', 'test')); 498 $this->assertFalse($linkDB->renameTag('', 'test'));
504 $this->assertFalse($linkDB->renameTag('', '')); 499 $this->assertFalse($linkDB->renameTag('', ''));
@@ -513,11 +508,11 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
513 public function testDeleteTag() 508 public function testDeleteTag()
514 { 509 {
515 self::$refDB->write(self::$testDatastore); 510 self::$refDB->write(self::$testDatastore);
516 $linkDB = new LinkDB(self::$testDatastore, true, false); 511 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
517 512
518 $res = $linkDB->renameTag('cartoon', null); 513 $res = $linkDB->renameTag('cartoon', null);
519 $this->assertEquals(3, count($res)); 514 $this->assertEquals(3, count($res));
520 $this->assertNotContains('cartoon', $linkDB[4]['tags']); 515 $this->assertNotContainsPolyfill('cartoon', $linkDB[4]['tags']);
521 } 516 }
522 517
523 /** 518 /**
@@ -619,4 +614,42 @@ class LinkDBTest extends \PHPUnit\Framework\TestCase
619 614
620 $this->assertEquals($expected, $tags, var_export($tags, true)); 615 $this->assertEquals($expected, $tags, var_export($tags, true));
621 } 616 }
617
618 /**
619 * Make sure that bookmarks with the same timestamp have a consistent order:
620 * if their creation date is equal, bookmarks are sorted by ID DESC.
621 */
622 public function testConsistentOrder()
623 {
624 $nextId = 43;
625 $creation = DateTime::createFromFormat('Ymd_His', '20190807_130444');
626 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
627 for ($i = 0; $i < 4; ++$i) {
628 $linkDB[$nextId + $i] = [
629 'id' => $nextId + $i,
630 'url' => 'http://'. $i,
631 'created' => $creation,
632 'title' => true,
633 'description' => true,
634 'tags' => true,
635 ];
636 }
637
638 // Check 4 new links 4 times
639 for ($i = 0; $i < 4; ++$i) {
640 $linkDB->save('tests');
641 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
642 $count = 3;
643 foreach ($linkDB as $link) {
644 if ($link['sticky'] === true) {
645 continue;
646 }
647 $this->assertEquals($nextId + $count, $link['id']);
648 $this->assertEquals('http://'. $count, $link['url']);
649 if (--$count < 0) {
650 break;
651 }
652 }
653 }
654 }
622} 655}
diff --git a/tests/bookmark/LinkFilterTest.php b/tests/legacy/LegacyLinkFilterTest.php
index 808f8122..45d7754d 100644
--- a/tests/bookmark/LinkFilterTest.php
+++ b/tests/legacy/LegacyLinkFilterTest.php
@@ -4,18 +4,20 @@ namespace Shaarli\Bookmark;
4 4
5use Exception; 5use Exception;
6use ReferenceLinkDB; 6use ReferenceLinkDB;
7use Shaarli\Legacy\LegacyLinkDB;
8use Shaarli\Legacy\LegacyLinkFilter;
7 9
8/** 10/**
9 * Class LinkFilterTest. 11 * Class LegacyLinkFilterTest.
10 */ 12 */
11class LinkFilterTest extends \PHPUnit\Framework\TestCase 13class LegacyLinkFilterTest extends \Shaarli\TestCase
12{ 14{
13 /** 15 /**
14 * @var string Test datastore path. 16 * @var string Test datastore path.
15 */ 17 */
16 protected static $testDatastore = 'sandbox/datastore.php'; 18 protected static $testDatastore = 'sandbox/datastore.php';
17 /** 19 /**
18 * @var LinkFilter instance. 20 * @var BookmarkFilter instance.
19 */ 21 */
20 protected static $linkFilter; 22 protected static $linkFilter;
21 23
@@ -25,19 +27,19 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
25 protected static $refDB; 27 protected static $refDB;
26 28
27 /** 29 /**
28 * @var LinkDB instance 30 * @var LegacyLinkDB instance
29 */ 31 */
30 protected static $linkDB; 32 protected static $linkDB;
31 33
32 /** 34 /**
33 * Instantiate linkFilter with ReferenceLinkDB data. 35 * Instantiate linkFilter with ReferenceLinkDB data.
34 */ 36 */
35 public static function setUpBeforeClass() 37 public static function setUpBeforeClass(): void
36 { 38 {
37 self::$refDB = new ReferenceLinkDB(); 39 self::$refDB = new ReferenceLinkDB(true);
38 self::$refDB->write(self::$testDatastore); 40 self::$refDB->write(self::$testDatastore);
39 self::$linkDB = new LinkDB(self::$testDatastore, true, false); 41 self::$linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
40 self::$linkFilter = new LinkFilter(self::$linkDB); 42 self::$linkFilter = new LegacyLinkFilter(self::$linkDB);
41 } 43 }
42 44
43 /** 45 /**
@@ -74,14 +76,14 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
74 76
75 $this->assertEquals( 77 $this->assertEquals(
76 ReferenceLinkDB::$NB_LINKS_TOTAL, 78 ReferenceLinkDB::$NB_LINKS_TOTAL,
77 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '')) 79 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, ''))
78 ); 80 );
79 81
80 $this->assertEquals( 82 $this->assertEquals(
81 self::$refDB->countUntaggedLinks(), 83 self::$refDB->countUntaggedLinks(),
82 count( 84 count(
83 self::$linkFilter->filter( 85 self::$linkFilter->filter(
84 LinkFilter::$FILTER_TAG, 86 LegacyLinkFilter::$FILTER_TAG,
85 /*$request=*/ 87 /*$request=*/
86 '', 88 '',
87 /*$casesensitive=*/ 89 /*$casesensitive=*/
@@ -96,89 +98,89 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
96 98
97 $this->assertEquals( 99 $this->assertEquals(
98 ReferenceLinkDB::$NB_LINKS_TOTAL, 100 ReferenceLinkDB::$NB_LINKS_TOTAL,
99 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '')) 101 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, ''))
100 ); 102 );
101 } 103 }
102 104
103 /** 105 /**
104 * Filter links using a tag 106 * Filter bookmarks using a tag
105 */ 107 */
106 public function testFilterOneTag() 108 public function testFilterOneTag()
107 { 109 {
108 $this->assertEquals( 110 $this->assertEquals(
109 4, 111 4,
110 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'web', false)) 112 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'web', false))
111 ); 113 );
112 114
113 $this->assertEquals( 115 $this->assertEquals(
114 4, 116 4,
115 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'web', false, 'all')) 117 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'web', false, 'all'))
116 ); 118 );
117 119
118 $this->assertEquals( 120 $this->assertEquals(
119 4, 121 4,
120 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'web', false, 'default-blabla')) 122 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'web', false, 'default-blabla'))
121 ); 123 );
122 124
123 // Private only. 125 // Private only.
124 $this->assertEquals( 126 $this->assertEquals(
125 1, 127 1,
126 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'web', false, 'private')) 128 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'web', false, 'private'))
127 ); 129 );
128 130
129 // Public only. 131 // Public only.
130 $this->assertEquals( 132 $this->assertEquals(
131 3, 133 3,
132 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'web', false, 'public')) 134 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'web', false, 'public'))
133 ); 135 );
134 } 136 }
135 137
136 /** 138 /**
137 * Filter links using a tag - case-sensitive 139 * Filter bookmarks using a tag - case-sensitive
138 */ 140 */
139 public function testFilterCaseSensitiveTag() 141 public function testFilterCaseSensitiveTag()
140 { 142 {
141 $this->assertEquals( 143 $this->assertEquals(
142 0, 144 0,
143 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'mercurial', true)) 145 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'mercurial', true))
144 ); 146 );
145 147
146 $this->assertEquals( 148 $this->assertEquals(
147 1, 149 1,
148 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'Mercurial', true)) 150 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'Mercurial', true))
149 ); 151 );
150 } 152 }
151 153
152 /** 154 /**
153 * Filter links using a tag combination 155 * Filter bookmarks using a tag combination
154 */ 156 */
155 public function testFilterMultipleTags() 157 public function testFilterMultipleTags()
156 { 158 {
157 $this->assertEquals( 159 $this->assertEquals(
158 2, 160 2,
159 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'dev cartoon', false)) 161 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'dev cartoon', false))
160 ); 162 );
161 } 163 }
162 164
163 /** 165 /**
164 * Filter links using a non-existent tag 166 * Filter bookmarks using a non-existent tag
165 */ 167 */
166 public function testFilterUnknownTag() 168 public function testFilterUnknownTag()
167 { 169 {
168 $this->assertEquals( 170 $this->assertEquals(
169 0, 171 0,
170 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'null', false)) 172 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'null', false))
171 ); 173 );
172 } 174 }
173 175
174 /** 176 /**
175 * Return links for a given day 177 * Return bookmarks for a given day
176 */ 178 */
177 public function testFilterDay() 179 public function testFilterDay()
178 { 180 {
179 $this->assertEquals( 181 $this->assertEquals(
180 4, 182 4,
181 count(self::$linkFilter->filter(LinkFilter::$FILTER_DAY, '20121206')) 183 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, '20121206'))
182 ); 184 );
183 } 185 }
184 186
@@ -189,28 +191,30 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
189 { 191 {
190 $this->assertEquals( 192 $this->assertEquals(
191 0, 193 0,
192 count(self::$linkFilter->filter(LinkFilter::$FILTER_DAY, '19700101')) 194 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, '19700101'))
193 ); 195 );
194 } 196 }
195 197
196 /** 198 /**
197 * Use an invalid date format 199 * Use an invalid date format
198 * @expectedException Exception
199 * @expectedExceptionMessageRegExp /Invalid date format/
200 */ 200 */
201 public function testFilterInvalidDayWithChars() 201 public function testFilterInvalidDayWithChars()
202 { 202 {
203 self::$linkFilter->filter(LinkFilter::$FILTER_DAY, 'Rainy day, dream away'); 203 $this->expectException(\Exception::class);
204 $this->expectExceptionMessageRegExp('/Invalid date format/');
205
206 self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, 'Rainy day, dream away');
204 } 207 }
205 208
206 /** 209 /**
207 * Use an invalid date format 210 * Use an invalid date format
208 * @expectedException Exception
209 * @expectedExceptionMessageRegExp /Invalid date format/
210 */ 211 */
211 public function testFilterInvalidDayDigits() 212 public function testFilterInvalidDayDigits()
212 { 213 {
213 self::$linkFilter->filter(LinkFilter::$FILTER_DAY, '20'); 214 $this->expectException(\Exception::class);
215 $this->expectExceptionMessageRegExp('/Invalid date format/');
216
217 self::$linkFilter->filter(LegacyLinkFilter::$FILTER_DAY, '20');
214 } 218 }
215 219
216 /** 220 /**
@@ -218,7 +222,7 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
218 */ 222 */
219 public function testFilterSmallHash() 223 public function testFilterSmallHash()
220 { 224 {
221 $links = self::$linkFilter->filter(LinkFilter::$FILTER_HASH, 'IuWvgA'); 225 $links = self::$linkFilter->filter(LegacyLinkFilter::$FILTER_HASH, 'IuWvgA');
222 226
223 $this->assertEquals( 227 $this->assertEquals(
224 1, 228 1,
@@ -233,12 +237,12 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
233 237
234 /** 238 /**
235 * No link for this hash 239 * No link for this hash
236 *
237 * @expectedException \Shaarli\Bookmark\Exception\LinkNotFoundException
238 */ 240 */
239 public function testFilterUnknownSmallHash() 241 public function testFilterUnknownSmallHash()
240 { 242 {
241 self::$linkFilter->filter(LinkFilter::$FILTER_HASH, 'Iblaah'); 243 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
244
245 self::$linkFilter->filter(LegacyLinkFilter::$FILTER_HASH, 'Iblaah');
242 } 246 }
243 247
244 /** 248 /**
@@ -248,7 +252,7 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
248 { 252 {
249 $this->assertEquals( 253 $this->assertEquals(
250 0, 254 0,
251 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'azertyuiop')) 255 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'azertyuiop'))
252 ); 256 );
253 } 257 }
254 258
@@ -259,12 +263,12 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
259 { 263 {
260 $this->assertEquals( 264 $this->assertEquals(
261 2, 265 2,
262 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'ars.userfriendly.org')) 266 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'ars.userfriendly.org'))
263 ); 267 );
264 268
265 $this->assertEquals( 269 $this->assertEquals(
266 2, 270 2,
267 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'ars org')) 271 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'ars org'))
268 ); 272 );
269 } 273 }
270 274
@@ -276,21 +280,21 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
276 // use miscellaneous cases 280 // use miscellaneous cases
277 $this->assertEquals( 281 $this->assertEquals(
278 2, 282 2,
279 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'userfriendly -')) 283 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'userfriendly -'))
280 ); 284 );
281 $this->assertEquals( 285 $this->assertEquals(
282 2, 286 2,
283 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'UserFriendly -')) 287 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'UserFriendly -'))
284 ); 288 );
285 $this->assertEquals( 289 $this->assertEquals(
286 2, 290 2,
287 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'uSeRFrIendlY -')) 291 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'uSeRFrIendlY -'))
288 ); 292 );
289 293
290 // use miscellaneous case and offset 294 // use miscellaneous case and offset
291 $this->assertEquals( 295 $this->assertEquals(
292 2, 296 2,
293 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'RFrIendL')) 297 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'RFrIendL'))
294 ); 298 );
295 } 299 }
296 300
@@ -301,17 +305,17 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
301 { 305 {
302 $this->assertEquals( 306 $this->assertEquals(
303 1, 307 1,
304 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'publishing media')) 308 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'publishing media'))
305 ); 309 );
306 310
307 $this->assertEquals( 311 $this->assertEquals(
308 1, 312 1,
309 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'mercurial w3c')) 313 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'mercurial w3c'))
310 ); 314 );
311 315
312 $this->assertEquals( 316 $this->assertEquals(
313 3, 317 3,
314 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '"free software"')) 318 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, '"free software"'))
315 ); 319 );
316 } 320 }
317 321
@@ -322,29 +326,29 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
322 { 326 {
323 $this->assertEquals( 327 $this->assertEquals(
324 6, 328 6,
325 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'web')) 329 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'web'))
326 ); 330 );
327 331
328 $this->assertEquals( 332 $this->assertEquals(
329 6, 333 6,
330 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'web', 'all')) 334 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'web', 'all'))
331 ); 335 );
332 336
333 $this->assertEquals( 337 $this->assertEquals(
334 6, 338 6,
335 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'web', 'bla')) 339 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'web', 'bla'))
336 ); 340 );
337 341
338 // Private only. 342 // Private only.
339 $this->assertEquals( 343 $this->assertEquals(
340 1, 344 1,
341 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'web', false, 'private')) 345 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'web', false, 'private'))
342 ); 346 );
343 347
344 // Public only. 348 // Public only.
345 $this->assertEquals( 349 $this->assertEquals(
346 5, 350 5,
347 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'web', false, 'public')) 351 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'web', false, 'public'))
348 ); 352 );
349 } 353 }
350 354
@@ -355,7 +359,7 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
355 { 359 {
356 $this->assertEquals( 360 $this->assertEquals(
357 3, 361 3,
358 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'free software')) 362 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'free software'))
359 ); 363 );
360 } 364 }
361 365
@@ -366,12 +370,12 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
366 { 370 {
367 $this->assertEquals( 371 $this->assertEquals(
368 1, 372 1,
369 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, 'free -gnu')) 373 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, 'free -gnu'))
370 ); 374 );
371 375
372 $this->assertEquals( 376 $this->assertEquals(
373 ReferenceLinkDB::$NB_LINKS_TOTAL - 1, 377 ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
374 count(self::$linkFilter->filter(LinkFilter::$FILTER_TEXT, '-revolution')) 378 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TEXT, '-revolution'))
375 ); 379 );
376 } 380 }
377 381
@@ -383,7 +387,7 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
383 $this->assertEquals( 387 $this->assertEquals(
384 2, 388 2,
385 count(self::$linkFilter->filter( 389 count(self::$linkFilter->filter(
386 LinkFilter::$FILTER_TEXT, 390 LegacyLinkFilter::$FILTER_TEXT,
387 '"Free Software " stallman "read this" @website stuff' 391 '"Free Software " stallman "read this" @website stuff'
388 )) 392 ))
389 ); 393 );
@@ -391,7 +395,7 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
391 $this->assertEquals( 395 $this->assertEquals(
392 1, 396 1,
393 count(self::$linkFilter->filter( 397 count(self::$linkFilter->filter(
394 LinkFilter::$FILTER_TEXT, 398 LegacyLinkFilter::$FILTER_TEXT,
395 '"free software " stallman "read this" -beard @website stuff' 399 '"free software " stallman "read this" -beard @website stuff'
396 )) 400 ))
397 ); 401 );
@@ -405,7 +409,7 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
405 $this->assertEquals( 409 $this->assertEquals(
406 0, 410 0,
407 count(self::$linkFilter->filter( 411 count(self::$linkFilter->filter(
408 LinkFilter::$FILTER_TEXT, 412 LegacyLinkFilter::$FILTER_TEXT,
409 '"designer naming"' 413 '"designer naming"'
410 )) 414 ))
411 ); 415 );
@@ -413,7 +417,7 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
413 $this->assertEquals( 417 $this->assertEquals(
414 0, 418 0,
415 count(self::$linkFilter->filter( 419 count(self::$linkFilter->filter(
416 LinkFilter::$FILTER_TEXT, 420 LegacyLinkFilter::$FILTER_TEXT,
417 '"designernaming"' 421 '"designernaming"'
418 )) 422 ))
419 ); 423 );
@@ -426,12 +430,12 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
426 { 430 {
427 $this->assertEquals( 431 $this->assertEquals(
428 1, 432 1,
429 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, 'gnu -free')) 433 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, 'gnu -free'))
430 ); 434 );
431 435
432 $this->assertEquals( 436 $this->assertEquals(
433 ReferenceLinkDB::$NB_LINKS_TOTAL - 1, 437 ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
434 count(self::$linkFilter->filter(LinkFilter::$FILTER_TAG, '-free')) 438 count(self::$linkFilter->filter(LegacyLinkFilter::$FILTER_TAG, '-free'))
435 ); 439 );
436 } 440 }
437 441
@@ -445,42 +449,42 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
445 $this->assertEquals( 449 $this->assertEquals(
446 1, 450 1,
447 count(self::$linkFilter->filter( 451 count(self::$linkFilter->filter(
448 LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT, 452 LegacyLinkFilter::$FILTER_TAG | LegacyLinkFilter::$FILTER_TEXT,
449 array($tags, $terms) 453 array($tags, $terms)
450 )) 454 ))
451 ); 455 );
452 $this->assertEquals( 456 $this->assertEquals(
453 2, 457 2,
454 count(self::$linkFilter->filter( 458 count(self::$linkFilter->filter(
455 LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT, 459 LegacyLinkFilter::$FILTER_TAG | LegacyLinkFilter::$FILTER_TEXT,
456 array('', $terms) 460 array('', $terms)
457 )) 461 ))
458 ); 462 );
459 $this->assertEquals( 463 $this->assertEquals(
460 1, 464 1,
461 count(self::$linkFilter->filter( 465 count(self::$linkFilter->filter(
462 LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT, 466 LegacyLinkFilter::$FILTER_TAG | LegacyLinkFilter::$FILTER_TEXT,
463 array(false, 'PSR-2') 467 array(false, 'PSR-2')
464 )) 468 ))
465 ); 469 );
466 $this->assertEquals( 470 $this->assertEquals(
467 1, 471 1,
468 count(self::$linkFilter->filter( 472 count(self::$linkFilter->filter(
469 LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT, 473 LegacyLinkFilter::$FILTER_TAG | LegacyLinkFilter::$FILTER_TEXT,
470 array($tags, '') 474 array($tags, '')
471 )) 475 ))
472 ); 476 );
473 $this->assertEquals( 477 $this->assertEquals(
474 ReferenceLinkDB::$NB_LINKS_TOTAL, 478 ReferenceLinkDB::$NB_LINKS_TOTAL,
475 count(self::$linkFilter->filter( 479 count(self::$linkFilter->filter(
476 LinkFilter::$FILTER_TAG | LinkFilter::$FILTER_TEXT, 480 LegacyLinkFilter::$FILTER_TAG | LegacyLinkFilter::$FILTER_TEXT,
477 '' 481 ''
478 )) 482 ))
479 ); 483 );
480 } 484 }
481 485
482 /** 486 /**
483 * Filter links by #hashtag. 487 * Filter bookmarks by #hashtag.
484 */ 488 */
485 public function testFilterByHashtag() 489 public function testFilterByHashtag()
486 { 490 {
@@ -488,7 +492,7 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
488 $this->assertEquals( 492 $this->assertEquals(
489 3, 493 3,
490 count(self::$linkFilter->filter( 494 count(self::$linkFilter->filter(
491 LinkFilter::$FILTER_TAG, 495 LegacyLinkFilter::$FILTER_TAG,
492 $hashtag 496 $hashtag
493 )) 497 ))
494 ); 498 );
@@ -497,7 +501,7 @@ class LinkFilterTest extends \PHPUnit\Framework\TestCase
497 $this->assertEquals( 501 $this->assertEquals(
498 1, 502 1,
499 count(self::$linkFilter->filter( 503 count(self::$linkFilter->filter(
500 LinkFilter::$FILTER_TAG, 504 LegacyLinkFilter::$FILTER_TAG,
501 $hashtag, 505 $hashtag,
502 false, 506 false,
503 'private' 507 'private'
diff --git a/tests/legacy/LegacyUpdaterTest.php b/tests/legacy/LegacyUpdaterTest.php
new file mode 100644
index 00000000..f7391b86
--- /dev/null
+++ b/tests/legacy/LegacyUpdaterTest.php
@@ -0,0 +1,886 @@
1<?php
2namespace Shaarli\Updater;
3
4use DateTime;
5use Exception;
6use Shaarli\Bookmark\Bookmark;
7use Shaarli\Config\ConfigJson;
8use Shaarli\Config\ConfigManager;
9use Shaarli\Config\ConfigPhp;
10use Shaarli\Legacy\LegacyLinkDB;
11use Shaarli\Legacy\LegacyUpdater;
12use Shaarli\Thumbnailer;
13
14require_once 'application/updater/UpdaterUtils.php';
15require_once 'tests/updater/DummyUpdater.php';
16require_once 'tests/utils/ReferenceLinkDB.php';
17require_once 'inc/rain.tpl.class.php';
18
19/**
20 * Class UpdaterTest.
21 * Runs unit tests against the updater class.
22 */
23class LegacyUpdaterTest extends \Shaarli\TestCase
24{
25 /**
26 * @var string Path to test datastore.
27 */
28 protected static $testDatastore = 'sandbox/datastore.php';
29
30 /**
31 * @var string Config file path (without extension).
32 */
33 protected static $configFile = 'sandbox/config';
34
35 /**
36 * @var ConfigManager
37 */
38 protected $conf;
39
40 /**
41 * Executed before each test.
42 */
43 protected function setUp(): void
44 {
45 copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php');
46 $this->conf = new ConfigManager(self::$configFile);
47 }
48
49 /**
50 * Test UpdaterUtils::read_updates_file with an empty/missing file.
51 */
52 public function testReadEmptyUpdatesFile()
53 {
54 $this->assertEquals(array(), UpdaterUtils::read_updates_file(''));
55 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt';
56 touch($updatesFile);
57 $this->assertEquals(array(), UpdaterUtils::read_updates_file($updatesFile));
58 unlink($updatesFile);
59 }
60
61 /**
62 * Test read/write updates file.
63 */
64 public function testReadWriteUpdatesFile()
65 {
66 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt';
67 $updatesMethods = array('m1', 'm2', 'm3');
68
69 UpdaterUtils::write_updates_file($updatesFile, $updatesMethods);
70 $readMethods = UpdaterUtils::read_updates_file($updatesFile);
71 $this->assertEquals($readMethods, $updatesMethods);
72
73 // Update
74 $updatesMethods[] = 'm4';
75 UpdaterUtils::write_updates_file($updatesFile, $updatesMethods);
76 $readMethods = UpdaterUtils::read_updates_file($updatesFile);
77 $this->assertEquals($readMethods, $updatesMethods);
78 unlink($updatesFile);
79 }
80
81 /**
82 * Test errors in UpdaterUtils::write_updates_file(): empty updates file.
83 */
84 public function testWriteEmptyUpdatesFile()
85 {
86 $this->expectException(\Exception::class);
87 $this->expectExceptionMessageRegExp('/Updates file path is not set(.*)/');
88
89 UpdaterUtils::write_updates_file('', array('test'));
90 }
91
92 /**
93 * Test errors in UpdaterUtils::write_updates_file(): not writable updates file.
94 */
95 public function testWriteUpdatesFileNotWritable()
96 {
97 $this->expectException(\Exception::class);
98 $this->expectExceptionMessageRegExp('/Unable to write(.*)/');
99
100 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt';
101 touch($updatesFile);
102 chmod($updatesFile, 0444);
103 try {
104 @UpdaterUtils::write_updates_file($updatesFile, array('test'));
105 } catch (Exception $e) {
106 unlink($updatesFile);
107 throw $e;
108 }
109 }
110
111 /**
112 * Test the update() method, with no update to run.
113 * 1. Everything already run.
114 * 2. User is logged out.
115 */
116 public function testNoUpdates()
117 {
118 $updates = array(
119 'updateMethodDummy1',
120 'updateMethodDummy2',
121 'updateMethodDummy3',
122 'updateMethodException',
123 );
124 $updater = new DummyUpdater($updates, array(), $this->conf, true);
125 $this->assertEquals(array(), $updater->update());
126
127 $updater = new DummyUpdater(array(), array(), $this->conf, false);
128 $this->assertEquals(array(), $updater->update());
129 }
130
131 /**
132 * Test the update() method, with all updates to run (except the failing one).
133 */
134 public function testUpdatesFirstTime()
135 {
136 $updates = array('updateMethodException',);
137 $expectedUpdates = array(
138 'updateMethodDummy1',
139 'updateMethodDummy2',
140 'updateMethodDummy3',
141 );
142 $updater = new DummyUpdater($updates, array(), $this->conf, true);
143 $this->assertEquals($expectedUpdates, $updater->update());
144 }
145
146 /**
147 * Test the update() method, only one update to run.
148 */
149 public function testOneUpdate()
150 {
151 $updates = array(
152 'updateMethodDummy1',
153 'updateMethodDummy3',
154 'updateMethodException',
155 );
156 $expectedUpdate = array('updateMethodDummy2');
157
158 $updater = new DummyUpdater($updates, array(), $this->conf, true);
159 $this->assertEquals($expectedUpdate, $updater->update());
160 }
161
162 /**
163 * Test Update failed.
164 */
165 public function testUpdateFailed()
166 {
167 $this->expectException(\Exception::class);
168
169 $updates = array(
170 'updateMethodDummy1',
171 'updateMethodDummy2',
172 'updateMethodDummy3',
173 );
174
175 $updater = new DummyUpdater($updates, array(), $this->conf, true);
176 $updater->update();
177 }
178
179 /**
180 * Test update mergeDeprecatedConfig:
181 * 1. init a config file.
182 * 2. init a options.php file with update value.
183 * 3. merge.
184 * 4. check updated value in config file.
185 */
186 public function testUpdateMergeDeprecatedConfig()
187 {
188 $this->conf->setConfigFile('tests/utils/config/configPhp');
189 $this->conf->reset();
190
191 $optionsFile = 'tests/updater/options.php';
192 $options = '<?php
193$GLOBALS[\'privateLinkByDefault\'] = true;';
194 file_put_contents($optionsFile, $options);
195
196 // tmp config file.
197 $this->conf->setConfigFile('tests/updater/config');
198
199 // merge configs
200 $updater = new LegacyUpdater(array(), array(), $this->conf, true);
201 // This writes a new config file in tests/updater/config.php
202 $updater->updateMethodMergeDeprecatedConfigFile();
203
204 // make sure updated field is changed
205 $this->conf->reload();
206 $this->assertTrue($this->conf->get('privacy.default_private_links'));
207 $this->assertFalse(is_file($optionsFile));
208 // Delete the generated file.
209 unlink($this->conf->getConfigFileExt());
210 }
211
212 /**
213 * Test mergeDeprecatedConfig in without options file.
214 */
215 public function testMergeDeprecatedConfigNoFile()
216 {
217 $updater = new LegacyUpdater(array(), array(), $this->conf, true);
218 $updater->updateMethodMergeDeprecatedConfigFile();
219
220 $this->assertEquals('root', $this->conf->get('credentials.login'));
221 }
222
223 /**
224 * Test renameDashTags update method.
225 */
226 public function testRenameDashTags()
227 {
228 $refDB = new \ReferenceLinkDB(true);
229 $refDB->write(self::$testDatastore);
230 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
231
232 $this->assertEmpty($linkDB->filterSearch(array('searchtags' => 'exclude')));
233 $updater = new LegacyUpdater(array(), $linkDB, $this->conf, true);
234 $updater->updateMethodRenameDashTags();
235 $this->assertNotEmpty($linkDB->filterSearch(array('searchtags' => 'exclude')));
236 }
237
238 /**
239 * Convert old PHP config file to JSON config.
240 */
241 public function testConfigToJson()
242 {
243 $configFile = 'tests/utils/config/configPhp';
244 $this->conf->setConfigFile($configFile);
245 $this->conf->reset();
246
247 // The ConfigIO is initialized with ConfigPhp.
248 $this->assertTrue($this->conf->getConfigIO() instanceof ConfigPhp);
249
250 $updater = new LegacyUpdater(array(), array(), $this->conf, false);
251 $done = $updater->updateMethodConfigToJson();
252 $this->assertTrue($done);
253
254 // The ConfigIO has been updated to ConfigJson.
255 $this->assertTrue($this->conf->getConfigIO() instanceof ConfigJson);
256 $this->assertTrue(file_exists($this->conf->getConfigFileExt()));
257
258 // Check JSON config data.
259 $this->conf->reload();
260 $this->assertEquals('root', $this->conf->get('credentials.login'));
261 $this->assertEquals('lala', $this->conf->get('redirector.url'));
262 $this->assertEquals('data/datastore.php', $this->conf->get('resource.datastore'));
263 $this->assertEquals('1', $this->conf->get('plugins.WALLABAG_VERSION'));
264
265 rename($configFile . '.save.php', $configFile . '.php');
266 unlink($this->conf->getConfigFileExt());
267 }
268
269 /**
270 * Launch config conversion update with an existing JSON file => nothing to do.
271 */
272 public function testConfigToJsonNothingToDo()
273 {
274 $filetime = filemtime($this->conf->getConfigFileExt());
275 $updater = new LegacyUpdater(array(), array(), $this->conf, false);
276 $done = $updater->updateMethodConfigToJson();
277 $this->assertTrue($done);
278 $expected = filemtime($this->conf->getConfigFileExt());
279 $this->assertEquals($expected, $filetime);
280 }
281
282 /**
283 * Test escapeUnescapedConfig with valid data.
284 */
285 public function testEscapeConfig()
286 {
287 $sandbox = 'sandbox/config';
288 copy(self::$configFile . '.json.php', $sandbox . '.json.php');
289 $this->conf = new ConfigManager($sandbox);
290 $title = '<script>alert("title");</script>';
291 $headerLink = '<script>alert("header_link");</script>';
292 $this->conf->set('general.title', $title);
293 $this->conf->set('general.header_link', $headerLink);
294 $updater = new LegacyUpdater(array(), array(), $this->conf, true);
295 $done = $updater->updateMethodEscapeUnescapedConfig();
296 $this->assertTrue($done);
297 $this->conf->reload();
298 $this->assertEquals(escape($title), $this->conf->get('general.title'));
299 $this->assertEquals(escape($headerLink), $this->conf->get('general.header_link'));
300 unlink($sandbox . '.json.php');
301 }
302
303 /**
304 * Test updateMethodApiSettings(): create default settings for the API (enabled + secret).
305 */
306 public function testUpdateApiSettings()
307 {
308 $confFile = 'sandbox/config';
309 copy(self::$configFile .'.json.php', $confFile .'.json.php');
310 $conf = new ConfigManager($confFile);
311 $updater = new LegacyUpdater(array(), array(), $conf, true);
312
313 $this->assertFalse($conf->exists('api.enabled'));
314 $this->assertFalse($conf->exists('api.secret'));
315 $updater->updateMethodApiSettings();
316 $conf->reload();
317 $this->assertTrue($conf->get('api.enabled'));
318 $this->assertTrue($conf->exists('api.secret'));
319 unlink($confFile .'.json.php');
320 }
321
322 /**
323 * Test updateMethodApiSettings(): already set, do nothing.
324 */
325 public function testUpdateApiSettingsNothingToDo()
326 {
327 $confFile = 'sandbox/config';
328 copy(self::$configFile .'.json.php', $confFile .'.json.php');
329 $conf = new ConfigManager($confFile);
330 $conf->set('api.enabled', false);
331 $conf->set('api.secret', '');
332 $updater = new LegacyUpdater(array(), array(), $conf, true);
333 $updater->updateMethodApiSettings();
334 $this->assertFalse($conf->get('api.enabled'));
335 $this->assertEmpty($conf->get('api.secret'));
336 unlink($confFile .'.json.php');
337 }
338
339 /**
340 * Test updateMethodDatastoreIds().
341 */
342 public function testDatastoreIds()
343 {
344 $links = array(
345 '20121206_182539' => array(
346 'linkdate' => '20121206_182539',
347 'title' => 'Geek and Poke',
348 'url' => 'http://geek-and-poke.com/',
349 'description' => 'desc',
350 'tags' => 'dev cartoon tag1 tag2 tag3 tag4 ',
351 'updated' => '20121206_190301',
352 'private' => false,
353 ),
354 '20121206_172539' => array(
355 'linkdate' => '20121206_172539',
356 'title' => 'UserFriendly - Samba',
357 'url' => 'http://ars.userfriendly.org/cartoons/?id=20010306',
358 'description' => '',
359 'tags' => 'samba cartoon web',
360 'private' => false,
361 ),
362 '20121206_142300' => array(
363 'linkdate' => '20121206_142300',
364 'title' => 'UserFriendly - Web Designer',
365 'url' => 'http://ars.userfriendly.org/cartoons/?id=20121206',
366 'description' => 'Naming conventions... #private',
367 'tags' => 'samba cartoon web',
368 'private' => true,
369 ),
370 );
371 $refDB = new \ReferenceLinkDB(true);
372 $refDB->setLinks($links);
373 $refDB->write(self::$testDatastore);
374 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
375
376 $checksum = hash_file('sha1', self::$testDatastore);
377
378 $this->conf->set('resource.data_dir', 'sandbox');
379 $this->conf->set('resource.datastore', self::$testDatastore);
380
381 $updater = new LegacyUpdater(array(), $linkDB, $this->conf, true);
382 $this->assertTrue($updater->updateMethodDatastoreIds());
383
384 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
385
386 $backupFiles = glob($this->conf->get('resource.data_dir') . '/datastore.'. date('YmdH') .'*.php');
387 $backup = null;
388 foreach ($backupFiles as $backupFile) {
389 if (strpos($backupFile, '_1') === false) {
390 $backup = $backupFile;
391 }
392 }
393 $this->assertNotNull($backup);
394 $this->assertFileExists($backup);
395 $this->assertEquals($checksum, hash_file('sha1', $backup));
396 unlink($backup);
397
398 $this->assertEquals(3, count($linkDB));
399 $this->assertTrue(isset($linkDB[0]));
400 $this->assertFalse(isset($linkDB[0]['linkdate']));
401 $this->assertEquals(0, $linkDB[0]['id']);
402 $this->assertEquals('UserFriendly - Web Designer', $linkDB[0]['title']);
403 $this->assertEquals('http://ars.userfriendly.org/cartoons/?id=20121206', $linkDB[0]['url']);
404 $this->assertEquals('Naming conventions... #private', $linkDB[0]['description']);
405 $this->assertEquals('samba cartoon web', $linkDB[0]['tags']);
406 $this->assertTrue($linkDB[0]['private']);
407 $this->assertEquals(
408 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_142300'),
409 $linkDB[0]['created']
410 );
411
412 $this->assertTrue(isset($linkDB[1]));
413 $this->assertFalse(isset($linkDB[1]['linkdate']));
414 $this->assertEquals(1, $linkDB[1]['id']);
415 $this->assertEquals('UserFriendly - Samba', $linkDB[1]['title']);
416 $this->assertEquals(
417 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_172539'),
418 $linkDB[1]['created']
419 );
420
421 $this->assertTrue(isset($linkDB[2]));
422 $this->assertFalse(isset($linkDB[2]['linkdate']));
423 $this->assertEquals(2, $linkDB[2]['id']);
424 $this->assertEquals('Geek and Poke', $linkDB[2]['title']);
425 $this->assertEquals(
426 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_182539'),
427 $linkDB[2]['created']
428 );
429 $this->assertEquals(
430 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_190301'),
431 $linkDB[2]['updated']
432 );
433 }
434
435 /**
436 * Test updateMethodDatastoreIds() with the update already applied: nothing to do.
437 */
438 public function testDatastoreIdsNothingToDo()
439 {
440 $refDB = new \ReferenceLinkDB(true);
441 $refDB->write(self::$testDatastore);
442 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
443
444 $this->conf->set('resource.data_dir', 'sandbox');
445 $this->conf->set('resource.datastore', self::$testDatastore);
446
447 $checksum = hash_file('sha1', self::$testDatastore);
448 $updater = new LegacyUpdater(array(), $linkDB, $this->conf, true);
449 $this->assertTrue($updater->updateMethodDatastoreIds());
450 $this->assertEquals($checksum, hash_file('sha1', self::$testDatastore));
451 }
452
453 /**
454 * Test defaultTheme update with default settings: nothing to do.
455 */
456 public function testDefaultThemeWithDefaultSettings()
457 {
458 $sandbox = 'sandbox/config';
459 copy(self::$configFile . '.json.php', $sandbox . '.json.php');
460 $this->conf = new ConfigManager($sandbox);
461 $updater = new LegacyUpdater([], [], $this->conf, true);
462 $this->assertTrue($updater->updateMethodDefaultTheme());
463
464 $this->assertEquals('tpl/', $this->conf->get('resource.raintpl_tpl'));
465 $this->assertEquals('default', $this->conf->get('resource.theme'));
466 $this->conf = new ConfigManager($sandbox);
467 $this->assertEquals('tpl/', $this->conf->get('resource.raintpl_tpl'));
468 $this->assertEquals('default', $this->conf->get('resource.theme'));
469 unlink($sandbox . '.json.php');
470 }
471
472 /**
473 * Test defaultTheme update with a custom theme in a subfolder
474 */
475 public function testDefaultThemeWithCustomTheme()
476 {
477 $theme = 'iamanartist';
478 $sandbox = 'sandbox/config';
479 copy(self::$configFile . '.json.php', $sandbox . '.json.php');
480 $this->conf = new ConfigManager($sandbox);
481 mkdir('sandbox/'. $theme);
482 touch('sandbox/'. $theme .'/linklist.html');
483 $this->conf->set('resource.raintpl_tpl', 'sandbox/'. $theme .'/');
484 $updater = new LegacyUpdater([], [], $this->conf, true);
485 $this->assertTrue($updater->updateMethodDefaultTheme());
486
487 $this->assertEquals('sandbox', $this->conf->get('resource.raintpl_tpl'));
488 $this->assertEquals($theme, $this->conf->get('resource.theme'));
489 $this->conf = new ConfigManager($sandbox);
490 $this->assertEquals('sandbox', $this->conf->get('resource.raintpl_tpl'));
491 $this->assertEquals($theme, $this->conf->get('resource.theme'));
492 unlink($sandbox . '.json.php');
493 unlink('sandbox/'. $theme .'/linklist.html');
494 rmdir('sandbox/'. $theme);
495 }
496
497 /**
498 * Test updateMethodEscapeMarkdown with markdown plugin enabled
499 * => setting markdown_escape set to false.
500 */
501 public function testEscapeMarkdownSettingToFalse()
502 {
503 $sandboxConf = 'sandbox/config';
504 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
505 $this->conf = new ConfigManager($sandboxConf);
506
507 $this->conf->set('general.enabled_plugins', ['markdown']);
508 $updater = new LegacyUpdater([], [], $this->conf, true);
509 $this->assertTrue($updater->updateMethodEscapeMarkdown());
510 $this->assertFalse($this->conf->get('security.markdown_escape'));
511
512 // reload from file
513 $this->conf = new ConfigManager($sandboxConf);
514 $this->assertFalse($this->conf->get('security.markdown_escape'));
515 }
516
517
518 /**
519 * Test updateMethodEscapeMarkdown with markdown plugin disabled
520 * => setting markdown_escape set to true.
521 */
522 public function testEscapeMarkdownSettingToTrue()
523 {
524 $sandboxConf = 'sandbox/config';
525 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
526 $this->conf = new ConfigManager($sandboxConf);
527
528 $this->conf->set('general.enabled_plugins', []);
529 $updater = new LegacyUpdater([], [], $this->conf, true);
530 $this->assertTrue($updater->updateMethodEscapeMarkdown());
531 $this->assertTrue($this->conf->get('security.markdown_escape'));
532
533 // reload from file
534 $this->conf = new ConfigManager($sandboxConf);
535 $this->assertTrue($this->conf->get('security.markdown_escape'));
536 }
537
538 /**
539 * Test updateMethodEscapeMarkdown with nothing to do (setting already enabled)
540 */
541 public function testEscapeMarkdownSettingNothingToDoEnabled()
542 {
543 $sandboxConf = 'sandbox/config';
544 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
545 $this->conf = new ConfigManager($sandboxConf);
546 $this->conf->set('security.markdown_escape', true);
547 $updater = new LegacyUpdater([], [], $this->conf, true);
548 $this->assertTrue($updater->updateMethodEscapeMarkdown());
549 $this->assertTrue($this->conf->get('security.markdown_escape'));
550 }
551
552 /**
553 * Test updateMethodEscapeMarkdown with nothing to do (setting already disabled)
554 */
555 public function testEscapeMarkdownSettingNothingToDoDisabled()
556 {
557 $this->conf->set('security.markdown_escape', false);
558 $updater = new LegacyUpdater([], [], $this->conf, true);
559 $this->assertTrue($updater->updateMethodEscapeMarkdown());
560 $this->assertFalse($this->conf->get('security.markdown_escape'));
561 }
562
563 /**
564 * Test updateMethodPiwikUrl with valid data
565 */
566 public function testUpdatePiwikUrlValid()
567 {
568 $sandboxConf = 'sandbox/config';
569 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
570 $this->conf = new ConfigManager($sandboxConf);
571 $url = 'mypiwik.tld';
572 $this->conf->set('plugins.PIWIK_URL', $url);
573 $updater = new LegacyUpdater([], [], $this->conf, true);
574 $this->assertTrue($updater->updateMethodPiwikUrl());
575 $this->assertEquals('http://'. $url, $this->conf->get('plugins.PIWIK_URL'));
576
577 // reload from file
578 $this->conf = new ConfigManager($sandboxConf);
579 $this->assertEquals('http://'. $url, $this->conf->get('plugins.PIWIK_URL'));
580 }
581
582 /**
583 * Test updateMethodPiwikUrl without setting
584 */
585 public function testUpdatePiwikUrlEmpty()
586 {
587 $updater = new LegacyUpdater([], [], $this->conf, true);
588 $this->assertTrue($updater->updateMethodPiwikUrl());
589 $this->assertEmpty($this->conf->get('plugins.PIWIK_URL'));
590 }
591
592 /**
593 * Test updateMethodPiwikUrl: valid URL, nothing to do
594 */
595 public function testUpdatePiwikUrlNothingToDo()
596 {
597 $url = 'https://mypiwik.tld';
598 $this->conf->set('plugins.PIWIK_URL', $url);
599 $updater = new LegacyUpdater([], [], $this->conf, true);
600 $this->assertTrue($updater->updateMethodPiwikUrl());
601 $this->assertEquals($url, $this->conf->get('plugins.PIWIK_URL'));
602 }
603
604 /**
605 * Test updateMethodAtomDefault with show_atom set to false
606 * => update to true.
607 */
608 public function testUpdateMethodAtomDefault()
609 {
610 $sandboxConf = 'sandbox/config';
611 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
612 $this->conf = new ConfigManager($sandboxConf);
613 $this->conf->set('feed.show_atom', false);
614 $updater = new LegacyUpdater([], [], $this->conf, true);
615 $this->assertTrue($updater->updateMethodAtomDefault());
616 $this->assertTrue($this->conf->get('feed.show_atom'));
617 // reload from file
618 $this->conf = new ConfigManager($sandboxConf);
619 $this->assertTrue($this->conf->get('feed.show_atom'));
620 }
621 /**
622 * Test updateMethodAtomDefault with show_atom not set.
623 * => nothing to do
624 */
625 public function testUpdateMethodAtomDefaultNoExist()
626 {
627 $sandboxConf = 'sandbox/config';
628 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
629 $this->conf = new ConfigManager($sandboxConf);
630 $updater = new LegacyUpdater([], [], $this->conf, true);
631 $this->assertTrue($updater->updateMethodAtomDefault());
632 $this->assertTrue($this->conf->get('feed.show_atom'));
633 }
634 /**
635 * Test updateMethodAtomDefault with show_atom set to true.
636 * => nothing to do
637 */
638 public function testUpdateMethodAtomDefaultAlreadyTrue()
639 {
640 $sandboxConf = 'sandbox/config';
641 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
642 $this->conf = new ConfigManager($sandboxConf);
643 $this->conf->set('feed.show_atom', true);
644 $updater = new LegacyUpdater([], [], $this->conf, true);
645 $this->assertTrue($updater->updateMethodAtomDefault());
646 $this->assertTrue($this->conf->get('feed.show_atom'));
647 }
648
649 /**
650 * Test updateMethodDownloadSizeAndTimeoutConf, it should be set if none is already defined.
651 */
652 public function testUpdateMethodDownloadSizeAndTimeoutConf()
653 {
654 $sandboxConf = 'sandbox/config';
655 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
656 $this->conf = new ConfigManager($sandboxConf);
657 $updater = new LegacyUpdater([], [], $this->conf, true);
658 $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
659 $this->assertEquals(4194304, $this->conf->get('general.download_max_size'));
660 $this->assertEquals(30, $this->conf->get('general.download_timeout'));
661
662 $this->conf = new ConfigManager($sandboxConf);
663 $this->assertEquals(4194304, $this->conf->get('general.download_max_size'));
664 $this->assertEquals(30, $this->conf->get('general.download_timeout'));
665 }
666
667 /**
668 * Test updateMethodDownloadSizeAndTimeoutConf, it shouldn't be set if it is already defined.
669 */
670 public function testUpdateMethodDownloadSizeAndTimeoutConfIgnore()
671 {
672 $sandboxConf = 'sandbox/config';
673 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
674 $this->conf = new ConfigManager($sandboxConf);
675 $this->conf->set('general.download_max_size', 38);
676 $this->conf->set('general.download_timeout', 70);
677 $updater = new LegacyUpdater([], [], $this->conf, true);
678 $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
679 $this->assertEquals(38, $this->conf->get('general.download_max_size'));
680 $this->assertEquals(70, $this->conf->get('general.download_timeout'));
681 }
682
683 /**
684 * Test updateMethodDownloadSizeAndTimeoutConf, only the maz size should be set here.
685 */
686 public function testUpdateMethodDownloadSizeAndTimeoutConfOnlySize()
687 {
688 $sandboxConf = 'sandbox/config';
689 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
690 $this->conf = new ConfigManager($sandboxConf);
691 $this->conf->set('general.download_max_size', 38);
692 $updater = new LegacyUpdater([], [], $this->conf, true);
693 $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
694 $this->assertEquals(38, $this->conf->get('general.download_max_size'));
695 $this->assertEquals(30, $this->conf->get('general.download_timeout'));
696 }
697
698 /**
699 * Test updateMethodDownloadSizeAndTimeoutConf, only the time out should be set here.
700 */
701 public function testUpdateMethodDownloadSizeAndTimeoutConfOnlyTimeout()
702 {
703 $sandboxConf = 'sandbox/config';
704 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
705 $this->conf = new ConfigManager($sandboxConf);
706 $this->conf->set('general.download_timeout', 3);
707 $updater = new LegacyUpdater([], [], $this->conf, true);
708 $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
709 $this->assertEquals(4194304, $this->conf->get('general.download_max_size'));
710 $this->assertEquals(3, $this->conf->get('general.download_timeout'));
711 }
712
713 /**
714 * Test updateMethodWebThumbnailer with thumbnails enabled.
715 */
716 public function testUpdateMethodWebThumbnailerEnabled()
717 {
718 $this->conf->remove('thumbnails');
719 $this->conf->set('thumbnail.enable_thumbnails', true);
720 $updater = new LegacyUpdater([], [], $this->conf, true, $_SESSION);
721 $this->assertTrue($updater->updateMethodWebThumbnailer());
722 $this->assertFalse($this->conf->exists('thumbnail'));
723 $this->assertEquals(\Shaarli\Thumbnailer::MODE_ALL, $this->conf->get('thumbnails.mode'));
724 $this->assertEquals(125, $this->conf->get('thumbnails.width'));
725 $this->assertEquals(90, $this->conf->get('thumbnails.height'));
726 $this->assertContainsPolyfill('You have enabled or changed thumbnails', $_SESSION['warnings'][0]);
727 }
728
729 /**
730 * Test updateMethodWebThumbnailer with thumbnails disabled.
731 */
732 public function testUpdateMethodWebThumbnailerDisabled()
733 {
734 if (isset($_SESSION['warnings'])) {
735 unset($_SESSION['warnings']);
736 }
737
738 $this->conf->remove('thumbnails');
739 $this->conf->set('thumbnail.enable_thumbnails', false);
740 $updater = new LegacyUpdater([], [], $this->conf, true, $_SESSION);
741 $this->assertTrue($updater->updateMethodWebThumbnailer());
742 $this->assertFalse($this->conf->exists('thumbnail'));
743 $this->assertEquals(Thumbnailer::MODE_NONE, $this->conf->get('thumbnails.mode'));
744 $this->assertEquals(125, $this->conf->get('thumbnails.width'));
745 $this->assertEquals(90, $this->conf->get('thumbnails.height'));
746 $this->assertTrue(empty($_SESSION['warnings']));
747 }
748
749 /**
750 * Test updateMethodWebThumbnailer with thumbnails disabled.
751 */
752 public function testUpdateMethodWebThumbnailerNothingToDo()
753 {
754 if (isset($_SESSION['warnings'])) {
755 unset($_SESSION['warnings']);
756 }
757
758 $updater = new LegacyUpdater([], [], $this->conf, true, $_SESSION);
759 $this->assertTrue($updater->updateMethodWebThumbnailer());
760 $this->assertFalse($this->conf->exists('thumbnail'));
761 $this->assertEquals(Thumbnailer::MODE_COMMON, $this->conf->get('thumbnails.mode'));
762 $this->assertEquals(90, $this->conf->get('thumbnails.width'));
763 $this->assertEquals(53, $this->conf->get('thumbnails.height'));
764 $this->assertTrue(empty($_SESSION['warnings']));
765 }
766
767 /**
768 * Test updateMethodSetSticky().
769 */
770 public function testUpdateStickyValid()
771 {
772 $blank = [
773 'id' => 1,
774 'url' => 'z',
775 'title' => '',
776 'description' => '',
777 'tags' => '',
778 'created' => new DateTime(),
779 ];
780 $links = [
781 1 => ['id' => 1] + $blank,
782 2 => ['id' => 2] + $blank,
783 ];
784 $refDB = new \ReferenceLinkDB(true);
785 $refDB->setLinks($links);
786 $refDB->write(self::$testDatastore);
787 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
788
789 $updater = new LegacyUpdater(array(), $linkDB, $this->conf, true);
790 $this->assertTrue($updater->updateMethodSetSticky());
791
792 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
793 foreach ($linkDB as $link) {
794 $this->assertFalse($link['sticky']);
795 }
796 }
797
798 /**
799 * Test updateMethodSetSticky().
800 */
801 public function testUpdateStickyNothingToDo()
802 {
803 $blank = [
804 'id' => 1,
805 'url' => 'z',
806 'title' => '',
807 'description' => '',
808 'tags' => '',
809 'created' => new DateTime(),
810 ];
811 $links = [
812 1 => ['id' => 1, 'sticky' => true] + $blank,
813 2 => ['id' => 2] + $blank,
814 ];
815 $refDB = new \ReferenceLinkDB(true);
816 $refDB->setLinks($links);
817 $refDB->write(self::$testDatastore);
818 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
819
820 $updater = new LegacyUpdater(array(), $linkDB, $this->conf, true);
821 $this->assertTrue($updater->updateMethodSetSticky());
822
823 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
824 $this->assertTrue($linkDB[1]['sticky']);
825 }
826
827 /**
828 * Test updateMethodRemoveRedirector().
829 */
830 public function testUpdateRemoveRedirector()
831 {
832 $sandboxConf = 'sandbox/config';
833 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
834 $this->conf = new ConfigManager($sandboxConf);
835 $updater = new LegacyUpdater([], null, $this->conf, true);
836 $this->assertTrue($updater->updateMethodRemoveRedirector());
837 $this->assertFalse($this->conf->exists('redirector'));
838 $this->conf = new ConfigManager($sandboxConf);
839 $this->assertFalse($this->conf->exists('redirector'));
840 }
841
842 /**
843 * Test updateMethodFormatterSetting()
844 */
845 public function testUpdateMethodFormatterSettingDefault()
846 {
847 $sandboxConf = 'sandbox/config';
848 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
849 $this->conf = new ConfigManager($sandboxConf);
850 $this->conf->set('formatter', 'default');
851 $updater = new LegacyUpdater([], null, $this->conf, true);
852 $enabledPlugins = $this->conf->get('general.enabled_plugins');
853 $this->assertFalse(in_array('markdown', $enabledPlugins));
854 $this->assertTrue($updater->updateMethodFormatterSetting());
855 $this->assertEquals('default', $this->conf->get('formatter'));
856 $this->assertEquals($enabledPlugins, $this->conf->get('general.enabled_plugins'));
857
858 $this->conf = new ConfigManager($sandboxConf);
859 $this->assertEquals('default', $this->conf->get('formatter'));
860 $this->assertEquals($enabledPlugins, $this->conf->get('general.enabled_plugins'));
861 }
862
863 /**
864 * Test updateMethodFormatterSetting()
865 */
866 public function testUpdateMethodFormatterSettingMarkdown()
867 {
868 $sandboxConf = 'sandbox/config';
869 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
870 $this->conf = new ConfigManager($sandboxConf);
871 $this->conf->set('formatter', 'default');
872 $updater = new LegacyUpdater([], null, $this->conf, true);
873 $enabledPlugins = $this->conf->get('general.enabled_plugins');
874 $enabledPlugins[] = 'markdown';
875 $this->conf->set('general.enabled_plugins', $enabledPlugins);
876
877 $this->assertTrue(in_array('markdown', $this->conf->get('general.enabled_plugins')));
878 $this->assertTrue($updater->updateMethodFormatterSetting());
879 $this->assertEquals('markdown', $this->conf->get('formatter'));
880 $this->assertFalse(in_array('markdown', $this->conf->get('general.enabled_plugins')));
881
882 $this->conf = new ConfigManager($sandboxConf);
883 $this->assertEquals('markdown', $this->conf->get('formatter'));
884 $this->assertFalse(in_array('markdown', $this->conf->get('general.enabled_plugins')));
885 }
886}
diff --git a/tests/netscape/BookmarkExportTest.php b/tests/netscape/BookmarkExportTest.php
index 6de9876d..9b95ccc9 100644
--- a/tests/netscape/BookmarkExportTest.php
+++ b/tests/netscape/BookmarkExportTest.php
@@ -1,14 +1,20 @@
1<?php 1<?php
2
2namespace Shaarli\Netscape; 3namespace Shaarli\Netscape;
3 4
4use Shaarli\Bookmark\LinkDB; 5use Shaarli\Bookmark\BookmarkFileService;
6use Shaarli\Config\ConfigManager;
7use Shaarli\Formatter\BookmarkFormatter;
8use Shaarli\Formatter\FormatterFactory;
9use Shaarli\History;
10use Shaarli\TestCase;
5 11
6require_once 'tests/utils/ReferenceLinkDB.php'; 12require_once 'tests/utils/ReferenceLinkDB.php';
7 13
8/** 14/**
9 * Netscape bookmark export 15 * Netscape bookmark export
10 */ 16 */
11class BookmarkExportTest extends \PHPUnit\Framework\TestCase 17class BookmarkExportTest extends TestCase
12{ 18{
13 /** 19 /**
14 * @var string datastore to test write operations 20 * @var string datastore to test write operations
@@ -16,41 +22,86 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
16 protected static $testDatastore = 'sandbox/datastore.php'; 22 protected static $testDatastore = 'sandbox/datastore.php';
17 23
18 /** 24 /**
25 * @var ConfigManager instance.
26 */
27 protected static $conf;
28
29 /**
19 * @var \ReferenceLinkDB instance. 30 * @var \ReferenceLinkDB instance.
20 */ 31 */
21 protected static $refDb = null; 32 protected static $refDb = null;
22 33
23 /** 34 /**
24 * @var LinkDB private LinkDB instance. 35 * @var BookmarkFileService private instance.
36 */
37 protected static $bookmarkService = null;
38
39 /**
40 * @var BookmarkFormatter instance
41 */
42 protected static $formatter;
43
44 /**
45 * @var History instance
46 */
47 protected static $history;
48
49 /**
50 * @var NetscapeBookmarkUtils
25 */ 51 */
26 protected static $linkDb = null; 52 protected $netscapeBookmarkUtils;
27 53
28 /** 54 /**
29 * Instantiate reference data 55 * Instantiate reference data
30 */ 56 */
31 public static function setUpBeforeClass() 57 public static function setUpBeforeClass(): void
32 { 58 {
33 self::$refDb = new \ReferenceLinkDB(); 59 static::$conf = new ConfigManager('tests/utils/config/configJson');
34 self::$refDb->write(self::$testDatastore); 60 static::$conf->set('resource.datastore', static::$testDatastore);
35 self::$linkDb = new LinkDB(self::$testDatastore, true, false); 61 static::$refDb = new \ReferenceLinkDB();
62 static::$refDb->write(static::$testDatastore);
63 static::$history = new History('sandbox/history.php');
64 static::$bookmarkService = new BookmarkFileService(static::$conf, static::$history, true);
65 $factory = new FormatterFactory(static::$conf, true);
66 static::$formatter = $factory->getFormatter('raw');
67 }
68
69 public function setUp(): void
70 {
71 $this->netscapeBookmarkUtils = new NetscapeBookmarkUtils(
72 static::$bookmarkService,
73 static::$conf,
74 static::$history
75 );
36 } 76 }
37 77
38 /** 78 /**
39 * Attempt to export an invalid link selection 79 * Attempt to export an invalid link selection
40 * @expectedException Exception
41 * @expectedExceptionMessageRegExp /Invalid export selection/
42 */ 80 */
43 public function testFilterAndFormatInvalid() 81 public function testFilterAndFormatInvalid()
44 { 82 {
45 NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'derp', false, ''); 83 $this->expectException(\Exception::class);
84 $this->expectExceptionMessageRegExp('/Invalid export selection/');
85
86 $this->netscapeBookmarkUtils->filterAndFormat(
87 self::$formatter,
88 'derp',
89 false,
90 ''
91 );
46 } 92 }
47 93
48 /** 94 /**
49 * Prepare all links for export 95 * Prepare all bookmarks for export
50 */ 96 */
51 public function testFilterAndFormatAll() 97 public function testFilterAndFormatAll()
52 { 98 {
53 $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'all', false, ''); 99 $links = $this->netscapeBookmarkUtils->filterAndFormat(
100 self::$formatter,
101 'all',
102 false,
103 ''
104 );
54 $this->assertEquals(self::$refDb->countLinks(), sizeof($links)); 105 $this->assertEquals(self::$refDb->countLinks(), sizeof($links));
55 foreach ($links as $link) { 106 foreach ($links as $link) {
56 $date = $link['created']; 107 $date = $link['created'];
@@ -66,11 +117,16 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
66 } 117 }
67 118
68 /** 119 /**
69 * Prepare private links for export 120 * Prepare private bookmarks for export
70 */ 121 */
71 public function testFilterAndFormatPrivate() 122 public function testFilterAndFormatPrivate()
72 { 123 {
73 $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'private', false, ''); 124 $links = $this->netscapeBookmarkUtils->filterAndFormat(
125 self::$formatter,
126 'private',
127 false,
128 ''
129 );
74 $this->assertEquals(self::$refDb->countPrivateLinks(), sizeof($links)); 130 $this->assertEquals(self::$refDb->countPrivateLinks(), sizeof($links));
75 foreach ($links as $link) { 131 foreach ($links as $link) {
76 $date = $link['created']; 132 $date = $link['created'];
@@ -86,11 +142,16 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
86 } 142 }
87 143
88 /** 144 /**
89 * Prepare public links for export 145 * Prepare public bookmarks for export
90 */ 146 */
91 public function testFilterAndFormatPublic() 147 public function testFilterAndFormatPublic()
92 { 148 {
93 $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public', false, ''); 149 $links = $this->netscapeBookmarkUtils->filterAndFormat(
150 self::$formatter,
151 'public',
152 false,
153 ''
154 );
94 $this->assertEquals(self::$refDb->countPublicLinks(), sizeof($links)); 155 $this->assertEquals(self::$refDb->countPublicLinks(), sizeof($links));
95 foreach ($links as $link) { 156 foreach ($links as $link) {
96 $date = $link['created']; 157 $date = $link['created'];
@@ -110,9 +171,14 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
110 */ 171 */
111 public function testFilterAndFormatDoNotPrependNoteUrl() 172 public function testFilterAndFormatDoNotPrependNoteUrl()
112 { 173 {
113 $links = NetscapeBookmarkUtils::filterAndFormat(self::$linkDb, 'public', false, ''); 174 $links = $this->netscapeBookmarkUtils->filterAndFormat(
175 self::$formatter,
176 'public',
177 false,
178 ''
179 );
114 $this->assertEquals( 180 $this->assertEquals(
115 '?WDWyig', 181 '/shaare/WDWyig',
116 $links[2]['url'] 182 $links[2]['url']
117 ); 183 );
118 } 184 }
@@ -123,14 +189,14 @@ class BookmarkExportTest extends \PHPUnit\Framework\TestCase
123 public function testFilterAndFormatPrependNoteUrl() 189 public function testFilterAndFormatPrependNoteUrl()
124 { 190 {
125 $indexUrl = 'http://localhost:7469/shaarli/'; 191 $indexUrl = 'http://localhost:7469/shaarli/';
126 $links = NetscapeBookmarkUtils::filterAndFormat( 192 $links = $this->netscapeBookmarkUtils->filterAndFormat(
127 self::$linkDb, 193 self::$formatter,
128 'public', 194 'public',
129 true, 195 true,
130 $indexUrl 196 $indexUrl
131 ); 197 );
132 $this->assertEquals( 198 $this->assertEquals(
133 $indexUrl . '?WDWyig', 199 $indexUrl . 'shaare/WDWyig',
134 $links[2]['url'] 200 $links[2]['url']
135 ); 201 );
136 } 202 }
diff --git a/tests/netscape/BookmarkImportTest.php b/tests/netscape/BookmarkImportTest.php
index ccafc161..c1e49b5f 100644
--- a/tests/netscape/BookmarkImportTest.php
+++ b/tests/netscape/BookmarkImportTest.php
@@ -1,26 +1,31 @@
1<?php 1<?php
2
2namespace Shaarli\Netscape; 3namespace Shaarli\Netscape;
3 4
4use DateTime; 5use DateTime;
5use Shaarli\Bookmark\LinkDB; 6use Psr\Http\Message\UploadedFileInterface;
7use Shaarli\Bookmark\Bookmark;
8use Shaarli\Bookmark\BookmarkFileService;
9use Shaarli\Bookmark\BookmarkFilter;
6use Shaarli\Config\ConfigManager; 10use Shaarli\Config\ConfigManager;
7use Shaarli\History; 11use Shaarli\History;
12use Shaarli\TestCase;
13use Slim\Http\UploadedFile;
8 14
9/** 15/**
10 * Utility function to load a file's metadata in a $_FILES-like array 16 * Utility function to load a file's metadata in a $_FILES-like array
11 * 17 *
12 * @param string $filename Basename of the file 18 * @param string $filename Basename of the file
13 * 19 *
14 * @return array A $_FILES-like array 20 * @return UploadedFileInterface Upload file in PSR-7 compatible object
15 */ 21 */
16function file2array($filename) 22function file2array($filename)
17{ 23{
18 return array( 24 return new UploadedFile(
19 'filetoupload' => array( 25 __DIR__ . '/input/' . $filename,
20 'name' => $filename, 26 $filename,
21 'tmp_name' => __DIR__ . '/input/' . $filename, 27 null,
22 'size' => filesize(__DIR__ . '/input/' . $filename) 28 filesize(__DIR__ . '/input/' . $filename)
23 )
24 ); 29 );
25} 30}
26 31
@@ -28,7 +33,7 @@ function file2array($filename)
28/** 33/**
29 * Netscape bookmark import 34 * Netscape bookmark import
30 */ 35 */
31class BookmarkImportTest extends \PHPUnit\Framework\TestCase 36class BookmarkImportTest extends TestCase
32{ 37{
33 /** 38 /**
34 * @var string datastore to test write operations 39 * @var string datastore to test write operations
@@ -41,9 +46,9 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
41 protected static $historyFilePath = 'sandbox/history.php'; 46 protected static $historyFilePath = 'sandbox/history.php';
42 47
43 /** 48 /**
44 * @var LinkDB private LinkDB instance 49 * @var BookmarkFileService private LinkDB instance
45 */ 50 */
46 protected $linkDb = null; 51 protected $bookmarkService = null;
47 52
48 /** 53 /**
49 * @var string Dummy page cache 54 * @var string Dummy page cache
@@ -61,11 +66,16 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
61 protected $history; 66 protected $history;
62 67
63 /** 68 /**
69 * @var NetscapeBookmarkUtils
70 */
71 protected $netscapeBookmarkUtils;
72
73 /**
64 * @var string Save the current timezone. 74 * @var string Save the current timezone.
65 */ 75 */
66 protected static $defaultTimeZone; 76 protected static $defaultTimeZone;
67 77
68 public static function setUpBeforeClass() 78 public static function setUpBeforeClass(): void
69 { 79 {
70 self::$defaultTimeZone = date_default_timezone_get(); 80 self::$defaultTimeZone = date_default_timezone_get();
71 // Timezone without DST for test consistency 81 // Timezone without DST for test consistency
@@ -75,28 +85,31 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
75 /** 85 /**
76 * Resets test data before each test 86 * Resets test data before each test
77 */ 87 */
78 protected function setUp() 88 protected function setUp(): void
79 { 89 {
80 if (file_exists(self::$testDatastore)) { 90 if (file_exists(self::$testDatastore)) {
81 unlink(self::$testDatastore); 91 unlink(self::$testDatastore);
82 } 92 }
83 // start with an empty datastore 93 // start with an empty datastore
84 file_put_contents(self::$testDatastore, '<?php /* S7QysKquBQA= */ ?>'); 94 file_put_contents(self::$testDatastore, '<?php /* S7QysKquBQA= */ ?>');
85 $this->linkDb = new LinkDB(self::$testDatastore, true, false); 95
86 $this->conf = new ConfigManager('tests/utils/config/configJson'); 96 $this->conf = new ConfigManager('tests/utils/config/configJson');
87 $this->conf->set('resource.page_cache', $this->pagecache); 97 $this->conf->set('resource.page_cache', $this->pagecache);
98 $this->conf->set('resource.datastore', self::$testDatastore);
88 $this->history = new History(self::$historyFilePath); 99 $this->history = new History(self::$historyFilePath);
100 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true);
101 $this->netscapeBookmarkUtils = new NetscapeBookmarkUtils($this->bookmarkService, $this->conf, $this->history);
89 } 102 }
90 103
91 /** 104 /**
92 * Delete history file. 105 * Delete history file.
93 */ 106 */
94 public function tearDown() 107 protected function tearDown(): void
95 { 108 {
96 @unlink(self::$historyFilePath); 109 @unlink(self::$historyFilePath);
97 } 110 }
98 111
99 public static function tearDownAfterClass() 112 public static function tearDownAfterClass(): void
100 { 113 {
101 date_default_timezone_set(self::$defaultTimeZone); 114 date_default_timezone_set(self::$defaultTimeZone);
102 } 115 }
@@ -110,9 +123,9 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
110 $this->assertEquals( 123 $this->assertEquals(
111 'File empty.htm (0 bytes) has an unknown file format.' 124 'File empty.htm (0 bytes) has an unknown file format.'
112 .' Nothing was imported.', 125 .' Nothing was imported.',
113 NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history) 126 $this->netscapeBookmarkUtils->import(null, $files)
114 ); 127 );
115 $this->assertEquals(0, count($this->linkDb)); 128 $this->assertEquals(0, $this->bookmarkService->count());
116 } 129 }
117 130
118 /** 131 /**
@@ -123,9 +136,9 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
123 $files = file2array('no_doctype.htm'); 136 $files = file2array('no_doctype.htm');
124 $this->assertEquals( 137 $this->assertEquals(
125 'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.', 138 'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.',
126 NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history) 139 $this->netscapeBookmarkUtils->import(null, $files)
127 ); 140 );
128 $this->assertEquals(0, count($this->linkDb)); 141 $this->assertEquals(0, $this->bookmarkService->count());
129 } 142 }
130 143
131 /** 144 /**
@@ -136,10 +149,10 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
136 $files = file2array('lowercase_doctype.htm'); 149 $files = file2array('lowercase_doctype.htm');
137 $this->assertStringMatchesFormat( 150 $this->assertStringMatchesFormat(
138 'File lowercase_doctype.htm (386 bytes) was successfully processed in %d seconds:' 151 'File lowercase_doctype.htm (386 bytes) was successfully processed in %d seconds:'
139 .' 2 links imported, 0 links overwritten, 0 links skipped.', 152 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
140 NetscapeBookmarkUtils::import(null, $files, $this->linkDb, $this->conf, $this->history) 153 $this->netscapeBookmarkUtils->import(null, $files)
141 ); 154 );
142 $this->assertEquals(2, count($this->linkDb)); 155 $this->assertEquals(2, $this->bookmarkService->count());
143 } 156 }
144 157
145 158
@@ -151,25 +164,24 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
151 $files = file2array('internet_explorer_encoding.htm'); 164 $files = file2array('internet_explorer_encoding.htm');
152 $this->assertStringMatchesFormat( 165 $this->assertStringMatchesFormat(
153 'File internet_explorer_encoding.htm (356 bytes) was successfully processed in %d seconds:' 166 'File internet_explorer_encoding.htm (356 bytes) was successfully processed in %d seconds:'
154 .' 1 links imported, 0 links overwritten, 0 links skipped.', 167 .' 1 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
155 NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history) 168 $this->netscapeBookmarkUtils->import([], $files)
156 ); 169 );
157 $this->assertEquals(1, count($this->linkDb)); 170 $this->assertEquals(1, $this->bookmarkService->count());
158 $this->assertEquals(0, count_private($this->linkDb)); 171 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
159 172
173 $bookmark = $this->bookmarkService->findByUrl('http://hginit.com/');
174 $this->assertEquals(0, $bookmark->getId());
160 $this->assertEquals( 175 $this->assertEquals(
161 array( 176 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160618_203944'),
162 'id' => 0, 177 $bookmark->getCreated()
163 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160618_203944'),
164 'title' => 'Hg Init a Mercurial tutorial by Joel Spolsky',
165 'url' => 'http://hginit.com/',
166 'description' => '',
167 'private' => 0,
168 'tags' => '',
169 'shorturl' => 'La37cg',
170 ),
171 $this->linkDb->getLinkFromUrl('http://hginit.com/')
172 ); 178 );
179 $this->assertEquals('Hg Init a Mercurial tutorial by Joel Spolsky', $bookmark->getTitle());
180 $this->assertEquals('http://hginit.com/', $bookmark->getUrl());
181 $this->assertEquals('', $bookmark->getDescription());
182 $this->assertFalse($bookmark->isPrivate());
183 $this->assertEquals('', $bookmark->getTagsString());
184 $this->assertEquals('La37cg', $bookmark->getShortUrl());
173 } 185 }
174 186
175 /** 187 /**
@@ -180,116 +192,115 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
180 $files = file2array('netscape_nested.htm'); 192 $files = file2array('netscape_nested.htm');
181 $this->assertStringMatchesFormat( 193 $this->assertStringMatchesFormat(
182 'File netscape_nested.htm (1337 bytes) was successfully processed in %d seconds:' 194 'File netscape_nested.htm (1337 bytes) was successfully processed in %d seconds:'
183 .' 8 links imported, 0 links overwritten, 0 links skipped.', 195 .' 8 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
184 NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history) 196 $this->netscapeBookmarkUtils->import([], $files)
185 );
186 $this->assertEquals(8, count($this->linkDb));
187 $this->assertEquals(2, count_private($this->linkDb));
188
189 $this->assertEquals(
190 array(
191 'id' => 0,
192 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235541'),
193 'title' => 'Nested 1',
194 'url' => 'http://nest.ed/1',
195 'description' => '',
196 'private' => 0,
197 'tags' => 'tag1 tag2',
198 'shorturl' => 'KyDNKA',
199 ),
200 $this->linkDb->getLinkFromUrl('http://nest.ed/1')
201 );
202 $this->assertEquals(
203 array(
204 'id' => 1,
205 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235542'),
206 'title' => 'Nested 1-1',
207 'url' => 'http://nest.ed/1-1',
208 'description' => '',
209 'private' => 0,
210 'tags' => 'folder1 tag1 tag2',
211 'shorturl' => 'T2LnXg',
212 ),
213 $this->linkDb->getLinkFromUrl('http://nest.ed/1-1')
214 );
215 $this->assertEquals(
216 array(
217 'id' => 2,
218 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235547'),
219 'title' => 'Nested 1-2',
220 'url' => 'http://nest.ed/1-2',
221 'description' => '',
222 'private' => 0,
223 'tags' => 'folder1 tag3 tag4',
224 'shorturl' => '46SZxA',
225 ),
226 $this->linkDb->getLinkFromUrl('http://nest.ed/1-2')
227 );
228 $this->assertEquals(
229 array(
230 'id' => 3,
231 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'),
232 'title' => 'Nested 2-1',
233 'url' => 'http://nest.ed/2-1',
234 'description' => 'First link of the second section',
235 'private' => 1,
236 'tags' => 'folder2',
237 'shorturl' => '4UHOSw',
238 ),
239 $this->linkDb->getLinkFromUrl('http://nest.ed/2-1')
240 );
241 $this->assertEquals(
242 array(
243 'id' => 4,
244 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'),
245 'title' => 'Nested 2-2',
246 'url' => 'http://nest.ed/2-2',
247 'description' => 'Second link of the second section',
248 'private' => 1,
249 'tags' => 'folder2',
250 'shorturl' => 'yfzwbw',
251 ),
252 $this->linkDb->getLinkFromUrl('http://nest.ed/2-2')
253 );
254 $this->assertEquals(
255 array(
256 'id' => 5,
257 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'),
258 'title' => 'Nested 3-1',
259 'url' => 'http://nest.ed/3-1',
260 'description' => '',
261 'private' => 0,
262 'tags' => 'folder3 folder3-1 tag3',
263 'shorturl' => 'UwxIUQ',
264 ),
265 $this->linkDb->getLinkFromUrl('http://nest.ed/3-1')
266 );
267 $this->assertEquals(
268 array(
269 'id' => 6,
270 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'),
271 'title' => 'Nested 3-2',
272 'url' => 'http://nest.ed/3-2',
273 'description' => '',
274 'private' => 0,
275 'tags' => 'folder3 folder3-1',
276 'shorturl' => 'p8dyZg',
277 ),
278 $this->linkDb->getLinkFromUrl('http://nest.ed/3-2')
279 );
280 $this->assertEquals(
281 array(
282 'id' => 7,
283 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160229_111541'),
284 'title' => 'Nested 2',
285 'url' => 'http://nest.ed/2',
286 'description' => '',
287 'private' => 0,
288 'tags' => 'tag4',
289 'shorturl' => 'Gt3Uug',
290 ),
291 $this->linkDb->getLinkFromUrl('http://nest.ed/2')
292 ); 197 );
198 $this->assertEquals(8, $this->bookmarkService->count());
199 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
200
201 $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/1');
202 $this->assertEquals(0, $bookmark->getId());
203 $this->assertEquals(
204 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235541'),
205 $bookmark->getCreated()
206 );
207 $this->assertEquals('Nested 1', $bookmark->getTitle());
208 $this->assertEquals('http://nest.ed/1', $bookmark->getUrl());
209 $this->assertEquals('', $bookmark->getDescription());
210 $this->assertFalse($bookmark->isPrivate());
211 $this->assertEquals('tag1 tag2', $bookmark->getTagsString());
212 $this->assertEquals('KyDNKA', $bookmark->getShortUrl());
213
214 $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/1-1');
215 $this->assertEquals(1, $bookmark->getId());
216 $this->assertEquals(
217 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235542'),
218 $bookmark->getCreated()
219 );
220 $this->assertEquals('Nested 1-1', $bookmark->getTitle());
221 $this->assertEquals('http://nest.ed/1-1', $bookmark->getUrl());
222 $this->assertEquals('', $bookmark->getDescription());
223 $this->assertFalse($bookmark->isPrivate());
224 $this->assertEquals('folder1 tag1 tag2', $bookmark->getTagsString());
225 $this->assertEquals('T2LnXg', $bookmark->getShortUrl());
226
227 $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/1-2');
228 $this->assertEquals(2, $bookmark->getId());
229 $this->assertEquals(
230 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235547'),
231 $bookmark->getCreated()
232 );
233 $this->assertEquals('Nested 1-2', $bookmark->getTitle());
234 $this->assertEquals('http://nest.ed/1-2', $bookmark->getUrl());
235 $this->assertEquals('', $bookmark->getDescription());
236 $this->assertFalse($bookmark->isPrivate());
237 $this->assertEquals('folder1 tag3 tag4', $bookmark->getTagsString());
238 $this->assertEquals('46SZxA', $bookmark->getShortUrl());
239
240 $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/2-1');
241 $this->assertEquals(3, $bookmark->getId());
242 $this->assertEquals(
243 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160202_202222'),
244 $bookmark->getCreated()
245 );
246 $this->assertEquals('Nested 2-1', $bookmark->getTitle());
247 $this->assertEquals('http://nest.ed/2-1', $bookmark->getUrl());
248 $this->assertEquals('First link of the second section', $bookmark->getDescription());
249 $this->assertTrue($bookmark->isPrivate());
250 $this->assertEquals('folder2', $bookmark->getTagsString());
251 $this->assertEquals('4UHOSw', $bookmark->getShortUrl());
252
253 $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/2-2');
254 $this->assertEquals(4, $bookmark->getId());
255 $this->assertEquals(
256 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160119_230227'),
257 $bookmark->getCreated()
258 );
259 $this->assertEquals('Nested 2-2', $bookmark->getTitle());
260 $this->assertEquals('http://nest.ed/2-2', $bookmark->getUrl());
261 $this->assertEquals('Second link of the second section', $bookmark->getDescription());
262 $this->assertTrue($bookmark->isPrivate());
263 $this->assertEquals('folder2', $bookmark->getTagsString());
264 $this->assertEquals('yfzwbw', $bookmark->getShortUrl());
265
266 $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/3-1');
267 $this->assertEquals(5, $bookmark->getId());
268 $this->assertEquals(
269 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160202_202222'),
270 $bookmark->getCreated()
271 );
272 $this->assertEquals('Nested 3-1', $bookmark->getTitle());
273 $this->assertEquals('http://nest.ed/3-1', $bookmark->getUrl());
274 $this->assertEquals('', $bookmark->getDescription());
275 $this->assertFalse($bookmark->isPrivate());
276 $this->assertEquals('folder3 folder3-1 tag3', $bookmark->getTagsString());
277 $this->assertEquals('UwxIUQ', $bookmark->getShortUrl());
278
279 $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/3-2');
280 $this->assertEquals(6, $bookmark->getId());
281 $this->assertEquals(
282 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160119_230227'),
283 $bookmark->getCreated()
284 );
285 $this->assertEquals('Nested 3-2', $bookmark->getTitle());
286 $this->assertEquals('http://nest.ed/3-2', $bookmark->getUrl());
287 $this->assertEquals('', $bookmark->getDescription());
288 $this->assertFalse($bookmark->isPrivate());
289 $this->assertEquals('folder3 folder3-1', $bookmark->getTagsString());
290 $this->assertEquals('p8dyZg', $bookmark->getShortUrl());
291
292 $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/2');
293 $this->assertEquals(7, $bookmark->getId());
294 $this->assertEquals(
295 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160229_111541'),
296 $bookmark->getCreated()
297 );
298 $this->assertEquals('Nested 2', $bookmark->getTitle());
299 $this->assertEquals('http://nest.ed/2', $bookmark->getUrl());
300 $this->assertEquals('', $bookmark->getDescription());
301 $this->assertFalse($bookmark->isPrivate());
302 $this->assertEquals('tag4', $bookmark->getTagsString());
303 $this->assertEquals('Gt3Uug', $bookmark->getShortUrl());
293 } 304 }
294 305
295 /** 306 /**
@@ -302,40 +313,38 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
302 $files = file2array('netscape_basic.htm'); 313 $files = file2array('netscape_basic.htm');
303 $this->assertStringMatchesFormat( 314 $this->assertStringMatchesFormat(
304 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 315 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
305 .' 2 links imported, 0 links overwritten, 0 links skipped.', 316 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
306 NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history) 317 $this->netscapeBookmarkUtils->import([], $files)
307 ); 318 );
308 319
309 $this->assertEquals(2, count($this->linkDb)); 320 $this->assertEquals(2, $this->bookmarkService->count());
310 $this->assertEquals(1, count_private($this->linkDb)); 321 $this->assertEquals(1, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
311 322
323 $bookmark = $this->bookmarkService->findByUrl('https://private.tld');
324 $this->assertEquals(0, $bookmark->getId());
312 $this->assertEquals( 325 $this->assertEquals(
313 array( 326 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20001010_135536'),
314 'id' => 0, 327 $bookmark->getCreated()
315 // Old link - UTC+4 (note that TZ in the import file is ignored).
316 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20001010_135536'),
317 'title' => 'Secret stuff',
318 'url' => 'https://private.tld',
319 'description' => "Super-secret stuff you're not supposed to know about",
320 'private' => 1,
321 'tags' => 'private secret',
322 'shorturl' => 'EokDtA',
323 ),
324 $this->linkDb->getLinkFromUrl('https://private.tld')
325 ); 328 );
329 $this->assertEquals('Secret stuff', $bookmark->getTitle());
330 $this->assertEquals('https://private.tld', $bookmark->getUrl());
331 $this->assertEquals('Super-secret stuff you\'re not supposed to know about', $bookmark->getDescription());
332 $this->assertTrue($bookmark->isPrivate());
333 $this->assertEquals('private secret', $bookmark->getTagsString());
334 $this->assertEquals('EokDtA', $bookmark->getShortUrl());
335
336 $bookmark = $this->bookmarkService->findByUrl('http://public.tld');
337 $this->assertEquals(1, $bookmark->getId());
326 $this->assertEquals( 338 $this->assertEquals(
327 array( 339 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235548'),
328 'id' => 1, 340 $bookmark->getCreated()
329 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'),
330 'title' => 'Public stuff',
331 'url' => 'http://public.tld',
332 'description' => '',
333 'private' => 0,
334 'tags' => 'public hello world',
335 'shorturl' => 'Er9ddA',
336 ),
337 $this->linkDb->getLinkFromUrl('http://public.tld')
338 ); 341 );
342 $this->assertEquals('Public stuff', $bookmark->getTitle());
343 $this->assertEquals('http://public.tld', $bookmark->getUrl());
344 $this->assertEquals('', $bookmark->getDescription());
345 $this->assertFalse($bookmark->isPrivate());
346 $this->assertEquals('public hello world', $bookmark->getTagsString());
347 $this->assertEquals('Er9ddA', $bookmark->getShortUrl());
339 } 348 }
340 349
341 /** 350 /**
@@ -347,43 +356,42 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
347 $files = file2array('netscape_basic.htm'); 356 $files = file2array('netscape_basic.htm');
348 $this->assertStringMatchesFormat( 357 $this->assertStringMatchesFormat(
349 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 358 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
350 .' 2 links imported, 0 links overwritten, 0 links skipped.', 359 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
351 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) 360 $this->netscapeBookmarkUtils->import($post, $files)
352 ); 361 );
353 $this->assertEquals(2, count($this->linkDb));
354 $this->assertEquals(1, count_private($this->linkDb));
355 362
363 $this->assertEquals(2, $this->bookmarkService->count());
364 $this->assertEquals(1, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
365
366 $bookmark = $this->bookmarkService->findByUrl('https://private.tld');
367 $this->assertEquals(0, $bookmark->getId());
356 $this->assertEquals( 368 $this->assertEquals(
357 array( 369 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20001010_135536'),
358 'id' => 0, 370 $bookmark->getCreated()
359 // Note that TZ in the import file is ignored.
360 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20001010_135536'),
361 'title' => 'Secret stuff',
362 'url' => 'https://private.tld',
363 'description' => "Super-secret stuff you're not supposed to know about",
364 'private' => 1,
365 'tags' => 'private secret',
366 'shorturl' => 'EokDtA',
367 ),
368 $this->linkDb->getLinkFromUrl('https://private.tld')
369 ); 371 );
372 $this->assertEquals('Secret stuff', $bookmark->getTitle());
373 $this->assertEquals('https://private.tld', $bookmark->getUrl());
374 $this->assertEquals('Super-secret stuff you\'re not supposed to know about', $bookmark->getDescription());
375 $this->assertTrue($bookmark->isPrivate());
376 $this->assertEquals('private secret', $bookmark->getTagsString());
377 $this->assertEquals('EokDtA', $bookmark->getShortUrl());
378
379 $bookmark = $this->bookmarkService->findByUrl('http://public.tld');
380 $this->assertEquals(1, $bookmark->getId());
370 $this->assertEquals( 381 $this->assertEquals(
371 array( 382 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235548'),
372 'id' => 1, 383 $bookmark->getCreated()
373 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'),
374 'title' => 'Public stuff',
375 'url' => 'http://public.tld',
376 'description' => '',
377 'private' => 0,
378 'tags' => 'public hello world',
379 'shorturl' => 'Er9ddA',
380 ),
381 $this->linkDb->getLinkFromUrl('http://public.tld')
382 ); 384 );
385 $this->assertEquals('Public stuff', $bookmark->getTitle());
386 $this->assertEquals('http://public.tld', $bookmark->getUrl());
387 $this->assertEquals('', $bookmark->getDescription());
388 $this->assertFalse($bookmark->isPrivate());
389 $this->assertEquals('public hello world', $bookmark->getTagsString());
390 $this->assertEquals('Er9ddA', $bookmark->getShortUrl());
383 } 391 }
384 392
385 /** 393 /**
386 * Import links as public 394 * Import bookmarks as public
387 */ 395 */
388 public function testImportAsPublic() 396 public function testImportAsPublic()
389 { 397 {
@@ -391,23 +399,17 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
391 $files = file2array('netscape_basic.htm'); 399 $files = file2array('netscape_basic.htm');
392 $this->assertStringMatchesFormat( 400 $this->assertStringMatchesFormat(
393 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 401 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
394 .' 2 links imported, 0 links overwritten, 0 links skipped.', 402 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
395 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) 403 $this->netscapeBookmarkUtils->import($post, $files)
396 );
397 $this->assertEquals(2, count($this->linkDb));
398 $this->assertEquals(0, count_private($this->linkDb));
399 $this->assertEquals(
400 0,
401 $this->linkDb[0]['private']
402 );
403 $this->assertEquals(
404 0,
405 $this->linkDb[1]['private']
406 ); 404 );
405 $this->assertEquals(2, $this->bookmarkService->count());
406 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
407 $this->assertFalse($this->bookmarkService->get(0)->isPrivate());
408 $this->assertFalse($this->bookmarkService->get(1)->isPrivate());
407 } 409 }
408 410
409 /** 411 /**
410 * Import links as private 412 * Import bookmarks as private
411 */ 413 */
412 public function testImportAsPrivate() 414 public function testImportAsPrivate()
413 { 415 {
@@ -415,45 +417,34 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
415 $files = file2array('netscape_basic.htm'); 417 $files = file2array('netscape_basic.htm');
416 $this->assertStringMatchesFormat( 418 $this->assertStringMatchesFormat(
417 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 419 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
418 .' 2 links imported, 0 links overwritten, 0 links skipped.', 420 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
419 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) 421 $this->netscapeBookmarkUtils->import($post, $files)
420 );
421 $this->assertEquals(2, count($this->linkDb));
422 $this->assertEquals(2, count_private($this->linkDb));
423 $this->assertEquals(
424 1,
425 $this->linkDb['0']['private']
426 );
427 $this->assertEquals(
428 1,
429 $this->linkDb['1']['private']
430 ); 422 );
423 $this->assertEquals(2, $this->bookmarkService->count());
424 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
425 $this->assertTrue($this->bookmarkService->get(0)->isPrivate());
426 $this->assertTrue($this->bookmarkService->get(1)->isPrivate());
431 } 427 }
432 428
433 /** 429 /**
434 * Overwrite private links so they become public 430 * Overwrite private bookmarks so they become public
435 */ 431 */
436 public function testOverwriteAsPublic() 432 public function testOverwriteAsPublic()
437 { 433 {
438 $files = file2array('netscape_basic.htm'); 434 $files = file2array('netscape_basic.htm');
439 435
440 // import links as private 436 // import bookmarks as private
441 $post = array('privacy' => 'private'); 437 $post = array('privacy' => 'private');
442 $this->assertStringMatchesFormat( 438 $this->assertStringMatchesFormat(
443 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 439 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
444 .' 2 links imported, 0 links overwritten, 0 links skipped.', 440 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
445 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) 441 $this->netscapeBookmarkUtils->import($post, $files)
446 );
447 $this->assertEquals(2, count($this->linkDb));
448 $this->assertEquals(2, count_private($this->linkDb));
449 $this->assertEquals(
450 1,
451 $this->linkDb[0]['private']
452 );
453 $this->assertEquals(
454 1,
455 $this->linkDb[1]['private']
456 ); 442 );
443 $this->assertEquals(2, $this->bookmarkService->count());
444 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
445 $this->assertTrue($this->bookmarkService->get(0)->isPrivate());
446 $this->assertTrue($this->bookmarkService->get(1)->isPrivate());
447
457 // re-import as public, enable overwriting 448 // re-import as public, enable overwriting
458 $post = array( 449 $post = array(
459 'privacy' => 'public', 450 'privacy' => 'public',
@@ -461,45 +452,33 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
461 ); 452 );
462 $this->assertStringMatchesFormat( 453 $this->assertStringMatchesFormat(
463 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 454 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
464 .' 2 links imported, 2 links overwritten, 0 links skipped.', 455 .' 2 bookmarks imported, 2 bookmarks overwritten, 0 bookmarks skipped.',
465 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) 456 $this->netscapeBookmarkUtils->import($post, $files)
466 );
467 $this->assertEquals(2, count($this->linkDb));
468 $this->assertEquals(0, count_private($this->linkDb));
469 $this->assertEquals(
470 0,
471 $this->linkDb[0]['private']
472 );
473 $this->assertEquals(
474 0,
475 $this->linkDb[1]['private']
476 ); 457 );
458 $this->assertEquals(2, $this->bookmarkService->count());
459 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
460 $this->assertFalse($this->bookmarkService->get(0)->isPrivate());
461 $this->assertFalse($this->bookmarkService->get(1)->isPrivate());
477 } 462 }
478 463
479 /** 464 /**
480 * Overwrite public links so they become private 465 * Overwrite public bookmarks so they become private
481 */ 466 */
482 public function testOverwriteAsPrivate() 467 public function testOverwriteAsPrivate()
483 { 468 {
484 $files = file2array('netscape_basic.htm'); 469 $files = file2array('netscape_basic.htm');
485 470
486 // import links as public 471 // import bookmarks as public
487 $post = array('privacy' => 'public'); 472 $post = array('privacy' => 'public');
488 $this->assertStringMatchesFormat( 473 $this->assertStringMatchesFormat(
489 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 474 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
490 .' 2 links imported, 0 links overwritten, 0 links skipped.', 475 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
491 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) 476 $this->netscapeBookmarkUtils->import($post, $files)
492 );
493 $this->assertEquals(2, count($this->linkDb));
494 $this->assertEquals(0, count_private($this->linkDb));
495 $this->assertEquals(
496 0,
497 $this->linkDb['0']['private']
498 );
499 $this->assertEquals(
500 0,
501 $this->linkDb['1']['private']
502 ); 477 );
478 $this->assertEquals(2, $this->bookmarkService->count());
479 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
480 $this->assertFalse($this->bookmarkService->get(0)->isPrivate());
481 $this->assertFalse($this->bookmarkService->get(1)->isPrivate());
503 482
504 // re-import as private, enable overwriting 483 // re-import as private, enable overwriting
505 $post = array( 484 $post = array(
@@ -508,23 +487,17 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
508 ); 487 );
509 $this->assertStringMatchesFormat( 488 $this->assertStringMatchesFormat(
510 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 489 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
511 .' 2 links imported, 2 links overwritten, 0 links skipped.', 490 .' 2 bookmarks imported, 2 bookmarks overwritten, 0 bookmarks skipped.',
512 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) 491 $this->netscapeBookmarkUtils->import($post, $files)
513 );
514 $this->assertEquals(2, count($this->linkDb));
515 $this->assertEquals(2, count_private($this->linkDb));
516 $this->assertEquals(
517 1,
518 $this->linkDb['0']['private']
519 );
520 $this->assertEquals(
521 1,
522 $this->linkDb['1']['private']
523 ); 492 );
493 $this->assertEquals(2, $this->bookmarkService->count());
494 $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
495 $this->assertTrue($this->bookmarkService->get(0)->isPrivate());
496 $this->assertTrue($this->bookmarkService->get(1)->isPrivate());
524 } 497 }
525 498
526 /** 499 /**
527 * Attept to import the same links twice without enabling overwriting 500 * Attept to import the same bookmarks twice without enabling overwriting
528 */ 501 */
529 public function testSkipOverwrite() 502 public function testSkipOverwrite()
530 { 503 {
@@ -532,21 +505,21 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
532 $files = file2array('netscape_basic.htm'); 505 $files = file2array('netscape_basic.htm');
533 $this->assertStringMatchesFormat( 506 $this->assertStringMatchesFormat(
534 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 507 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
535 .' 2 links imported, 0 links overwritten, 0 links skipped.', 508 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
536 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) 509 $this->netscapeBookmarkUtils->import($post, $files)
537 ); 510 );
538 $this->assertEquals(2, count($this->linkDb)); 511 $this->assertEquals(2, $this->bookmarkService->count());
539 $this->assertEquals(0, count_private($this->linkDb)); 512 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
540 513
541 // re-import as private, DO NOT enable overwriting 514 // re-import as private, DO NOT enable overwriting
542 $post = array('privacy' => 'private'); 515 $post = array('privacy' => 'private');
543 $this->assertStringMatchesFormat( 516 $this->assertStringMatchesFormat(
544 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 517 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
545 .' 0 links imported, 0 links overwritten, 2 links skipped.', 518 .' 0 bookmarks imported, 0 bookmarks overwritten, 2 bookmarks skipped.',
546 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) 519 $this->netscapeBookmarkUtils->import($post, $files)
547 ); 520 );
548 $this->assertEquals(2, count($this->linkDb)); 521 $this->assertEquals(2, $this->bookmarkService->count());
549 $this->assertEquals(0, count_private($this->linkDb)); 522 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
550 } 523 }
551 524
552 /** 525 /**
@@ -561,19 +534,13 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
561 $files = file2array('netscape_basic.htm'); 534 $files = file2array('netscape_basic.htm');
562 $this->assertStringMatchesFormat( 535 $this->assertStringMatchesFormat(
563 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 536 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
564 .' 2 links imported, 0 links overwritten, 0 links skipped.', 537 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
565 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) 538 $this->netscapeBookmarkUtils->import($post, $files)
566 );
567 $this->assertEquals(2, count($this->linkDb));
568 $this->assertEquals(0, count_private($this->linkDb));
569 $this->assertEquals(
570 'tag1 tag2 tag3 private secret',
571 $this->linkDb['0']['tags']
572 );
573 $this->assertEquals(
574 'tag1 tag2 tag3 public hello world',
575 $this->linkDb['1']['tags']
576 ); 539 );
540 $this->assertEquals(2, $this->bookmarkService->count());
541 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
542 $this->assertEquals('tag1 tag2 tag3 private secret', $this->bookmarkService->get(0)->getTagsString());
543 $this->assertEquals('tag1 tag2 tag3 public hello world', $this->bookmarkService->get(1)->getTagsString());
577 } 544 }
578 545
579 /** 546 /**
@@ -588,18 +555,18 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
588 $files = file2array('netscape_basic.htm'); 555 $files = file2array('netscape_basic.htm');
589 $this->assertStringMatchesFormat( 556 $this->assertStringMatchesFormat(
590 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:' 557 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
591 .' 2 links imported, 0 links overwritten, 0 links skipped.', 558 .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
592 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history) 559 $this->netscapeBookmarkUtils->import($post, $files)
593 ); 560 );
594 $this->assertEquals(2, count($this->linkDb)); 561 $this->assertEquals(2, $this->bookmarkService->count());
595 $this->assertEquals(0, count_private($this->linkDb)); 562 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
596 $this->assertEquals( 563 $this->assertEquals(
597 'tag1&amp; tag2 &quot;tag3&quot; private secret', 564 'tag1&amp; tag2 &quot;tag3&quot; private secret',
598 $this->linkDb['0']['tags'] 565 $this->bookmarkService->get(0)->getTagsString()
599 ); 566 );
600 $this->assertEquals( 567 $this->assertEquals(
601 'tag1&amp; tag2 &quot;tag3&quot; public hello world', 568 'tag1&amp; tag2 &quot;tag3&quot; public hello world',
602 $this->linkDb['1']['tags'] 569 $this->bookmarkService->get(1)->getTagsString()
603 ); 570 );
604 } 571 }
605 572
@@ -613,23 +580,14 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
613 $files = file2array('same_date.htm'); 580 $files = file2array('same_date.htm');
614 $this->assertStringMatchesFormat( 581 $this->assertStringMatchesFormat(
615 'File same_date.htm (453 bytes) was successfully processed in %d seconds:' 582 'File same_date.htm (453 bytes) was successfully processed in %d seconds:'
616 .' 3 links imported, 0 links overwritten, 0 links skipped.', 583 .' 3 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
617 NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf, $this->history) 584 $this->netscapeBookmarkUtils->import(array(), $files)
618 ); 585 );
619 $this->assertEquals(3, count($this->linkDb)); 586 $this->assertEquals(3, $this->bookmarkService->count());
620 $this->assertEquals(0, count_private($this->linkDb)); 587 $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
621 $this->assertEquals( 588 $this->assertEquals(0, $this->bookmarkService->get(0)->getId());
622 0, 589 $this->assertEquals(1, $this->bookmarkService->get(1)->getId());
623 $this->linkDb[0]['id'] 590 $this->assertEquals(2, $this->bookmarkService->get(2)->getId());
624 );
625 $this->assertEquals(
626 1,
627 $this->linkDb[1]['id']
628 );
629 $this->assertEquals(
630 2,
631 $this->linkDb[2]['id']
632 );
633 } 591 }
634 592
635 public function testImportCreateUpdateHistory() 593 public function testImportCreateUpdateHistory()
@@ -639,14 +597,14 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
639 'overwrite' => 'true', 597 'overwrite' => 'true',
640 ]; 598 ];
641 $files = file2array('netscape_basic.htm'); 599 $files = file2array('netscape_basic.htm');
642 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history); 600 $this->netscapeBookmarkUtils->import($post, $files);
643 $history = $this->history->getHistory(); 601 $history = $this->history->getHistory();
644 $this->assertEquals(1, count($history)); 602 $this->assertEquals(1, count($history));
645 $this->assertEquals(History::IMPORT, $history[0]['event']); 603 $this->assertEquals(History::IMPORT, $history[0]['event']);
646 $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']); 604 $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']);
647 605
648 // re-import as private, enable overwriting 606 // re-import as private, enable overwriting
649 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history); 607 $this->netscapeBookmarkUtils->import($post, $files);
650 $history = $this->history->getHistory(); 608 $history = $this->history->getHistory();
651 $this->assertEquals(2, count($history)); 609 $this->assertEquals(2, count($history));
652 $this->assertEquals(History::IMPORT, $history[0]['event']); 610 $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 510288bb..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 links. 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
@@ -54,18 +71,18 @@ class PluginArchiveorgTest extends \PHPUnit\Framework\TestCase
54 } 71 }
55 72
56 /** 73 /**
57 * Test render_linklist hook on internal links. 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 bdfab439..16ecf357 100644
--- a/tests/plugins/PluginIssoTest.php
+++ b/tests/plugins/PluginIssoTest.php
@@ -2,9 +2,10 @@
2namespace Shaarli\Plugin\Isso; 2namespace Shaarli\Plugin\Isso;
3 3
4use DateTime; 4use DateTime;
5use Shaarli\Bookmark\LinkDB; 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');
@@ -60,7 +61,7 @@ class PluginIssoTest extends \PHPUnit\Framework\TestCase
60 array( 61 array(
61 'id' => 12, 62 'id' => 12,
62 'url' => $str, 63 'url' => $str,
63 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date), 64 'created' => DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $date),
64 ) 65 )
65 ) 66 )
66 ); 67 );
@@ -85,9 +86,9 @@ class PluginIssoTest extends \PHPUnit\Framework\TestCase
85 } 86 }
86 87
87 /** 88 /**
88 * Test isso plugin when multiple links 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');
@@ -102,27 +103,27 @@ class PluginIssoTest extends \PHPUnit\Framework\TestCase
102 'id' => 12, 103 'id' => 12,
103 'url' => $str, 104 'url' => $str,
104 'shorturl' => $short1 = 'abcd', 105 'shorturl' => $short1 = 'abcd',
105 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date1), 106 'created' => DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $date1),
106 ), 107 ),
107 array( 108 array(
108 'id' => 13, 109 'id' => 13,
109 'url' => $str . '2', 110 'url' => $str . '2',
110 'shorturl' => $short2 = 'efgh', 111 'shorturl' => $short2 = 'efgh',
111 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date2), 112 'created' => DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $date2),
112 ), 113 ),
113 ) 114 )
114 ); 115 );
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');
@@ -136,7 +137,7 @@ class PluginIssoTest extends \PHPUnit\Framework\TestCase
136 'id' => 12, 137 'id' => 12,
137 'url' => $str, 138 'url' => $str,
138 'shorturl' => $short1 = 'abcd', 139 'shorturl' => $short1 = 'abcd',
139 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, $date), 140 'created' => DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, $date),
140 ) 141 )
141 ), 142 ),
142 'search_term' => $str 143 'search_term' => $str
@@ -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/PluginMarkdownTest.php b/tests/plugins/PluginMarkdownTest.php
deleted file mode 100644
index 9ddbc558..00000000
--- a/tests/plugins/PluginMarkdownTest.php
+++ /dev/null
@@ -1,306 +0,0 @@
1<?php
2namespace Shaarli\Plugin\Markdown;
3
4use Shaarli\Config\ConfigManager;
5use Shaarli\Plugin\PluginManager;
6
7/**
8 * PluginMarkdownTest.php
9 */
10
11require_once 'application/bookmark/LinkUtils.php';
12require_once 'application/Utils.php';
13require_once 'plugins/markdown/markdown.php';
14
15/**
16 * Class PluginMarkdownTest
17 * Unit test for the Markdown plugin
18 */
19class PluginMarkdownTest extends \PHPUnit\Framework\TestCase
20{
21 /**
22 * @var ConfigManager instance.
23 */
24 protected $conf;
25
26 /**
27 * Reset plugin path
28 */
29 public function setUp()
30 {
31 PluginManager::$PLUGINS_PATH = 'plugins';
32 $this->conf = new ConfigManager('tests/utils/config/configJson');
33 $this->conf->set('security.allowed_protocols', ['ftp', 'magnet']);
34 }
35
36 /**
37 * Test render_linklist hook.
38 * Only check that there is basic markdown rendering.
39 */
40 public function testMarkdownLinklist()
41 {
42 $markdown = '# My title' . PHP_EOL . 'Very interesting content.';
43 $data = array(
44 'links' => array(
45 0 => array(
46 'description' => $markdown,
47 ),
48 ),
49 );
50
51 $data = hook_markdown_render_linklist($data, $this->conf);
52 $this->assertNotFalse(strpos($data['links'][0]['description'], '<h1>'));
53 $this->assertNotFalse(strpos($data['links'][0]['description'], '<p>'));
54
55 $this->assertEquals($markdown, $data['links'][0]['description_src']);
56 }
57
58 /**
59 * Test render_feed hook.
60 */
61 public function testMarkdownFeed()
62 {
63 $markdown = '# My title' . PHP_EOL . 'Very interesting content.';
64 $markdown .= '&#8212; <a href="http://domain.tld/?0oc_VQ" title="Permalien">Permalien</a>';
65 $data = array(
66 'links' => array(
67 0 => array(
68 'description' => $markdown,
69 ),
70 ),
71 );
72
73 $data = hook_markdown_render_feed($data, $this->conf);
74 $this->assertNotFalse(strpos($data['links'][0]['description'], '<h1>'));
75 $this->assertNotFalse(strpos($data['links'][0]['description'], '<p>'));
76 $this->assertStringEndsWith(
77 '&#8212; <a href="http://domain.tld/?0oc_VQ">Permalien</a></p></div>',
78 $data['links'][0]['description']
79 );
80 }
81
82 /**
83 * Test render_daily hook.
84 * Only check that there is basic markdown rendering.
85 */
86 public function testMarkdownDaily()
87 {
88 $markdown = '# My title' . PHP_EOL . 'Very interesting content.';
89 $data = array(
90 // Columns data
91 'linksToDisplay' => array(
92 // nth link
93 0 => array(
94 'formatedDescription' => $markdown,
95 ),
96 ),
97 );
98
99 $data = hook_markdown_render_daily($data, $this->conf);
100 $this->assertNotFalse(strpos($data['linksToDisplay'][0]['formatedDescription'], '<h1>'));
101 $this->assertNotFalse(strpos($data['linksToDisplay'][0]['formatedDescription'], '<p>'));
102 }
103
104 /**
105 * Test reverse_text2clickable().
106 */
107 public function testReverseText2clickable()
108 {
109 $text = 'stuff http://hello.there/is=someone#here otherstuff';
110 $clickableText = text2clickable($text);
111 $reversedText = reverse_text2clickable($clickableText);
112 $this->assertEquals($text, $reversedText);
113 }
114
115 /**
116 * Test reverse_text2clickable().
117 */
118 public function testReverseText2clickableHashtags()
119 {
120 $text = file_get_contents('tests/plugins/resources/hashtags.raw');
121 $md = file_get_contents('tests/plugins/resources/hashtags.md');
122 $clickableText = hashtag_autolink($text);
123 $reversedText = reverse_text2clickable($clickableText);
124 $this->assertEquals($md, $reversedText);
125 }
126
127 /**
128 * Test reverse_nl2br().
129 */
130 public function testReverseNl2br()
131 {
132 $text = 'stuff' . PHP_EOL . 'otherstuff';
133 $processedText = nl2br($text);
134 $reversedText = reverse_nl2br($processedText);
135 $this->assertEquals($text, $reversedText);
136 }
137
138 /**
139 * Test reverse_space2nbsp().
140 */
141 public function testReverseSpace2nbsp()
142 {
143 $text = ' stuff' . PHP_EOL . ' otherstuff and another';
144 $processedText = space2nbsp($text);
145 $reversedText = reverse_space2nbsp($processedText);
146 $this->assertEquals($text, $reversedText);
147 }
148
149 public function testReverseFeedPermalink()
150 {
151 $text = 'Description... ';
152 $text .= '&#8212; <a href="http://domain.tld/?0oc_VQ" title="Permalien">Permalien</a>';
153 $expected = 'Description... &#8212; [Permalien](http://domain.tld/?0oc_VQ)';
154 $processedText = reverse_feed_permalink($text);
155
156 $this->assertEquals($expected, $processedText);
157 }
158
159 public function testReverseLastFeedPermalink()
160 {
161 $text = 'Description... ';
162 $text .= '<br>&#8212; <a href="http://domain.tld/?0oc_VQ" title="Permalien">Permalien</a>';
163 $expected = $text;
164 $text .= '<br>&#8212; <a href="http://domain.tld/?0oc_VQ" title="Permalien">Permalien</a>';
165 $expected .= '<br>&#8212; [Permalien](http://domain.tld/?0oc_VQ)';
166 $processedText = reverse_feed_permalink($text);
167
168 $this->assertEquals($expected, $processedText);
169 }
170
171 public function testReverseNoFeedPermalink()
172 {
173 $text = 'Hello! Where are you from?';
174 $expected = $text;
175 $processedText = reverse_feed_permalink($text);
176
177 $this->assertEquals($expected, $processedText);
178 }
179
180 /**
181 * Test sanitize_html().
182 */
183 public function testSanitizeHtml()
184 {
185 $input = '< script src="js.js"/>';
186 $input .= '< script attr>alert(\'xss\');</script>';
187 $input .= '<style> * { display: none }</style>';
188 $output = escape($input);
189 $input .= '<a href="#" onmouseHover="alert(\'xss\');" attr="tt">link</a>';
190 $output .= '<a href="#" attr="tt">link</a>';
191 $input .= '<a href="#" onmouseHover=alert(\'xss\'); attr="tt">link</a>';
192 $output .= '<a href="#" attr="tt">link</a>';
193 $this->assertEquals($output, sanitize_html($input));
194 // Do not touch escaped HTML.
195 $input = escape($input);
196 $this->assertEquals($input, sanitize_html($input));
197 }
198
199 /**
200 * Test the no markdown tag.
201 */
202 public function testNoMarkdownTag()
203 {
204 $str = 'All _work_ and `no play` makes Jack a *dull* boy.';
205 $data = array(
206 'links' => array(array(
207 'description' => $str,
208 'tags' => NO_MD_TAG,
209 'taglist' => array(NO_MD_TAG),
210 ))
211 );
212
213 $processed = hook_markdown_render_linklist($data, $this->conf);
214 $this->assertEquals($str, $processed['links'][0]['description']);
215
216 $processed = hook_markdown_render_feed($data, $this->conf);
217 $this->assertEquals($str, $processed['links'][0]['description']);
218
219 $data = array(
220 // Columns data
221 'linksToDisplay' => array(
222 // nth link
223 0 => array(
224 'formatedDescription' => $str,
225 'tags' => NO_MD_TAG,
226 'taglist' => array(),
227 ),
228 ),
229 );
230
231 $data = hook_markdown_render_daily($data, $this->conf);
232 $this->assertEquals($str, $data['linksToDisplay'][0]['formatedDescription']);
233 }
234
235 /**
236 * Test that a close value to nomarkdown is not understand as nomarkdown (previous value `.nomarkdown`).
237 */
238 public function testNoMarkdownNotExcactlyMatching()
239 {
240 $str = 'All _work_ and `no play` makes Jack a *dull* boy.';
241 $data = array(
242 'links' => array(array(
243 'description' => $str,
244 'tags' => '.' . NO_MD_TAG,
245 'taglist' => array('.'. NO_MD_TAG),
246 ))
247 );
248
249 $data = hook_markdown_render_feed($data, $this->conf);
250 $this->assertContains('<em>', $data['links'][0]['description']);
251 }
252
253 /**
254 * Make sure that the generated HTML match the reference HTML file.
255 */
256 public function testMarkdownGlobalProcessDescription()
257 {
258 $md = file_get_contents('tests/plugins/resources/markdown.md');
259 $md = format_description($md);
260 $html = file_get_contents('tests/plugins/resources/markdown.html');
261
262 $data = process_markdown(
263 $md,
264 $this->conf->get('security.markdown_escape', true),
265 $this->conf->get('security.allowed_protocols')
266 );
267 $this->assertEquals($html, $data . PHP_EOL);
268 }
269
270 /**
271 * Make sure that the HTML tags are escaped.
272 */
273 public function testMarkdownWithHtmlEscape()
274 {
275 $md = '**strong** <strong>strong</strong>';
276 $html = '<div class="markdown"><p><strong>strong</strong> &lt;strong&gt;strong&lt;/strong&gt;</p></div>';
277 $data = array(
278 'links' => array(
279 0 => array(
280 'description' => $md,
281 ),
282 ),
283 );
284 $data = hook_markdown_render_linklist($data, $this->conf);
285 $this->assertEquals($html, $data['links'][0]['description']);
286 }
287
288 /**
289 * Make sure that the HTML tags aren't escaped with the setting set to false.
290 */
291 public function testMarkdownWithHtmlNoEscape()
292 {
293 $this->conf->set('security.markdown_escape', false);
294 $md = '**strong** <strong>strong</strong>';
295 $html = '<div class="markdown"><p><strong>strong</strong> <strong>strong</strong></p></div>';
296 $data = array(
297 'links' => array(
298 0 => array(
299 'description' => $md,
300 ),
301 ),
302 );
303 $data = hook_markdown_render_linklist($data, $this->conf);
304 $this->assertEquals($html, $data['links'][0]['description']);
305 }
306}
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..698d3d10 100644
--- a/tests/security/BanManagerTest.php
+++ b/tests/security/BanManagerTest.php
@@ -3,8 +3,8 @@
3 3
4namespace Shaarli\Security; 4namespace Shaarli\Security;
5 5
6use PHPUnit\Framework\TestCase;
7use Shaarli\FileUtils; 6use Shaarli\FileUtils;
7use Shaarli\TestCase;
8 8
9/** 9/**
10 * Test coverage for BanManager 10 * Test coverage for BanManager
@@ -32,7 +32,7 @@ class BanManagerTest extends TestCase
32 /** 32 /**
33 * Prepare or reset test resources 33 * Prepare or reset test resources
34 */ 34 */
35 public function setUp() 35 protected function setUp(): void
36 { 36 {
37 if (file_exists($this->banFile)) { 37 if (file_exists($this->banFile)) {
38 unlink($this->banFile); 38 unlink($this->banFile);
diff --git a/tests/security/LoginManagerTest.php b/tests/security/LoginManagerTest.php
index eef0f22a..d302983d 100644
--- a/tests/security/LoginManagerTest.php
+++ b/tests/security/LoginManagerTest.php
@@ -1,9 +1,8 @@
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 Shaarli\TestCase;
7 6
8/** 7/**
9 * Test coverage for LoginManager 8 * Test coverage for LoginManager
@@ -58,10 +57,13 @@ class LoginManagerTest extends TestCase
58 /** @var string Salt used by hash functions */ 57 /** @var string Salt used by hash functions */
59 protected $salt = '669e24fa9c5a59a613f98e8e38327384504a4af2'; 58 protected $salt = '669e24fa9c5a59a613f98e8e38327384504a4af2';
60 59
60 /** @var CookieManager */
61 protected $cookieManager;
62
61 /** 63 /**
62 * Prepare or reset test resources 64 * Prepare or reset test resources
63 */ 65 */
64 public function setUp() 66 protected function setUp(): void
65 { 67 {
66 if (file_exists($this->banFile)) { 68 if (file_exists($this->banFile)) {
67 unlink($this->banFile); 69 unlink($this->banFile);
@@ -78,13 +80,18 @@ class LoginManagerTest extends TestCase
78 'security.ban_after' => 2, 80 'security.ban_after' => 2,
79 'security.ban_duration' => 3600, 81 'security.ban_duration' => 3600,
80 'security.trusted_proxies' => [$this->trustedProxy], 82 'security.trusted_proxies' => [$this->trustedProxy],
83 'ldap.host' => '',
81 ]); 84 ]);
82 85
83 $this->cookie = []; 86 $this->cookie = [];
84 $this->session = []; 87 $this->session = [];
85 88
86 $this->sessionManager = new SessionManager($this->session, $this->configManager); 89 $this->cookieManager = $this->createMock(CookieManager::class);
87 $this->loginManager = new LoginManager($this->configManager, $this->sessionManager); 90 $this->cookieManager->method('getCookieParameter')->willReturnCallback(function (string $key) {
91 return $this->cookie[$key] ?? null;
92 });
93 $this->sessionManager = new SessionManager($this->session, $this->configManager, 'session_path');
94 $this->loginManager = new LoginManager($this->configManager, $this->sessionManager, $this->cookieManager);
88 $this->server['REMOTE_ADDR'] = $this->ipAddr; 95 $this->server['REMOTE_ADDR'] = $this->ipAddr;
89 } 96 }
90 97
@@ -192,8 +199,8 @@ class LoginManagerTest extends TestCase
192 $configManager = new \FakeConfigManager([ 199 $configManager = new \FakeConfigManager([
193 'resource.ban_file' => $this->banFile, 200 'resource.ban_file' => $this->banFile,
194 ]); 201 ]);
195 $loginManager = new LoginManager($configManager, null); 202 $loginManager = new LoginManager($configManager, null, $this->cookieManager);
196 $loginManager->checkLoginState([], ''); 203 $loginManager->checkLoginState('');
197 204
198 $this->assertFalse($loginManager->isLoggedIn()); 205 $this->assertFalse($loginManager->isLoggedIn());
199 } 206 }
@@ -209,9 +216,9 @@ class LoginManagerTest extends TestCase
209 'expires_on' => time() + 100, 216 'expires_on' => time() + 100,
210 ]; 217 ];
211 $this->loginManager->generateStaySignedInToken($this->clientIpAddress); 218 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
212 $this->cookie[LoginManager::$STAY_SIGNED_IN_COOKIE] = 'nope'; 219 $this->cookie[CookieManager::STAY_SIGNED_IN] = 'nope';
213 220
214 $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress); 221 $this->loginManager->checkLoginState($this->clientIpAddress);
215 222
216 $this->assertTrue($this->loginManager->isLoggedIn()); 223 $this->assertTrue($this->loginManager->isLoggedIn());
217 $this->assertTrue(empty($this->session['username'])); 224 $this->assertTrue(empty($this->session['username']));
@@ -223,9 +230,9 @@ class LoginManagerTest extends TestCase
223 public function testCheckLoginStateStaySignedInWithValidToken() 230 public function testCheckLoginStateStaySignedInWithValidToken()
224 { 231 {
225 $this->loginManager->generateStaySignedInToken($this->clientIpAddress); 232 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
226 $this->cookie[LoginManager::$STAY_SIGNED_IN_COOKIE] = $this->loginManager->getStaySignedInToken(); 233 $this->cookie[CookieManager::STAY_SIGNED_IN] = $this->loginManager->getStaySignedInToken();
227 234
228 $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress); 235 $this->loginManager->checkLoginState($this->clientIpAddress);
229 236
230 $this->assertTrue($this->loginManager->isLoggedIn()); 237 $this->assertTrue($this->loginManager->isLoggedIn());
231 $this->assertEquals($this->login, $this->session['username']); 238 $this->assertEquals($this->login, $this->session['username']);
@@ -240,7 +247,7 @@ class LoginManagerTest extends TestCase
240 $this->loginManager->generateStaySignedInToken($this->clientIpAddress); 247 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
241 $this->session['expires_on'] = time() - 100; 248 $this->session['expires_on'] = time() - 100;
242 249
243 $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress); 250 $this->loginManager->checkLoginState($this->clientIpAddress);
244 251
245 $this->assertFalse($this->loginManager->isLoggedIn()); 252 $this->assertFalse($this->loginManager->isLoggedIn());
246 } 253 }
@@ -252,7 +259,7 @@ class LoginManagerTest extends TestCase
252 { 259 {
253 $this->loginManager->generateStaySignedInToken($this->clientIpAddress); 260 $this->loginManager->generateStaySignedInToken($this->clientIpAddress);
254 261
255 $this->loginManager->checkLoginState($this->cookie, '10.7.157.98'); 262 $this->loginManager->checkLoginState('10.7.157.98');
256 263
257 $this->assertFalse($this->loginManager->isLoggedIn()); 264 $this->assertFalse($this->loginManager->isLoggedIn());
258 } 265 }
@@ -296,4 +303,37 @@ class LoginManagerTest extends TestCase
296 $this->loginManager->checkCredentials('', '', $this->login, $this->password) 303 $this->loginManager->checkCredentials('', '', $this->login, $this->password)
297 ); 304 );
298 } 305 }
306
307 /**
308 * Check user credentials through LDAP - server unreachable
309 */
310 public function testCheckCredentialsFromUnreachableLdap()
311 {
312 $this->configManager->set('ldap.host', 'dummy');
313 $this->assertFalse(
314 $this->loginManager->checkCredentials('', '', $this->login, $this->password)
315 );
316 }
317
318 /**
319 * Check user credentials through LDAP - wrong login and password supplied
320 */
321 public function testCheckCredentialsFromLdapWrongLoginAndPassword()
322 {
323 $this->configManager->set('ldap.host', 'dummy');
324 $this->assertFalse(
325 $this->loginManager->checkCredentialsFromLdap($this->login, $this->password, function() { return null; }, function() { return false; })
326 );
327 }
328
329 /**
330 * Check user credentials through LDAP - correct login and password supplied
331 */
332 public function testCheckCredentialsFromLdapGoodLoginAndPassword()
333 {
334 $this->configManager->set('ldap.host', 'dummy');
335 $this->assertTrue(
336 $this->loginManager->checkCredentialsFromLdap($this->login, $this->password, function() { return null; }, function() { return true; })
337 );
338 }
299} 339}
diff --git a/tests/security/SessionManagerTest.php b/tests/security/SessionManagerTest.php
index f264505e..3f9c3ef5 100644
--- a/tests/security/SessionManagerTest.php
+++ b/tests/security/SessionManagerTest.php
@@ -1,12 +1,8 @@
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\TestCase;
9use Shaarli\Security\SessionManager;
10 6
11/** 7/**
12 * Test coverage for SessionManager 8 * Test coverage for SessionManager
@@ -28,23 +24,23 @@ class SessionManagerTest extends TestCase
28 /** 24 /**
29 * Assign reference data 25 * Assign reference data
30 */ 26 */
31 public static function setUpBeforeClass() 27 public static function setUpBeforeClass(): void
32 { 28 {
33 self::$sidHashes = ReferenceSessionIdHashes::getHashes(); 29 self::$sidHashes = \ReferenceSessionIdHashes::getHashes();
34 } 30 }
35 31
36 /** 32 /**
37 * Initialize or reset test resources 33 * Initialize or reset test resources
38 */ 34 */
39 public function setUp() 35 protected function setUp(): void
40 { 36 {
41 $this->conf = new FakeConfigManager([ 37 $this->conf = new \FakeConfigManager([
42 'credentials.login' => 'johndoe', 38 'credentials.login' => 'johndoe',
43 'credentials.salt' => 'salt', 39 'credentials.salt' => 'salt',
44 'security.session_protection_disabled' => false, 40 'security.session_protection_disabled' => false,
45 ]); 41 ]);
46 $this->session = []; 42 $this->session = [];
47 $this->sessionManager = new SessionManager($this->session, $this->conf); 43 $this->sessionManager = new SessionManager($this->session, $this->conf, 'session_path');
48 } 44 }
49 45
50 /** 46 /**
@@ -69,7 +65,7 @@ class SessionManagerTest extends TestCase
69 $token => 1, 65 $token => 1,
70 ], 66 ],
71 ]; 67 ];
72 $sessionManager = new SessionManager($session, $this->conf); 68 $sessionManager = new SessionManager($session, $this->conf, 'session_path');
73 69
74 // check and destroy the token 70 // check and destroy the token
75 $this->assertTrue($sessionManager->checkToken($token)); 71 $this->assertTrue($sessionManager->checkToken($token));
@@ -211,15 +207,16 @@ class SessionManagerTest extends TestCase
211 'expires_on' => time() + 1000, 207 'expires_on' => time() + 1000,
212 'username' => 'johndoe', 208 'username' => 'johndoe',
213 'visibility' => 'public', 209 'visibility' => 'public',
214 'untaggedonly' => false, 210 'untaggedonly' => true,
215 ]; 211 ];
216 $this->sessionManager->logout(); 212 $this->sessionManager->logout();
217 213
218 $this->assertFalse(isset($this->session['ip'])); 214 $this->assertArrayNotHasKey('ip', $this->session);
219 $this->assertFalse(isset($this->session['expires_on'])); 215 $this->assertArrayNotHasKey('expires_on', $this->session);
220 $this->assertFalse(isset($this->session['username'])); 216 $this->assertArrayNotHasKey('username', $this->session);
221 $this->assertFalse(isset($this->session['visibility'])); 217 $this->assertArrayNotHasKey('visibility', $this->session);
222 $this->assertFalse(isset($this->session['untaggedonly'])); 218 $this->assertArrayHasKey('untaggedonly', $this->session);
219 $this->assertTrue($this->session['untaggedonly']);
223 } 220 }
224 221
225 /** 222 /**
@@ -269,4 +266,61 @@ class SessionManagerTest extends TestCase
269 $this->session['ip'] = 'ip_id_one'; 266 $this->session['ip'] = 'ip_id_one';
270 $this->assertTrue($this->sessionManager->hasClientIpChanged('ip_id_two')); 267 $this->assertTrue($this->sessionManager->hasClientIpChanged('ip_id_two'));
271 } 268 }
269
270 /**
271 * Test creating an entry in the session array
272 */
273 public function testSetSessionParameterCreate(): void
274 {
275 $this->sessionManager->setSessionParameter('abc', 'def');
276
277 static::assertSame('def', $this->session['abc']);
278 }
279
280 /**
281 * Test updating an entry in the session array
282 */
283 public function testSetSessionParameterUpdate(): void
284 {
285 $this->session['abc'] = 'ghi';
286
287 $this->sessionManager->setSessionParameter('abc', 'def');
288
289 static::assertSame('def', $this->session['abc']);
290 }
291
292 /**
293 * Test updating an entry in the session array with null value
294 */
295 public function testSetSessionParameterUpdateNull(): void
296 {
297 $this->session['abc'] = 'ghi';
298
299 $this->sessionManager->setSessionParameter('abc', null);
300
301 static::assertArrayHasKey('abc', $this->session);
302 static::assertNull($this->session['abc']);
303 }
304
305 /**
306 * Test deleting an existing entry in the session array
307 */
308 public function testDeleteSessionParameter(): void
309 {
310 $this->session['abc'] = 'def';
311
312 $this->sessionManager->deleteSessionParameter('abc');
313
314 static::assertArrayNotHasKey('abc', $this->session);
315 }
316
317 /**
318 * Test deleting a non existent entry in the session array
319 */
320 public function testDeleteSessionParameterNotExisting(): void
321 {
322 $this->sessionManager->deleteSessionParameter('abc');
323
324 static::assertArrayNotHasKey('abc', $this->session);
325 }
272} 326}
diff --git a/tests/updater/DummyUpdater.php b/tests/updater/DummyUpdater.php
index 9e866f1f..3403233f 100644
--- a/tests/updater/DummyUpdater.php
+++ b/tests/updater/DummyUpdater.php
@@ -4,6 +4,7 @@ namespace Shaarli\Updater;
4use Exception; 4use Exception;
5use ReflectionClass; 5use ReflectionClass;
6use ReflectionMethod; 6use ReflectionMethod;
7use Shaarli\Bookmark\BookmarkFileService;
7use Shaarli\Bookmark\LinkDB; 8use Shaarli\Bookmark\LinkDB;
8use Shaarli\Config\ConfigManager; 9use Shaarli\Config\ConfigManager;
9 10
@@ -16,14 +17,14 @@ class DummyUpdater extends Updater
16 /** 17 /**
17 * Object constructor. 18 * Object constructor.
18 * 19 *
19 * @param array $doneUpdates Updates which are already done. 20 * @param array $doneUpdates Updates which are already done.
20 * @param LinkDB $linkDB LinkDB instance. 21 * @param BookmarkFileService $bookmarkService LinkDB instance.
21 * @param ConfigManager $conf Configuration Manager instance. 22 * @param ConfigManager $conf Configuration Manager instance.
22 * @param boolean $isLoggedIn True if the user is logged in. 23 * @param boolean $isLoggedIn True if the user is logged in.
23 */ 24 */
24 public function __construct($doneUpdates, $linkDB, $conf, $isLoggedIn) 25 public function __construct($doneUpdates, $bookmarkService, $conf, $isLoggedIn)
25 { 26 {
26 parent::__construct($doneUpdates, $linkDB, $conf, $isLoggedIn); 27 parent::__construct($doneUpdates, $bookmarkService, $conf, $isLoggedIn);
27 28
28 // Retrieve all update methods. 29 // Retrieve all update methods.
29 // For unit test, only retrieve final methods, 30 // For unit test, only retrieve final methods,
@@ -36,7 +37,7 @@ class DummyUpdater extends Updater
36 * 37 *
37 * @return bool true. 38 * @return bool true.
38 */ 39 */
39 final private function updateMethodDummy1() 40 final protected function updateMethodDummy1()
40 { 41 {
41 return true; 42 return true;
42 } 43 }
@@ -46,7 +47,7 @@ class DummyUpdater extends Updater
46 * 47 *
47 * @return bool true. 48 * @return bool true.
48 */ 49 */
49 final private function updateMethodDummy2() 50 final protected function updateMethodDummy2()
50 { 51 {
51 return true; 52 return true;
52 } 53 }
@@ -56,7 +57,7 @@ class DummyUpdater extends Updater
56 * 57 *
57 * @return bool true. 58 * @return bool true.
58 */ 59 */
59 final private function updateMethodDummy3() 60 final protected function updateMethodDummy3()
60 { 61 {
61 return true; 62 return true;
62 } 63 }
@@ -66,7 +67,7 @@ class DummyUpdater extends Updater
66 * 67 *
67 * @throws Exception error. 68 * @throws Exception error.
68 */ 69 */
69 final private function updateMethodException() 70 final protected function updateMethodException()
70 { 71 {
71 throw new Exception('whatever'); 72 throw new Exception('whatever');
72 } 73 }
diff --git a/tests/updater/UpdaterTest.php b/tests/updater/UpdaterTest.php
index 93bc86c1..a6280b8c 100644
--- a/tests/updater/UpdaterTest.php
+++ b/tests/updater/UpdaterTest.php
@@ -1,24 +1,19 @@
1<?php 1<?php
2namespace Shaarli\Updater; 2namespace Shaarli\Updater;
3 3
4use DateTime;
5use Exception; 4use Exception;
6use Shaarli\Bookmark\LinkDB; 5use Shaarli\Bookmark\BookmarkFileService;
7use Shaarli\Config\ConfigJson; 6use Shaarli\Bookmark\BookmarkServiceInterface;
8use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
9use Shaarli\Config\ConfigPhp; 8use Shaarli\History;
10use Shaarli\Thumbnailer; 9use Shaarli\TestCase;
11 10
12require_once 'application/updater/UpdaterUtils.php';
13require_once 'tests/updater/DummyUpdater.php';
14require_once 'tests/utils/ReferenceLinkDB.php';
15require_once 'inc/rain.tpl.class.php';
16 11
17/** 12/**
18 * Class UpdaterTest. 13 * Class UpdaterTest.
19 * Runs unit tests against the updater class. 14 * Runs unit tests against the updater class.
20 */ 15 */
21class UpdaterTest extends \PHPUnit\Framework\TestCase 16class UpdaterTest extends TestCase
22{ 17{
23 /** 18 /**
24 * @var string Path to test datastore. 19 * @var string Path to test datastore.
@@ -35,24 +30,38 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
35 */ 30 */
36 protected $conf; 31 protected $conf;
37 32
33 /** @var BookmarkServiceInterface */
34 protected $bookmarkService;
35
36 /** @var \ReferenceLinkDB */
37 protected $refDB;
38
39 /** @var Updater */
40 protected $updater;
41
38 /** 42 /**
39 * Executed before each test. 43 * Executed before each test.
40 */ 44 */
41 public function setUp() 45 protected function setUp(): void
42 { 46 {
47 $this->refDB = new \ReferenceLinkDB();
48 $this->refDB->write(self::$testDatastore);
49
43 copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php'); 50 copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php');
44 $this->conf = new ConfigManager(self::$configFile); 51 $this->conf = new ConfigManager(self::$configFile);
52 $this->bookmarkService = new BookmarkFileService($this->conf, $this->createMock(History::class), true);
53 $this->updater = new Updater([], $this->bookmarkService, $this->conf, true);
45 } 54 }
46 55
47 /** 56 /**
48 * Test read_updates_file with an empty/missing file. 57 * Test UpdaterUtils::read_updates_file with an empty/missing file.
49 */ 58 */
50 public function testReadEmptyUpdatesFile() 59 public function testReadEmptyUpdatesFile()
51 { 60 {
52 $this->assertEquals(array(), read_updates_file('')); 61 $this->assertEquals(array(), UpdaterUtils::read_updates_file(''));
53 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt'; 62 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt';
54 touch($updatesFile); 63 touch($updatesFile);
55 $this->assertEquals(array(), read_updates_file($updatesFile)); 64 $this->assertEquals(array(), UpdaterUtils::read_updates_file($updatesFile));
56 unlink($updatesFile); 65 unlink($updatesFile);
57 } 66 }
58 67
@@ -64,42 +73,42 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
64 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt'; 73 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt';
65 $updatesMethods = array('m1', 'm2', 'm3'); 74 $updatesMethods = array('m1', 'm2', 'm3');
66 75
67 write_updates_file($updatesFile, $updatesMethods); 76 UpdaterUtils::write_updates_file($updatesFile, $updatesMethods);
68 $readMethods = read_updates_file($updatesFile); 77 $readMethods = UpdaterUtils::read_updates_file($updatesFile);
69 $this->assertEquals($readMethods, $updatesMethods); 78 $this->assertEquals($readMethods, $updatesMethods);
70 79
71 // Update 80 // Update
72 $updatesMethods[] = 'm4'; 81 $updatesMethods[] = 'm4';
73 write_updates_file($updatesFile, $updatesMethods); 82 UpdaterUtils::write_updates_file($updatesFile, $updatesMethods);
74 $readMethods = read_updates_file($updatesFile); 83 $readMethods = UpdaterUtils::read_updates_file($updatesFile);
75 $this->assertEquals($readMethods, $updatesMethods); 84 $this->assertEquals($readMethods, $updatesMethods);
76 unlink($updatesFile); 85 unlink($updatesFile);
77 } 86 }
78 87
79 /** 88 /**
80 * Test errors in write_updates_file(): empty updates file. 89 * Test errors in UpdaterUtils::write_updates_file(): empty updates file.
81 *
82 * @expectedException Exception
83 * @expectedExceptionMessageRegExp /Updates file path is not set(.*)/
84 */ 90 */
85 public function testWriteEmptyUpdatesFile() 91 public function testWriteEmptyUpdatesFile()
86 { 92 {
87 write_updates_file('', array('test')); 93 $this->expectException(\Exception::class);
94 $this->expectExceptionMessageRegExp('/Updates file path is not set(.*)/');
95
96 UpdaterUtils::write_updates_file('', array('test'));
88 } 97 }
89 98
90 /** 99 /**
91 * Test errors in write_updates_file(): not writable updates file. 100 * Test errors in UpdaterUtils::write_updates_file(): not writable updates file.
92 *
93 * @expectedException Exception
94 * @expectedExceptionMessageRegExp /Unable to write(.*)/
95 */ 101 */
96 public function testWriteUpdatesFileNotWritable() 102 public function testWriteUpdatesFileNotWritable()
97 { 103 {
104 $this->expectException(\Exception::class);
105 $this->expectExceptionMessageRegExp('/Unable to write(.*)/');
106
98 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt'; 107 $updatesFile = $this->conf->get('resource.data_dir') . '/updates.txt';
99 touch($updatesFile); 108 touch($updatesFile);
100 chmod($updatesFile, 0444); 109 chmod($updatesFile, 0444);
101 try { 110 try {
102 @write_updates_file($updatesFile, array('test')); 111 @UpdaterUtils::write_updates_file($updatesFile, array('test'));
103 } catch (Exception $e) { 112 } catch (Exception $e) {
104 unlink($updatesFile); 113 unlink($updatesFile);
105 throw $e; 114 throw $e;
@@ -159,11 +168,11 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
159 168
160 /** 169 /**
161 * Test Update failed. 170 * Test Update failed.
162 *
163 * @expectedException \Exception
164 */ 171 */
165 public function testUpdateFailed() 172 public function testUpdateFailed()
166 { 173 {
174 $this->expectException(\Exception::class);
175
167 $updates = array( 176 $updates = array(
168 'updateMethodDummy1', 177 'updateMethodDummy1',
169 'updateMethodDummy2', 178 'updateMethodDummy2',
@@ -174,653 +183,39 @@ class UpdaterTest extends \PHPUnit\Framework\TestCase
174 $updater->update(); 183 $updater->update();
175 } 184 }
176 185
177 /** 186 public function testUpdateMethodRelativeHomeLinkRename(): void
178 * Test update mergeDeprecatedConfig:
179 * 1. init a config file.
180 * 2. init a options.php file with update value.
181 * 3. merge.
182 * 4. check updated value in config file.
183 */
184 public function testUpdateMergeDeprecatedConfig()
185 {
186 $this->conf->setConfigFile('tests/utils/config/configPhp');
187 $this->conf->reset();
188
189 $optionsFile = 'tests/updater/options.php';
190 $options = '<?php
191$GLOBALS[\'privateLinkByDefault\'] = true;';
192 file_put_contents($optionsFile, $options);
193
194 // tmp config file.
195 $this->conf->setConfigFile('tests/updater/config');
196
197 // merge configs
198 $updater = new Updater(array(), array(), $this->conf, true);
199 // This writes a new config file in tests/updater/config.php
200 $updater->updateMethodMergeDeprecatedConfigFile();
201
202 // make sure updated field is changed
203 $this->conf->reload();
204 $this->assertTrue($this->conf->get('privacy.default_private_links'));
205 $this->assertFalse(is_file($optionsFile));
206 // Delete the generated file.
207 unlink($this->conf->getConfigFileExt());
208 }
209
210 /**
211 * Test mergeDeprecatedConfig in without options file.
212 */
213 public function testMergeDeprecatedConfigNoFile()
214 {
215 $updater = new Updater(array(), array(), $this->conf, true);
216 $updater->updateMethodMergeDeprecatedConfigFile();
217
218 $this->assertEquals('root', $this->conf->get('credentials.login'));
219 }
220
221 /**
222 * Test renameDashTags update method.
223 */
224 public function testRenameDashTags()
225 {
226 $refDB = new \ReferenceLinkDB();
227 $refDB->write(self::$testDatastore);
228 $linkDB = new LinkDB(self::$testDatastore, true, false);
229
230 $this->assertEmpty($linkDB->filterSearch(array('searchtags' => 'exclude')));
231 $updater = new Updater(array(), $linkDB, $this->conf, true);
232 $updater->updateMethodRenameDashTags();
233 $this->assertNotEmpty($linkDB->filterSearch(array('searchtags' => 'exclude')));
234 }
235
236 /**
237 * Convert old PHP config file to JSON config.
238 */
239 public function testConfigToJson()
240 {
241 $configFile = 'tests/utils/config/configPhp';
242 $this->conf->setConfigFile($configFile);
243 $this->conf->reset();
244
245 // The ConfigIO is initialized with ConfigPhp.
246 $this->assertTrue($this->conf->getConfigIO() instanceof ConfigPhp);
247
248 $updater = new Updater(array(), array(), $this->conf, false);
249 $done = $updater->updateMethodConfigToJson();
250 $this->assertTrue($done);
251
252 // The ConfigIO has been updated to ConfigJson.
253 $this->assertTrue($this->conf->getConfigIO() instanceof ConfigJson);
254 $this->assertTrue(file_exists($this->conf->getConfigFileExt()));
255
256 // Check JSON config data.
257 $this->conf->reload();
258 $this->assertEquals('root', $this->conf->get('credentials.login'));
259 $this->assertEquals('lala', $this->conf->get('redirector.url'));
260 $this->assertEquals('data/datastore.php', $this->conf->get('resource.datastore'));
261 $this->assertEquals('1', $this->conf->get('plugins.WALLABAG_VERSION'));
262
263 rename($configFile . '.save.php', $configFile . '.php');
264 unlink($this->conf->getConfigFileExt());
265 }
266
267 /**
268 * Launch config conversion update with an existing JSON file => nothing to do.
269 */
270 public function testConfigToJsonNothingToDo()
271 {
272 $filetime = filemtime($this->conf->getConfigFileExt());
273 $updater = new Updater(array(), array(), $this->conf, false);
274 $done = $updater->updateMethodConfigToJson();
275 $this->assertTrue($done);
276 $expected = filemtime($this->conf->getConfigFileExt());
277 $this->assertEquals($expected, $filetime);
278 }
279
280 /**
281 * Test escapeUnescapedConfig with valid data.
282 */
283 public function testEscapeConfig()
284 {
285 $sandbox = 'sandbox/config';
286 copy(self::$configFile . '.json.php', $sandbox . '.json.php');
287 $this->conf = new ConfigManager($sandbox);
288 $title = '<script>alert("title");</script>';
289 $headerLink = '<script>alert("header_link");</script>';
290 $this->conf->set('general.title', $title);
291 $this->conf->set('general.header_link', $headerLink);
292 $updater = new Updater(array(), array(), $this->conf, true);
293 $done = $updater->updateMethodEscapeUnescapedConfig();
294 $this->assertTrue($done);
295 $this->conf->reload();
296 $this->assertEquals(escape($title), $this->conf->get('general.title'));
297 $this->assertEquals(escape($headerLink), $this->conf->get('general.header_link'));
298 unlink($sandbox . '.json.php');
299 }
300
301 /**
302 * Test updateMethodApiSettings(): create default settings for the API (enabled + secret).
303 */
304 public function testUpdateApiSettings()
305 {
306 $confFile = 'sandbox/config';
307 copy(self::$configFile .'.json.php', $confFile .'.json.php');
308 $conf = new ConfigManager($confFile);
309 $updater = new Updater(array(), array(), $conf, true);
310
311 $this->assertFalse($conf->exists('api.enabled'));
312 $this->assertFalse($conf->exists('api.secret'));
313 $updater->updateMethodApiSettings();
314 $conf->reload();
315 $this->assertTrue($conf->get('api.enabled'));
316 $this->assertTrue($conf->exists('api.secret'));
317 unlink($confFile .'.json.php');
318 }
319
320 /**
321 * Test updateMethodApiSettings(): already set, do nothing.
322 */
323 public function testUpdateApiSettingsNothingToDo()
324 { 187 {
325 $confFile = 'sandbox/config'; 188 $this->updater->setBasePath('/subfolder');
326 copy(self::$configFile .'.json.php', $confFile .'.json.php'); 189 $this->conf->set('general.header_link', '?');
327 $conf = new ConfigManager($confFile);
328 $conf->set('api.enabled', false);
329 $conf->set('api.secret', '');
330 $updater = new Updater(array(), array(), $conf, true);
331 $updater->updateMethodApiSettings();
332 $this->assertFalse($conf->get('api.enabled'));
333 $this->assertEmpty($conf->get('api.secret'));
334 unlink($confFile .'.json.php');
335 }
336 190
337 /** 191 $this->updater->updateMethodRelativeHomeLink();
338 * Test updateMethodDatastoreIds().
339 */
340 public function testDatastoreIds()
341 {
342 $links = array(
343 '20121206_182539' => array(
344 'linkdate' => '20121206_182539',
345 'title' => 'Geek and Poke',
346 'url' => 'http://geek-and-poke.com/',
347 'description' => 'desc',
348 'tags' => 'dev cartoon tag1 tag2 tag3 tag4 ',
349 'updated' => '20121206_190301',
350 'private' => false,
351 ),
352 '20121206_172539' => array(
353 'linkdate' => '20121206_172539',
354 'title' => 'UserFriendly - Samba',
355 'url' => 'http://ars.userfriendly.org/cartoons/?id=20010306',
356 'description' => '',
357 'tags' => 'samba cartoon web',
358 'private' => false,
359 ),
360 '20121206_142300' => array(
361 'linkdate' => '20121206_142300',
362 'title' => 'UserFriendly - Web Designer',
363 'url' => 'http://ars.userfriendly.org/cartoons/?id=20121206',
364 'description' => 'Naming conventions... #private',
365 'tags' => 'samba cartoon web',
366 'private' => true,
367 ),
368 );
369 $refDB = new \ReferenceLinkDB();
370 $refDB->setLinks($links);
371 $refDB->write(self::$testDatastore);
372 $linkDB = new LinkDB(self::$testDatastore, true, false);
373
374 $checksum = hash_file('sha1', self::$testDatastore);
375
376 $this->conf->set('resource.data_dir', 'sandbox');
377 $this->conf->set('resource.datastore', self::$testDatastore);
378
379 $updater = new Updater(array(), $linkDB, $this->conf, true);
380 $this->assertTrue($updater->updateMethodDatastoreIds());
381
382 $linkDB = new LinkDB(self::$testDatastore, true, false);
383
384 $backup = glob($this->conf->get('resource.data_dir') . '/datastore.'. date('YmdH') .'*.php');
385 $backup = $backup[0];
386
387 $this->assertFileExists($backup);
388 $this->assertEquals($checksum, hash_file('sha1', $backup));
389 unlink($backup);
390
391 $this->assertEquals(3, count($linkDB));
392 $this->assertTrue(isset($linkDB[0]));
393 $this->assertFalse(isset($linkDB[0]['linkdate']));
394 $this->assertEquals(0, $linkDB[0]['id']);
395 $this->assertEquals('UserFriendly - Web Designer', $linkDB[0]['title']);
396 $this->assertEquals('http://ars.userfriendly.org/cartoons/?id=20121206', $linkDB[0]['url']);
397 $this->assertEquals('Naming conventions... #private', $linkDB[0]['description']);
398 $this->assertEquals('samba cartoon web', $linkDB[0]['tags']);
399 $this->assertTrue($linkDB[0]['private']);
400 $this->assertEquals(
401 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_142300'),
402 $linkDB[0]['created']
403 );
404 192
405 $this->assertTrue(isset($linkDB[1])); 193 static::assertSame('/subfolder/', $this->conf->get('general.header_link'));
406 $this->assertFalse(isset($linkDB[1]['linkdate']));
407 $this->assertEquals(1, $linkDB[1]['id']);
408 $this->assertEquals('UserFriendly - Samba', $linkDB[1]['title']);
409 $this->assertEquals(
410 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_172539'),
411 $linkDB[1]['created']
412 );
413
414 $this->assertTrue(isset($linkDB[2]));
415 $this->assertFalse(isset($linkDB[2]['linkdate']));
416 $this->assertEquals(2, $linkDB[2]['id']);
417 $this->assertEquals('Geek and Poke', $linkDB[2]['title']);
418 $this->assertEquals(
419 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_182539'),
420 $linkDB[2]['created']
421 );
422 $this->assertEquals(
423 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_190301'),
424 $linkDB[2]['updated']
425 );
426 } 194 }
427 195
428 /** 196 public function testUpdateMethodRelativeHomeLinkDoNotRename(): void
429 * Test updateMethodDatastoreIds() with the update already applied: nothing to do.
430 */
431 public function testDatastoreIdsNothingToDo()
432 { 197 {
433 $refDB = new \ReferenceLinkDB(); 198 $this->conf->set('general.header_link', '~/my-blog');
434 $refDB->write(self::$testDatastore);
435 $linkDB = new LinkDB(self::$testDatastore, true, false);
436 199
437 $this->conf->set('resource.data_dir', 'sandbox'); 200 $this->updater->updateMethodRelativeHomeLink();
438 $this->conf->set('resource.datastore', self::$testDatastore);
439 201
440 $checksum = hash_file('sha1', self::$testDatastore); 202 static::assertSame('~/my-blog', $this->conf->get('general.header_link'));
441 $updater = new Updater(array(), $linkDB, $this->conf, true);
442 $this->assertTrue($updater->updateMethodDatastoreIds());
443 $this->assertEquals($checksum, hash_file('sha1', self::$testDatastore));
444 } 203 }
445 204
446 /** 205 public function testUpdateMethodMigrateExistingNotesUrl(): void
447 * Test defaultTheme update with default settings: nothing to do.
448 */
449 public function testDefaultThemeWithDefaultSettings()
450 { 206 {
451 $sandbox = 'sandbox/config'; 207 $this->updater->updateMethodMigrateExistingNotesUrl();
452 copy(self::$configFile . '.json.php', $sandbox . '.json.php');
453 $this->conf = new ConfigManager($sandbox);
454 $updater = new Updater([], [], $this->conf, true);
455 $this->assertTrue($updater->updateMethodDefaultTheme());
456
457 $this->assertEquals('tpl/', $this->conf->get('resource.raintpl_tpl'));
458 $this->assertEquals('default', $this->conf->get('resource.theme'));
459 $this->conf = new ConfigManager($sandbox);
460 $this->assertEquals('tpl/', $this->conf->get('resource.raintpl_tpl'));
461 $this->assertEquals('default', $this->conf->get('resource.theme'));
462 unlink($sandbox . '.json.php');
463 }
464 208
465 /** 209 static::assertSame($this->refDB->getLinks()[0]->getUrl(), $this->bookmarkService->get(0)->getUrl());
466 * Test defaultTheme update with a custom theme in a subfolder 210 static::assertSame($this->refDB->getLinks()[1]->getUrl(), $this->bookmarkService->get(1)->getUrl());
467 */ 211 static::assertSame($this->refDB->getLinks()[4]->getUrl(), $this->bookmarkService->get(4)->getUrl());
468 public function testDefaultThemeWithCustomTheme() 212 static::assertSame($this->refDB->getLinks()[6]->getUrl(), $this->bookmarkService->get(6)->getUrl());
469 { 213 static::assertSame($this->refDB->getLinks()[7]->getUrl(), $this->bookmarkService->get(7)->getUrl());
470 $theme = 'iamanartist'; 214 static::assertSame($this->refDB->getLinks()[8]->getUrl(), $this->bookmarkService->get(8)->getUrl());
471 $sandbox = 'sandbox/config'; 215 static::assertSame($this->refDB->getLinks()[9]->getUrl(), $this->bookmarkService->get(9)->getUrl());
472 copy(self::$configFile . '.json.php', $sandbox . '.json.php'); 216 static::assertSame('/shaare/WDWyig', $this->bookmarkService->get(42)->getUrl());
473 $this->conf = new ConfigManager($sandbox); 217 static::assertSame('/shaare/WDWyig', $this->bookmarkService->get(41)->getUrl());
474 mkdir('sandbox/'. $theme); 218 static::assertSame('/shaare/0gCTjQ', $this->bookmarkService->get(10)->getUrl());
475 touch('sandbox/'. $theme .'/linklist.html'); 219 static::assertSame('/shaare/PCRizQ', $this->bookmarkService->get(11)->getUrl());
476 $this->conf->set('resource.raintpl_tpl', 'sandbox/'. $theme .'/');
477 $updater = new Updater([], [], $this->conf, true);
478 $this->assertTrue($updater->updateMethodDefaultTheme());
479
480 $this->assertEquals('sandbox', $this->conf->get('resource.raintpl_tpl'));
481 $this->assertEquals($theme, $this->conf->get('resource.theme'));
482 $this->conf = new ConfigManager($sandbox);
483 $this->assertEquals('sandbox', $this->conf->get('resource.raintpl_tpl'));
484 $this->assertEquals($theme, $this->conf->get('resource.theme'));
485 unlink($sandbox . '.json.php');
486 unlink('sandbox/'. $theme .'/linklist.html');
487 rmdir('sandbox/'. $theme);
488 }
489
490 /**
491 * Test updateMethodEscapeMarkdown with markdown plugin enabled
492 * => setting markdown_escape set to false.
493 */
494 public function testEscapeMarkdownSettingToFalse()
495 {
496 $sandboxConf = 'sandbox/config';
497 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
498 $this->conf = new ConfigManager($sandboxConf);
499
500 $this->conf->set('general.enabled_plugins', ['markdown']);
501 $updater = new Updater([], [], $this->conf, true);
502 $this->assertTrue($updater->updateMethodEscapeMarkdown());
503 $this->assertFalse($this->conf->get('security.markdown_escape'));
504
505 // reload from file
506 $this->conf = new ConfigManager($sandboxConf);
507 $this->assertFalse($this->conf->get('security.markdown_escape'));
508 }
509
510
511 /**
512 * Test updateMethodEscapeMarkdown with markdown plugin disabled
513 * => setting markdown_escape set to true.
514 */
515 public function testEscapeMarkdownSettingToTrue()
516 {
517 $sandboxConf = 'sandbox/config';
518 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
519 $this->conf = new ConfigManager($sandboxConf);
520
521 $this->conf->set('general.enabled_plugins', []);
522 $updater = new Updater([], [], $this->conf, true);
523 $this->assertTrue($updater->updateMethodEscapeMarkdown());
524 $this->assertTrue($this->conf->get('security.markdown_escape'));
525
526 // reload from file
527 $this->conf = new ConfigManager($sandboxConf);
528 $this->assertTrue($this->conf->get('security.markdown_escape'));
529 }
530
531 /**
532 * Test updateMethodEscapeMarkdown with nothing to do (setting already enabled)
533 */
534 public function testEscapeMarkdownSettingNothingToDoEnabled()
535 {
536 $sandboxConf = 'sandbox/config';
537 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
538 $this->conf = new ConfigManager($sandboxConf);
539 $this->conf->set('security.markdown_escape', true);
540 $updater = new Updater([], [], $this->conf, true);
541 $this->assertTrue($updater->updateMethodEscapeMarkdown());
542 $this->assertTrue($this->conf->get('security.markdown_escape'));
543 }
544
545 /**
546 * Test updateMethodEscapeMarkdown with nothing to do (setting already disabled)
547 */
548 public function testEscapeMarkdownSettingNothingToDoDisabled()
549 {
550 $this->conf->set('security.markdown_escape', false);
551 $updater = new Updater([], [], $this->conf, true);
552 $this->assertTrue($updater->updateMethodEscapeMarkdown());
553 $this->assertFalse($this->conf->get('security.markdown_escape'));
554 }
555
556 /**
557 * Test updateMethodPiwikUrl with valid data
558 */
559 public function testUpdatePiwikUrlValid()
560 {
561 $sandboxConf = 'sandbox/config';
562 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
563 $this->conf = new ConfigManager($sandboxConf);
564 $url = 'mypiwik.tld';
565 $this->conf->set('plugins.PIWIK_URL', $url);
566 $updater = new Updater([], [], $this->conf, true);
567 $this->assertTrue($updater->updateMethodPiwikUrl());
568 $this->assertEquals('http://'. $url, $this->conf->get('plugins.PIWIK_URL'));
569
570 // reload from file
571 $this->conf = new ConfigManager($sandboxConf);
572 $this->assertEquals('http://'. $url, $this->conf->get('plugins.PIWIK_URL'));
573 }
574
575 /**
576 * Test updateMethodPiwikUrl without setting
577 */
578 public function testUpdatePiwikUrlEmpty()
579 {
580 $updater = new Updater([], [], $this->conf, true);
581 $this->assertTrue($updater->updateMethodPiwikUrl());
582 $this->assertEmpty($this->conf->get('plugins.PIWIK_URL'));
583 }
584
585 /**
586 * Test updateMethodPiwikUrl: valid URL, nothing to do
587 */
588 public function testUpdatePiwikUrlNothingToDo()
589 {
590 $url = 'https://mypiwik.tld';
591 $this->conf->set('plugins.PIWIK_URL', $url);
592 $updater = new Updater([], [], $this->conf, true);
593 $this->assertTrue($updater->updateMethodPiwikUrl());
594 $this->assertEquals($url, $this->conf->get('plugins.PIWIK_URL'));
595 }
596
597 /**
598 * Test updateMethodAtomDefault with show_atom set to false
599 * => update to true.
600 */
601 public function testUpdateMethodAtomDefault()
602 {
603 $sandboxConf = 'sandbox/config';
604 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
605 $this->conf = new ConfigManager($sandboxConf);
606 $this->conf->set('feed.show_atom', false);
607 $updater = new Updater([], [], $this->conf, true);
608 $this->assertTrue($updater->updateMethodAtomDefault());
609 $this->assertTrue($this->conf->get('feed.show_atom'));
610 // reload from file
611 $this->conf = new ConfigManager($sandboxConf);
612 $this->assertTrue($this->conf->get('feed.show_atom'));
613 }
614 /**
615 * Test updateMethodAtomDefault with show_atom not set.
616 * => nothing to do
617 */
618 public function testUpdateMethodAtomDefaultNoExist()
619 {
620 $sandboxConf = 'sandbox/config';
621 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
622 $this->conf = new ConfigManager($sandboxConf);
623 $updater = new Updater([], [], $this->conf, true);
624 $this->assertTrue($updater->updateMethodAtomDefault());
625 $this->assertTrue($this->conf->get('feed.show_atom'));
626 }
627 /**
628 * Test updateMethodAtomDefault with show_atom set to true.
629 * => nothing to do
630 */
631 public function testUpdateMethodAtomDefaultAlreadyTrue()
632 {
633 $sandboxConf = 'sandbox/config';
634 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
635 $this->conf = new ConfigManager($sandboxConf);
636 $this->conf->set('feed.show_atom', true);
637 $updater = new Updater([], [], $this->conf, true);
638 $this->assertTrue($updater->updateMethodAtomDefault());
639 $this->assertTrue($this->conf->get('feed.show_atom'));
640 }
641
642 /**
643 * Test updateMethodDownloadSizeAndTimeoutConf, it should be set if none is already defined.
644 */
645 public function testUpdateMethodDownloadSizeAndTimeoutConf()
646 {
647 $sandboxConf = 'sandbox/config';
648 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
649 $this->conf = new ConfigManager($sandboxConf);
650 $updater = new Updater([], [], $this->conf, true);
651 $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
652 $this->assertEquals(4194304, $this->conf->get('general.download_max_size'));
653 $this->assertEquals(30, $this->conf->get('general.download_timeout'));
654
655 $this->conf = new ConfigManager($sandboxConf);
656 $this->assertEquals(4194304, $this->conf->get('general.download_max_size'));
657 $this->assertEquals(30, $this->conf->get('general.download_timeout'));
658 }
659
660 /**
661 * Test updateMethodDownloadSizeAndTimeoutConf, it shouldn't be set if it is already defined.
662 */
663 public function testUpdateMethodDownloadSizeAndTimeoutConfIgnore()
664 {
665 $sandboxConf = 'sandbox/config';
666 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
667 $this->conf = new ConfigManager($sandboxConf);
668 $this->conf->set('general.download_max_size', 38);
669 $this->conf->set('general.download_timeout', 70);
670 $updater = new Updater([], [], $this->conf, true);
671 $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
672 $this->assertEquals(38, $this->conf->get('general.download_max_size'));
673 $this->assertEquals(70, $this->conf->get('general.download_timeout'));
674 }
675
676 /**
677 * Test updateMethodDownloadSizeAndTimeoutConf, only the maz size should be set here.
678 */
679 public function testUpdateMethodDownloadSizeAndTimeoutConfOnlySize()
680 {
681 $sandboxConf = 'sandbox/config';
682 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
683 $this->conf = new ConfigManager($sandboxConf);
684 $this->conf->set('general.download_max_size', 38);
685 $updater = new Updater([], [], $this->conf, true);
686 $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
687 $this->assertEquals(38, $this->conf->get('general.download_max_size'));
688 $this->assertEquals(30, $this->conf->get('general.download_timeout'));
689 }
690
691 /**
692 * Test updateMethodDownloadSizeAndTimeoutConf, only the time out should be set here.
693 */
694 public function testUpdateMethodDownloadSizeAndTimeoutConfOnlyTimeout()
695 {
696 $sandboxConf = 'sandbox/config';
697 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
698 $this->conf = new ConfigManager($sandboxConf);
699 $this->conf->set('general.download_timeout', 3);
700 $updater = new Updater([], [], $this->conf, true);
701 $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf());
702 $this->assertEquals(4194304, $this->conf->get('general.download_max_size'));
703 $this->assertEquals(3, $this->conf->get('general.download_timeout'));
704 }
705
706 /**
707 * Test updateMethodWebThumbnailer with thumbnails enabled.
708 */
709 public function testUpdateMethodWebThumbnailerEnabled()
710 {
711 $this->conf->remove('thumbnails');
712 $this->conf->set('thumbnail.enable_thumbnails', true);
713 $updater = new Updater([], [], $this->conf, true, $_SESSION);
714 $this->assertTrue($updater->updateMethodWebThumbnailer());
715 $this->assertFalse($this->conf->exists('thumbnail'));
716 $this->assertEquals(\Shaarli\Thumbnailer::MODE_ALL, $this->conf->get('thumbnails.mode'));
717 $this->assertEquals(125, $this->conf->get('thumbnails.width'));
718 $this->assertEquals(90, $this->conf->get('thumbnails.height'));
719 $this->assertContains('You have enabled or changed thumbnails', $_SESSION['warnings'][0]);
720 }
721
722 /**
723 * Test updateMethodWebThumbnailer with thumbnails disabled.
724 */
725 public function testUpdateMethodWebThumbnailerDisabled()
726 {
727 $this->conf->remove('thumbnails');
728 $this->conf->set('thumbnail.enable_thumbnails', false);
729 $updater = new Updater([], [], $this->conf, true, $_SESSION);
730 $this->assertTrue($updater->updateMethodWebThumbnailer());
731 $this->assertFalse($this->conf->exists('thumbnail'));
732 $this->assertEquals(Thumbnailer::MODE_NONE, $this->conf->get('thumbnails.mode'));
733 $this->assertEquals(125, $this->conf->get('thumbnails.width'));
734 $this->assertEquals(90, $this->conf->get('thumbnails.height'));
735 $this->assertTrue(empty($_SESSION['warnings']));
736 }
737
738 /**
739 * Test updateMethodWebThumbnailer with thumbnails disabled.
740 */
741 public function testUpdateMethodWebThumbnailerNothingToDo()
742 {
743 $updater = new Updater([], [], $this->conf, true, $_SESSION);
744 $this->assertTrue($updater->updateMethodWebThumbnailer());
745 $this->assertFalse($this->conf->exists('thumbnail'));
746 $this->assertEquals(Thumbnailer::MODE_COMMON, $this->conf->get('thumbnails.mode'));
747 $this->assertEquals(90, $this->conf->get('thumbnails.width'));
748 $this->assertEquals(53, $this->conf->get('thumbnails.height'));
749 $this->assertTrue(empty($_SESSION['warnings']));
750 }
751
752 /**
753 * Test updateMethodSetSticky().
754 */
755 public function testUpdateStickyValid()
756 {
757 $blank = [
758 'id' => 1,
759 'url' => 'z',
760 'title' => '',
761 'description' => '',
762 'tags' => '',
763 'created' => new DateTime(),
764 ];
765 $links = [
766 1 => ['id' => 1] + $blank,
767 2 => ['id' => 2] + $blank,
768 ];
769 $refDB = new \ReferenceLinkDB();
770 $refDB->setLinks($links);
771 $refDB->write(self::$testDatastore);
772 $linkDB = new LinkDB(self::$testDatastore, true, false);
773
774 $updater = new Updater(array(), $linkDB, $this->conf, true);
775 $this->assertTrue($updater->updateMethodSetSticky());
776
777 $linkDB = new LinkDB(self::$testDatastore, true, false);
778 foreach ($linkDB as $link) {
779 $this->assertFalse($link['sticky']);
780 }
781 }
782
783 /**
784 * Test updateMethodSetSticky().
785 */
786 public function testUpdateStickyNothingToDo()
787 {
788 $blank = [
789 'id' => 1,
790 'url' => 'z',
791 'title' => '',
792 'description' => '',
793 'tags' => '',
794 'created' => new DateTime(),
795 ];
796 $links = [
797 1 => ['id' => 1, 'sticky' => true] + $blank,
798 2 => ['id' => 2] + $blank,
799 ];
800 $refDB = new \ReferenceLinkDB();
801 $refDB->setLinks($links);
802 $refDB->write(self::$testDatastore);
803 $linkDB = new LinkDB(self::$testDatastore, true, false);
804
805 $updater = new Updater(array(), $linkDB, $this->conf, true);
806 $this->assertTrue($updater->updateMethodSetSticky());
807
808 $linkDB = new LinkDB(self::$testDatastore, true, false);
809 $this->assertTrue($linkDB[1]['sticky']);
810 }
811
812 /**
813 * Test updateMethodRemoveRedirector().
814 */
815 public function testUpdateRemoveRedirector()
816 {
817 $sandboxConf = 'sandbox/config';
818 copy(self::$configFile . '.json.php', $sandboxConf . '.json.php');
819 $this->conf = new ConfigManager($sandboxConf);
820 $updater = new Updater([], null, $this->conf, true);
821 $this->assertTrue($updater->updateMethodRemoveRedirector());
822 $this->assertFalse($this->conf->exists('redirector'));
823 $this->conf = new ConfigManager($sandboxConf);
824 $this->assertFalse($this->conf->exists('redirector'));
825 } 220 }
826} 221}
diff --git a/tests/utils/FakeBookmarkService.php b/tests/utils/FakeBookmarkService.php
new file mode 100644
index 00000000..1ec5bc3d
--- /dev/null
+++ b/tests/utils/FakeBookmarkService.php
@@ -0,0 +1,18 @@
1<?php
2
3
4use Shaarli\Bookmark\BookmarkArray;
5use Shaarli\Bookmark\BookmarkFilter;
6use Shaarli\Bookmark\BookmarkIO;
7use Shaarli\Bookmark\BookmarkFileService;
8use Shaarli\Bookmark\Exception\EmptyDataStoreException;
9use Shaarli\Config\ConfigManager;
10use Shaarli\History;
11
12class FakeBookmarkService extends BookmarkFileService
13{
14 public function getBookmarks()
15 {
16 return $this->bookmarks;
17 }
18}
diff --git a/tests/utils/ReferenceHistory.php b/tests/utils/ReferenceHistory.php
index e411c417..516c9f51 100644
--- a/tests/utils/ReferenceHistory.php
+++ b/tests/utils/ReferenceHistory.php
@@ -76,7 +76,7 @@ class ReferenceHistory
76 } 76 }
77 77
78 /** 78 /**
79 * Returns the number of links in the reference data 79 * Returns the number of bookmarks in the reference data
80 */ 80 */
81 public function count() 81 public function count()
82 { 82 {
diff --git a/tests/utils/ReferenceLinkDB.php b/tests/utils/ReferenceLinkDB.php
index c12bcb67..fc3cb109 100644
--- a/tests/utils/ReferenceLinkDB.php
+++ b/tests/utils/ReferenceLinkDB.php
@@ -1,30 +1,39 @@
1<?php 1<?php
2 2
3use Shaarli\Bookmark\LinkDB; 3use Shaarli\Bookmark\Bookmark;
4use Shaarli\Bookmark\BookmarkArray;
4 5
5/** 6/**
6 * Populates a reference datastore to test LinkDB 7 * Populates a reference datastore to test Bookmark
7 */ 8 */
8class ReferenceLinkDB 9class ReferenceLinkDB
9{ 10{
10 public static $NB_LINKS_TOTAL = 11; 11 public static $NB_LINKS_TOTAL = 11;
11 12
12 private $_links = array(); 13 private $bookmarks = array();
13 private $_publicCount = 0; 14 private $_publicCount = 0;
14 private $_privateCount = 0; 15 private $_privateCount = 0;
15 16
17 private $isLegacy;
18
16 /** 19 /**
17 * Populates the test DB with reference data 20 * Populates the test DB with reference data
21 *
22 * @param bool $isLegacy Use links as array instead of Bookmark object
18 */ 23 */
19 public function __construct() 24 public function __construct($isLegacy = false)
20 { 25 {
26 $this->isLegacy = $isLegacy;
27 if (! $this->isLegacy) {
28 $this->bookmarks = new BookmarkArray();
29 }
21 $this->addLink( 30 $this->addLink(
22 11, 31 11,
23 'Pined older', 32 'Pined older',
24 '?PCRizQ', 33 '/shaare/PCRizQ',
25 'This is an older pinned link', 34 'This is an older pinned link',
26 0, 35 0,
27 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20100309_101010'), 36 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100309_101010'),
28 '', 37 '',
29 null, 38 null,
30 'PCRizQ', 39 'PCRizQ',
@@ -34,10 +43,10 @@ class ReferenceLinkDB
34 $this->addLink( 43 $this->addLink(
35 10, 44 10,
36 'Pined', 45 'Pined',
37 '?0gCTjQ', 46 '/shaare/0gCTjQ',
38 'This is a pinned link', 47 'This is a pinned link',
39 0, 48 0,
40 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121207_152312'), 49 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121207_152312'),
41 '', 50 '',
42 null, 51 null,
43 '0gCTjQ', 52 '0gCTjQ',
@@ -47,10 +56,10 @@ class ReferenceLinkDB
47 $this->addLink( 56 $this->addLink(
48 41, 57 41,
49 'Link title: @website', 58 'Link title: @website',
50 '?WDWyig', 59 '/shaare/WDWyig',
51 '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',
52 0, 61 0,
53 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114651'), 62 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114651'),
54 'sTuff', 63 'sTuff',
55 null, 64 null,
56 'WDWyig' 65 'WDWyig'
@@ -59,10 +68,10 @@ class ReferenceLinkDB
59 $this->addLink( 68 $this->addLink(
60 42, 69 42,
61 'Note: I have a big ID but an old date', 70 'Note: I have a big ID but an old date',
62 '?WDWyig', 71 '/shaare/WDWyig',
63 'Used to test links reordering.', 72 'Used to test bookmarks reordering.',
64 0, 73 0,
65 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20100310_101010'), 74 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20100310_101010'),
66 'ut' 75 'ut'
67 ); 76 );
68 77
@@ -72,7 +81,7 @@ class ReferenceLinkDB
72 'http://www.php-fig.org/psr/psr-2/', 81 'http://www.php-fig.org/psr/psr-2/',
73 '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.',
74 0, 83 0,
75 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_152312'), 84 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_152312'),
76 '' 85 ''
77 ); 86 );
78 87
@@ -82,9 +91,9 @@ class ReferenceLinkDB
82 'https://static.fsf.org/nosvn/faif-2.0.pdf', 91 'https://static.fsf.org/nosvn/faif-2.0.pdf',
83 'Richard Stallman and the Free Software Revolution. Read this. #hashtag', 92 'Richard Stallman and the Free Software Revolution. Read this. #hashtag',
84 0, 93 0,
85 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20150310_114633'), 94 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150310_114633'),
86 'free gnu software stallman -exclude stuff hashtag', 95 'free gnu software stallman -exclude stuff hashtag',
87 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160803_093033') 96 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160803_093033')
88 ); 97 );
89 98
90 $this->addLink( 99 $this->addLink(
@@ -93,9 +102,9 @@ class ReferenceLinkDB
93 'http://mediagoblin.org/', 102 'http://mediagoblin.org/',
94 'A free software media publishing platform #hashtagOther', 103 'A free software media publishing platform #hashtagOther',
95 0, 104 0,
96 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20130614_184135'), 105 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130614_184135'),
97 'gnu media web .hidden hashtag', 106 'gnu media web .hidden hashtag',
98 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20130615_184230'), 107 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20130615_184230'),
99 'IuWvgA' 108 'IuWvgA'
100 ); 109 );
101 110
@@ -105,7 +114,7 @@ class ReferenceLinkDB
105 'https://dvcs.w3.org/hg/markup-validator/summary', 114 'https://dvcs.w3.org/hg/markup-validator/summary',
106 'Mercurial repository for the W3C Validator #private', 115 'Mercurial repository for the W3C Validator #private',
107 1, 116 1,
108 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20141125_084734'), 117 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20141125_084734'),
109 'css html w3c web Mercurial' 118 'css html w3c web Mercurial'
110 ); 119 );
111 120
@@ -115,7 +124,7 @@ class ReferenceLinkDB
115 'http://ars.userfriendly.org/cartoons/?id=20121206', 124 'http://ars.userfriendly.org/cartoons/?id=20121206',
116 'Naming conventions... #private', 125 'Naming conventions... #private',
117 0, 126 0,
118 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_142300'), 127 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_142300'),
119 'dev cartoon web' 128 'dev cartoon web'
120 ); 129 );
121 130
@@ -125,7 +134,7 @@ class ReferenceLinkDB
125 'http://ars.userfriendly.org/cartoons/?id=20010306', 134 'http://ars.userfriendly.org/cartoons/?id=20010306',
126 'Tropical printing', 135 'Tropical printing',
127 0, 136 0,
128 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_172539'), 137 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_172539'),
129 'samba cartoon web' 138 'samba cartoon web'
130 ); 139 );
131 140
@@ -135,7 +144,7 @@ class ReferenceLinkDB
135 'http://geek-and-poke.com/', 144 'http://geek-and-poke.com/',
136 '', 145 '',
137 1, 146 1,
138 DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20121206_182539'), 147 DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20121206_182539'),
139 'dev cartoon tag1 tag2 tag3 tag4 ' 148 'dev cartoon tag1 tag2 tag3 tag4 '
140 ); 149 );
141 } 150 }
@@ -164,10 +173,15 @@ class ReferenceLinkDB
164 'tags' => $tags, 173 'tags' => $tags,
165 'created' => $date, 174 'created' => $date,
166 'updated' => $updated, 175 'updated' => $updated,
167 'shorturl' => $shorturl ? $shorturl : smallHash($date->format(LinkDB::LINK_DATE_FORMAT) . $id), 176 'shorturl' => $shorturl ? $shorturl : smallHash($date->format(Bookmark::LINK_DATE_FORMAT) . $id),
168 'sticky' => $pinned 177 'sticky' => $pinned
169 ); 178 );
170 $this->_links[$id] = $link; 179 if (! $this->isLegacy) {
180 $bookmark = new Bookmark();
181 $this->bookmarks[$id] = $bookmark->fromArray($link);
182 } else {
183 $this->bookmarks[$id] = $link;
184 }
171 185
172 if ($private) { 186 if ($private) {
173 $this->_privateCount++; 187 $this->_privateCount++;
@@ -184,37 +198,38 @@ class ReferenceLinkDB
184 $this->reorder(); 198 $this->reorder();
185 file_put_contents( 199 file_put_contents(
186 $filename, 200 $filename,
187 '<?php /* '.base64_encode(gzdeflate(serialize($this->_links))).' */ ?>' 201 '<?php /* '.base64_encode(gzdeflate(serialize($this->bookmarks))).' */ ?>'
188 ); 202 );
189 } 203 }
190 204
191 /** 205 /**
192 * Reorder links by creation date (newest first). 206 * Reorder links by creation date (newest first).
193 * 207 *
194 * Also update the urls and ids mapping arrays.
195 *
196 * @param string $order ASC|DESC 208 * @param string $order ASC|DESC
197 */ 209 */
198 public function reorder($order = 'DESC') 210 public function reorder($order = 'DESC')
199 { 211 {
200 // backward compatibility: ignore reorder if the the `created` field doesn't exist 212 if (! $this->isLegacy) {
201 if (! isset(array_values($this->_links)[0]['created'])) { 213 $this->bookmarks->reorder($order);
202 return; 214 } else {
203 } 215 $order = $order === 'ASC' ? -1 : 1;
204 216 // backward compatibility: ignore reorder if the the `created` field doesn't exist
205 $order = $order === 'ASC' ? -1 : 1; 217 if (! isset(array_values($this->bookmarks)[0]['created'])) {
206 // Reorder array by dates. 218 return;
207 usort($this->_links, function ($a, $b) use ($order) {
208 if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) {
209 return $a['sticky'] ? -1 : 1;
210 } 219 }
211 220
212 return $a['created'] < $b['created'] ? 1 * $order : -1 * $order; 221 usort($this->bookmarks, function ($a, $b) use ($order) {
213 }); 222 if (isset($a['sticky']) && isset($b['sticky']) && $a['sticky'] !== $b['sticky']) {
223 return $a['sticky'] ? -1 : 1;
224 }
225
226 return $a['created'] < $b['created'] ? 1 * $order : -1 * $order;
227 });
228 }
214 } 229 }
215 230
216 /** 231 /**
217 * Returns the number of links in the reference data 232 * Returns the number of bookmarks in the reference data
218 */ 233 */
219 public function countLinks() 234 public function countLinks()
220 { 235 {
@@ -222,7 +237,7 @@ class ReferenceLinkDB
222 } 237 }
223 238
224 /** 239 /**
225 * Returns the number of public links in the reference data 240 * Returns the number of public bookmarks in the reference data
226 */ 241 */
227 public function countPublicLinks() 242 public function countPublicLinks()
228 { 243 {
@@ -230,7 +245,7 @@ class ReferenceLinkDB
230 } 245 }
231 246
232 /** 247 /**
233 * Returns the number of private links in the reference data 248 * Returns the number of private bookmarks in the reference data
234 */ 249 */
235 public function countPrivateLinks() 250 public function countPrivateLinks()
236 { 251 {
@@ -238,14 +253,20 @@ class ReferenceLinkDB
238 } 253 }
239 254
240 /** 255 /**
241 * Returns the number of links without tag 256 * Returns the number of bookmarks without tag
242 */ 257 */
243 public function countUntaggedLinks() 258 public function countUntaggedLinks()
244 { 259 {
245 $cpt = 0; 260 $cpt = 0;
246 foreach ($this->_links as $link) { 261 foreach ($this->bookmarks as $link) {
247 if (empty($link['tags'])) { 262 if (! $this->isLegacy) {
248 ++$cpt; 263 if (empty($link->getTags())) {
264 ++$cpt;
265 }
266 } else {
267 if (empty($link['tags'])) {
268 ++$cpt;
269 }
249 } 270 }
250 } 271 }
251 return $cpt; 272 return $cpt;
@@ -254,16 +275,16 @@ class ReferenceLinkDB
254 public function getLinks() 275 public function getLinks()
255 { 276 {
256 $this->reorder(); 277 $this->reorder();
257 return $this->_links; 278 return $this->bookmarks;
258 } 279 }
259 280
260 /** 281 /**
261 * Setter to override link creation. 282 * Setter to override link creation.
262 * 283 *
263 * @param array $links List of links. 284 * @param array $links List of bookmarks.
264 */ 285 */
265 public function setLinks($links) 286 public function setLinks($links)
266 { 287 {
267 $this->_links = $links; 288 $this->bookmarks = $links;
268 } 289 }
269} 290}
diff --git a/tests/utils/config/configJson.json.php b/tests/utils/config/configJson.json.php
index 1549ddfc..b04dc303 100644
--- a/tests/utils/config/configJson.json.php
+++ b/tests/utils/config/configJson.json.php
@@ -41,12 +41,12 @@
41 "foo": "bar" 41 "foo": "bar"
42 }, 42 },
43 "resource": { 43 "resource": {
44 "datastore": "tests\/utils\/config\/datastore.php", 44 "datastore": "sandbox/datastore.php",
45 "data_dir": "sandbox\/", 45 "data_dir": "sandbox\/",
46 "raintpl_tpl": "tpl\/", 46 "raintpl_tpl": "tpl\/",
47 "config": "data\/config.php", 47 "config": "data\/config.php",
48 "ban_file": "data\/ipbans.php", 48 "ban_file": "data\/ipbans.php",
49 "updates": "data\/updates.txt", 49 "updates": "sandbox/updates.txt",
50 "log": "data\/log.txt", 50 "log": "data\/log.txt",
51 "update_check": "data\/lastupdatecheck.txt", 51 "update_check": "data\/lastupdatecheck.txt",
52 "history": "data\/history.php", 52 "history": "data\/history.php",
@@ -59,7 +59,7 @@
59 "WALLABAG_VERSION": 1 59 "WALLABAG_VERSION": 1
60 }, 60 },
61 "dev": { 61 "dev": {
62 "debug": true 62 "debug": false
63 }, 63 },
64 "updates": { 64 "updates": {
65 "check_updates": false, 65 "check_updates": false,