diff options
author | ArthurHoaro <arthur@hoa.ro> | 2018-07-28 11:19:53 +0200 |
---|---|---|
committer | ArthurHoaro <arthur@hoa.ro> | 2018-07-28 11:19:53 +0200 |
commit | 7982c3ff183aa985177bdaeacda4feb22a739e00 (patch) | |
tree | 728e07251072f3a1df63c017c0dce54fa1acb390 /tests | |
parent | 2075321f6569dfa610905991b315aae1956b7f78 (diff) | |
parent | ed7e1be12d65bdb1b924c7efb6a84fd591192c6c (diff) | |
download | Shaarli-7982c3ff183aa985177bdaeacda4feb22a739e00.tar.gz Shaarli-7982c3ff183aa985177bdaeacda4feb22a739e00.tar.zst Shaarli-7982c3ff183aa985177bdaeacda4feb22a739e00.zip |
Merge tag 'v0.10.0' into latest
Release v0.10.0
Diffstat (limited to 'tests')
31 files changed, 2042 insertions, 184 deletions
diff --git a/tests/HttpUtils/ClientIpIdTest.php b/tests/HttpUtils/ClientIpIdTest.php new file mode 100644 index 00000000..c15ac5cc --- /dev/null +++ b/tests/HttpUtils/ClientIpIdTest.php | |||
@@ -0,0 +1,52 @@ | |||
1 | <?php | ||
2 | /** | ||
3 | * HttpUtils' tests | ||
4 | */ | ||
5 | |||
6 | require_once 'application/HttpUtils.php'; | ||
7 | |||
8 | /** | ||
9 | * Unitary tests for client_ip_id() | ||
10 | */ | ||
11 | class ClientIpIdTest extends PHPUnit_Framework_TestCase | ||
12 | { | ||
13 | /** | ||
14 | * Get a remote client ID based on its IP | ||
15 | */ | ||
16 | public function testClientIpIdRemote() | ||
17 | { | ||
18 | $this->assertEquals( | ||
19 | '10.1.167.42', | ||
20 | client_ip_id(['REMOTE_ADDR' => '10.1.167.42']) | ||
21 | ); | ||
22 | } | ||
23 | |||
24 | /** | ||
25 | * Get a remote client ID based on its IP and proxy information (1) | ||
26 | */ | ||
27 | public function testClientIpIdRemoteForwarded() | ||
28 | { | ||
29 | $this->assertEquals( | ||
30 | '10.1.167.42_127.0.1.47', | ||
31 | client_ip_id([ | ||
32 | 'REMOTE_ADDR' => '10.1.167.42', | ||
33 | 'HTTP_X_FORWARDED_FOR' => '127.0.1.47' | ||
34 | ]) | ||
35 | ); | ||
36 | } | ||
37 | |||
38 | /** | ||
39 | * Get a remote client ID based on its IP and proxy information (2) | ||
40 | */ | ||
41 | public function testClientIpIdRemoteForwardedClient() | ||
42 | { | ||
43 | $this->assertEquals( | ||
44 | '10.1.167.42_10.1.167.56_127.0.1.47', | ||
45 | client_ip_id([ | ||
46 | 'REMOTE_ADDR' => '10.1.167.42', | ||
47 | 'HTTP_X_FORWARDED_FOR' => '10.1.167.56', | ||
48 | 'HTTP_CLIENT_IP' => '127.0.1.47' | ||
49 | ]) | ||
50 | ); | ||
51 | } | ||
52 | } | ||
diff --git a/tests/LanguagesTest.php b/tests/LanguagesTest.php index 864ce630..4951e09a 100644 --- a/tests/LanguagesTest.php +++ b/tests/LanguagesTest.php | |||
@@ -176,6 +176,32 @@ class LanguagesTest extends \PHPUnit_Framework_TestCase | |||
176 | } | 176 | } |
177 | 177 | ||
178 | /** | 178 | /** |
179 | * Test t() with an extension language file coming from the theme in gettext mode | ||
180 | */ | ||
181 | public function testTranslationThemeExtensionGettext() | ||
182 | { | ||
183 | $this->conf->set('translation.mode', 'gettext'); | ||
184 | $this->conf->set('raintpl_tpl', 'tests/utils/customtpl/'); | ||
185 | $this->conf->set('theme', 'dummy'); | ||
186 | new Languages('en', $this->conf); | ||
187 | $txt = 'rooster'; // ignore me poedit | ||
188 | $this->assertEquals('rooster', t($txt, $txt, 1, 'dummy')); | ||
189 | } | ||
190 | |||
191 | /** | ||
192 | * Test t() with an extension language file coming from the theme in PHP mode | ||
193 | */ | ||
194 | public function testTranslationThemeExtensionPhp() | ||
195 | { | ||
196 | $this->conf->set('translation.mode', 'php'); | ||
197 | $this->conf->set('raintpl_tpl', 'tests/utils/customtpl/'); | ||
198 | $this->conf->set('theme', 'dummy'); | ||
199 | new Languages('en', $this->conf); | ||
200 | $txt = 'rooster'; // ignore me poedit | ||
201 | $this->assertEquals('rooster', t($txt, $txt, 1, 'dummy')); | ||
202 | } | ||
203 | |||
204 | /** | ||
179 | * Test t() with an extension language file in gettext mode | 205 | * Test t() with an extension language file in gettext mode |
180 | */ | 206 | */ |
181 | public function testTranslationExtensionGettext() | 207 | public function testTranslationExtensionGettext() |
diff --git a/tests/LinkDBTest.php b/tests/LinkDBTest.php index 5b2f3667..3b980878 100644 --- a/tests/LinkDBTest.php +++ b/tests/LinkDBTest.php | |||
@@ -542,4 +542,104 @@ class LinkDBTest extends PHPUnit_Framework_TestCase | |||
542 | $this->assertEquals(3, count($res)); | 542 | $this->assertEquals(3, count($res)); |
543 | $this->assertNotContains('cartoon', $linkDB[4]['tags']); | 543 | $this->assertNotContains('cartoon', $linkDB[4]['tags']); |
544 | } | 544 | } |
545 | |||
546 | /** | ||
547 | * Test linksCountPerTag all tags without filter. | ||
548 | * Equal occurrences should be sorted alphabetically. | ||
549 | */ | ||
550 | public function testCountLinkPerTagAllNoFilter() | ||
551 | { | ||
552 | $expected = [ | ||
553 | 'web' => 4, | ||
554 | 'cartoon' => 3, | ||
555 | 'dev' => 2, | ||
556 | 'gnu' => 2, | ||
557 | 'hashtag' => 2, | ||
558 | 'sTuff' => 2, | ||
559 | '-exclude' => 1, | ||
560 | '.hidden' => 1, | ||
561 | 'Mercurial' => 1, | ||
562 | 'css' => 1, | ||
563 | 'free' => 1, | ||
564 | 'html' => 1, | ||
565 | 'media' => 1, | ||
566 | 'samba' => 1, | ||
567 | 'software' => 1, | ||
568 | 'stallman' => 1, | ||
569 | 'tag1' => 1, | ||
570 | 'tag2' => 1, | ||
571 | 'tag3' => 1, | ||
572 | 'tag4' => 1, | ||
573 | 'ut' => 1, | ||
574 | 'w3c' => 1, | ||
575 | ]; | ||
576 | $tags = self::$privateLinkDB->linksCountPerTag(); | ||
577 | |||
578 | $this->assertEquals($expected, $tags, var_export($tags, true)); | ||
579 | } | ||
580 | |||
581 | /** | ||
582 | * Test linksCountPerTag all tags with filter. | ||
583 | * Equal occurrences should be sorted alphabetically. | ||
584 | */ | ||
585 | public function testCountLinkPerTagAllWithFilter() | ||
586 | { | ||
587 | $expected = [ | ||
588 | 'gnu' => 2, | ||
589 | 'hashtag' => 2, | ||
590 | '-exclude' => 1, | ||
591 | '.hidden' => 1, | ||
592 | 'free' => 1, | ||
593 | 'media' => 1, | ||
594 | 'software' => 1, | ||
595 | 'stallman' => 1, | ||
596 | 'stuff' => 1, | ||
597 | 'web' => 1, | ||
598 | ]; | ||
599 | $tags = self::$privateLinkDB->linksCountPerTag(['gnu']); | ||
600 | |||
601 | $this->assertEquals($expected, $tags, var_export($tags, true)); | ||
602 | } | ||
603 | |||
604 | /** | ||
605 | * Test linksCountPerTag public tags with filter. | ||
606 | * Equal occurrences should be sorted alphabetically. | ||
607 | */ | ||
608 | public function testCountLinkPerTagPublicWithFilter() | ||
609 | { | ||
610 | $expected = [ | ||
611 | 'gnu' => 2, | ||
612 | 'hashtag' => 2, | ||
613 | '-exclude' => 1, | ||
614 | '.hidden' => 1, | ||
615 | 'free' => 1, | ||
616 | 'media' => 1, | ||
617 | 'software' => 1, | ||
618 | 'stallman' => 1, | ||
619 | 'stuff' => 1, | ||
620 | 'web' => 1, | ||
621 | ]; | ||
622 | $tags = self::$privateLinkDB->linksCountPerTag(['gnu'], 'public'); | ||
623 | |||
624 | $this->assertEquals($expected, $tags, var_export($tags, true)); | ||
625 | } | ||
626 | |||
627 | /** | ||
628 | * Test linksCountPerTag public tags with filter. | ||
629 | * Equal occurrences should be sorted alphabetically. | ||
630 | */ | ||
631 | public function testCountLinkPerTagPrivateWithFilter() | ||
632 | { | ||
633 | $expected = [ | ||
634 | 'cartoon' => 1, | ||
635 | 'dev' => 1, | ||
636 | 'tag1' => 1, | ||
637 | 'tag2' => 1, | ||
638 | 'tag3' => 1, | ||
639 | 'tag4' => 1, | ||
640 | ]; | ||
641 | $tags = self::$privateLinkDB->linksCountPerTag(['dev'], 'private'); | ||
642 | |||
643 | $this->assertEquals($expected, $tags, var_export($tags, true)); | ||
644 | } | ||
545 | } | 645 | } |
diff --git a/tests/NetscapeBookmarkUtils/BookmarkImportTest.php b/tests/NetscapeBookmarkUtils/BookmarkImportTest.php index 4961aa2c..f0a958cb 100644 --- a/tests/NetscapeBookmarkUtils/BookmarkImportTest.php +++ b/tests/NetscapeBookmarkUtils/BookmarkImportTest.php | |||
@@ -127,6 +127,21 @@ class BookmarkImportTest extends PHPUnit_Framework_TestCase | |||
127 | } | 127 | } |
128 | 128 | ||
129 | /** | 129 | /** |
130 | * Attempt to import bookmarks from a file with a lowercase Doctype | ||
131 | */ | ||
132 | public function testImportLowecaseDoctype() | ||
133 | { | ||
134 | $files = file2array('lowercase_doctype.htm'); | ||
135 | $this->assertStringMatchesFormat( | ||
136 | 'File lowercase_doctype.htm (386 bytes) was successfully processed in %d seconds:' | ||
137 | .' 2 links imported, 0 links overwritten, 0 links skipped.', | ||
138 | NetscapeBookmarkUtils::import(null, $files, $this->linkDb, $this->conf, $this->history) | ||
139 | ); | ||
140 | $this->assertEquals(2, count($this->linkDb)); | ||
141 | } | ||
142 | |||
143 | |||
144 | /** | ||
130 | * Ensure IE dumps are supported | 145 | * Ensure IE dumps are supported |
131 | */ | 146 | */ |
132 | public function testImportInternetExplorerEncoding() | 147 | public function testImportInternetExplorerEncoding() |
diff --git a/tests/NetscapeBookmarkUtils/input/lowercase_doctype.htm b/tests/NetscapeBookmarkUtils/input/lowercase_doctype.htm new file mode 100644 index 00000000..8911ad19 --- /dev/null +++ b/tests/NetscapeBookmarkUtils/input/lowercase_doctype.htm | |||
@@ -0,0 +1,8 @@ | |||
1 | <!DOCTYPE netscape-bookmark-file-1> | ||
2 | <TITLE>Bookmarks</TITLE> | ||
3 | <H1>Bookmarks</H1> | ||
4 | <DL><p> | ||
5 | <DT><A HREF="https://private.tld" ADD_DATE="10/Oct/2000:13:55:36 +0300" PRIVATE="1" TAGS="private secret">Secret stuff</A> | ||
6 | <DD>Super-secret stuff you're not supposed to know about | ||
7 | <DT><A HREF="http://public.tld" ADD_DATE="1456433748" PRIVATE="0" TAGS="public hello world">Public stuff</A> | ||
8 | </DL><p> | ||
diff --git a/tests/SessionManagerTest.php b/tests/SessionManagerTest.php deleted file mode 100644 index aa75962a..00000000 --- a/tests/SessionManagerTest.php +++ /dev/null | |||
@@ -1,149 +0,0 @@ | |||
1 | <?php | ||
2 | require_once 'tests/utils/FakeConfigManager.php'; | ||
3 | |||
4 | // Initialize reference data _before_ PHPUnit starts a session | ||
5 | require_once 'tests/utils/ReferenceSessionIdHashes.php'; | ||
6 | ReferenceSessionIdHashes::genAllHashes(); | ||
7 | |||
8 | use \Shaarli\SessionManager; | ||
9 | use \PHPUnit\Framework\TestCase; | ||
10 | |||
11 | |||
12 | /** | ||
13 | * Test coverage for SessionManager | ||
14 | */ | ||
15 | class SessionManagerTest extends TestCase | ||
16 | { | ||
17 | // Session ID hashes | ||
18 | protected static $sidHashes = null; | ||
19 | |||
20 | // Fake ConfigManager | ||
21 | protected static $conf = null; | ||
22 | |||
23 | /** | ||
24 | * Assign reference data | ||
25 | */ | ||
26 | public static function setUpBeforeClass() | ||
27 | { | ||
28 | self::$sidHashes = ReferenceSessionIdHashes::getHashes(); | ||
29 | self::$conf = new FakeConfigManager(); | ||
30 | } | ||
31 | |||
32 | /** | ||
33 | * Generate a session token | ||
34 | */ | ||
35 | public function testGenerateToken() | ||
36 | { | ||
37 | $session = []; | ||
38 | $sessionManager = new SessionManager($session, self::$conf); | ||
39 | |||
40 | $token = $sessionManager->generateToken(); | ||
41 | |||
42 | $this->assertEquals(1, $session['tokens'][$token]); | ||
43 | $this->assertEquals(40, strlen($token)); | ||
44 | } | ||
45 | |||
46 | /** | ||
47 | * Check a session token | ||
48 | */ | ||
49 | public function testCheckToken() | ||
50 | { | ||
51 | $token = '4dccc3a45ad9d03e5542b90c37d8db6d10f2b38b'; | ||
52 | $session = [ | ||
53 | 'tokens' => [ | ||
54 | $token => 1, | ||
55 | ], | ||
56 | ]; | ||
57 | $sessionManager = new SessionManager($session, self::$conf); | ||
58 | |||
59 | // check and destroy the token | ||
60 | $this->assertTrue($sessionManager->checkToken($token)); | ||
61 | $this->assertFalse(isset($session['tokens'][$token])); | ||
62 | |||
63 | // ensure the token has been destroyed | ||
64 | $this->assertFalse($sessionManager->checkToken($token)); | ||
65 | } | ||
66 | |||
67 | /** | ||
68 | * Generate and check a session token | ||
69 | */ | ||
70 | public function testGenerateAndCheckToken() | ||
71 | { | ||
72 | $session = []; | ||
73 | $sessionManager = new SessionManager($session, self::$conf); | ||
74 | |||
75 | $token = $sessionManager->generateToken(); | ||
76 | |||
77 | // ensure a token has been generated | ||
78 | $this->assertEquals(1, $session['tokens'][$token]); | ||
79 | $this->assertEquals(40, strlen($token)); | ||
80 | |||
81 | // check and destroy the token | ||
82 | $this->assertTrue($sessionManager->checkToken($token)); | ||
83 | $this->assertFalse(isset($session['tokens'][$token])); | ||
84 | |||
85 | // ensure the token has been destroyed | ||
86 | $this->assertFalse($sessionManager->checkToken($token)); | ||
87 | } | ||
88 | |||
89 | /** | ||
90 | * Check an invalid session token | ||
91 | */ | ||
92 | public function testCheckInvalidToken() | ||
93 | { | ||
94 | $session = []; | ||
95 | $sessionManager = new SessionManager($session, self::$conf); | ||
96 | |||
97 | $this->assertFalse($sessionManager->checkToken('4dccc3a45ad9d03e5542b90c37d8db6d10f2b38b')); | ||
98 | } | ||
99 | |||
100 | /** | ||
101 | * Test SessionManager::checkId with a valid ID - TEST ALL THE HASHES! | ||
102 | * | ||
103 | * This tests extensively covers all hash algorithms / bit representations | ||
104 | */ | ||
105 | public function testIsAnyHashSessionIdValid() | ||
106 | { | ||
107 | foreach (self::$sidHashes as $algo => $bpcs) { | ||
108 | foreach ($bpcs as $bpc => $hash) { | ||
109 | $this->assertTrue(SessionManager::checkId($hash)); | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * Test checkId with a valid ID - SHA-1 hashes | ||
116 | */ | ||
117 | public function testIsSha1SessionIdValid() | ||
118 | { | ||
119 | $this->assertTrue(SessionManager::checkId(sha1('shaarli'))); | ||
120 | } | ||
121 | |||
122 | /** | ||
123 | * Test checkId with a valid ID - SHA-256 hashes | ||
124 | */ | ||
125 | public function testIsSha256SessionIdValid() | ||
126 | { | ||
127 | $this->assertTrue(SessionManager::checkId(hash('sha256', 'shaarli'))); | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * Test checkId with a valid ID - SHA-512 hashes | ||
132 | */ | ||
133 | public function testIsSha512SessionIdValid() | ||
134 | { | ||
135 | $this->assertTrue(SessionManager::checkId(hash('sha512', 'shaarli'))); | ||
136 | } | ||
137 | |||
138 | /** | ||
139 | * Test checkId with invalid IDs. | ||
140 | */ | ||
141 | public function testIsSessionIdInvalid() | ||
142 | { | ||
143 | $this->assertFalse(SessionManager::checkId('')); | ||
144 | $this->assertFalse(SessionManager::checkId([])); | ||
145 | $this->assertFalse( | ||
146 | SessionManager::checkId('c0ZqcWF3VFE2NmJBdm1HMVQ0ZHJ3UmZPbTFsNGhkNHI=') | ||
147 | ); | ||
148 | } | ||
149 | } | ||
diff --git a/tests/ThumbnailerTest.php b/tests/ThumbnailerTest.php new file mode 100644 index 00000000..08311545 --- /dev/null +++ b/tests/ThumbnailerTest.php | |||
@@ -0,0 +1,114 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Shaarli; | ||
4 | |||
5 | use PHPUnit\Framework\TestCase; | ||
6 | use Shaarli\Config\ConfigManager; | ||
7 | use WebThumbnailer\Application\ConfigManager as WTConfigManager; | ||
8 | |||
9 | /** | ||
10 | * Class ThumbnailerTest | ||
11 | * | ||
12 | * We only make 1 thumb test because: | ||
13 | * | ||
14 | * 1. the thumbnailer library is itself tested | ||
15 | * 2. we don't want to make too many external requests during the tests | ||
16 | */ | ||
17 | class ThumbnailerTest extends TestCase | ||
18 | { | ||
19 | const WIDTH = 190; | ||
20 | |||
21 | const HEIGHT = 210; | ||
22 | |||
23 | /** | ||
24 | * @var Thumbnailer; | ||
25 | */ | ||
26 | protected $thumbnailer; | ||
27 | |||
28 | /** | ||
29 | * @var ConfigManager | ||
30 | */ | ||
31 | protected $conf; | ||
32 | |||
33 | public function setUp() | ||
34 | { | ||
35 | $this->conf = new ConfigManager('tests/utils/config/configJson'); | ||
36 | $this->conf->set('thumbnails.mode', Thumbnailer::MODE_ALL); | ||
37 | $this->conf->set('thumbnails.width', self::WIDTH); | ||
38 | $this->conf->set('thumbnails.height', self::HEIGHT); | ||
39 | $this->conf->set('dev.debug', true); | ||
40 | |||
41 | $this->thumbnailer = new Thumbnailer($this->conf); | ||
42 | // cache files in the sandbox | ||
43 | WTConfigManager::addFile('tests/utils/config/wt.json'); | ||
44 | } | ||
45 | |||
46 | public function tearDown() | ||
47 | { | ||
48 | $this->rrmdirContent('sandbox/'); | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * Test a thumbnail with a custom size in 'all' mode. | ||
53 | */ | ||
54 | public function testThumbnailAllValid() | ||
55 | { | ||
56 | $thumb = $this->thumbnailer->get('https://github.com/shaarli/Shaarli/'); | ||
57 | $this->assertNotFalse($thumb); | ||
58 | $image = imagecreatefromstring(file_get_contents($thumb)); | ||
59 | $this->assertEquals(self::WIDTH, imagesx($image)); | ||
60 | $this->assertEquals(self::HEIGHT, imagesy($image)); | ||
61 | } | ||
62 | |||
63 | /** | ||
64 | * Test a thumbnail with a custom size in 'common' mode. | ||
65 | */ | ||
66 | public function testThumbnailCommonValid() | ||
67 | { | ||
68 | $this->conf->set('thumbnails.mode', Thumbnailer::MODE_COMMON); | ||
69 | $thumb = $this->thumbnailer->get('https://imgur.com/jlFgGpe'); | ||
70 | $this->assertNotFalse($thumb); | ||
71 | $image = imagecreatefromstring(file_get_contents($thumb)); | ||
72 | $this->assertEquals(self::WIDTH, imagesx($image)); | ||
73 | $this->assertEquals(self::HEIGHT, imagesy($image)); | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * Test a thumbnail in 'common' mode which isn't include in common websites. | ||
78 | */ | ||
79 | public function testThumbnailCommonInvalid() | ||
80 | { | ||
81 | $this->conf->set('thumbnails.mode', Thumbnailer::MODE_COMMON); | ||
82 | $thumb = $this->thumbnailer->get('https://github.com/shaarli/Shaarli/'); | ||
83 | $this->assertFalse($thumb); | ||
84 | } | ||
85 | |||
86 | /** | ||
87 | * Test a thumbnail that can't be retrieved. | ||
88 | */ | ||
89 | public function testThumbnailNotValid() | ||
90 | { | ||
91 | $oldlog = ini_get('error_log'); | ||
92 | ini_set('error_log', '/dev/null'); | ||
93 | |||
94 | $thumbnailer = new Thumbnailer(new ConfigManager()); | ||
95 | $thumb = $thumbnailer->get('nope'); | ||
96 | $this->assertFalse($thumb); | ||
97 | |||
98 | ini_set('error_log', $oldlog); | ||
99 | } | ||
100 | |||
101 | protected function rrmdirContent($dir) { | ||
102 | if (is_dir($dir)) { | ||
103 | $objects = scandir($dir); | ||
104 | foreach ($objects as $object) { | ||
105 | if ($object != "." && $object != "..") { | ||
106 | if (is_dir($dir."/".$object)) | ||
107 | $this->rrmdirContent($dir."/".$object); | ||
108 | else | ||
109 | unlink($dir."/".$object); | ||
110 | } | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | } | ||
diff --git a/tests/Updater/UpdaterTest.php b/tests/Updater/UpdaterTest.php index fed175df..cacee2d2 100644 --- a/tests/Updater/UpdaterTest.php +++ b/tests/Updater/UpdaterTest.php | |||
@@ -2,6 +2,7 @@ | |||
2 | use Shaarli\Config\ConfigJson; | 2 | use Shaarli\Config\ConfigJson; |
3 | use Shaarli\Config\ConfigManager; | 3 | use Shaarli\Config\ConfigManager; |
4 | use Shaarli\Config\ConfigPhp; | 4 | use Shaarli\Config\ConfigPhp; |
5 | use Shaarli\Thumbnailer; | ||
5 | 6 | ||
6 | require_once 'tests/Updater/DummyUpdater.php'; | 7 | require_once 'tests/Updater/DummyUpdater.php'; |
7 | require_once 'inc/rain.tpl.class.php'; | 8 | require_once 'inc/rain.tpl.class.php'; |
@@ -20,7 +21,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
20 | /** | 21 | /** |
21 | * @var string Config file path (without extension). | 22 | * @var string Config file path (without extension). |
22 | */ | 23 | */ |
23 | protected static $configFile = 'tests/utils/config/configJson'; | 24 | protected static $configFile = 'sandbox/config'; |
24 | 25 | ||
25 | /** | 26 | /** |
26 | * @var ConfigManager | 27 | * @var ConfigManager |
@@ -32,6 +33,7 @@ class UpdaterTest extends PHPUnit_Framework_TestCase | |||
32 | */ | 33 | */ |
33 | public function setUp() | 34 | public function setUp() |
34 | { | 35 | { |
36 | copy('tests/utils/config/configJson.json.php', self::$configFile .'.json.php'); | ||
35 | $this->conf = new ConfigManager(self::$configFile); | 37 | $this->conf = new ConfigManager(self::$configFile); |
36 | } | 38 | } |
37 | 39 | ||
@@ -620,4 +622,114 @@ $GLOBALS[\'privateLinkByDefault\'] = true;'; | |||
620 | $this->assertTrue($updater->updateMethodAtomDefault()); | 622 | $this->assertTrue($updater->updateMethodAtomDefault()); |
621 | $this->assertTrue($this->conf->get('feed.show_atom')); | 623 | $this->assertTrue($this->conf->get('feed.show_atom')); |
622 | } | 624 | } |
625 | |||
626 | /** | ||
627 | * Test updateMethodDownloadSizeAndTimeoutConf, it should be set if none is already defined. | ||
628 | */ | ||
629 | public function testUpdateMethodDownloadSizeAndTimeoutConf() | ||
630 | { | ||
631 | $sandboxConf = 'sandbox/config'; | ||
632 | copy(self::$configFile . '.json.php', $sandboxConf . '.json.php'); | ||
633 | $this->conf = new ConfigManager($sandboxConf); | ||
634 | $updater = new Updater([], [], $this->conf, true); | ||
635 | $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf()); | ||
636 | $this->assertEquals(4194304, $this->conf->get('general.download_max_size')); | ||
637 | $this->assertEquals(30, $this->conf->get('general.download_timeout')); | ||
638 | |||
639 | $this->conf = new ConfigManager($sandboxConf); | ||
640 | $this->assertEquals(4194304, $this->conf->get('general.download_max_size')); | ||
641 | $this->assertEquals(30, $this->conf->get('general.download_timeout')); | ||
642 | } | ||
643 | |||
644 | /** | ||
645 | * Test updateMethodDownloadSizeAndTimeoutConf, it shouldn't be set if it is already defined. | ||
646 | */ | ||
647 | public function testUpdateMethodDownloadSizeAndTimeoutConfIgnore() | ||
648 | { | ||
649 | $sandboxConf = 'sandbox/config'; | ||
650 | copy(self::$configFile . '.json.php', $sandboxConf . '.json.php'); | ||
651 | $this->conf = new ConfigManager($sandboxConf); | ||
652 | $this->conf->set('general.download_max_size', 38); | ||
653 | $this->conf->set('general.download_timeout', 70); | ||
654 | $updater = new Updater([], [], $this->conf, true); | ||
655 | $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf()); | ||
656 | $this->assertEquals(38, $this->conf->get('general.download_max_size')); | ||
657 | $this->assertEquals(70, $this->conf->get('general.download_timeout')); | ||
658 | } | ||
659 | |||
660 | /** | ||
661 | * Test updateMethodDownloadSizeAndTimeoutConf, only the maz size should be set here. | ||
662 | */ | ||
663 | public function testUpdateMethodDownloadSizeAndTimeoutConfOnlySize() | ||
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 | $updater = new Updater([], [], $this->conf, true); | ||
670 | $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf()); | ||
671 | $this->assertEquals(38, $this->conf->get('general.download_max_size')); | ||
672 | $this->assertEquals(30, $this->conf->get('general.download_timeout')); | ||
673 | } | ||
674 | |||
675 | /** | ||
676 | * Test updateMethodDownloadSizeAndTimeoutConf, only the time out should be set here. | ||
677 | */ | ||
678 | public function testUpdateMethodDownloadSizeAndTimeoutConfOnlyTimeout() | ||
679 | { | ||
680 | $sandboxConf = 'sandbox/config'; | ||
681 | copy(self::$configFile . '.json.php', $sandboxConf . '.json.php'); | ||
682 | $this->conf = new ConfigManager($sandboxConf); | ||
683 | $this->conf->set('general.download_timeout', 3); | ||
684 | $updater = new Updater([], [], $this->conf, true); | ||
685 | $this->assertTrue($updater->updateMethodDownloadSizeAndTimeoutConf()); | ||
686 | $this->assertEquals(4194304, $this->conf->get('general.download_max_size')); | ||
687 | $this->assertEquals(3, $this->conf->get('general.download_timeout')); | ||
688 | } | ||
689 | |||
690 | /** | ||
691 | * Test updateMethodWebThumbnailer with thumbnails enabled. | ||
692 | */ | ||
693 | public function testUpdateMethodWebThumbnailerEnabled() | ||
694 | { | ||
695 | $this->conf->remove('thumbnails'); | ||
696 | $this->conf->set('thumbnail.enable_thumbnails', true); | ||
697 | $updater = new Updater([], [], $this->conf, true, $_SESSION); | ||
698 | $this->assertTrue($updater->updateMethodWebThumbnailer()); | ||
699 | $this->assertFalse($this->conf->exists('thumbnail')); | ||
700 | $this->assertEquals(\Shaarli\Thumbnailer::MODE_ALL, $this->conf->get('thumbnails.mode')); | ||
701 | $this->assertEquals(125, $this->conf->get('thumbnails.width')); | ||
702 | $this->assertEquals(90, $this->conf->get('thumbnails.height')); | ||
703 | $this->assertContains('You have enabled or changed thumbnails', $_SESSION['warnings'][0]); | ||
704 | } | ||
705 | |||
706 | /** | ||
707 | * Test updateMethodWebThumbnailer with thumbnails disabled. | ||
708 | */ | ||
709 | public function testUpdateMethodWebThumbnailerDisabled() | ||
710 | { | ||
711 | $this->conf->remove('thumbnails'); | ||
712 | $this->conf->set('thumbnail.enable_thumbnails', false); | ||
713 | $updater = new Updater([], [], $this->conf, true, $_SESSION); | ||
714 | $this->assertTrue($updater->updateMethodWebThumbnailer()); | ||
715 | $this->assertFalse($this->conf->exists('thumbnail')); | ||
716 | $this->assertEquals(Thumbnailer::MODE_NONE, $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->assertTrue(empty($_SESSION['warnings'])); | ||
720 | } | ||
721 | |||
722 | /** | ||
723 | * Test updateMethodWebThumbnailer with thumbnails disabled. | ||
724 | */ | ||
725 | public function testUpdateMethodWebThumbnailerNothingToDo() | ||
726 | { | ||
727 | $updater = new Updater([], [], $this->conf, true, $_SESSION); | ||
728 | $this->assertTrue($updater->updateMethodWebThumbnailer()); | ||
729 | $this->assertFalse($this->conf->exists('thumbnail')); | ||
730 | $this->assertEquals(Thumbnailer::MODE_COMMON, $this->conf->get('thumbnails.mode')); | ||
731 | $this->assertEquals(90, $this->conf->get('thumbnails.width')); | ||
732 | $this->assertEquals(53, $this->conf->get('thumbnails.height')); | ||
733 | $this->assertTrue(empty($_SESSION['warnings'])); | ||
734 | } | ||
623 | } | 735 | } |
diff --git a/tests/api/controllers/HistoryTest.php b/tests/api/controllers/history/HistoryTest.php index 61046d97..61046d97 100644 --- a/tests/api/controllers/HistoryTest.php +++ b/tests/api/controllers/history/HistoryTest.php | |||
diff --git a/tests/api/controllers/InfoTest.php b/tests/api/controllers/info/InfoTest.php index f7e63bfa..f7e63bfa 100644 --- a/tests/api/controllers/InfoTest.php +++ b/tests/api/controllers/info/InfoTest.php | |||
diff --git a/tests/api/controllers/DeleteLinkTest.php b/tests/api/controllers/links/DeleteLinkTest.php index 7d797137..7d797137 100644 --- a/tests/api/controllers/DeleteLinkTest.php +++ b/tests/api/controllers/links/DeleteLinkTest.php | |||
diff --git a/tests/api/controllers/GetLinkIdTest.php b/tests/api/controllers/links/GetLinkIdTest.php index 57528d5a..57528d5a 100644 --- a/tests/api/controllers/GetLinkIdTest.php +++ b/tests/api/controllers/links/GetLinkIdTest.php | |||
diff --git a/tests/api/controllers/GetLinksTest.php b/tests/api/controllers/links/GetLinksTest.php index d22ed3bf..d22ed3bf 100644 --- a/tests/api/controllers/GetLinksTest.php +++ b/tests/api/controllers/links/GetLinksTest.php | |||
diff --git a/tests/api/controllers/PostLinkTest.php b/tests/api/controllers/links/PostLinkTest.php index 31954e39..100a9170 100644 --- a/tests/api/controllers/PostLinkTest.php +++ b/tests/api/controllers/links/PostLinkTest.php | |||
@@ -3,11 +3,13 @@ | |||
3 | namespace Shaarli\Api\Controllers; | 3 | namespace Shaarli\Api\Controllers; |
4 | 4 | ||
5 | 5 | ||
6 | use PHPUnit\Framework\TestCase; | ||
6 | use Shaarli\Config\ConfigManager; | 7 | use Shaarli\Config\ConfigManager; |
7 | use Slim\Container; | 8 | use Slim\Container; |
8 | use Slim\Http\Environment; | 9 | use Slim\Http\Environment; |
9 | use Slim\Http\Request; | 10 | use Slim\Http\Request; |
10 | use Slim\Http\Response; | 11 | use Slim\Http\Response; |
12 | use Slim\Router; | ||
11 | 13 | ||
12 | /** | 14 | /** |
13 | * Class PostLinkTest | 15 | * Class PostLinkTest |
@@ -16,7 +18,7 @@ use Slim\Http\Response; | |||
16 | * | 18 | * |
17 | * @package Shaarli\Api\Controllers | 19 | * @package Shaarli\Api\Controllers |
18 | */ | 20 | */ |
19 | class PostLinkTest extends \PHPUnit_Framework_TestCase | 21 | class PostLinkTest extends TestCase |
20 | { | 22 | { |
21 | /** | 23 | /** |
22 | * @var string datastore to test write operations | 24 | * @var string datastore to test write operations |
@@ -78,7 +80,7 @@ class PostLinkTest extends \PHPUnit_Framework_TestCase | |||
78 | 80 | ||
79 | $this->controller = new Links($this->container); | 81 | $this->controller = new Links($this->container); |
80 | 82 | ||
81 | $mock = $this->getMock('\Slim\Router', ['relativePathFor']); | 83 | $mock = $this->createMock(Router::class); |
82 | $mock->expects($this->any()) | 84 | $mock->expects($this->any()) |
83 | ->method('relativePathFor') | 85 | ->method('relativePathFor') |
84 | ->willReturn('api/v1/links/1'); | 86 | ->willReturn('api/v1/links/1'); |
diff --git a/tests/api/controllers/PutLinkTest.php b/tests/api/controllers/links/PutLinkTest.php index 8a562571..8a562571 100644 --- a/tests/api/controllers/PutLinkTest.php +++ b/tests/api/controllers/links/PutLinkTest.php | |||
diff --git a/tests/api/controllers/tags/DeleteTagTest.php b/tests/api/controllers/tags/DeleteTagTest.php new file mode 100644 index 00000000..e0787ce2 --- /dev/null +++ b/tests/api/controllers/tags/DeleteTagTest.php | |||
@@ -0,0 +1,164 @@ | |||
1 | <?php | ||
2 | |||
3 | |||
4 | namespace Shaarli\Api\Controllers; | ||
5 | |||
6 | use Shaarli\Config\ConfigManager; | ||
7 | use Slim\Container; | ||
8 | use Slim\Http\Environment; | ||
9 | use Slim\Http\Request; | ||
10 | use Slim\Http\Response; | ||
11 | |||
12 | class DeleteTagTest extends \PHPUnit_Framework_TestCase | ||
13 | { | ||
14 | /** | ||
15 | * @var string datastore to test write operations | ||
16 | */ | ||
17 | protected static $testDatastore = 'sandbox/datastore.php'; | ||
18 | |||
19 | /** | ||
20 | * @var string datastore to test write operations | ||
21 | */ | ||
22 | protected static $testHistory = 'sandbox/history.php'; | ||
23 | |||
24 | /** | ||
25 | * @var ConfigManager instance | ||
26 | */ | ||
27 | protected $conf; | ||
28 | |||
29 | /** | ||
30 | * @var \ReferenceLinkDB instance. | ||
31 | */ | ||
32 | protected $refDB = null; | ||
33 | |||
34 | /** | ||
35 | * @var \LinkDB instance. | ||
36 | */ | ||
37 | protected $linkDB; | ||
38 | |||
39 | /** | ||
40 | * @var \History instance. | ||
41 | */ | ||
42 | protected $history; | ||
43 | |||
44 | /** | ||
45 | * @var Container instance. | ||
46 | */ | ||
47 | protected $container; | ||
48 | |||
49 | /** | ||
50 | * @var Tags controller instance. | ||
51 | */ | ||
52 | protected $controller; | ||
53 | |||
54 | /** | ||
55 | * Before each test, instantiate a new Api with its config, plugins and links. | ||
56 | */ | ||
57 | public function setUp() | ||
58 | { | ||
59 | $this->conf = new ConfigManager('tests/utils/config/configJson'); | ||
60 | $this->refDB = new \ReferenceLinkDB(); | ||
61 | $this->refDB->write(self::$testDatastore); | ||
62 | $this->linkDB = new \LinkDB(self::$testDatastore, true, false); | ||
63 | $refHistory = new \ReferenceHistory(); | ||
64 | $refHistory->write(self::$testHistory); | ||
65 | $this->history = new \History(self::$testHistory); | ||
66 | $this->container = new Container(); | ||
67 | $this->container['conf'] = $this->conf; | ||
68 | $this->container['db'] = $this->linkDB; | ||
69 | $this->container['history'] = $this->history; | ||
70 | |||
71 | $this->controller = new Tags($this->container); | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * After each test, remove the test datastore. | ||
76 | */ | ||
77 | public function tearDown() | ||
78 | { | ||
79 | @unlink(self::$testDatastore); | ||
80 | @unlink(self::$testHistory); | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * Test DELETE tag endpoint: the tag should be removed. | ||
85 | */ | ||
86 | public function testDeleteTagValid() | ||
87 | { | ||
88 | $tagName = 'gnu'; | ||
89 | $tags = $this->linkDB->linksCountPerTag(); | ||
90 | $this->assertTrue($tags[$tagName] > 0); | ||
91 | $env = Environment::mock([ | ||
92 | 'REQUEST_METHOD' => 'DELETE', | ||
93 | ]); | ||
94 | $request = Request::createFromEnvironment($env); | ||
95 | |||
96 | $response = $this->controller->deleteTag($request, new Response(), ['tagName' => $tagName]); | ||
97 | $this->assertEquals(204, $response->getStatusCode()); | ||
98 | $this->assertEmpty((string) $response->getBody()); | ||
99 | |||
100 | $this->linkDB = new \LinkDB(self::$testDatastore, true, false); | ||
101 | $tags = $this->linkDB->linksCountPerTag(); | ||
102 | $this->assertFalse(isset($tags[$tagName])); | ||
103 | |||
104 | // 2 links affected | ||
105 | $historyEntry = $this->history->getHistory()[0]; | ||
106 | $this->assertEquals(\History::UPDATED, $historyEntry['event']); | ||
107 | $this->assertTrue( | ||
108 | (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] | ||
109 | ); | ||
110 | $historyEntry = $this->history->getHistory()[1]; | ||
111 | $this->assertEquals(\History::UPDATED, $historyEntry['event']); | ||
112 | $this->assertTrue( | ||
113 | (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] | ||
114 | ); | ||
115 | } | ||
116 | |||
117 | /** | ||
118 | * Test DELETE tag endpoint: the tag should be removed. | ||
119 | */ | ||
120 | public function testDeleteTagCaseSensitivity() | ||
121 | { | ||
122 | $tagName = 'sTuff'; | ||
123 | $tags = $this->linkDB->linksCountPerTag(); | ||
124 | $this->assertTrue($tags[$tagName] > 0); | ||
125 | $env = Environment::mock([ | ||
126 | 'REQUEST_METHOD' => 'DELETE', | ||
127 | ]); | ||
128 | $request = Request::createFromEnvironment($env); | ||
129 | |||
130 | $response = $this->controller->deleteTag($request, new Response(), ['tagName' => $tagName]); | ||
131 | $this->assertEquals(204, $response->getStatusCode()); | ||
132 | $this->assertEmpty((string) $response->getBody()); | ||
133 | |||
134 | $this->linkDB = new \LinkDB(self::$testDatastore, true, false); | ||
135 | $tags = $this->linkDB->linksCountPerTag(); | ||
136 | $this->assertFalse(isset($tags[$tagName])); | ||
137 | $this->assertTrue($tags[strtolower($tagName)] > 0); | ||
138 | |||
139 | $historyEntry = $this->history->getHistory()[0]; | ||
140 | $this->assertEquals(\History::UPDATED, $historyEntry['event']); | ||
141 | $this->assertTrue( | ||
142 | (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] | ||
143 | ); | ||
144 | } | ||
145 | |||
146 | /** | ||
147 | * Test DELETE tag endpoint: reach not existing tag. | ||
148 | * | ||
149 | * @expectedException Shaarli\Api\Exceptions\ApiTagNotFoundException | ||
150 | * @expectedExceptionMessage Tag not found | ||
151 | */ | ||
152 | public function testDeleteLink404() | ||
153 | { | ||
154 | $tagName = 'nopenope'; | ||
155 | $tags = $this->linkDB->linksCountPerTag(); | ||
156 | $this->assertFalse(isset($tags[$tagName])); | ||
157 | $env = Environment::mock([ | ||
158 | 'REQUEST_METHOD' => 'DELETE', | ||
159 | ]); | ||
160 | $request = Request::createFromEnvironment($env); | ||
161 | |||
162 | $this->controller->deleteTag($request, new Response(), ['tagName' => $tagName]); | ||
163 | } | ||
164 | } | ||
diff --git a/tests/api/controllers/tags/GetTagNameTest.php b/tests/api/controllers/tags/GetTagNameTest.php new file mode 100644 index 00000000..afac228e --- /dev/null +++ b/tests/api/controllers/tags/GetTagNameTest.php | |||
@@ -0,0 +1,129 @@ | |||
1 | <?php | ||
2 | |||
3 | namespace Shaarli\Api\Controllers; | ||
4 | |||
5 | use Shaarli\Config\ConfigManager; | ||
6 | |||
7 | use Slim\Container; | ||
8 | use Slim\Http\Environment; | ||
9 | use Slim\Http\Request; | ||
10 | use Slim\Http\Response; | ||
11 | |||
12 | /** | ||
13 | * Class GetTagNameTest | ||
14 | * | ||
15 | * Test getTag by tag name API service. | ||
16 | * | ||
17 | * @package Shaarli\Api\Controllers | ||
18 | */ | ||
19 | class GetTagNameTest extends \PHPUnit_Framework_TestCase | ||
20 | { | ||
21 | /** | ||
22 | * @var string datastore to test write operations | ||
23 | */ | ||
24 | protected static $testDatastore = 'sandbox/datastore.php'; | ||
25 | |||
26 | /** | ||
27 | * @var ConfigManager instance | ||
28 | */ | ||
29 | protected $conf; | ||
30 | |||
31 | /** | ||
32 | * @var \ReferenceLinkDB instance. | ||
33 | */ | ||
34 | protected $refDB = null; | ||
35 | |||
36 | /** | ||
37 | * @var Container instance. | ||
38 | */ | ||
39 | protected $container; | ||
40 | |||
41 | /** | ||
42 | * @var Tags controller instance. | ||
43 | */ | ||
44 | protected $controller; | ||
45 | |||
46 | /** | ||
47 | * Number of JSON fields per link. | ||
48 | */ | ||
49 | const NB_FIELDS_TAG = 2; | ||
50 | |||
51 | /** | ||
52 | * Before each test, instantiate a new Api with its config, plugins and links. | ||
53 | */ | ||
54 | public function setUp() | ||
55 | { | ||
56 | $this->conf = new ConfigManager('tests/utils/config/configJson'); | ||
57 | $this->refDB = new \ReferenceLinkDB(); | ||
58 | $this->refDB->write(self::$testDatastore); | ||
59 | |||
60 | $this->container = new Container(); | ||
61 | $this->container['conf'] = $this->conf; | ||
62 | $this->container['db'] = new \LinkDB(self::$testDatastore, true, false); | ||
63 | $this->container['history'] = null; | ||
64 | |||
65 | $this->controller = new Tags($this->container); | ||
66 | } | ||
67 | |||
68 | /** | ||
69 | * After each test, remove the test datastore. | ||
70 | */ | ||
71 | public function tearDown() | ||
72 | { | ||
73 | @unlink(self::$testDatastore); | ||
74 | } | ||
75 | |||
76 | /** | ||
77 | * Test basic getTag service: return gnu tag with 2 occurrences. | ||
78 | */ | ||
79 | public function testGetTag() | ||
80 | { | ||
81 | $tagName = 'gnu'; | ||
82 | $env = Environment::mock([ | ||
83 | 'REQUEST_METHOD' => 'GET', | ||
84 | ]); | ||
85 | $request = Request::createFromEnvironment($env); | ||
86 | |||
87 | $response = $this->controller->getTag($request, new Response(), ['tagName' => $tagName]); | ||
88 | $this->assertEquals(200, $response->getStatusCode()); | ||
89 | $data = json_decode((string) $response->getBody(), true); | ||
90 | $this->assertEquals(self::NB_FIELDS_TAG, count($data)); | ||
91 | $this->assertEquals($tagName, $data['name']); | ||
92 | $this->assertEquals(2, $data['occurrences']); | ||
93 | } | ||
94 | |||
95 | /** | ||
96 | * Test getTag service which is not case sensitive: occurrences with both sTuff and stuff | ||
97 | */ | ||
98 | public function testGetTagNotCaseSensitive() | ||
99 | { | ||
100 | $tagName = 'sTuff'; | ||
101 | $env = Environment::mock([ | ||
102 | 'REQUEST_METHOD' => 'GET', | ||
103 | ]); | ||
104 | $request = Request::createFromEnvironment($env); | ||
105 | |||
106 | $response = $this->controller->getTag($request, new Response(), ['tagName' => $tagName]); | ||
107 | $this->assertEquals(200, $response->getStatusCode()); | ||
108 | $data = json_decode((string) $response->getBody(), true); | ||
109 | $this->assertEquals(self::NB_FIELDS_TAG, count($data)); | ||
110 | $this->assertEquals($tagName, $data['name']); | ||
111 | $this->assertEquals(2, $data['occurrences']); | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * Test basic getTag service: get non existent tag => ApiTagNotFoundException. | ||
116 | * | ||
117 | * @expectedException Shaarli\Api\Exceptions\ApiTagNotFoundException | ||
118 | * @expectedExceptionMessage Tag not found | ||
119 | */ | ||
120 | public function testGetTag404() | ||
121 | { | ||
122 | $env = Environment::mock([ | ||
123 | 'REQUEST_METHOD' => 'GET', | ||
124 | ]); | ||
125 | $request = Request::createFromEnvironment($env); | ||
126 | |||
127 | $this->controller->getTag($request, new Response(), ['tagName' => 'nopenope']); | ||
128 | } | ||
129 | } | ||
diff --git a/tests/api/controllers/tags/GetTagsTest.php b/tests/api/controllers/tags/GetTagsTest.php new file mode 100644 index 00000000..3fab31b0 --- /dev/null +++ b/tests/api/controllers/tags/GetTagsTest.php | |||
@@ -0,0 +1,209 @@ | |||
1 | <?php | ||
2 | namespace Shaarli\Api\Controllers; | ||
3 | |||
4 | use Shaarli\Config\ConfigManager; | ||
5 | |||
6 | use Slim\Container; | ||
7 | use Slim\Http\Environment; | ||
8 | use Slim\Http\Request; | ||
9 | use Slim\Http\Response; | ||
10 | |||
11 | /** | ||
12 | * Class GetTagsTest | ||
13 | * | ||
14 | * Test get tag list REST API service. | ||
15 | * | ||
16 | * @package Shaarli\Api\Controllers | ||
17 | */ | ||
18 | class GetTagsTest extends \PHPUnit_Framework_TestCase | ||
19 | { | ||
20 | /** | ||
21 | * @var string datastore to test write operations | ||
22 | */ | ||
23 | protected static $testDatastore = 'sandbox/datastore.php'; | ||
24 | |||
25 | /** | ||
26 | * @var ConfigManager instance | ||
27 | */ | ||
28 | protected $conf; | ||
29 | |||
30 | /** | ||
31 | * @var \ReferenceLinkDB instance. | ||
32 | */ | ||
33 | protected $refDB = null; | ||
34 | |||
35 | /** | ||
36 | * @var Container instance. | ||
37 | */ | ||
38 | protected $container; | ||
39 | |||
40 | /** | ||
41 | * @var \LinkDB instance. | ||
42 | */ | ||
43 | protected $linkDB; | ||
44 | |||
45 | /** | ||
46 | * @var Tags controller instance. | ||
47 | */ | ||
48 | protected $controller; | ||
49 | |||
50 | /** | ||
51 | * Number of JSON field per link. | ||
52 | */ | ||
53 | const NB_FIELDS_TAG = 2; | ||
54 | |||
55 | /** | ||
56 | * Before every test, instantiate a new Api with its config, plugins and links. | ||
57 | */ | ||
58 | public function setUp() | ||
59 | { | ||
60 | $this->conf = new ConfigManager('tests/utils/config/configJson'); | ||
61 | $this->refDB = new \ReferenceLinkDB(); | ||
62 | $this->refDB->write(self::$testDatastore); | ||
63 | |||
64 | $this->container = new Container(); | ||
65 | $this->container['conf'] = $this->conf; | ||
66 | $this->linkDB = new \LinkDB(self::$testDatastore, true, false); | ||
67 | $this->container['db'] = $this->linkDB; | ||
68 | $this->container['history'] = null; | ||
69 | |||
70 | $this->controller = new Tags($this->container); | ||
71 | } | ||
72 | |||
73 | /** | ||
74 | * After every test, remove the test datastore. | ||
75 | */ | ||
76 | public function tearDown() | ||
77 | { | ||
78 | @unlink(self::$testDatastore); | ||
79 | } | ||
80 | |||
81 | /** | ||
82 | * Test basic getTags service: returns all tags. | ||
83 | */ | ||
84 | public function testGetTagsAll() | ||
85 | { | ||
86 | $tags = $this->linkDB->linksCountPerTag(); | ||
87 | $env = Environment::mock([ | ||
88 | 'REQUEST_METHOD' => 'GET', | ||
89 | ]); | ||
90 | $request = Request::createFromEnvironment($env); | ||
91 | |||
92 | $response = $this->controller->getTags($request, new Response()); | ||
93 | $this->assertEquals(200, $response->getStatusCode()); | ||
94 | $data = json_decode((string) $response->getBody(), true); | ||
95 | $this->assertEquals(count($tags), count($data)); | ||
96 | |||
97 | // Check order | ||
98 | $this->assertEquals(self::NB_FIELDS_TAG, count($data[0])); | ||
99 | $this->assertEquals('web', $data[0]['name']); | ||
100 | $this->assertEquals(4, $data[0]['occurrences']); | ||
101 | $this->assertEquals(self::NB_FIELDS_TAG, count($data[1])); | ||
102 | $this->assertEquals('cartoon', $data[1]['name']); | ||
103 | $this->assertEquals(3, $data[1]['occurrences']); | ||
104 | // Case insensitive | ||
105 | $this->assertEquals(self::NB_FIELDS_TAG, count($data[5])); | ||
106 | $this->assertEquals('sTuff', $data[5]['name']); | ||
107 | $this->assertEquals(2, $data[5]['occurrences']); | ||
108 | // End | ||
109 | $this->assertEquals(self::NB_FIELDS_TAG, count($data[count($data) - 1])); | ||
110 | $this->assertEquals('w3c', $data[count($data) - 1]['name']); | ||
111 | $this->assertEquals(1, $data[count($data) - 1]['occurrences']); | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * Test getTags service with offset and limit parameter: | ||
116 | * limit=1 and offset=1 should return only the second tag, cartoon with 3 occurrences | ||
117 | */ | ||
118 | public function testGetTagsOffsetLimit() | ||
119 | { | ||
120 | $env = Environment::mock([ | ||
121 | 'REQUEST_METHOD' => 'GET', | ||
122 | 'QUERY_STRING' => 'offset=1&limit=1' | ||
123 | ]); | ||
124 | $request = Request::createFromEnvironment($env); | ||
125 | $response = $this->controller->getTags($request, new Response()); | ||
126 | $this->assertEquals(200, $response->getStatusCode()); | ||
127 | $data = json_decode((string) $response->getBody(), true); | ||
128 | $this->assertEquals(1, count($data)); | ||
129 | $this->assertEquals(self::NB_FIELDS_TAG, count($data[0])); | ||
130 | $this->assertEquals('cartoon', $data[0]['name']); | ||
131 | $this->assertEquals(3, $data[0]['occurrences']); | ||
132 | } | ||
133 | |||
134 | /** | ||
135 | * Test getTags with limit=all (return all tags). | ||
136 | */ | ||
137 | public function testGetTagsLimitAll() | ||
138 | { | ||
139 | $tags = $this->linkDB->linksCountPerTag(); | ||
140 | $env = Environment::mock([ | ||
141 | 'REQUEST_METHOD' => 'GET', | ||
142 | 'QUERY_STRING' => 'limit=all' | ||
143 | ]); | ||
144 | $request = Request::createFromEnvironment($env); | ||
145 | $response = $this->controller->getTags($request, new Response()); | ||
146 | $this->assertEquals(200, $response->getStatusCode()); | ||
147 | $data = json_decode((string) $response->getBody(), true); | ||
148 | $this->assertEquals(count($tags), count($data)); | ||
149 | } | ||
150 | |||
151 | /** | ||
152 | * Test getTags service with offset and limit parameter: | ||
153 | * limit=1 and offset=1 should not return any tag | ||
154 | */ | ||
155 | public function testGetTagsOffsetTooHigh() | ||
156 | { | ||
157 | $env = Environment::mock([ | ||
158 | 'REQUEST_METHOD' => 'GET', | ||
159 | 'QUERY_STRING' => 'offset=100' | ||
160 | ]); | ||
161 | $request = Request::createFromEnvironment($env); | ||
162 | $response = $this->controller->getTags($request, new Response()); | ||
163 | $this->assertEquals(200, $response->getStatusCode()); | ||
164 | $data = json_decode((string) $response->getBody(), true); | ||
165 | $this->assertEmpty(count($data)); | ||
166 | } | ||
167 | |||
168 | /** | ||
169 | * Test getTags with visibility parameter set to private | ||
170 | */ | ||
171 | public function testGetTagsVisibilityPrivate() | ||
172 | { | ||
173 | $tags = $this->linkDB->linksCountPerTag([], 'private'); | ||
174 | $env = Environment::mock([ | ||
175 | 'REQUEST_METHOD' => 'GET', | ||
176 | 'QUERY_STRING' => 'visibility=private' | ||
177 | ]); | ||
178 | $request = Request::createFromEnvironment($env); | ||
179 | $response = $this->controller->getTags($request, new Response()); | ||
180 | $this->assertEquals(200, $response->getStatusCode()); | ||
181 | $data = json_decode((string) $response->getBody(), true); | ||
182 | $this->assertEquals(count($tags), count($data)); | ||
183 | $this->assertEquals(self::NB_FIELDS_TAG, count($data[0])); | ||
184 | $this->assertEquals('Mercurial', $data[0]['name']); | ||
185 | $this->assertEquals(1, $data[0]['occurrences']); | ||
186 | } | ||
187 | |||
188 | /** | ||
189 | * Test getTags with visibility parameter set to public | ||
190 | */ | ||
191 | public function testGetTagsVisibilityPublic() | ||
192 | { | ||
193 | $tags = $this->linkDB->linksCountPerTag([], 'public'); | ||
194 | $env = Environment::mock( | ||
195 | [ | ||
196 | 'REQUEST_METHOD' => 'GET', | ||
197 | 'QUERY_STRING' => 'visibility=public' | ||
198 | ] | ||
199 | ); | ||
200 | $request = Request::createFromEnvironment($env); | ||
201 | $response = $this->controller->getTags($request, new Response()); | ||
202 | $this->assertEquals(200, $response->getStatusCode()); | ||
203 | $data = json_decode((string)$response->getBody(), true); | ||
204 | $this->assertEquals(count($tags), count($data)); | ||
205 | $this->assertEquals(self::NB_FIELDS_TAG, count($data[0])); | ||
206 | $this->assertEquals('web', $data[0]['name']); | ||
207 | $this->assertEquals(3, $data[0]['occurrences']); | ||
208 | } | ||
209 | } | ||
diff --git a/tests/api/controllers/tags/PutTagTest.php b/tests/api/controllers/tags/PutTagTest.php new file mode 100644 index 00000000..6f7dec22 --- /dev/null +++ b/tests/api/controllers/tags/PutTagTest.php | |||
@@ -0,0 +1,209 @@ | |||
1 | <?php | ||
2 | |||
3 | |||
4 | namespace Shaarli\Api\Controllers; | ||
5 | |||
6 | |||
7 | use Shaarli\Api\Exceptions\ApiBadParametersException; | ||
8 | use Shaarli\Config\ConfigManager; | ||
9 | use Slim\Container; | ||
10 | use Slim\Http\Environment; | ||
11 | use Slim\Http\Request; | ||
12 | use Slim\Http\Response; | ||
13 | |||
14 | class PutTagTest extends \PHPUnit_Framework_TestCase | ||
15 | { | ||
16 | /** | ||
17 | * @var string datastore to test write operations | ||
18 | */ | ||
19 | protected static $testDatastore = 'sandbox/datastore.php'; | ||
20 | |||
21 | /** | ||
22 | * @var string datastore to test write operations | ||
23 | */ | ||
24 | protected static $testHistory = 'sandbox/history.php'; | ||
25 | |||
26 | /** | ||
27 | * @var ConfigManager instance | ||
28 | */ | ||
29 | protected $conf; | ||
30 | |||
31 | /** | ||
32 | * @var \ReferenceLinkDB instance. | ||
33 | */ | ||
34 | protected $refDB = null; | ||
35 | |||
36 | /** | ||
37 | * @var \History instance. | ||
38 | */ | ||
39 | protected $history; | ||
40 | |||
41 | /** | ||
42 | * @var Container instance. | ||
43 | */ | ||
44 | protected $container; | ||
45 | |||
46 | /** | ||
47 | * @var \LinkDB instance. | ||
48 | */ | ||
49 | protected $linkDB; | ||
50 | |||
51 | /** | ||
52 | * @var Tags controller instance. | ||
53 | */ | ||
54 | protected $controller; | ||
55 | |||
56 | /** | ||
57 | * Number of JSON field per link. | ||
58 | */ | ||
59 | const NB_FIELDS_TAG = 2; | ||
60 | |||
61 | /** | ||
62 | * Before every test, instantiate a new Api with its config, plugins and links. | ||
63 | */ | ||
64 | public function setUp() | ||
65 | { | ||
66 | $this->conf = new ConfigManager('tests/utils/config/configJson.json.php'); | ||
67 | $this->refDB = new \ReferenceLinkDB(); | ||
68 | $this->refDB->write(self::$testDatastore); | ||
69 | |||
70 | $refHistory = new \ReferenceHistory(); | ||
71 | $refHistory->write(self::$testHistory); | ||
72 | $this->history = new \History(self::$testHistory); | ||
73 | |||
74 | $this->container = new Container(); | ||
75 | $this->container['conf'] = $this->conf; | ||
76 | $this->linkDB = new \LinkDB(self::$testDatastore, true, false); | ||
77 | $this->container['db'] = $this->linkDB; | ||
78 | $this->container['history'] = $this->history; | ||
79 | |||
80 | $this->controller = new Tags($this->container); | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * After every test, remove the test datastore. | ||
85 | */ | ||
86 | public function tearDown() | ||
87 | { | ||
88 | @unlink(self::$testDatastore); | ||
89 | @unlink(self::$testHistory); | ||
90 | } | ||
91 | |||
92 | /** | ||
93 | * Test tags update | ||
94 | */ | ||
95 | public function testPutLinkValid() | ||
96 | { | ||
97 | $env = Environment::mock([ | ||
98 | 'REQUEST_METHOD' => 'PUT', | ||
99 | ]); | ||
100 | $tagName = 'gnu'; | ||
101 | $update = ['name' => $newName = 'newtag']; | ||
102 | $request = Request::createFromEnvironment($env); | ||
103 | $request = $request->withParsedBody($update); | ||
104 | |||
105 | $response = $this->controller->putTag($request, new Response(), ['tagName' => $tagName]); | ||
106 | $this->assertEquals(200, $response->getStatusCode()); | ||
107 | $data = json_decode((string) $response->getBody(), true); | ||
108 | $this->assertEquals(self::NB_FIELDS_TAG, count($data)); | ||
109 | $this->assertEquals($newName, $data['name']); | ||
110 | $this->assertEquals(2, $data['occurrences']); | ||
111 | |||
112 | $tags = $this->linkDB->linksCountPerTag(); | ||
113 | $this->assertNotTrue(isset($tags[$tagName])); | ||
114 | $this->assertEquals(2, $tags[$newName]); | ||
115 | |||
116 | $historyEntry = $this->history->getHistory()[0]; | ||
117 | $this->assertEquals(\History::UPDATED, $historyEntry['event']); | ||
118 | $this->assertTrue( | ||
119 | (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] | ||
120 | ); | ||
121 | $historyEntry = $this->history->getHistory()[1]; | ||
122 | $this->assertEquals(\History::UPDATED, $historyEntry['event']); | ||
123 | $this->assertTrue( | ||
124 | (new \DateTime())->add(\DateInterval::createFromDateString('-5 seconds')) < $historyEntry['datetime'] | ||
125 | ); | ||
126 | } | ||
127 | |||
128 | /** | ||
129 | * Test tag update with an existing tag: they should be merged | ||
130 | */ | ||
131 | public function testPutTagMerge() | ||
132 | { | ||
133 | $tagName = 'gnu'; | ||
134 | $newName = 'w3c'; | ||
135 | |||
136 | $tags = $this->linkDB->linksCountPerTag(); | ||
137 | $this->assertEquals(1, $tags[$newName]); | ||
138 | $this->assertEquals(2, $tags[$tagName]); | ||
139 | |||
140 | $env = Environment::mock([ | ||
141 | 'REQUEST_METHOD' => 'PUT', | ||
142 | ]); | ||
143 | $update = ['name' => $newName]; | ||
144 | $request = Request::createFromEnvironment($env); | ||
145 | $request = $request->withParsedBody($update); | ||
146 | |||
147 | $response = $this->controller->putTag($request, new Response(), ['tagName' => $tagName]); | ||
148 | $this->assertEquals(200, $response->getStatusCode()); | ||
149 | $data = json_decode((string) $response->getBody(), true); | ||
150 | $this->assertEquals(self::NB_FIELDS_TAG, count($data)); | ||
151 | $this->assertEquals($newName, $data['name']); | ||
152 | $this->assertEquals(3, $data['occurrences']); | ||
153 | |||
154 | $tags = $this->linkDB->linksCountPerTag(); | ||
155 | $this->assertNotTrue(isset($tags[$tagName])); | ||
156 | $this->assertEquals(3, $tags[$newName]); | ||
157 | } | ||
158 | |||
159 | /** | ||
160 | * 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 | */ | ||
165 | public function testPutTagEmpty() | ||
166 | { | ||
167 | $tagName = 'gnu'; | ||
168 | $newName = ''; | ||
169 | |||
170 | $tags = $this->linkDB->linksCountPerTag(); | ||
171 | $this->assertEquals(2, $tags[$tagName]); | ||
172 | |||
173 | $env = Environment::mock([ | ||
174 | 'REQUEST_METHOD' => 'PUT', | ||
175 | ]); | ||
176 | $request = Request::createFromEnvironment($env); | ||
177 | |||
178 | $env = Environment::mock([ | ||
179 | 'REQUEST_METHOD' => 'PUT', | ||
180 | ]); | ||
181 | $update = ['name' => $newName]; | ||
182 | $request = Request::createFromEnvironment($env); | ||
183 | $request = $request->withParsedBody($update); | ||
184 | |||
185 | try { | ||
186 | $this->controller->putTag($request, new Response(), ['tagName' => $tagName]); | ||
187 | } catch (ApiBadParametersException $e) { | ||
188 | $tags = $this->linkDB->linksCountPerTag(); | ||
189 | $this->assertEquals(2, $tags[$tagName]); | ||
190 | throw $e; | ||
191 | } | ||
192 | } | ||
193 | |||
194 | /** | ||
195 | * Test tag update on non existent tag => ApiTagNotFoundException. | ||
196 | * | ||
197 | * @expectedException Shaarli\Api\Exceptions\ApiTagNotFoundException | ||
198 | * @expectedExceptionMessage Tag not found | ||
199 | */ | ||
200 | public function testPutTag404() | ||
201 | { | ||
202 | $env = Environment::mock([ | ||
203 | 'REQUEST_METHOD' => 'PUT', | ||
204 | ]); | ||
205 | $request = Request::createFromEnvironment($env); | ||
206 | |||
207 | $this->controller->putTag($request, new Response(), ['tagName' => 'nopenope']); | ||
208 | } | ||
209 | } | ||
diff --git a/tests/config/ConfigManagerTest.php b/tests/config/ConfigManagerTest.php index 1ec447b2..4a4e94ac 100644 --- a/tests/config/ConfigManagerTest.php +++ b/tests/config/ConfigManagerTest.php | |||
@@ -81,6 +81,18 @@ class ConfigManagerTest extends \PHPUnit_Framework_TestCase | |||
81 | $this->assertEquals('testSetWriteGetNested', $this->conf->get('foo.bar.key.stuff')); | 81 | $this->assertEquals('testSetWriteGetNested', $this->conf->get('foo.bar.key.stuff')); |
82 | } | 82 | } |
83 | 83 | ||
84 | public function testSetDeleteNested() | ||
85 | { | ||
86 | $this->conf->set('foo.bar.key.stuff', 'testSetDeleteNested'); | ||
87 | $this->assertTrue($this->conf->exists('foo.bar')); | ||
88 | $this->assertTrue($this->conf->exists('foo.bar.key.stuff')); | ||
89 | $this->assertEquals('testSetDeleteNested', $this->conf->get('foo.bar.key.stuff')); | ||
90 | |||
91 | $this->conf->remove('foo.bar'); | ||
92 | $this->assertFalse($this->conf->exists('foo.bar.key.stuff')); | ||
93 | $this->assertFalse($this->conf->exists('foo.bar')); | ||
94 | } | ||
95 | |||
84 | /** | 96 | /** |
85 | * Set with an empty key. | 97 | * Set with an empty key. |
86 | * | 98 | * |
@@ -104,6 +116,17 @@ class ConfigManagerTest extends \PHPUnit_Framework_TestCase | |||
104 | } | 116 | } |
105 | 117 | ||
106 | /** | 118 | /** |
119 | * Remove with an empty key. | ||
120 | * | ||
121 | * @expectedException \Exception | ||
122 | * @expectedExceptionMessageRegExp #^Invalid setting key parameter. String expected, got.*# | ||
123 | */ | ||
124 | public function testRmoveEmptyKey() | ||
125 | { | ||
126 | $this->conf->remove(''); | ||
127 | } | ||
128 | |||
129 | /** | ||
107 | * Try to write the config without mandatory parameter (e.g. 'login'). | 130 | * Try to write the config without mandatory parameter (e.g. 'login'). |
108 | * | 131 | * |
109 | * @expectedException Shaarli\Config\Exception\MissingFieldConfigException | 132 | * @expectedException Shaarli\Config\Exception\MissingFieldConfigException |
diff --git a/tests/languages/fr/LanguagesFrTest.php b/tests/languages/fr/LanguagesFrTest.php index 79d05172..0cf74891 100644 --- a/tests/languages/fr/LanguagesFrTest.php +++ b/tests/languages/fr/LanguagesFrTest.php | |||
@@ -172,4 +172,30 @@ class LanguagesFrTest extends \PHPUnit_Framework_TestCase | |||
172 | $this->assertEquals('voiture', t($txt, $txt, 1, 'test')); | 172 | $this->assertEquals('voiture', t($txt, $txt, 1, 'test')); |
173 | $this->assertEquals('Fouille', t('Search', 'Search', 1, 'test')); | 173 | $this->assertEquals('Fouille', t('Search', 'Search', 1, 'test')); |
174 | } | 174 | } |
175 | |||
176 | /** | ||
177 | * Test t() with an extension language file coming from the theme in gettext mode | ||
178 | */ | ||
179 | public function testTranslationThemeExtensionGettext() | ||
180 | { | ||
181 | $this->conf->set('translation.mode', 'gettext'); | ||
182 | $this->conf->set('raintpl_tpl', 'tests/utils/customtpl/'); | ||
183 | $this->conf->set('theme', 'dummy'); | ||
184 | new Languages('en', $this->conf); | ||
185 | $txt = 'rooster'; // ignore me poedit | ||
186 | $this->assertEquals('coq', t($txt, $txt, 1, 'dummy')); | ||
187 | } | ||
188 | |||
189 | /** | ||
190 | * Test t() with an extension language file coming from the theme in PHP mode | ||
191 | */ | ||
192 | public function testTranslationThemeExtensionPhp() | ||
193 | { | ||
194 | $this->conf->set('translation.mode', 'php'); | ||
195 | $this->conf->set('raintpl_tpl', 'tests/utils/customtpl/'); | ||
196 | $this->conf->set('theme', 'dummy'); | ||
197 | new Languages('en', $this->conf); | ||
198 | $txt = 'rooster'; // ignore me poedit | ||
199 | $this->assertEquals('coq', t($txt, $txt, 1, 'dummy')); | ||
200 | } | ||
175 | } | 201 | } |
diff --git a/tests/plugins/PluginMarkdownTest.php b/tests/plugins/PluginMarkdownTest.php index 96891f1f..b31e817f 100644 --- a/tests/plugins/PluginMarkdownTest.php +++ b/tests/plugins/PluginMarkdownTest.php | |||
@@ -50,6 +50,30 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase | |||
50 | } | 50 | } |
51 | 51 | ||
52 | /** | 52 | /** |
53 | * Test render_feed hook. | ||
54 | */ | ||
55 | public function testMarkdownFeed() | ||
56 | { | ||
57 | $markdown = '# My title' . PHP_EOL . 'Very interesting content.'; | ||
58 | $markdown .= '— <a href="http://domain.tld/?0oc_VQ" title="Permalien">Permalien</a>'; | ||
59 | $data = array( | ||
60 | 'links' => array( | ||
61 | 0 => array( | ||
62 | 'description' => $markdown, | ||
63 | ), | ||
64 | ), | ||
65 | ); | ||
66 | |||
67 | $data = hook_markdown_render_feed($data, $this->conf); | ||
68 | $this->assertNotFalse(strpos($data['links'][0]['description'], '<h1>')); | ||
69 | $this->assertNotFalse(strpos($data['links'][0]['description'], '<p>')); | ||
70 | $this->assertStringEndsWith( | ||
71 | '— <a href="http://domain.tld/?0oc_VQ">Permalien</a></p></div>', | ||
72 | $data['links'][0]['description'] | ||
73 | ); | ||
74 | } | ||
75 | |||
76 | /** | ||
53 | * Test render_daily hook. | 77 | * Test render_daily hook. |
54 | * Only check that there is basic markdown rendering. | 78 | * Only check that there is basic markdown rendering. |
55 | */ | 79 | */ |
@@ -58,20 +82,17 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase | |||
58 | $markdown = '# My title' . PHP_EOL . 'Very interesting content.'; | 82 | $markdown = '# My title' . PHP_EOL . 'Very interesting content.'; |
59 | $data = array( | 83 | $data = array( |
60 | // Columns data | 84 | // Columns data |
61 | 'cols' => array( | 85 | 'linksToDisplay' => array( |
62 | // First, second, third. | 86 | // nth link |
63 | 0 => array( | 87 | 0 => array( |
64 | // nth link | 88 | 'formatedDescription' => $markdown, |
65 | 0 => array( | ||
66 | 'formatedDescription' => $markdown, | ||
67 | ), | ||
68 | ), | 89 | ), |
69 | ), | 90 | ), |
70 | ); | 91 | ); |
71 | 92 | ||
72 | $data = hook_markdown_render_daily($data, $this->conf); | 93 | $data = hook_markdown_render_daily($data, $this->conf); |
73 | $this->assertNotFalse(strpos($data['cols'][0][0]['formatedDescription'], '<h1>')); | 94 | $this->assertNotFalse(strpos($data['linksToDisplay'][0]['formatedDescription'], '<h1>')); |
74 | $this->assertNotFalse(strpos($data['cols'][0][0]['formatedDescription'], '<p>')); | 95 | $this->assertNotFalse(strpos($data['linksToDisplay'][0]['formatedDescription'], '<p>')); |
75 | } | 96 | } |
76 | 97 | ||
77 | /** | 98 | /** |
@@ -107,6 +128,37 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase | |||
107 | $this->assertEquals($text, $reversedText); | 128 | $this->assertEquals($text, $reversedText); |
108 | } | 129 | } |
109 | 130 | ||
131 | public function testReverseFeedPermalink() | ||
132 | { | ||
133 | $text = 'Description... '; | ||
134 | $text .= '— <a href="http://domain.tld/?0oc_VQ" title="Permalien">Permalien</a>'; | ||
135 | $expected = 'Description... — [Permalien](http://domain.tld/?0oc_VQ)'; | ||
136 | $processedText = reverse_feed_permalink($text); | ||
137 | |||
138 | $this->assertEquals($expected, $processedText); | ||
139 | } | ||
140 | |||
141 | public function testReverseLastFeedPermalink() | ||
142 | { | ||
143 | $text = 'Description... '; | ||
144 | $text .= '<br>— <a href="http://domain.tld/?0oc_VQ" title="Permalien">Permalien</a>'; | ||
145 | $expected = $text; | ||
146 | $text .= '<br>— <a href="http://domain.tld/?0oc_VQ" title="Permalien">Permalien</a>'; | ||
147 | $expected .= '<br>— [Permalien](http://domain.tld/?0oc_VQ)'; | ||
148 | $processedText = reverse_feed_permalink($text); | ||
149 | |||
150 | $this->assertEquals($expected, $processedText); | ||
151 | } | ||
152 | |||
153 | public function testReverseNoFeedPermalink() | ||
154 | { | ||
155 | $text = 'Hello! Where are you from?'; | ||
156 | $expected = $text; | ||
157 | $processedText = reverse_feed_permalink($text); | ||
158 | |||
159 | $this->assertEquals($expected, $processedText); | ||
160 | } | ||
161 | |||
110 | /** | 162 | /** |
111 | * Test sanitize_html(). | 163 | * Test sanitize_html(). |
112 | */ | 164 | */ |
@@ -148,21 +200,18 @@ class PluginMarkdownTest extends PHPUnit_Framework_TestCase | |||
148 | 200 | ||
149 | $data = array( | 201 | $data = array( |
150 | // Columns data | 202 | // Columns data |
151 | 'cols' => array( | 203 | 'linksToDisplay' => array( |
152 | // First, second, third. | 204 | // nth link |
153 | 0 => array( | 205 | 0 => array( |
154 | // nth link | 206 | 'formatedDescription' => $str, |
155 | 0 => array( | 207 | 'tags' => NO_MD_TAG, |
156 | 'formatedDescription' => $str, | 208 | 'taglist' => array(), |
157 | 'tags' => NO_MD_TAG, | ||
158 | 'taglist' => array(), | ||
159 | ), | ||
160 | ), | 209 | ), |
161 | ), | 210 | ), |
162 | ); | 211 | ); |
163 | 212 | ||
164 | $data = hook_markdown_render_daily($data, $this->conf); | 213 | $data = hook_markdown_render_daily($data, $this->conf); |
165 | $this->assertEquals($str, $data['cols'][0][0]['formatedDescription']); | 214 | $this->assertEquals($str, $data['linksToDisplay'][0]['formatedDescription']); |
166 | } | 215 | } |
167 | 216 | ||
168 | /** | 217 | /** |
diff --git a/tests/plugins/resources/markdown.html b/tests/plugins/resources/markdown.html index 844a6f31..f1df4e7e 100644 --- a/tests/plugins/resources/markdown.html +++ b/tests/plugins/resources/markdown.html | |||
@@ -8,7 +8,7 @@ | |||
8 | </ul> | 8 | </ul> |
9 | <ol> | 9 | <ol> |
10 | <li><a href="http://link.tld">zero</a> | 10 | <li><a href="http://link.tld">zero</a> |
11 | <ol> | 11 | <ol start="2"> |
12 | <li><a href="http://link.tld">two</a></li> | 12 | <li><a href="http://link.tld">two</a></li> |
13 | <li><a href="http://link.tld">three</a></li> | 13 | <li><a href="http://link.tld">three</a></li> |
14 | <li><a href="http://link.tld">four</a></li> | 14 | <li><a href="http://link.tld">four</a></li> |
@@ -29,5 +29,5 @@ next #foo</code></pre> | |||
29 | <a href="https://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 /> | 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 /> | 31 | <a href="magnet:test.tld/path/?query=value#hash">link</a><br /> |
32 | <a href="http://alert('xss')">link</a><br /> | 32 | <a href="http://alert('xss')">link</a><br /> |
33 | <a href="http://test.tld/path/?query=value#hash">link</a></p></div> \ No newline at end of file | 33 | <a href="http://test.tld/path/?query=value#hash">link</a></p></div> \ No newline at end of file |
diff --git a/tests/plugins/test/test.php b/tests/plugins/test/test.php index 3d750c90..2aaf5122 100644 --- a/tests/plugins/test/test.php +++ b/tests/plugins/test/test.php | |||
@@ -11,7 +11,7 @@ function hook_test_random($data) | |||
11 | { | 11 | { |
12 | if (isset($data['_PAGE_']) && $data['_PAGE_'] == 'test') { | 12 | if (isset($data['_PAGE_']) && $data['_PAGE_'] == 'test') { |
13 | $data[1] = 'page test'; | 13 | $data[1] = 'page test'; |
14 | } else if (isset($data['_LOGGEDIN_']) && $data['_LOGGEDIN_'] === true) { | 14 | } elseif (isset($data['_LOGGEDIN_']) && $data['_LOGGEDIN_'] === true) { |
15 | $data[1] = 'loggedin'; | 15 | $data[1] = 'loggedin'; |
16 | } else { | 16 | } else { |
17 | $data[1] = $data[0]; | 17 | $data[1] = $data[0]; |
diff --git a/tests/security/LoginManagerTest.php b/tests/security/LoginManagerTest.php new file mode 100644 index 00000000..f26cd1eb --- /dev/null +++ b/tests/security/LoginManagerTest.php | |||
@@ -0,0 +1,374 @@ | |||
1 | <?php | ||
2 | namespace Shaarli\Security; | ||
3 | |||
4 | require_once 'tests/utils/FakeConfigManager.php'; | ||
5 | use \PHPUnit\Framework\TestCase; | ||
6 | |||
7 | /** | ||
8 | * Test coverage for LoginManager | ||
9 | */ | ||
10 | class LoginManagerTest extends TestCase | ||
11 | { | ||
12 | /** @var \FakeConfigManager Configuration Manager instance */ | ||
13 | protected $configManager = null; | ||
14 | |||
15 | /** @var LoginManager Login Manager instance */ | ||
16 | protected $loginManager = null; | ||
17 | |||
18 | /** @var SessionManager Session Manager instance */ | ||
19 | protected $sessionManager = null; | ||
20 | |||
21 | /** @var string Banned IP filename */ | ||
22 | protected $banFile = 'sandbox/ipbans.php'; | ||
23 | |||
24 | /** @var string Log filename */ | ||
25 | protected $logFile = 'sandbox/shaarli.log'; | ||
26 | |||
27 | /** @var array Simulates the $_COOKIE array */ | ||
28 | protected $cookie = []; | ||
29 | |||
30 | /** @var array Simulates the $GLOBALS array */ | ||
31 | protected $globals = []; | ||
32 | |||
33 | /** @var array Simulates the $_SERVER array */ | ||
34 | protected $server = []; | ||
35 | |||
36 | /** @var array Simulates the $_SESSION array */ | ||
37 | protected $session = []; | ||
38 | |||
39 | /** @var string Advertised client IP address */ | ||
40 | protected $clientIpAddress = '10.1.47.179'; | ||
41 | |||
42 | /** @var string Local client IP address */ | ||
43 | protected $ipAddr = '127.0.0.1'; | ||
44 | |||
45 | /** @var string Trusted proxy IP address */ | ||
46 | protected $trustedProxy = '10.1.1.100'; | ||
47 | |||
48 | /** @var string User login */ | ||
49 | protected $login = 'johndoe'; | ||
50 | |||
51 | /** @var string User password */ | ||
52 | protected $password = 'IC4nHazL0g1n?'; | ||
53 | |||
54 | /** @var string Hash of the salted user password */ | ||
55 | protected $passwordHash = ''; | ||
56 | |||
57 | /** @var string Salt used by hash functions */ | ||
58 | protected $salt = '669e24fa9c5a59a613f98e8e38327384504a4af2'; | ||
59 | |||
60 | /** | ||
61 | * Prepare or reset test resources | ||
62 | */ | ||
63 | public function setUp() | ||
64 | { | ||
65 | if (file_exists($this->banFile)) { | ||
66 | unlink($this->banFile); | ||
67 | } | ||
68 | |||
69 | $this->passwordHash = sha1($this->password . $this->login . $this->salt); | ||
70 | |||
71 | $this->configManager = new \FakeConfigManager([ | ||
72 | 'credentials.login' => $this->login, | ||
73 | 'credentials.hash' => $this->passwordHash, | ||
74 | 'credentials.salt' => $this->salt, | ||
75 | 'resource.ban_file' => $this->banFile, | ||
76 | 'resource.log' => $this->logFile, | ||
77 | 'security.ban_after' => 4, | ||
78 | 'security.ban_duration' => 3600, | ||
79 | 'security.trusted_proxies' => [$this->trustedProxy], | ||
80 | ]); | ||
81 | |||
82 | $this->cookie = []; | ||
83 | |||
84 | $this->globals = &$GLOBALS; | ||
85 | unset($this->globals['IPBANS']); | ||
86 | |||
87 | $this->session = []; | ||
88 | |||
89 | $this->sessionManager = new SessionManager($this->session, $this->configManager); | ||
90 | $this->loginManager = new LoginManager($this->globals, $this->configManager, $this->sessionManager); | ||
91 | $this->server['REMOTE_ADDR'] = $this->ipAddr; | ||
92 | } | ||
93 | |||
94 | /** | ||
95 | * Wipe test resources | ||
96 | */ | ||
97 | public function tearDown() | ||
98 | { | ||
99 | unset($this->globals['IPBANS']); | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * Instantiate a LoginManager and load ban records | ||
104 | */ | ||
105 | public function testReadBanFile() | ||
106 | { | ||
107 | file_put_contents( | ||
108 | $this->banFile, | ||
109 | "<?php\n\$GLOBALS['IPBANS']=array('FAILURES' => array('127.0.0.1' => 99));\n?>" | ||
110 | ); | ||
111 | new LoginManager($this->globals, $this->configManager, null); | ||
112 | $this->assertEquals(99, $this->globals['IPBANS']['FAILURES']['127.0.0.1']); | ||
113 | } | ||
114 | |||
115 | /** | ||
116 | * Record a failed login attempt | ||
117 | */ | ||
118 | public function testHandleFailedLogin() | ||
119 | { | ||
120 | $this->loginManager->handleFailedLogin($this->server); | ||
121 | $this->assertEquals(1, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]); | ||
122 | |||
123 | $this->loginManager->handleFailedLogin($this->server); | ||
124 | $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]); | ||
125 | } | ||
126 | |||
127 | /** | ||
128 | * Record a failed login attempt - IP behind a trusted proxy | ||
129 | */ | ||
130 | public function testHandleFailedLoginBehindTrustedProxy() | ||
131 | { | ||
132 | $server = [ | ||
133 | 'REMOTE_ADDR' => $this->trustedProxy, | ||
134 | 'HTTP_X_FORWARDED_FOR' => $this->ipAddr, | ||
135 | ]; | ||
136 | $this->loginManager->handleFailedLogin($server); | ||
137 | $this->assertEquals(1, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]); | ||
138 | |||
139 | $this->loginManager->handleFailedLogin($server); | ||
140 | $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]); | ||
141 | } | ||
142 | |||
143 | /** | ||
144 | * Record a failed login attempt - IP behind a trusted proxy but not forwarded | ||
145 | */ | ||
146 | public function testHandleFailedLoginBehindTrustedProxyNoIp() | ||
147 | { | ||
148 | $server = [ | ||
149 | 'REMOTE_ADDR' => $this->trustedProxy, | ||
150 | ]; | ||
151 | $this->loginManager->handleFailedLogin($server); | ||
152 | $this->assertFalse(isset($this->globals['IPBANS']['FAILURES'][$this->ipAddr])); | ||
153 | |||
154 | $this->loginManager->handleFailedLogin($server); | ||
155 | $this->assertFalse(isset($this->globals['IPBANS']['FAILURES'][$this->ipAddr])); | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * Record a failed login attempt and ban the IP after too many failures | ||
160 | */ | ||
161 | public function testHandleFailedLoginBanIp() | ||
162 | { | ||
163 | $this->loginManager->handleFailedLogin($this->server); | ||
164 | $this->assertEquals(1, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]); | ||
165 | $this->assertTrue($this->loginManager->canLogin($this->server)); | ||
166 | |||
167 | $this->loginManager->handleFailedLogin($this->server); | ||
168 | $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]); | ||
169 | $this->assertTrue($this->loginManager->canLogin($this->server)); | ||
170 | |||
171 | $this->loginManager->handleFailedLogin($this->server); | ||
172 | $this->assertEquals(3, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]); | ||
173 | $this->assertTrue($this->loginManager->canLogin($this->server)); | ||
174 | |||
175 | $this->loginManager->handleFailedLogin($this->server); | ||
176 | $this->assertEquals(4, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]); | ||
177 | $this->assertFalse($this->loginManager->canLogin($this->server)); | ||
178 | |||
179 | // handleFailedLogin is not supposed to be called at this point: | ||
180 | // - no login form should be displayed once an IP has been banned | ||
181 | // - yet this could happen when using custom templates / scripts | ||
182 | $this->loginManager->handleFailedLogin($this->server); | ||
183 | $this->assertEquals(5, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]); | ||
184 | $this->assertFalse($this->loginManager->canLogin($this->server)); | ||
185 | } | ||
186 | |||
187 | /** | ||
188 | * Nothing to do | ||
189 | */ | ||
190 | public function testHandleSuccessfulLogin() | ||
191 | { | ||
192 | $this->assertTrue($this->loginManager->canLogin($this->server)); | ||
193 | |||
194 | $this->loginManager->handleSuccessfulLogin($this->server); | ||
195 | $this->assertTrue($this->loginManager->canLogin($this->server)); | ||
196 | } | ||
197 | |||
198 | /** | ||
199 | * Erase failure records after successfully logging in from this IP | ||
200 | */ | ||
201 | public function testHandleSuccessfulLoginAfterFailure() | ||
202 | { | ||
203 | $this->loginManager->handleFailedLogin($this->server); | ||
204 | $this->loginManager->handleFailedLogin($this->server); | ||
205 | $this->assertEquals(2, $this->globals['IPBANS']['FAILURES'][$this->ipAddr]); | ||
206 | $this->assertTrue($this->loginManager->canLogin($this->server)); | ||
207 | |||
208 | $this->loginManager->handleSuccessfulLogin($this->server); | ||
209 | $this->assertTrue($this->loginManager->canLogin($this->server)); | ||
210 | $this->assertFalse(isset($this->globals['IPBANS']['FAILURES'][$this->ipAddr])); | ||
211 | $this->assertFalse(isset($this->globals['IPBANS']['BANS'][$this->ipAddr])); | ||
212 | } | ||
213 | |||
214 | /** | ||
215 | * The IP is not banned | ||
216 | */ | ||
217 | public function testCanLoginIpNotBanned() | ||
218 | { | ||
219 | $this->assertTrue($this->loginManager->canLogin($this->server)); | ||
220 | } | ||
221 | |||
222 | /** | ||
223 | * The IP is banned | ||
224 | */ | ||
225 | public function testCanLoginIpBanned() | ||
226 | { | ||
227 | // ban the IP for an hour | ||
228 | $this->globals['IPBANS']['FAILURES'][$this->ipAddr] = 10; | ||
229 | $this->globals['IPBANS']['BANS'][$this->ipAddr] = time() + 3600; | ||
230 | |||
231 | $this->assertFalse($this->loginManager->canLogin($this->server)); | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * The IP is banned, and the ban duration is over | ||
236 | */ | ||
237 | public function testCanLoginIpBanExpired() | ||
238 | { | ||
239 | // ban the IP for an hour | ||
240 | $this->globals['IPBANS']['FAILURES'][$this->ipAddr] = 10; | ||
241 | $this->globals['IPBANS']['BANS'][$this->ipAddr] = time() + 3600; | ||
242 | $this->assertFalse($this->loginManager->canLogin($this->server)); | ||
243 | |||
244 | // lift the ban | ||
245 | $this->globals['IPBANS']['BANS'][$this->ipAddr] = time() - 3600; | ||
246 | $this->assertTrue($this->loginManager->canLogin($this->server)); | ||
247 | } | ||
248 | |||
249 | /** | ||
250 | * Generate a token depending on the user credentials and client IP | ||
251 | */ | ||
252 | public function testGenerateStaySignedInToken() | ||
253 | { | ||
254 | $this->loginManager->generateStaySignedInToken($this->clientIpAddress); | ||
255 | |||
256 | $this->assertEquals( | ||
257 | sha1($this->passwordHash . $this->clientIpAddress . $this->salt), | ||
258 | $this->loginManager->getStaySignedInToken() | ||
259 | ); | ||
260 | } | ||
261 | |||
262 | /** | ||
263 | * Check user login - Shaarli has not yet been configured | ||
264 | */ | ||
265 | public function testCheckLoginStateNotConfigured() | ||
266 | { | ||
267 | $configManager = new \FakeConfigManager([ | ||
268 | 'resource.ban_file' => $this->banFile, | ||
269 | ]); | ||
270 | $loginManager = new LoginManager($this->globals, $configManager, null); | ||
271 | $loginManager->checkLoginState([], ''); | ||
272 | |||
273 | $this->assertFalse($loginManager->isLoggedIn()); | ||
274 | } | ||
275 | |||
276 | /** | ||
277 | * Check user login - the client cookie does not match the server token | ||
278 | */ | ||
279 | public function testCheckLoginStateStaySignedInWithInvalidToken() | ||
280 | { | ||
281 | // simulate a previous login | ||
282 | $this->session = [ | ||
283 | 'ip' => $this->clientIpAddress, | ||
284 | 'expires_on' => time() + 100, | ||
285 | ]; | ||
286 | $this->loginManager->generateStaySignedInToken($this->clientIpAddress); | ||
287 | $this->cookie[LoginManager::$STAY_SIGNED_IN_COOKIE] = 'nope'; | ||
288 | |||
289 | $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress); | ||
290 | |||
291 | $this->assertTrue($this->loginManager->isLoggedIn()); | ||
292 | $this->assertTrue(empty($this->session['username'])); | ||
293 | } | ||
294 | |||
295 | /** | ||
296 | * Check user login - the client cookie matches the server token | ||
297 | */ | ||
298 | public function testCheckLoginStateStaySignedInWithValidToken() | ||
299 | { | ||
300 | $this->loginManager->generateStaySignedInToken($this->clientIpAddress); | ||
301 | $this->cookie[LoginManager::$STAY_SIGNED_IN_COOKIE] = $this->loginManager->getStaySignedInToken(); | ||
302 | |||
303 | $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress); | ||
304 | |||
305 | $this->assertTrue($this->loginManager->isLoggedIn()); | ||
306 | $this->assertEquals($this->login, $this->session['username']); | ||
307 | $this->assertEquals($this->clientIpAddress, $this->session['ip']); | ||
308 | } | ||
309 | |||
310 | /** | ||
311 | * Check user login - the session has expired | ||
312 | */ | ||
313 | public function testCheckLoginStateSessionExpired() | ||
314 | { | ||
315 | $this->loginManager->generateStaySignedInToken($this->clientIpAddress); | ||
316 | $this->session['expires_on'] = time() - 100; | ||
317 | |||
318 | $this->loginManager->checkLoginState($this->cookie, $this->clientIpAddress); | ||
319 | |||
320 | $this->assertFalse($this->loginManager->isLoggedIn()); | ||
321 | } | ||
322 | |||
323 | /** | ||
324 | * Check user login - the remote client IP has changed | ||
325 | */ | ||
326 | public function testCheckLoginStateClientIpChanged() | ||
327 | { | ||
328 | $this->loginManager->generateStaySignedInToken($this->clientIpAddress); | ||
329 | |||
330 | $this->loginManager->checkLoginState($this->cookie, '10.7.157.98'); | ||
331 | |||
332 | $this->assertFalse($this->loginManager->isLoggedIn()); | ||
333 | } | ||
334 | |||
335 | /** | ||
336 | * Check user credentials - wrong login supplied | ||
337 | */ | ||
338 | public function testCheckCredentialsWrongLogin() | ||
339 | { | ||
340 | $this->assertFalse( | ||
341 | $this->loginManager->checkCredentials('', '', 'b4dl0g1n', $this->password) | ||
342 | ); | ||
343 | } | ||
344 | |||
345 | /** | ||
346 | * Check user credentials - wrong password supplied | ||
347 | */ | ||
348 | public function testCheckCredentialsWrongPassword() | ||
349 | { | ||
350 | $this->assertFalse( | ||
351 | $this->loginManager->checkCredentials('', '', $this->login, 'b4dp455wd') | ||
352 | ); | ||
353 | } | ||
354 | |||
355 | /** | ||
356 | * Check user credentials - wrong login and password supplied | ||
357 | */ | ||
358 | public function testCheckCredentialsWrongLoginAndPassword() | ||
359 | { | ||
360 | $this->assertFalse( | ||
361 | $this->loginManager->checkCredentials('', '', 'b4dl0g1n', 'b4dp455wd') | ||
362 | ); | ||
363 | } | ||
364 | |||
365 | /** | ||
366 | * Check user credentials - correct login and password supplied | ||
367 | */ | ||
368 | public function testCheckCredentialsGoodLoginAndPassword() | ||
369 | { | ||
370 | $this->assertTrue( | ||
371 | $this->loginManager->checkCredentials('', '', $this->login, $this->password) | ||
372 | ); | ||
373 | } | ||
374 | } | ||
diff --git a/tests/security/SessionManagerTest.php b/tests/security/SessionManagerTest.php new file mode 100644 index 00000000..9bd868f8 --- /dev/null +++ b/tests/security/SessionManagerTest.php | |||
@@ -0,0 +1,273 @@ | |||
1 | <?php | ||
2 | require_once 'tests/utils/FakeConfigManager.php'; | ||
3 | |||
4 | // Initialize reference data _before_ PHPUnit starts a session | ||
5 | require_once 'tests/utils/ReferenceSessionIdHashes.php'; | ||
6 | ReferenceSessionIdHashes::genAllHashes(); | ||
7 | |||
8 | use \Shaarli\Security\SessionManager; | ||
9 | use \PHPUnit\Framework\TestCase; | ||
10 | |||
11 | |||
12 | /** | ||
13 | * Test coverage for SessionManager | ||
14 | */ | ||
15 | class SessionManagerTest extends TestCase | ||
16 | { | ||
17 | /** @var array Session ID hashes */ | ||
18 | protected static $sidHashes = null; | ||
19 | |||
20 | /** @var \FakeConfigManager ConfigManager substitute for testing */ | ||
21 | protected $conf = null; | ||
22 | |||
23 | /** @var array $_SESSION array for testing */ | ||
24 | protected $session = []; | ||
25 | |||
26 | /** @var SessionManager Server-side session management abstraction */ | ||
27 | protected $sessionManager = null; | ||
28 | |||
29 | /** | ||
30 | * Assign reference data | ||
31 | */ | ||
32 | public static function setUpBeforeClass() | ||
33 | { | ||
34 | self::$sidHashes = ReferenceSessionIdHashes::getHashes(); | ||
35 | } | ||
36 | |||
37 | /** | ||
38 | * Initialize or reset test resources | ||
39 | */ | ||
40 | public function setUp() | ||
41 | { | ||
42 | $this->conf = new FakeConfigManager([ | ||
43 | 'credentials.login' => 'johndoe', | ||
44 | 'credentials.salt' => 'salt', | ||
45 | 'security.session_protection_disabled' => false, | ||
46 | ]); | ||
47 | $this->session = []; | ||
48 | $this->sessionManager = new SessionManager($this->session, $this->conf); | ||
49 | } | ||
50 | |||
51 | /** | ||
52 | * Generate a session token | ||
53 | */ | ||
54 | public function testGenerateToken() | ||
55 | { | ||
56 | $token = $this->sessionManager->generateToken(); | ||
57 | |||
58 | $this->assertEquals(1, $this->session['tokens'][$token]); | ||
59 | $this->assertEquals(40, strlen($token)); | ||
60 | } | ||
61 | |||
62 | /** | ||
63 | * Check a session token | ||
64 | */ | ||
65 | public function testCheckToken() | ||
66 | { | ||
67 | $token = '4dccc3a45ad9d03e5542b90c37d8db6d10f2b38b'; | ||
68 | $session = [ | ||
69 | 'tokens' => [ | ||
70 | $token => 1, | ||
71 | ], | ||
72 | ]; | ||
73 | $sessionManager = new SessionManager($session, $this->conf); | ||
74 | |||
75 | // check and destroy the token | ||
76 | $this->assertTrue($sessionManager->checkToken($token)); | ||
77 | $this->assertFalse(isset($session['tokens'][$token])); | ||
78 | |||
79 | // ensure the token has been destroyed | ||
80 | $this->assertFalse($sessionManager->checkToken($token)); | ||
81 | } | ||
82 | |||
83 | /** | ||
84 | * Generate and check a session token | ||
85 | */ | ||
86 | public function testGenerateAndCheckToken() | ||
87 | { | ||
88 | $token = $this->sessionManager->generateToken(); | ||
89 | |||
90 | // ensure a token has been generated | ||
91 | $this->assertEquals(1, $this->session['tokens'][$token]); | ||
92 | $this->assertEquals(40, strlen($token)); | ||
93 | |||
94 | // check and destroy the token | ||
95 | $this->assertTrue($this->sessionManager->checkToken($token)); | ||
96 | $this->assertFalse(isset($this->session['tokens'][$token])); | ||
97 | |||
98 | // ensure the token has been destroyed | ||
99 | $this->assertFalse($this->sessionManager->checkToken($token)); | ||
100 | } | ||
101 | |||
102 | /** | ||
103 | * Check an invalid session token | ||
104 | */ | ||
105 | public function testCheckInvalidToken() | ||
106 | { | ||
107 | $this->assertFalse($this->sessionManager->checkToken('4dccc3a45ad9d03e5542b90c37d8db6d10f2b38b')); | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * Test SessionManager::checkId with a valid ID - TEST ALL THE HASHES! | ||
112 | * | ||
113 | * This tests extensively covers all hash algorithms / bit representations | ||
114 | */ | ||
115 | public function testIsAnyHashSessionIdValid() | ||
116 | { | ||
117 | foreach (self::$sidHashes as $algo => $bpcs) { | ||
118 | foreach ($bpcs as $bpc => $hash) { | ||
119 | $this->assertTrue(SessionManager::checkId($hash)); | ||
120 | } | ||
121 | } | ||
122 | } | ||
123 | |||
124 | /** | ||
125 | * Test checkId with a valid ID - SHA-1 hashes | ||
126 | */ | ||
127 | public function testIsSha1SessionIdValid() | ||
128 | { | ||
129 | $this->assertTrue(SessionManager::checkId(sha1('shaarli'))); | ||
130 | } | ||
131 | |||
132 | /** | ||
133 | * Test checkId with a valid ID - SHA-256 hashes | ||
134 | */ | ||
135 | public function testIsSha256SessionIdValid() | ||
136 | { | ||
137 | $this->assertTrue(SessionManager::checkId(hash('sha256', 'shaarli'))); | ||
138 | } | ||
139 | |||
140 | /** | ||
141 | * Test checkId with a valid ID - SHA-512 hashes | ||
142 | */ | ||
143 | public function testIsSha512SessionIdValid() | ||
144 | { | ||
145 | $this->assertTrue(SessionManager::checkId(hash('sha512', 'shaarli'))); | ||
146 | } | ||
147 | |||
148 | /** | ||
149 | * Test checkId with invalid IDs. | ||
150 | */ | ||
151 | public function testIsSessionIdInvalid() | ||
152 | { | ||
153 | $this->assertFalse(SessionManager::checkId('')); | ||
154 | $this->assertFalse(SessionManager::checkId([])); | ||
155 | $this->assertFalse( | ||
156 | SessionManager::checkId('c0ZqcWF3VFE2NmJBdm1HMVQ0ZHJ3UmZPbTFsNGhkNHI=') | ||
157 | ); | ||
158 | } | ||
159 | |||
160 | /** | ||
161 | * Store login information after a successful login | ||
162 | */ | ||
163 | public function testStoreLoginInfo() | ||
164 | { | ||
165 | $this->sessionManager->storeLoginInfo('ip_id'); | ||
166 | |||
167 | $this->assertGreaterThan(time(), $this->session['expires_on']); | ||
168 | $this->assertEquals('ip_id', $this->session['ip']); | ||
169 | $this->assertEquals('johndoe', $this->session['username']); | ||
170 | } | ||
171 | |||
172 | /** | ||
173 | * Extend a server-side session by SessionManager::$SHORT_TIMEOUT | ||
174 | */ | ||
175 | public function testExtendSession() | ||
176 | { | ||
177 | $this->sessionManager->extendSession(); | ||
178 | |||
179 | $this->assertGreaterThan(time(), $this->session['expires_on']); | ||
180 | $this->assertLessThanOrEqual( | ||
181 | time() + SessionManager::$SHORT_TIMEOUT, | ||
182 | $this->session['expires_on'] | ||
183 | ); | ||
184 | } | ||
185 | |||
186 | /** | ||
187 | * Extend a server-side session by SessionManager::$LONG_TIMEOUT | ||
188 | */ | ||
189 | public function testExtendSessionStaySignedIn() | ||
190 | { | ||
191 | $this->sessionManager->setStaySignedIn(true); | ||
192 | $this->sessionManager->extendSession(); | ||
193 | |||
194 | $this->assertGreaterThan(time(), $this->session['expires_on']); | ||
195 | $this->assertGreaterThan( | ||
196 | time() + SessionManager::$LONG_TIMEOUT - 10, | ||
197 | $this->session['expires_on'] | ||
198 | ); | ||
199 | $this->assertLessThanOrEqual( | ||
200 | time() + SessionManager::$LONG_TIMEOUT, | ||
201 | $this->session['expires_on'] | ||
202 | ); | ||
203 | } | ||
204 | |||
205 | /** | ||
206 | * Unset session variables after logging out | ||
207 | */ | ||
208 | public function testLogout() | ||
209 | { | ||
210 | $this->session = [ | ||
211 | 'ip' => 'ip_id', | ||
212 | 'expires_on' => time() + 1000, | ||
213 | 'username' => 'johndoe', | ||
214 | 'visibility' => 'public', | ||
215 | 'untaggedonly' => false, | ||
216 | ]; | ||
217 | $this->sessionManager->logout(); | ||
218 | |||
219 | $this->assertFalse(isset($this->session['ip'])); | ||
220 | $this->assertFalse(isset($this->session['expires_on'])); | ||
221 | $this->assertFalse(isset($this->session['username'])); | ||
222 | $this->assertFalse(isset($this->session['visibility'])); | ||
223 | $this->assertFalse(isset($this->session['untaggedonly'])); | ||
224 | } | ||
225 | |||
226 | /** | ||
227 | * The session is active and expiration time has been reached | ||
228 | */ | ||
229 | public function testHasExpiredTimeElapsed() | ||
230 | { | ||
231 | $this->session['expires_on'] = time() - 10; | ||
232 | |||
233 | $this->assertTrue($this->sessionManager->hasSessionExpired()); | ||
234 | } | ||
235 | |||
236 | /** | ||
237 | * The session is active and expiration time has not been reached | ||
238 | */ | ||
239 | public function testHasNotExpired() | ||
240 | { | ||
241 | $this->session['expires_on'] = time() + 1000; | ||
242 | |||
243 | $this->assertFalse($this->sessionManager->hasSessionExpired()); | ||
244 | } | ||
245 | |||
246 | /** | ||
247 | * Session hijacking protection is disabled, we assume the IP has not changed | ||
248 | */ | ||
249 | public function testHasClientIpChangedNoSessionProtection() | ||
250 | { | ||
251 | $this->conf->set('security.session_protection_disabled', true); | ||
252 | |||
253 | $this->assertFalse($this->sessionManager->hasClientIpChanged('')); | ||
254 | } | ||
255 | |||
256 | /** | ||
257 | * The client IP identifier has not changed | ||
258 | */ | ||
259 | public function testHasClientIpChangedNope() | ||
260 | { | ||
261 | $this->session['ip'] = 'ip_id'; | ||
262 | $this->assertFalse($this->sessionManager->hasClientIpChanged('ip_id')); | ||
263 | } | ||
264 | |||
265 | /** | ||
266 | * The client IP identifier has changed | ||
267 | */ | ||
268 | public function testHasClientIpChanged() | ||
269 | { | ||
270 | $this->session['ip'] = 'ip_id_one'; | ||
271 | $this->assertTrue($this->sessionManager->hasClientIpChanged('ip_id_two')); | ||
272 | } | ||
273 | } | ||
diff --git a/tests/utils/FakeConfigManager.php b/tests/utils/FakeConfigManager.php index f29760cb..360b34a9 100644 --- a/tests/utils/FakeConfigManager.php +++ b/tests/utils/FakeConfigManager.php | |||
@@ -5,8 +5,53 @@ | |||
5 | */ | 5 | */ |
6 | class FakeConfigManager | 6 | class FakeConfigManager |
7 | { | 7 | { |
8 | public static function get($key) | 8 | protected $values = []; |
9 | |||
10 | /** | ||
11 | * Initialize with test values | ||
12 | * | ||
13 | * @param array $values Initial values | ||
14 | */ | ||
15 | public function __construct($values = []) | ||
16 | { | ||
17 | $this->values = $values; | ||
18 | } | ||
19 | |||
20 | /** | ||
21 | * Set a given value | ||
22 | * | ||
23 | * @param string $key Key of the value to set | ||
24 | * @param mixed $value Value to set | ||
25 | */ | ||
26 | public function set($key, $value) | ||
27 | { | ||
28 | $this->values[$key] = $value; | ||
29 | } | ||
30 | |||
31 | /** | ||
32 | * Get a given configuration value | ||
33 | * | ||
34 | * @param string $key Index of the value to retrieve | ||
35 | * | ||
36 | * @return mixed The value if set, else the name of the key | ||
37 | */ | ||
38 | public function get($key) | ||
9 | { | 39 | { |
40 | if (isset($this->values[$key])) { | ||
41 | return $this->values[$key]; | ||
42 | } | ||
10 | return $key; | 43 | return $key; |
11 | } | 44 | } |
45 | |||
46 | /** | ||
47 | * Check if a setting exists | ||
48 | * | ||
49 | * @param string $setting Asked setting, keys separated with dots | ||
50 | * | ||
51 | * @return bool true if the setting exists, false otherwise | ||
52 | */ | ||
53 | public function exists($setting) | ||
54 | { | ||
55 | return array_key_exists($setting, $this->values); | ||
56 | } | ||
12 | } | 57 | } |
diff --git a/tests/utils/config/configJson.json.php b/tests/utils/config/configJson.json.php index 9c9288f3..1549ddfc 100644 --- a/tests/utils/config/configJson.json.php +++ b/tests/utils/config/configJson.json.php | |||
@@ -1,35 +1,84 @@ | |||
1 | <?php /* | 1 | <?php /* |
2 | { | 2 | { |
3 | "credentials": { | 3 | "credentials": { |
4 | "login":"root", | 4 | "login": "root", |
5 | "hash":"hash", | 5 | "hash": "hash", |
6 | "salt":"salt" | 6 | "salt": "salt" |
7 | }, | 7 | }, |
8 | "security": { | 8 | "security": { |
9 | "session_protection_disabled":false | 9 | "session_protection_disabled": false, |
10 | "ban_after": 4, | ||
11 | "ban_duration": 1800, | ||
12 | "open_shaarli": false, | ||
13 | "allowed_protocols": [ | ||
14 | "ftp", | ||
15 | "ftps", | ||
16 | "magnet" | ||
17 | ] | ||
10 | }, | 18 | }, |
11 | "general": { | 19 | "general": { |
12 | "timezone":"Europe\/Paris", | 20 | "timezone": "Europe\/Paris", |
13 | "title": "Shaarli", | 21 | "title": "Shaarli", |
14 | "header_link": "?" | 22 | "header_link": "?", |
23 | "links_per_page": 20, | ||
24 | "enabled_plugins": [ | ||
25 | "qrcode" | ||
26 | ], | ||
27 | "default_note_title": "Note: " | ||
15 | }, | 28 | }, |
16 | "privacy": { | 29 | "privacy": { |
17 | "default_private_links":true | 30 | "default_private_links": true, |
31 | "hide_public_links": false, | ||
32 | "force_login": false, | ||
33 | "hide_timestamps": false, | ||
34 | "remember_user_default": true | ||
18 | }, | 35 | }, |
19 | "redirector": { | 36 | "redirector": { |
20 | "url":"lala" | 37 | "url": "lala", |
38 | "encode_url": true | ||
21 | }, | 39 | }, |
22 | "config": { | 40 | "config": { |
23 | "foo": "bar" | 41 | "foo": "bar" |
24 | }, | 42 | }, |
25 | "resource": { | 43 | "resource": { |
26 | "datastore": "tests\/utils\/config\/datastore.php", | 44 | "datastore": "tests\/utils\/config\/datastore.php", |
27 | "data_dir": "sandbox/", | 45 | "data_dir": "sandbox\/", |
28 | "raintpl_tpl": "tpl/" | 46 | "raintpl_tpl": "tpl\/", |
47 | "config": "data\/config.php", | ||
48 | "ban_file": "data\/ipbans.php", | ||
49 | "updates": "data\/updates.txt", | ||
50 | "log": "data\/log.txt", | ||
51 | "update_check": "data\/lastupdatecheck.txt", | ||
52 | "history": "data\/history.php", | ||
53 | "theme": "default", | ||
54 | "raintpl_tmp": "tmp\/", | ||
55 | "thumbnails_cache": "cache", | ||
56 | "page_cache": "pagecache" | ||
29 | }, | 57 | }, |
30 | "plugins": { | 58 | "plugins": { |
31 | "WALLABAG_VERSION": 1 | 59 | "WALLABAG_VERSION": 1 |
60 | }, | ||
61 | "dev": { | ||
62 | "debug": true | ||
63 | }, | ||
64 | "updates": { | ||
65 | "check_updates": false, | ||
66 | "check_updates_branch": "stable", | ||
67 | "check_updates_interval": 86400 | ||
68 | }, | ||
69 | "feed": { | ||
70 | "rss_permalinks": true, | ||
71 | "show_atom": true | ||
72 | }, | ||
73 | "translation": { | ||
74 | "language": "auto", | ||
75 | "mode": "php", | ||
76 | "extensions": [] | ||
77 | }, | ||
78 | "thumbnails": { | ||
79 | "mode": "common", | ||
80 | "width": 90, | ||
81 | "height": 53 | ||
32 | } | 82 | } |
33 | } | 83 | } |
34 | */ ?> | 84 | */ ?> |
35 | |||
diff --git a/tests/utils/config/wt.json b/tests/utils/config/wt.json new file mode 100644 index 00000000..69ce49a6 --- /dev/null +++ b/tests/utils/config/wt.json | |||
@@ -0,0 +1,12 @@ | |||
1 | { | ||
2 | "settings": { | ||
3 | "default": { | ||
4 | "_comment": "infinite cache", | ||
5 | "cache_duration": -1, | ||
6 | "timeout": 10 | ||
7 | }, | ||
8 | "path": { | ||
9 | "cache": "sandbox/" | ||
10 | } | ||
11 | } | ||
12 | } \ No newline at end of file | ||
diff --git a/tests/utils/customtpl/dummy/language/fr/LC_MESSAGES/dummy.mo b/tests/utils/customtpl/dummy/language/fr/LC_MESSAGES/dummy.mo new file mode 100644 index 00000000..8daae0c9 --- /dev/null +++ b/tests/utils/customtpl/dummy/language/fr/LC_MESSAGES/dummy.mo | |||
Binary files differ | |||
diff --git a/tests/utils/customtpl/dummy/language/fr/LC_MESSAGES/dummy.po b/tests/utils/customtpl/dummy/language/fr/LC_MESSAGES/dummy.po new file mode 100644 index 00000000..90d1abb0 --- /dev/null +++ b/tests/utils/customtpl/dummy/language/fr/LC_MESSAGES/dummy.po | |||
@@ -0,0 +1,16 @@ | |||
1 | msgid "" | ||
2 | msgstr "" | ||
3 | "Project-Id-Version: Theme extension test\n" | ||
4 | "POT-Creation-Date: 2017-05-20 13:54+0200\n" | ||
5 | "PO-Revision-Date: 2018-03-26 19:09+0200\n" | ||
6 | "Last-Translator: \n" | ||
7 | "Language-Team: Shaarli\n" | ||
8 | "Language: fr_FR\n" | ||
9 | "MIME-Version: 1.0\n" | ||
10 | "Content-Type: text/plain; charset=UTF-8\n" | ||
11 | "Content-Transfer-Encoding: 8bit\n" | ||
12 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" | ||
13 | "X-Generator: Poedit 2.0.6\n" | ||
14 | |||
15 | msgid "rooster" | ||
16 | msgstr "coq" | ||