aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/bookmark
diff options
context:
space:
mode:
Diffstat (limited to 'tests/bookmark')
-rw-r--r--tests/bookmark/BookmarkArrayTest.php13
-rw-r--r--tests/bookmark/BookmarkFileServiceTest.php259
-rw-r--r--tests/bookmark/BookmarkFilterTest.php46
-rw-r--r--tests/bookmark/BookmarkInitializerTest.php13
-rw-r--r--tests/bookmark/BookmarkTest.php107
-rw-r--r--tests/bookmark/LinkUtilsTest.php446
6 files changed, 615 insertions, 269 deletions
diff --git a/tests/bookmark/BookmarkArrayTest.php b/tests/bookmark/BookmarkArrayTest.php
index ebed9bfc..1953078c 100644
--- a/tests/bookmark/BookmarkArrayTest.php
+++ b/tests/bookmark/BookmarkArrayTest.php
@@ -91,19 +91,6 @@ class BookmarkArrayTest extends TestCase
91 } 91 }
92 92
93 /** 93 /**
94 * Test adding a bad entry: invalid ID type
95 */
96 public function testArrayAccessAddBadEntryIdType()
97 {
98 $this->expectException(\Shaarli\Bookmark\Exception\InvalidBookmarkException::class);
99
100 $array = new BookmarkArray();
101 $bookmark = (new Bookmark())->setId('nope');
102 $bookmark->validate();
103 $array[] = $bookmark;
104 }
105
106 /**
107 * Test adding a bad entry: ID/offset not consistent 94 * Test adding a bad entry: ID/offset not consistent
108 */ 95 */
109 public function testArrayAccessAddBadEntryIdOffset() 96 public function testArrayAccessAddBadEntryIdOffset()
diff --git a/tests/bookmark/BookmarkFileServiceTest.php b/tests/bookmark/BookmarkFileServiceTest.php
index c399822b..f619aff3 100644
--- a/tests/bookmark/BookmarkFileServiceTest.php
+++ b/tests/bookmark/BookmarkFileServiceTest.php
@@ -6,6 +6,7 @@
6namespace Shaarli\Bookmark; 6namespace Shaarli\Bookmark;
7 7
8use DateTime; 8use DateTime;
9use malkusch\lock\mutex\NoMutex;
9use ReferenceLinkDB; 10use ReferenceLinkDB;
10use ReflectionClass; 11use ReflectionClass;
11use Shaarli; 12use Shaarli;
@@ -52,6 +53,9 @@ class BookmarkFileServiceTest extends TestCase
52 */ 53 */
53 protected $privateLinkDB = null; 54 protected $privateLinkDB = null;
54 55
56 /** @var NoMutex */
57 protected $mutex;
58
55 /** 59 /**
56 * Instantiates public and private LinkDBs with test data 60 * Instantiates public and private LinkDBs with test data
57 * 61 *
@@ -68,6 +72,8 @@ class BookmarkFileServiceTest extends TestCase
68 */ 72 */
69 protected function setUp(): void 73 protected function setUp(): void
70 { 74 {
75 $this->mutex = new NoMutex();
76
71 if (file_exists(self::$testDatastore)) { 77 if (file_exists(self::$testDatastore)) {
72 unlink(self::$testDatastore); 78 unlink(self::$testDatastore);
73 } 79 }
@@ -87,8 +93,8 @@ class BookmarkFileServiceTest extends TestCase
87 $this->refDB = new \ReferenceLinkDB(); 93 $this->refDB = new \ReferenceLinkDB();
88 $this->refDB->write(self::$testDatastore); 94 $this->refDB->write(self::$testDatastore);
89 $this->history = new History('sandbox/history.php'); 95 $this->history = new History('sandbox/history.php');
90 $this->publicLinkDB = new BookmarkFileService($this->conf, $this->history, false); 96 $this->publicLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, false);
91 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 97 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
92 } 98 }
93 99
94 /** 100 /**
@@ -105,7 +111,7 @@ class BookmarkFileServiceTest extends TestCase
105 $db = self::getMethod('migrate'); 111 $db = self::getMethod('migrate');
106 $db->invokeArgs($this->privateLinkDB, []); 112 $db->invokeArgs($this->privateLinkDB, []);
107 113
108 $db = new \FakeBookmarkService($this->conf, $this->history, true); 114 $db = new \FakeBookmarkService($this->conf, $this->history, $this->mutex, true);
109 $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks()); 115 $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks());
110 $this->assertEquals($this->refDB->countLinks(), $db->count()); 116 $this->assertEquals($this->refDB->countLinks(), $db->count());
111 } 117 }
@@ -174,7 +180,7 @@ class BookmarkFileServiceTest extends TestCase
174 $this->assertEquals($updated, $bookmark->getUpdated()); 180 $this->assertEquals($updated, $bookmark->getUpdated());
175 181
176 // reload from file 182 // reload from file
177 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 183 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
178 184
179 $bookmark = $this->privateLinkDB->get(43); 185 $bookmark = $this->privateLinkDB->get(43);
180 $this->assertEquals(43, $bookmark->getId()); 186 $this->assertEquals(43, $bookmark->getId());
@@ -212,7 +218,7 @@ class BookmarkFileServiceTest extends TestCase
212 $this->assertNull($bookmark->getUpdated()); 218 $this->assertNull($bookmark->getUpdated());
213 219
214 // reload from file 220 // reload from file
215 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 221 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
216 222
217 $bookmark = $this->privateLinkDB->get(43); 223 $bookmark = $this->privateLinkDB->get(43);
218 $this->assertEquals(43, $bookmark->getId()); 224 $this->assertEquals(43, $bookmark->getId());
@@ -242,7 +248,7 @@ class BookmarkFileServiceTest extends TestCase
242 $this->assertEquals(43, $bookmark->getId()); 248 $this->assertEquals(43, $bookmark->getId());
243 249
244 // reload from file 250 // reload from file
245 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 251 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
246 252
247 $this->privateLinkDB->get(43); 253 $this->privateLinkDB->get(43);
248 } 254 }
@@ -259,17 +265,6 @@ class BookmarkFileServiceTest extends TestCase
259 } 265 }
260 266
261 /** 267 /**
262 * Test add() method with an entry which is not a bookmark instance
263 */
264 public function testAddNotABookmark()
265 {
266 $this->expectException(\Exception::class);
267 $this->expectExceptionMessage('Provided data is invalid');
268
269 $this->privateLinkDB->add(['title' => 'hi!']);
270 }
271
272 /**
273 * Test add() method with a Bookmark already containing an ID 268 * Test add() method with a Bookmark already containing an ID
274 */ 269 */
275 public function testAddWithId() 270 public function testAddWithId()
@@ -314,7 +309,7 @@ class BookmarkFileServiceTest extends TestCase
314 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated()); 309 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated());
315 310
316 // reload from file 311 // reload from file
317 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 312 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
318 313
319 $bookmark = $this->privateLinkDB->get(42); 314 $bookmark = $this->privateLinkDB->get(42);
320 $this->assertEquals(42, $bookmark->getId()); 315 $this->assertEquals(42, $bookmark->getId());
@@ -355,7 +350,7 @@ class BookmarkFileServiceTest extends TestCase
355 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated()); 350 $this->assertTrue(new \DateTime('5 seconds ago') < $bookmark->getUpdated());
356 351
357 // reload from file 352 // reload from file
358 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 353 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
359 354
360 $bookmark = $this->privateLinkDB->get(42); 355 $bookmark = $this->privateLinkDB->get(42);
361 $this->assertEquals(42, $bookmark->getId()); 356 $this->assertEquals(42, $bookmark->getId());
@@ -388,7 +383,7 @@ class BookmarkFileServiceTest extends TestCase
388 $this->assertEquals($title, $bookmark->getTitle()); 383 $this->assertEquals($title, $bookmark->getTitle());
389 384
390 // reload from file 385 // reload from file
391 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 386 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
392 387
393 $bookmark = $this->privateLinkDB->get(42); 388 $bookmark = $this->privateLinkDB->get(42);
394 $this->assertEquals(42, $bookmark->getId()); 389 $this->assertEquals(42, $bookmark->getId());
@@ -407,17 +402,6 @@ class BookmarkFileServiceTest extends TestCase
407 } 402 }
408 403
409 /** 404 /**
410 * Test set() method with an entry which is not a bookmark instance
411 */
412 public function testSetNotABookmark()
413 {
414 $this->expectException(\Exception::class);
415 $this->expectExceptionMessage('Provided data is invalid');
416
417 $this->privateLinkDB->set(['title' => 'hi!']);
418 }
419
420 /**
421 * Test set() method with a Bookmark without an ID defined. 405 * Test set() method with a Bookmark without an ID defined.
422 */ 406 */
423 public function testSetWithoutId() 407 public function testSetWithoutId()
@@ -452,7 +436,7 @@ class BookmarkFileServiceTest extends TestCase
452 $this->assertEquals(43, $bookmark->getId()); 436 $this->assertEquals(43, $bookmark->getId());
453 437
454 // reload from file 438 // reload from file
455 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 439 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
456 440
457 $bookmark = $this->privateLinkDB->get(43); 441 $bookmark = $this->privateLinkDB->get(43);
458 $this->assertEquals(43, $bookmark->getId()); 442 $this->assertEquals(43, $bookmark->getId());
@@ -472,7 +456,7 @@ class BookmarkFileServiceTest extends TestCase
472 $this->assertEquals($title, $bookmark->getTitle()); 456 $this->assertEquals($title, $bookmark->getTitle());
473 457
474 // reload from file 458 // reload from file
475 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 459 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
476 460
477 $bookmark = $this->privateLinkDB->get(42); 461 $bookmark = $this->privateLinkDB->get(42);
478 $this->assertEquals(42, $bookmark->getId()); 462 $this->assertEquals(42, $bookmark->getId());
@@ -491,17 +475,6 @@ class BookmarkFileServiceTest extends TestCase
491 } 475 }
492 476
493 /** 477 /**
494 * Test addOrSet() method with an entry which is not a bookmark instance
495 */
496 public function testAddOrSetNotABookmark()
497 {
498 $this->expectException(\Exception::class);
499 $this->expectExceptionMessage('Provided data is invalid');
500
501 $this->privateLinkDB->addOrSet(['title' => 'hi!']);
502 }
503
504 /**
505 * Test addOrSet() method for a bookmark without any field set and without writing the data store 478 * Test addOrSet() method for a bookmark without any field set and without writing the data store
506 */ 479 */
507 public function testAddOrSetMinimalNoWrite() 480 public function testAddOrSetMinimalNoWrite()
@@ -515,7 +488,7 @@ class BookmarkFileServiceTest extends TestCase
515 $this->assertEquals($title, $bookmark->getTitle()); 488 $this->assertEquals($title, $bookmark->getTitle());
516 489
517 // reload from file 490 // reload from file
518 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 491 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
519 492
520 $bookmark = $this->privateLinkDB->get(42); 493 $bookmark = $this->privateLinkDB->get(42);
521 $this->assertEquals(42, $bookmark->getId()); 494 $this->assertEquals(42, $bookmark->getId());
@@ -541,7 +514,7 @@ class BookmarkFileServiceTest extends TestCase
541 $this->assertInstanceOf(BookmarkNotFoundException::class, $exception); 514 $this->assertInstanceOf(BookmarkNotFoundException::class, $exception);
542 515
543 // reload from file 516 // reload from file
544 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, true); 517 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
545 518
546 $this->privateLinkDB->get(42); 519 $this->privateLinkDB->get(42);
547 } 520 }
@@ -559,17 +532,6 @@ class BookmarkFileServiceTest extends TestCase
559 } 532 }
560 533
561 /** 534 /**
562 * Test remove() method with an entry which is not a bookmark instance
563 */
564 public function testRemoveNotABookmark()
565 {
566 $this->expectException(\Exception::class);
567 $this->expectExceptionMessage('Provided data is invalid');
568
569 $this->privateLinkDB->remove(['title' => 'hi!']);
570 }
571
572 /**
573 * Test remove() method with a Bookmark with an unknown ID 535 * Test remove() method with a Bookmark with an unknown ID
574 */ 536 */
575 public function testRemoveWithUnknownId() 537 public function testRemoveWithUnknownId()
@@ -645,7 +607,7 @@ class BookmarkFileServiceTest extends TestCase
645 607
646 $conf = new ConfigManager('tests/utils/config/configJson'); 608 $conf = new ConfigManager('tests/utils/config/configJson');
647 $conf->set('resource.datastore', 'null/store.db'); 609 $conf->set('resource.datastore', 'null/store.db');
648 new BookmarkFileService($conf, $this->history, true); 610 new BookmarkFileService($conf, $this->history, $this->mutex, true);
649 } 611 }
650 612
651 /** 613 /**
@@ -655,7 +617,7 @@ class BookmarkFileServiceTest extends TestCase
655 { 617 {
656 unlink(self::$testDatastore); 618 unlink(self::$testDatastore);
657 $this->assertFileNotExists(self::$testDatastore); 619 $this->assertFileNotExists(self::$testDatastore);
658 new BookmarkFileService($this->conf, $this->history, true); 620 new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
659 $this->assertFileExists(self::$testDatastore); 621 $this->assertFileExists(self::$testDatastore);
660 622
661 // ensure the correct data has been written 623 // ensure the correct data has been written
@@ -669,7 +631,7 @@ class BookmarkFileServiceTest extends TestCase
669 { 631 {
670 unlink(self::$testDatastore); 632 unlink(self::$testDatastore);
671 $this->assertFileNotExists(self::$testDatastore); 633 $this->assertFileNotExists(self::$testDatastore);
672 $db = new \FakeBookmarkService($this->conf, $this->history, false); 634 $db = new \FakeBookmarkService($this->conf, $this->history, $this->mutex, false);
673 $this->assertFileNotExists(self::$testDatastore); 635 $this->assertFileNotExists(self::$testDatastore);
674 $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks()); 636 $this->assertInstanceOf(BookmarkArray::class, $db->getBookmarks());
675 $this->assertCount(0, $db->getBookmarks()); 637 $this->assertCount(0, $db->getBookmarks());
@@ -702,13 +664,13 @@ class BookmarkFileServiceTest extends TestCase
702 */ 664 */
703 public function testSave() 665 public function testSave()
704 { 666 {
705 $testDB = new BookmarkFileService($this->conf, $this->history, true); 667 $testDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
706 $dbSize = $testDB->count(); 668 $dbSize = $testDB->count();
707 669
708 $bookmark = new Bookmark(); 670 $bookmark = new Bookmark();
709 $testDB->add($bookmark); 671 $testDB->add($bookmark);
710 672
711 $testDB = new BookmarkFileService($this->conf, $this->history, true); 673 $testDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
712 $this->assertEquals($dbSize + 1, $testDB->count()); 674 $this->assertEquals($dbSize + 1, $testDB->count());
713 } 675 }
714 676
@@ -718,28 +680,12 @@ class BookmarkFileServiceTest extends TestCase
718 public function testCountHiddenPublic() 680 public function testCountHiddenPublic()
719 { 681 {
720 $this->conf->set('privacy.hide_public_links', true); 682 $this->conf->set('privacy.hide_public_links', true);
721 $linkDB = new BookmarkFileService($this->conf, $this->history, false); 683 $linkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, false);
722 684
723 $this->assertEquals(0, $linkDB->count()); 685 $this->assertEquals(0, $linkDB->count());
724 } 686 }
725 687
726 /** 688 /**
727 * List the days for which bookmarks have been posted
728 */
729 public function testDays()
730 {
731 $this->assertEquals(
732 ['20100309', '20100310', '20121206', '20121207', '20130614', '20150310'],
733 $this->publicLinkDB->days()
734 );
735
736 $this->assertEquals(
737 ['20100309', '20100310', '20121206', '20121207', '20130614', '20141125', '20150310'],
738 $this->privateLinkDB->days()
739 );
740 }
741
742 /**
743 * The URL corresponds to an existing entry in the DB 689 * The URL corresponds to an existing entry in the DB
744 */ 690 */
745 public function testGetKnownLinkFromURL() 691 public function testGetKnownLinkFromURL()
@@ -786,6 +732,10 @@ class BookmarkFileServiceTest extends TestCase
786 // They need to be grouped with the first case found - order by date DESC: `sTuff`. 732 // They need to be grouped with the first case found - order by date DESC: `sTuff`.
787 'sTuff' => 2, 733 'sTuff' => 2,
788 'ut' => 1, 734 'ut' => 1,
735 'assurance' => 1,
736 'coding-style' => 1,
737 'quality' => 1,
738 'standards' => 1,
789 ], 739 ],
790 $this->publicLinkDB->bookmarksCountPerTag() 740 $this->publicLinkDB->bookmarksCountPerTag()
791 ); 741 );
@@ -814,6 +764,10 @@ class BookmarkFileServiceTest extends TestCase
814 'tag3' => 1, 764 'tag3' => 1,
815 'tag4' => 1, 765 'tag4' => 1,
816 'ut' => 1, 766 'ut' => 1,
767 'assurance' => 1,
768 'coding-style' => 1,
769 'quality' => 1,
770 'standards' => 1,
817 ], 771 ],
818 $this->privateLinkDB->bookmarksCountPerTag() 772 $this->privateLinkDB->bookmarksCountPerTag()
819 ); 773 );
@@ -928,6 +882,37 @@ class BookmarkFileServiceTest extends TestCase
928 } 882 }
929 883
930 /** 884 /**
885 * Test filterHash() on a private bookmark while logged out.
886 */
887 public function testFilterHashPrivateWhileLoggedOut()
888 {
889 $this->expectException(BookmarkNotFoundException::class);
890 $this->expectExceptionMessage('The link you are trying to reach does not exist or has been deleted');
891
892 $hash = smallHash('20141125_084734' . 6);
893
894 $this->publicLinkDB->findByHash($hash);
895 }
896
897 /**
898 * Test filterHash() with private key.
899 */
900 public function testFilterHashWithPrivateKey()
901 {
902 $hash = smallHash('20141125_084734' . 6);
903 $privateKey = 'this is usually auto generated';
904
905 $bookmark = $this->privateLinkDB->findByHash($hash);
906 $bookmark->addAdditionalContentEntry('private_key', $privateKey);
907 $this->privateLinkDB->save();
908
909 $this->privateLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, false);
910 $bookmark = $this->privateLinkDB->findByHash($hash, $privateKey);
911
912 static::assertSame(6, $bookmark->getId());
913 }
914
915 /**
931 * Test linksCountPerTag all tags without filter. 916 * Test linksCountPerTag all tags without filter.
932 * Equal occurrences should be sorted alphabetically. 917 * Equal occurrences should be sorted alphabetically.
933 */ 918 */
@@ -956,6 +941,10 @@ class BookmarkFileServiceTest extends TestCase
956 'tag4' => 1, 941 'tag4' => 1,
957 'ut' => 1, 942 'ut' => 1,
958 'w3c' => 1, 943 'w3c' => 1,
944 'assurance' => 1,
945 'coding-style' => 1,
946 'quality' => 1,
947 'standards' => 1,
959 ]; 948 ];
960 $tags = $this->privateLinkDB->bookmarksCountPerTag(); 949 $tags = $this->privateLinkDB->bookmarksCountPerTag();
961 950
@@ -1054,6 +1043,10 @@ class BookmarkFileServiceTest extends TestCase
1054 'stallman' => 1, 1043 'stallman' => 1,
1055 'ut' => 1, 1044 'ut' => 1,
1056 'w3c' => 1, 1045 'w3c' => 1,
1046 'assurance' => 1,
1047 'coding-style' => 1,
1048 'quality' => 1,
1049 'standards' => 1,
1057 ]; 1050 ];
1058 $bookmark = new Bookmark(); 1051 $bookmark = new Bookmark();
1059 $bookmark->setTags(['newTagToCount', BookmarkMarkdownFormatter::NO_MD_TAG]); 1052 $bookmark->setTags(['newTagToCount', BookmarkMarkdownFormatter::NO_MD_TAG]);
@@ -1065,33 +1058,105 @@ class BookmarkFileServiceTest extends TestCase
1065 } 1058 }
1066 1059
1067 /** 1060 /**
1068 * Test filterDay while logged in 1061 * Test find by dates in the middle of the datastore (sorted by dates) with a single bookmark as a result.
1069 */ 1062 */
1070 public function testFilterDayLoggedIn(): void 1063 public function testFilterByDateMidTimePeriodSingleBookmark(): void
1071 { 1064 {
1072 $bookmarks = $this->privateLinkDB->filterDay('20121206'); 1065 $bookmarks = $this->privateLinkDB->findByDate(
1073 $expectedIds = [4, 9, 1, 0]; 1066 DateTime::createFromFormat('Ymd_His', '20121206_150000'),
1067 DateTime::createFromFormat('Ymd_His', '20121206_160000'),
1068 $before,
1069 $after
1070 );
1074 1071
1075 static::assertCount(4, $bookmarks); 1072 static::assertCount(1, $bookmarks);
1076 foreach ($bookmarks as $bookmark) { 1073
1077 $i = ($i ?? -1) + 1; 1074 static::assertSame(9, $bookmarks[0]->getId());
1078 static::assertSame($expectedIds[$i], $bookmark->getId()); 1075 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_142300'), $before);
1079 } 1076 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_172539'), $after);
1080 } 1077 }
1081 1078
1082 /** 1079 /**
1083 * Test filterDay while logged out 1080 * Test find by dates in the middle of the datastore (sorted by dates) with a multiple bookmarks as a result.
1084 */ 1081 */
1085 public function testFilterDayLoggedOut(): void 1082 public function testFilterByDateMidTimePeriodMultipleBookmarks(): void
1086 { 1083 {
1087 $bookmarks = $this->publicLinkDB->filterDay('20121206'); 1084 $bookmarks = $this->privateLinkDB->findByDate(
1088 $expectedIds = [4, 9, 1]; 1085 DateTime::createFromFormat('Ymd_His', '20121206_150000'),
1086 DateTime::createFromFormat('Ymd_His', '20121206_180000'),
1087 $before,
1088 $after
1089 );
1089 1090
1090 static::assertCount(3, $bookmarks); 1091 static::assertCount(2, $bookmarks);
1091 foreach ($bookmarks as $bookmark) { 1092
1092 $i = ($i ?? -1) + 1; 1093 static::assertSame(1, $bookmarks[0]->getId());
1093 static::assertSame($expectedIds[$i], $bookmark->getId()); 1094 static::assertSame(9, $bookmarks[1]->getId());
1094 } 1095 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_142300'), $before);
1096 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20121206_182539'), $after);
1097 }
1098
1099 /**
1100 * Test find by dates at the end of the datastore (sorted by dates).
1101 */
1102 public function testFilterByDateLastTimePeriod(): void
1103 {
1104 $after = new DateTime();
1105 $bookmarks = $this->privateLinkDB->findByDate(
1106 DateTime::createFromFormat('Ymd_His', '20150310_114640'),
1107 DateTime::createFromFormat('Ymd_His', '20450101_010101'),
1108 $before,
1109 $after
1110 );
1111
1112 static::assertCount(1, $bookmarks);
1113
1114 static::assertSame(41, $bookmarks[0]->getId());
1115 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20150310_114633'), $before);
1116 static::assertNull($after);
1117 }
1118
1119 /**
1120 * Test find by dates at the beginning of the datastore (sorted by dates).
1121 */
1122 public function testFilterByDateFirstTimePeriod(): void
1123 {
1124 $before = new DateTime();
1125 $bookmarks = $this->privateLinkDB->findByDate(
1126 DateTime::createFromFormat('Ymd_His', '20000101_101010'),
1127 DateTime::createFromFormat('Ymd_His', '20100309_110000'),
1128 $before,
1129 $after
1130 );
1131
1132 static::assertCount(1, $bookmarks);
1133
1134 static::assertSame(11, $bookmarks[0]->getId());
1135 static::assertNull($before);
1136 static::assertEquals(DateTime::createFromFormat('Ymd_His', '20100310_101010'), $after);
1137 }
1138
1139 /**
1140 * Test getLatest with a sticky bookmark: it should be ignored and return the latest by creation date instead.
1141 */
1142 public function testGetLatestWithSticky(): void
1143 {
1144 $bookmark = $this->publicLinkDB->getLatest();
1145
1146 static::assertSame(41, $bookmark->getId());
1147 }
1148
1149 /**
1150 * Test getLatest with a sticky bookmark: it should be ignored and return the latest by creation date instead.
1151 */
1152 public function testGetLatestEmptyDatastore(): void
1153 {
1154 unlink($this->conf->get('resource.datastore'));
1155 $this->publicLinkDB = new BookmarkFileService($this->conf, $this->history, $this->mutex, false);
1156
1157 $bookmark = $this->publicLinkDB->getLatest();
1158
1159 static::assertNull($bookmark);
1095 } 1160 }
1096 1161
1097 /** 1162 /**
diff --git a/tests/bookmark/BookmarkFilterTest.php b/tests/bookmark/BookmarkFilterTest.php
index 48c7f824..835674f2 100644
--- a/tests/bookmark/BookmarkFilterTest.php
+++ b/tests/bookmark/BookmarkFilterTest.php
@@ -2,7 +2,7 @@
2 2
3namespace Shaarli\Bookmark; 3namespace Shaarli\Bookmark;
4 4
5use Exception; 5use malkusch\lock\mutex\NoMutex;
6use ReferenceLinkDB; 6use ReferenceLinkDB;
7use Shaarli\Config\ConfigManager; 7use Shaarli\Config\ConfigManager;
8use Shaarli\History; 8use Shaarli\History;
@@ -37,13 +37,14 @@ class BookmarkFilterTest extends TestCase
37 */ 37 */
38 public static function setUpBeforeClass(): void 38 public static function setUpBeforeClass(): void
39 { 39 {
40 $mutex = new NoMutex();
40 $conf = new ConfigManager('tests/utils/config/configJson'); 41 $conf = new ConfigManager('tests/utils/config/configJson');
41 $conf->set('resource.datastore', self::$testDatastore); 42 $conf->set('resource.datastore', self::$testDatastore);
42 self::$refDB = new \ReferenceLinkDB(); 43 self::$refDB = new \ReferenceLinkDB();
43 self::$refDB->write(self::$testDatastore); 44 self::$refDB->write(self::$testDatastore);
44 $history = new History('sandbox/history.php'); 45 $history = new History('sandbox/history.php');
45 self::$bookmarkService = new \FakeBookmarkService($conf, $history, true); 46 self::$bookmarkService = new \FakeBookmarkService($conf, $history, $mutex, true);
46 self::$linkFilter = new BookmarkFilter(self::$bookmarkService->getBookmarks()); 47 self::$linkFilter = new BookmarkFilter(self::$bookmarkService->getBookmarks(), $conf);
47 } 48 }
48 49
49 /** 50 /**
@@ -523,4 +524,43 @@ class BookmarkFilterTest extends TestCase
523 )) 524 ))
524 ); 525 );
525 } 526 }
527
528 /**
529 * Test search result highlights in every field of bookmark reference #9.
530 */
531 public function testFullTextSearchHighlight(): void
532 {
533 $bookmarks = self::$linkFilter->filter(
534 BookmarkFilter::$FILTER_TEXT,
535 '"psr-2" coding guide http fig "psr-2/" "This guide" basic standard. coding-style quality assurance'
536 );
537
538 static::assertCount(1, $bookmarks);
539 static::assertArrayHasKey(9, $bookmarks);
540
541 $bookmark = $bookmarks[9];
542 $expectedHighlights = [
543 'title' => [
544 ['start' => 0, 'end' => 5], // "psr-2"
545 ['start' => 7, 'end' => 13], // coding
546 ['start' => 20, 'end' => 25], // guide
547 ],
548 'description' => [
549 ['start' => 0, 'end' => 10], // "This guide"
550 ['start' => 45, 'end' => 50], // basic
551 ['start' => 58, 'end' => 67], // standard.
552 ],
553 'url' => [
554 ['start' => 0, 'end' => 4], // http
555 ['start' => 15, 'end' => 18], // fig
556 ['start' => 27, 'end' => 33], // "psr-2/"
557 ],
558 'tags' => [
559 ['start' => 0, 'end' => 12], // coding-style
560 ['start' => 23, 'end' => 30], // quality
561 ['start' => 31, 'end' => 40], // assurance
562 ],
563 ];
564 static::assertSame($expectedHighlights, $bookmark->getAdditionalContentEntry('search_highlight'));
565 }
526} 566}
diff --git a/tests/bookmark/BookmarkInitializerTest.php b/tests/bookmark/BookmarkInitializerTest.php
index 25704004..0c8420ce 100644
--- a/tests/bookmark/BookmarkInitializerTest.php
+++ b/tests/bookmark/BookmarkInitializerTest.php
@@ -2,6 +2,7 @@
2 2
3namespace Shaarli\Bookmark; 3namespace Shaarli\Bookmark;
4 4
5use malkusch\lock\mutex\NoMutex;
5use Shaarli\Config\ConfigManager; 6use Shaarli\Config\ConfigManager;
6use Shaarli\History; 7use Shaarli\History;
7use Shaarli\TestCase; 8use Shaarli\TestCase;
@@ -34,11 +35,15 @@ class BookmarkInitializerTest extends TestCase
34 /** @var BookmarkInitializer instance */ 35 /** @var BookmarkInitializer instance */
35 protected $initializer; 36 protected $initializer;
36 37
38 /** @var NoMutex */
39 protected $mutex;
40
37 /** 41 /**
38 * Initialize an empty BookmarkFileService 42 * Initialize an empty BookmarkFileService
39 */ 43 */
40 public function setUp(): void 44 public function setUp(): void
41 { 45 {
46 $this->mutex = new NoMutex();
42 if (file_exists(self::$testDatastore)) { 47 if (file_exists(self::$testDatastore)) {
43 unlink(self::$testDatastore); 48 unlink(self::$testDatastore);
44 } 49 }
@@ -47,7 +52,7 @@ class BookmarkInitializerTest extends TestCase
47 $this->conf = new ConfigManager(self::$testConf); 52 $this->conf = new ConfigManager(self::$testConf);
48 $this->conf->set('resource.datastore', self::$testDatastore); 53 $this->conf->set('resource.datastore', self::$testDatastore);
49 $this->history = new History('sandbox/history.php'); 54 $this->history = new History('sandbox/history.php');
50 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 55 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
51 56
52 $this->initializer = new BookmarkInitializer($this->bookmarkService); 57 $this->initializer = new BookmarkInitializer($this->bookmarkService);
53 } 58 }
@@ -59,7 +64,7 @@ class BookmarkInitializerTest extends TestCase
59 { 64 {
60 $refDB = new \ReferenceLinkDB(); 65 $refDB = new \ReferenceLinkDB();
61 $refDB->write(self::$testDatastore); 66 $refDB->write(self::$testDatastore);
62 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 67 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
63 $this->initializer = new BookmarkInitializer($this->bookmarkService); 68 $this->initializer = new BookmarkInitializer($this->bookmarkService);
64 69
65 $this->initializer->initialize(); 70 $this->initializer->initialize();
@@ -90,7 +95,7 @@ class BookmarkInitializerTest extends TestCase
90 $this->bookmarkService->save(); 95 $this->bookmarkService->save();
91 96
92 // Reload from file 97 // Reload from file
93 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 98 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
94 $this->assertEquals($refDB->countLinks() + 3, $this->bookmarkService->count()); 99 $this->assertEquals($refDB->countLinks() + 3, $this->bookmarkService->count());
95 100
96 $bookmark = $this->bookmarkService->get(43); 101 $bookmark = $this->bookmarkService->get(43);
@@ -121,7 +126,7 @@ class BookmarkInitializerTest extends TestCase
121 public function testInitializeNonExistentDataStore(): void 126 public function testInitializeNonExistentDataStore(): void
122 { 127 {
123 $this->conf->set('resource.datastore', static::$testDatastore . '_empty'); 128 $this->conf->set('resource.datastore', static::$testDatastore . '_empty');
124 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, true); 129 $this->bookmarkService = new BookmarkFileService($this->conf, $this->history, $this->mutex, true);
125 130
126 $this->initializer->initialize(); 131 $this->initializer->initialize();
127 132
diff --git a/tests/bookmark/BookmarkTest.php b/tests/bookmark/BookmarkTest.php
index afec2440..cb91b26b 100644
--- a/tests/bookmark/BookmarkTest.php
+++ b/tests/bookmark/BookmarkTest.php
@@ -79,6 +79,23 @@ class BookmarkTest extends TestCase
79 } 79 }
80 80
81 /** 81 /**
82 * Test fromArray() with a link with a custom tags separator
83 */
84 public function testFromArrayCustomTagsSeparator()
85 {
86 $data = [
87 'id' => 1,
88 'tags' => ['tag1', 'tag2', 'chair'],
89 ];
90
91 $bookmark = (new Bookmark())->fromArray($data, '@');
92 $this->assertEquals($data['id'], $bookmark->getId());
93 $this->assertEquals($data['tags'], $bookmark->getTags());
94 $this->assertEquals('tag1@tag2@chair', $bookmark->getTagsString('@'));
95 }
96
97
98 /**
82 * Test validate() with a valid minimal bookmark 99 * Test validate() with a valid minimal bookmark
83 */ 100 */
84 public function testValidateValidFullBookmark() 101 public function testValidateValidFullBookmark()
@@ -154,25 +171,6 @@ class BookmarkTest extends TestCase
154 } 171 }
155 172
156 /** 173 /**
157 * Test validate() with a a bookmark with a non integer ID.
158 */
159 public function testValidateNotValidStringId()
160 {
161 $bookmark = new Bookmark();
162 $bookmark->setId('str');
163 $bookmark->setShortUrl('abc');
164 $bookmark->setCreated(\DateTime::createFromFormat('Ymd_His', '20190514_200102'));
165 $exception = null;
166 try {
167 $bookmark->validate();
168 } catch (InvalidBookmarkException $e) {
169 $exception = $e;
170 }
171 $this->assertNotNull($exception);
172 $this->assertContainsPolyfill('- ID: str'. PHP_EOL, $exception->getMessage());
173 }
174
175 /**
176 * Test validate() with a a bookmark without short url. 174 * Test validate() with a a bookmark without short url.
177 */ 175 */
178 public function testValidateNotValidNoShortUrl() 176 public function testValidateNotValidNoShortUrl()
@@ -211,25 +209,6 @@ class BookmarkTest extends TestCase
211 } 209 }
212 210
213 /** 211 /**
214 * Test validate() with a a bookmark with a bad created datetime.
215 */
216 public function testValidateNotValidBadCreated()
217 {
218 $bookmark = new Bookmark();
219 $bookmark->setId(1);
220 $bookmark->setShortUrl('abc');
221 $bookmark->setCreated('hi!');
222 $exception = null;
223 try {
224 $bookmark->validate();
225 } catch (InvalidBookmarkException $e) {
226 $exception = $e;
227 }
228 $this->assertNotNull($exception);
229 $this->assertContainsPolyfill('- Created: Not a DateTime object'. PHP_EOL, $exception->getMessage());
230 }
231
232 /**
233 * Test setId() and make sure that default fields are generated. 212 * Test setId() and make sure that default fields are generated.
234 */ 213 */
235 public function testSetIdEmptyGeneratedFields() 214 public function testSetIdEmptyGeneratedFields()
@@ -290,7 +269,7 @@ class BookmarkTest extends TestCase
290 { 269 {
291 $bookmark = new Bookmark(); 270 $bookmark = new Bookmark();
292 271
293 $str = 'tag1 tag2 tag3.tag3-2, tag4 , -tag5 '; 272 $str = 'tag1 tag2 tag3.tag3-2 tag4 -tag5 ';
294 $bookmark->setTagsString($str); 273 $bookmark->setTagsString($str);
295 $this->assertEquals( 274 $this->assertEquals(
296 [ 275 [
@@ -314,9 +293,9 @@ class BookmarkTest extends TestCase
314 $array = [ 293 $array = [
315 'tag1 ', 294 'tag1 ',
316 ' tag2', 295 ' tag2',
317 'tag3.tag3-2,', 296 'tag3.tag3-2',
318 ', tag4', 297 ' tag4',
319 ', ', 298 ' ',
320 '-tag5 ', 299 '-tag5 ',
321 ]; 300 ];
322 $bookmark->setTags($array); 301 $bookmark->setTags($array);
@@ -385,4 +364,48 @@ class BookmarkTest extends TestCase
385 $bookmark->deleteTag('nope'); 364 $bookmark->deleteTag('nope');
386 $this->assertEquals(['tag1', 'tag2', 'chair'], $bookmark->getTags()); 365 $this->assertEquals(['tag1', 'tag2', 'chair'], $bookmark->getTags());
387 } 366 }
367
368 /**
369 * Test shouldUpdateThumbnail() with bookmarks needing an update.
370 */
371 public function testShouldUpdateThumbnail(): void
372 {
373 $bookmark = (new Bookmark())->setUrl('http://domain.tld/with-image');
374
375 static::assertTrue($bookmark->shouldUpdateThumbnail());
376
377 $bookmark = (new Bookmark())
378 ->setUrl('http://domain.tld/with-image')
379 ->setThumbnail('unknown file')
380 ;
381
382 static::assertTrue($bookmark->shouldUpdateThumbnail());
383 }
384
385 /**
386 * Test shouldUpdateThumbnail() with bookmarks that should not update.
387 */
388 public function testShouldNotUpdateThumbnail(): void
389 {
390 $bookmark = (new Bookmark());
391
392 static::assertFalse($bookmark->shouldUpdateThumbnail());
393
394 $bookmark = (new Bookmark())
395 ->setUrl('ftp://domain.tld/other-protocol', ['ftp'])
396 ;
397
398 static::assertFalse($bookmark->shouldUpdateThumbnail());
399
400 $bookmark = (new Bookmark())
401 ->setUrl('http://domain.tld/with-image')
402 ->setThumbnail(__FILE__)
403 ;
404
405 static::assertFalse($bookmark->shouldUpdateThumbnail());
406
407 $bookmark = (new Bookmark())->setUrl('/shaare/abcdef');
408
409 static::assertFalse($bookmark->shouldUpdateThumbnail());
410 }
388} 411}
diff --git a/tests/bookmark/LinkUtilsTest.php b/tests/bookmark/LinkUtilsTest.php
index ef00b92f..ddab4e3c 100644
--- a/tests/bookmark/LinkUtilsTest.php
+++ b/tests/bookmark/LinkUtilsTest.php
@@ -94,8 +94,108 @@ class LinkUtilsTest extends TestCase
94 public function testHtmlExtractExistentNameTag() 94 public function testHtmlExtractExistentNameTag()
95 { 95 {
96 $description = 'Bob and Alice share cookies.'; 96 $description = 'Bob and Alice share cookies.';
97
98 // Simple one line
97 $html = '<html><meta>stuff2</meta><meta name="description" content="' . $description . '"/></html>'; 99 $html = '<html><meta>stuff2</meta><meta name="description" content="' . $description . '"/></html>';
98 $this->assertEquals($description, html_extract_tag('description', $html)); 100 $this->assertEquals($description, html_extract_tag('description', $html));
101
102 // Simple OpenGraph
103 $html = '<meta property="og:description" content="' . $description . '">';
104 $this->assertEquals($description, html_extract_tag('description', $html));
105
106 // Simple reversed OpenGraph
107 $html = '<meta content="' . $description . '" property="og:description">';
108 $this->assertEquals($description, html_extract_tag('description', $html));
109
110 // ItemProp OpenGraph
111 $html = '<meta itemprop="og:description" content="' . $description . '">';
112 $this->assertEquals($description, html_extract_tag('description', $html));
113
114 // OpenGraph without quotes
115 $html = '<meta property=og:description content="' . $description . '">';
116 $this->assertEquals($description, html_extract_tag('description', $html));
117
118 // OpenGraph reversed without quotes
119 $html = '<meta content="' . $description . '" property=og:description>';
120 $this->assertEquals($description, html_extract_tag('description', $html));
121
122 // OpenGraph with noise
123 $html = '<meta tag1="content1" property="og:description" tag2="content2" content="' .
124 $description . '" tag3="content3">';
125 $this->assertEquals($description, html_extract_tag('description', $html));
126
127 // OpenGraph reversed with noise
128 $html = '<meta tag1="content1" content="' . $description . '" ' .
129 'tag3="content3" tag2="content2" property="og:description">';
130 $this->assertEquals($description, html_extract_tag('description', $html));
131
132 // OpenGraph multiple properties start
133 $html = '<meta property="unrelated og:description" content="' . $description . '">';
134 $this->assertEquals($description, html_extract_tag('description', $html));
135
136 // OpenGraph multiple properties end
137 $html = '<meta property="og:description unrelated" content="' . $description . '">';
138 $this->assertEquals($description, html_extract_tag('description', $html));
139
140 // OpenGraph multiple properties both end
141 $html = '<meta property="og:unrelated1 og:description og:unrelated2" content="' . $description . '">';
142 $this->assertEquals($description, html_extract_tag('description', $html));
143
144 // OpenGraph multiple properties both end with noise
145 $html = '<meta tag1="content1" property="og:unrelated1 og:description og:unrelated2" '.
146 'tag2="content2" content="' . $description . '" tag3="content3">';
147 $this->assertEquals($description, html_extract_tag('description', $html));
148
149 // OpenGraph reversed multiple properties start
150 $html = '<meta content="' . $description . '" property="unrelated og:description">';
151 $this->assertEquals($description, html_extract_tag('description', $html));
152
153 // OpenGraph reversed multiple properties end
154 $html = '<meta content="' . $description . '" property="og:description unrelated">';
155 $this->assertEquals($description, html_extract_tag('description', $html));
156
157 // OpenGraph reversed multiple properties both end
158 $html = '<meta content="' . $description . '" property="og:unrelated1 og:description og:unrelated2">';
159 $this->assertEquals($description, html_extract_tag('description', $html));
160
161 // OpenGraph reversed multiple properties both end with noise
162 $html = '<meta tag1="content1" content="' . $description . '" tag2="content2" '.
163 'property="og:unrelated1 og:description og:unrelated2" tag3="content3">';
164 $this->assertEquals($description, html_extract_tag('description', $html));
165
166 // Suggestion from #1375
167 $html = '<meta property="og:description" name="description" content="' . $description . '">';
168 $this->assertEquals($description, html_extract_tag('description', $html));
169 }
170
171 /**
172 * Test html_extract_tag() with double quoted content containing single quote, and the opposite.
173 */
174 public function testHtmlExtractExistentNameTagWithMixedQuotes(): void
175 {
176 $description = 'Bob and Alice share M&M\'s.';
177
178 $html = '<meta property="og:description" content="' . $description . '">';
179 $this->assertEquals($description, html_extract_tag('description', $html));
180
181 $html = '<meta tag1="content1" property="og:unrelated1 og:description og:unrelated2" '.
182 'tag2="content2" content="' . $description . '" tag3="content3">';
183 $this->assertEquals($description, html_extract_tag('description', $html));
184
185 $html = '<meta property="og:description" name="description" content="' . $description . '">';
186 $this->assertEquals($description, html_extract_tag('description', $html));
187
188 $description = 'Bob and Alice share "cookies".';
189
190 $html = '<meta property="og:description" content=\'' . $description . '\'>';
191 $this->assertEquals($description, html_extract_tag('description', $html));
192
193 $html = '<meta tag1="content1" property="og:unrelated1 og:description og:unrelated2" '.
194 'tag2="content2" content=\'' . $description . '\' tag3="content3">';
195 $this->assertEquals($description, html_extract_tag('description', $html));
196
197 $html = '<meta property="og:description" name="description" content=\'' . $description . '\'>';
198 $this->assertEquals($description, html_extract_tag('description', $html));
99 } 199 }
100 200
101 /** 201 /**
@@ -105,6 +205,25 @@ class LinkUtilsTest extends TestCase
105 { 205 {
106 $html = '<html><meta>stuff2</meta><meta name="image" content="img"/></html>'; 206 $html = '<html><meta>stuff2</meta><meta name="image" content="img"/></html>';
107 $this->assertFalse(html_extract_tag('description', $html)); 207 $this->assertFalse(html_extract_tag('description', $html));
208
209 // Partial meta tag
210 $html = '<meta content="Brief description">';
211 $this->assertFalse(html_extract_tag('description', $html));
212
213 $html = '<meta property="og:description">';
214 $this->assertFalse(html_extract_tag('description', $html));
215
216 $html = '<meta tag1="content1" property="og:description">';
217 $this->assertFalse(html_extract_tag('description', $html));
218
219 $html = '<meta property="og:description" tag1="content1">';
220 $this->assertFalse(html_extract_tag('description', $html));
221
222 $html = '<meta tag1="content1" content="Brief description">';
223 $this->assertFalse(html_extract_tag('description', $html));
224
225 $html = '<meta content="Brief description" tag1="content1">';
226 $this->assertFalse(html_extract_tag('description', $html));
108 } 227 }
109 228
110 /** 229 /**
@@ -127,60 +246,93 @@ class LinkUtilsTest extends TestCase
127 } 246 }
128 247
129 /** 248 /**
249 * Test the header callback with valid value
250 */
251 public function testCurlHeaderCallbackOk(): void
252 {
253 $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_ok');
254 $data = [
255 'HTTP/1.1 200 OK',
256 'Server: GitHub.com',
257 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
258 'Content-Type: text/html; charset=utf-8',
259 'Status: 200 OK',
260 ];
261
262 foreach ($data as $chunk) {
263 static::assertIsInt($callback(null, $chunk));
264 }
265
266 static::assertSame('utf-8', $charset);
267 }
268
269 /**
130 * Test the download callback with valid value 270 * Test the download callback with valid value
131 */ 271 */
132 public function testCurlDownloadCallbackOk() 272 public function testCurlDownloadCallbackOk(): void
133 { 273 {
274 $charset = 'utf-8';
134 $callback = get_curl_download_callback( 275 $callback = get_curl_download_callback(
135 $charset, 276 $charset,
136 $title, 277 $title,
137 $desc, 278 $desc,
138 $keywords, 279 $keywords,
139 false, 280 false,
140 'ut_curl_getinfo_ok' 281 ' '
141 ); 282 );
283
142 $data = [ 284 $data = [
143 'HTTP/1.1 200 OK', 285 'th=device-width">'
144 'Server: GitHub.com',
145 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
146 'Content-Type: text/html; charset=utf-8',
147 'Status: 200 OK',
148 'end' => 'th=device-width">'
149 . '<title>Refactoring · GitHub</title>' 286 . '<title>Refactoring · GitHub</title>'
150 . '<link rel="search" type="application/opensea', 287 . '<link rel="search" type="application/opensea',
151 '<title>ignored</title>' 288 '<title>ignored</title>'
152 . '<meta name="description" content="desc" />' 289 . '<meta name="description" content="desc" />'
153 . '<meta name="keywords" content="key1,key2" />', 290 . '<meta name="keywords" content="key1,key2" />',
154 ]; 291 ];
155 foreach ($data as $key => $line) { 292
156 $ignore = null; 293 foreach ($data as $chunk) {
157 $expected = $key !== 'end' ? strlen($line) : false; 294 static::assertSame(strlen($chunk), $callback(null, $chunk));
158 $this->assertEquals($expected, $callback($ignore, $line));
159 if ($expected === false) {
160 break;
161 }
162 } 295 }
163 $this->assertEquals('utf-8', $charset); 296
164 $this->assertEquals('Refactoring · GitHub', $title); 297 static::assertSame('utf-8', $charset);
165 $this->assertEmpty($desc); 298 static::assertSame('Refactoring · GitHub', $title);
166 $this->assertEmpty($keywords); 299 static::assertEmpty($desc);
300 static::assertEmpty($keywords);
301 }
302
303 /**
304 * Test the header callback with valid value
305 */
306 public function testCurlHeaderCallbackNoCharset(): void
307 {
308 $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_no_charset');
309 $data = [
310 'HTTP/1.1 200 OK',
311 ];
312
313 foreach ($data as $chunk) {
314 static::assertSame(strlen($chunk), $callback(null, $chunk));
315 }
316
317 static::assertFalse($charset);
167 } 318 }
168 319
169 /** 320 /**
170 * Test the download callback with valid values and no charset 321 * Test the download callback with valid values and no charset
171 */ 322 */
172 public function testCurlDownloadCallbackOkNoCharset() 323 public function testCurlDownloadCallbackOkNoCharset(): void
173 { 324 {
325 $charset = null;
174 $callback = get_curl_download_callback( 326 $callback = get_curl_download_callback(
175 $charset, 327 $charset,
176 $title, 328 $title,
177 $desc, 329 $desc,
178 $keywords, 330 $keywords,
179 false, 331 false,
180 'ut_curl_getinfo_no_charset' 332 ' '
181 ); 333 );
334
182 $data = [ 335 $data = [
183 'HTTP/1.1 200 OK',
184 'end' => 'th=device-width">' 336 'end' => 'th=device-width">'
185 . '<title>Refactoring · GitHub</title>' 337 . '<title>Refactoring · GitHub</title>'
186 . '<link rel="search" type="application/opensea', 338 . '<link rel="search" type="application/opensea',
@@ -188,10 +340,11 @@ class LinkUtilsTest extends TestCase
188 . '<meta name="description" content="desc" />' 340 . '<meta name="description" content="desc" />'
189 . '<meta name="keywords" content="key1,key2" />', 341 . '<meta name="keywords" content="key1,key2" />',
190 ]; 342 ];
191 foreach ($data as $key => $line) { 343
192 $ignore = null; 344 foreach ($data as $chunk) {
193 $this->assertEquals(strlen($line), $callback($ignore, $line)); 345 static::assertSame(strlen($chunk), $callback(null, $chunk));
194 } 346 }
347
195 $this->assertEmpty($charset); 348 $this->assertEmpty($charset);
196 $this->assertEquals('Refactoring · GitHub', $title); 349 $this->assertEquals('Refactoring · GitHub', $title);
197 $this->assertEmpty($desc); 350 $this->assertEmpty($desc);
@@ -201,18 +354,19 @@ class LinkUtilsTest extends TestCase
201 /** 354 /**
202 * Test the download callback with valid values and no charset 355 * Test the download callback with valid values and no charset
203 */ 356 */
204 public function testCurlDownloadCallbackOkHtmlCharset() 357 public function testCurlDownloadCallbackOkHtmlCharset(): void
205 { 358 {
359 $charset = null;
206 $callback = get_curl_download_callback( 360 $callback = get_curl_download_callback(
207 $charset, 361 $charset,
208 $title, 362 $title,
209 $desc, 363 $desc,
210 $keywords, 364 $keywords,
211 false, 365 false,
212 'ut_curl_getinfo_no_charset' 366 ' '
213 ); 367 );
368
214 $data = [ 369 $data = [
215 'HTTP/1.1 200 OK',
216 '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />', 370 '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',
217 'end' => 'th=device-width">' 371 'end' => 'th=device-width">'
218 . '<title>Refactoring · GitHub</title>' 372 . '<title>Refactoring · GitHub</title>'
@@ -221,14 +375,10 @@ class LinkUtilsTest extends TestCase
221 . '<meta name="description" content="desc" />' 375 . '<meta name="description" content="desc" />'
222 . '<meta name="keywords" content="key1,key2" />', 376 . '<meta name="keywords" content="key1,key2" />',
223 ]; 377 ];
224 foreach ($data as $key => $line) { 378 foreach ($data as $chunk) {
225 $ignore = null; 379 static::assertSame(strlen($chunk), $callback(null, $chunk));
226 $expected = $key !== 'end' ? strlen($line) : false;
227 $this->assertEquals($expected, $callback($ignore, $line));
228 if ($expected === false) {
229 break;
230 }
231 } 380 }
381
232 $this->assertEquals('utf-8', $charset); 382 $this->assertEquals('utf-8', $charset);
233 $this->assertEquals('Refactoring · GitHub', $title); 383 $this->assertEquals('Refactoring · GitHub', $title);
234 $this->assertEmpty($desc); 384 $this->assertEmpty($desc);
@@ -238,25 +388,27 @@ class LinkUtilsTest extends TestCase
238 /** 388 /**
239 * Test the download callback with valid values and no title 389 * Test the download callback with valid values and no title
240 */ 390 */
241 public function testCurlDownloadCallbackOkNoTitle() 391 public function testCurlDownloadCallbackOkNoTitle(): void
242 { 392 {
393 $charset = 'utf-8';
243 $callback = get_curl_download_callback( 394 $callback = get_curl_download_callback(
244 $charset, 395 $charset,
245 $title, 396 $title,
246 $desc, 397 $desc,
247 $keywords, 398 $keywords,
248 false, 399 false,
249 'ut_curl_getinfo_ok' 400 ' '
250 ); 401 );
402
251 $data = [ 403 $data = [
252 'HTTP/1.1 200 OK',
253 'end' => 'th=device-width">Refactoring · GitHub<link rel="search" type="application/opensea', 404 'end' => 'th=device-width">Refactoring · GitHub<link rel="search" type="application/opensea',
254 'ignored', 405 'ignored',
255 ]; 406 ];
256 foreach ($data as $key => $line) { 407
257 $ignore = null; 408 foreach ($data as $chunk) {
258 $this->assertEquals(strlen($line), $callback($ignore, $line)); 409 static::assertSame(strlen($chunk), $callback(null, $chunk));
259 } 410 }
411
260 $this->assertEquals('utf-8', $charset); 412 $this->assertEquals('utf-8', $charset);
261 $this->assertEmpty($title); 413 $this->assertEmpty($title);
262 $this->assertEmpty($desc); 414 $this->assertEmpty($desc);
@@ -264,81 +416,56 @@ class LinkUtilsTest extends TestCase
264 } 416 }
265 417
266 /** 418 /**
267 * Test the download callback with an invalid content type. 419 * Test the header callback with an invalid content type.
268 */ 420 */
269 public function testCurlDownloadCallbackInvalidContentType() 421 public function testCurlHeaderCallbackInvalidContentType(): void
270 { 422 {
271 $callback = get_curl_download_callback( 423 $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_ct_ko');
272 $charset, 424 $data = [
273 $title, 425 'HTTP/1.1 200 OK',
274 $desc, 426 ];
275 $keywords, 427
276 false, 428 static::assertFalse($callback(null, $data[0]));
277 'ut_curl_getinfo_ct_ko' 429 static::assertNull($charset);
278 );
279 $ignore = null;
280 $this->assertFalse($callback($ignore, ''));
281 $this->assertEmpty($charset);
282 $this->assertEmpty($title);
283 } 430 }
284 431
285 /** 432 /**
286 * Test the download callback with an invalid response code. 433 * Test the header callback with an invalid response code.
287 */ 434 */
288 public function testCurlDownloadCallbackInvalidResponseCode() 435 public function testCurlHeaderCallbackInvalidResponseCode(): void
289 { 436 {
290 $callback = $callback = get_curl_download_callback( 437 $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_rc_ko');
291 $charset, 438
292 $title, 439 static::assertFalse($callback(null, ''));
293 $desc, 440 static::assertNull($charset);
294 $keywords,
295 false,
296 'ut_curl_getinfo_rc_ko'
297 );
298 $ignore = null;
299 $this->assertFalse($callback($ignore, ''));
300 $this->assertEmpty($charset);
301 $this->assertEmpty($title);
302 } 441 }
303 442
304 /** 443 /**
305 * Test the download callback with an invalid content type and response code. 444 * Test the header callback with an invalid content type and response code.
306 */ 445 */
307 public function testCurlDownloadCallbackInvalidContentTypeAndResponseCode() 446 public function testCurlHeaderCallbackInvalidContentTypeAndResponseCode(): void
308 { 447 {
309 $callback = $callback = get_curl_download_callback( 448 $callback = get_curl_header_callback($charset, 'ut_curl_getinfo_rs_ct_ko');
310 $charset, 449
311 $title, 450 static::assertFalse($callback(null, ''));
312 $desc, 451 static::assertNull($charset);
313 $keywords,
314 false,
315 'ut_curl_getinfo_rs_ct_ko'
316 );
317 $ignore = null;
318 $this->assertFalse($callback($ignore, ''));
319 $this->assertEmpty($charset);
320 $this->assertEmpty($title);
321 } 452 }
322 453
323 /** 454 /**
324 * Test the download callback with valid value, and retrieve_description option enabled. 455 * Test the download callback with valid value, and retrieve_description option enabled.
325 */ 456 */
326 public function testCurlDownloadCallbackOkWithDesc() 457 public function testCurlDownloadCallbackOkWithDesc(): void
327 { 458 {
459 $charset = 'utf-8';
328 $callback = get_curl_download_callback( 460 $callback = get_curl_download_callback(
329 $charset, 461 $charset,
330 $title, 462 $title,
331 $desc, 463 $desc,
332 $keywords, 464 $keywords,
333 true, 465 true,
334 'ut_curl_getinfo_ok' 466 ' '
335 ); 467 );
336 $data = [ 468 $data = [
337 'HTTP/1.1 200 OK',
338 'Server: GitHub.com',
339 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
340 'Content-Type: text/html; charset=utf-8',
341 'Status: 200 OK',
342 'th=device-width">' 469 'th=device-width">'
343 . '<title>Refactoring · GitHub</title>' 470 . '<title>Refactoring · GitHub</title>'
344 . '<link rel="search" type="application/opensea', 471 . '<link rel="search" type="application/opensea',
@@ -346,14 +473,11 @@ class LinkUtilsTest extends TestCase
346 . '<meta name="description" content="link desc" />' 473 . '<meta name="description" content="link desc" />'
347 . '<meta name="keywords" content="key1,key2" />', 474 . '<meta name="keywords" content="key1,key2" />',
348 ]; 475 ];
349 foreach ($data as $key => $line) { 476
350 $ignore = null; 477 foreach ($data as $chunk) {
351 $expected = $key !== 'end' ? strlen($line) : false; 478 static::assertSame(strlen($chunk), $callback(null, $chunk));
352 $this->assertEquals($expected, $callback($ignore, $line));
353 if ($expected === false) {
354 break;
355 }
356 } 479 }
480
357 $this->assertEquals('utf-8', $charset); 481 $this->assertEquals('utf-8', $charset);
358 $this->assertEquals('Refactoring · GitHub', $title); 482 $this->assertEquals('Refactoring · GitHub', $title);
359 $this->assertEquals('link desc', $desc); 483 $this->assertEquals('link desc', $desc);
@@ -364,8 +488,9 @@ class LinkUtilsTest extends TestCase
364 * Test the download callback with valid value, and retrieve_description option enabled, 488 * Test the download callback with valid value, and retrieve_description option enabled,
365 * but no desc or keyword defined in the page. 489 * but no desc or keyword defined in the page.
366 */ 490 */
367 public function testCurlDownloadCallbackOkWithDescNotFound() 491 public function testCurlDownloadCallbackOkWithDescNotFound(): void
368 { 492 {
493 $charset = 'utf-8';
369 $callback = get_curl_download_callback( 494 $callback = get_curl_download_callback(
370 $charset, 495 $charset,
371 $title, 496 $title,
@@ -375,24 +500,16 @@ class LinkUtilsTest extends TestCase
375 'ut_curl_getinfo_ok' 500 'ut_curl_getinfo_ok'
376 ); 501 );
377 $data = [ 502 $data = [
378 'HTTP/1.1 200 OK',
379 'Server: GitHub.com',
380 'Date: Sat, 28 Oct 2017 12:01:33 GMT',
381 'Content-Type: text/html; charset=utf-8',
382 'Status: 200 OK',
383 'th=device-width">' 503 'th=device-width">'
384 . '<title>Refactoring · GitHub</title>' 504 . '<title>Refactoring · GitHub</title>'
385 . '<link rel="search" type="application/opensea', 505 . '<link rel="search" type="application/opensea',
386 'end' => '<title>ignored</title>', 506 'end' => '<title>ignored</title>',
387 ]; 507 ];
388 foreach ($data as $key => $line) { 508
389 $ignore = null; 509 foreach ($data as $chunk) {
390 $expected = $key !== 'end' ? strlen($line) : false; 510 static::assertSame(strlen($chunk), $callback(null, $chunk));
391 $this->assertEquals($expected, $callback($ignore, $line));
392 if ($expected === false) {
393 break;
394 }
395 } 511 }
512
396 $this->assertEquals('utf-8', $charset); 513 $this->assertEquals('utf-8', $charset);
397 $this->assertEquals('Refactoring · GitHub', $title); 514 $this->assertEquals('Refactoring · GitHub', $title);
398 $this->assertEmpty($desc); 515 $this->assertEmpty($desc);
@@ -493,6 +610,115 @@ class LinkUtilsTest extends TestCase
493 } 610 }
494 611
495 /** 612 /**
613 * Test tags_str2array with whitespace separator.
614 */
615 public function testTagsStr2ArrayWithSpaceSeparator(): void
616 {
617 $separator = ' ';
618
619 static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1 tag2 tag3', $separator));
620 static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1 tag2 tag3', $separator));
621 static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array(' tag1 tag2 tag3 ', $separator));
622 static::assertSame(['tag1@', 'tag2,', '.tag3'], tags_str2array(' tag1@ tag2, .tag3 ', $separator));
623 static::assertSame([], tags_str2array('', $separator));
624 static::assertSame([], tags_str2array(' ', $separator));
625 static::assertSame([], tags_str2array(null, $separator));
626 }
627
628 /**
629 * Test tags_str2array with @ separator.
630 */
631 public function testTagsStr2ArrayWithCharSeparator(): void
632 {
633 $separator = '@';
634
635 static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1@tag2@tag3', $separator));
636 static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('tag1@@@@tag2@@@@tag3', $separator));
637 static::assertSame(['tag1', 'tag2', 'tag3'], tags_str2array('@@@tag1@@@tag2@@@@tag3@@', $separator));
638 static::assertSame(
639 ['tag1#', 'tag2, and other', '.tag3'],
640 tags_str2array('@@@ tag1# @@@ tag2, and other @@@@.tag3@@', $separator)
641 );
642 static::assertSame([], tags_str2array('', $separator));
643 static::assertSame([], tags_str2array(' ', $separator));
644 static::assertSame([], tags_str2array(null, $separator));
645 }
646
647 /**
648 * Test tags_array2str with ' ' separator.
649 */
650 public function testTagsArray2StrWithSpaceSeparator(): void
651 {
652 $separator = ' ';
653
654 static::assertSame('tag1 tag2 tag3', tags_array2str(['tag1', 'tag2', 'tag3'], $separator));
655 static::assertSame('tag1, tag2@ tag3', tags_array2str(['tag1,', 'tag2@', 'tag3'], $separator));
656 static::assertSame('tag1 tag2 tag3', tags_array2str([' tag1 ', 'tag2', 'tag3 '], $separator));
657 static::assertSame('tag1 tag2 tag3', tags_array2str([' tag1 ', ' ', 'tag2', ' ', 'tag3 '], $separator));
658 static::assertSame('tag1', tags_array2str([' tag1 '], $separator));
659 static::assertSame('', tags_array2str([' '], $separator));
660 static::assertSame('', tags_array2str([], $separator));
661 static::assertSame('', tags_array2str(null, $separator));
662 }
663
664 /**
665 * Test tags_array2str with @ separator.
666 */
667 public function testTagsArray2StrWithCharSeparator(): void
668 {
669 $separator = '@';
670
671 static::assertSame('tag1@tag2@tag3', tags_array2str(['tag1', 'tag2', 'tag3'], $separator));
672 static::assertSame('tag1,@tag2@tag3', tags_array2str(['tag1,', 'tag2@', 'tag3'], $separator));
673 static::assertSame(
674 'tag1@tag2, and other@tag3',
675 tags_array2str(['@@@@ tag1@@@', ' @tag2, and other @', 'tag3@@@@'], $separator)
676 );
677 static::assertSame('tag1@tag2@tag3', tags_array2str(['@@@tag1@@@', '@', 'tag2', '@@@', 'tag3@@@'], $separator));
678 static::assertSame('tag1', tags_array2str(['@@@@tag1@@@@'], $separator));
679 static::assertSame('', tags_array2str(['@@@'], $separator));
680 static::assertSame('', tags_array2str([], $separator));
681 static::assertSame('', tags_array2str(null, $separator));
682 }
683
684 /**
685 * Test tags_array2str with @ separator.
686 */
687 public function testTagsFilterWithSpaceSeparator(): void
688 {
689 $separator = ' ';
690
691 static::assertSame(['tag1', 'tag2', 'tag3'], tags_filter(['tag1', 'tag2', 'tag3'], $separator));
692 static::assertSame(['tag1,', 'tag2@', 'tag3'], tags_filter(['tag1,', 'tag2@', 'tag3'], $separator));
693 static::assertSame(['tag1', 'tag2', 'tag3'], tags_filter([' tag1 ', 'tag2', 'tag3 '], $separator));
694 static::assertSame(['tag1', 'tag2', 'tag3'], tags_filter([' tag1 ', ' ', 'tag2', ' ', 'tag3 '], $separator));
695 static::assertSame(['tag1'], tags_filter([' tag1 '], $separator));
696 static::assertSame([], tags_filter([' '], $separator));
697 static::assertSame([], tags_filter([], $separator));
698 static::assertSame([], tags_filter(null, $separator));
699 }
700
701 /**
702 * Test tags_array2str with @ separator.
703 */
704 public function testTagsArrayFilterWithSpaceSeparator(): void
705 {
706 $separator = '@';
707
708 static::assertSame(['tag1', 'tag2', 'tag3'], tags_filter(['tag1', 'tag2', 'tag3'], $separator));
709 static::assertSame(['tag1,', 'tag2#', 'tag3'], tags_filter(['tag1,', 'tag2#', 'tag3'], $separator));
710 static::assertSame(
711 ['tag1', 'tag2, and other', 'tag3'],
712 tags_filter(['@@@@ tag1@@@', ' @tag2, and other @', 'tag3@@@@'], $separator)
713 );
714 static::assertSame(['tag1', 'tag2', 'tag3'], tags_filter(['@@@tag1@@@', '@', 'tag2', '@@@', 'tag3@@@'], $separator));
715 static::assertSame(['tag1'], tags_filter(['@@@@tag1@@@@'], $separator));
716 static::assertSame([], tags_filter(['@@@'], $separator));
717 static::assertSame([], tags_filter([], $separator));
718 static::assertSame([], tags_filter(null, $separator));
719 }
720
721 /**
496 * Util function to build an hashtag link. 722 * Util function to build an hashtag link.
497 * 723 *
498 * @param string $hashtag Hashtag name. 724 * @param string $hashtag Hashtag name.