]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - tests/legacy/LegacyLinkDBTest.php
Compatibility with PHPUnit 9
[github/shaarli/Shaarli.git] / tests / legacy / LegacyLinkDBTest.php
1 <?php
2 /**
3 * Link datastore tests
4 */
5
6 namespace Shaarli\Legacy;
7
8 use DateTime;
9 use ReferenceLinkDB;
10 use ReflectionClass;
11 use Shaarli;
12 use Shaarli\Bookmark\Bookmark;
13
14 require_once 'application/Utils.php';
15 require_once 'tests/utils/ReferenceLinkDB.php';
16
17
18 /**
19 * Unitary tests for LegacyLinkDBTest
20 */
21 class LegacyLinkDBTest extends \Shaarli\TestCase
22 {
23 // datastore to test write operations
24 protected static $testDatastore = 'sandbox/datastore.php';
25
26 /**
27 * @var ReferenceLinkDB instance.
28 */
29 protected static $refDB = null;
30
31 /**
32 * @var LegacyLinkDB public LinkDB instance.
33 */
34 protected static $publicLinkDB = null;
35
36 /**
37 * @var LegacyLinkDB private LinkDB instance.
38 */
39 protected static $privateLinkDB = null;
40
41 /**
42 * Instantiates public and private LinkDBs with test data
43 *
44 * The reference datastore contains public and private bookmarks that
45 * will be used to test LinkDB's methods:
46 * - access filtering (public/private),
47 * - link searches:
48 * - by day,
49 * - by tag,
50 * - by text,
51 * - etc.
52 *
53 * Resets test data for each test
54 */
55 protected function setUp(): void
56 {
57 if (file_exists(self::$testDatastore)) {
58 unlink(self::$testDatastore);
59 }
60
61 self::$refDB = new ReferenceLinkDB(true);
62 self::$refDB->write(self::$testDatastore);
63 self::$publicLinkDB = new LegacyLinkDB(self::$testDatastore, false, false);
64 self::$privateLinkDB = new LegacyLinkDB(self::$testDatastore, true, false);
65 }
66
67 /**
68 * Allows to test LinkDB's private methods
69 *
70 * @see
71 * https://sebastian-bergmann.de/archives/881-Testing-Your-Privates.html
72 * http://stackoverflow.com/a/2798203
73 */
74 protected static function getMethod($name)
75 {
76 $class = new ReflectionClass('Shaarli\Legacy\LegacyLinkDB');
77 $method = $class->getMethod($name);
78 $method->setAccessible(true);
79 return $method;
80 }
81
82 /**
83 * Instantiate LinkDB objects - logged in user
84 */
85 public function testConstructLoggedIn()
86 {
87 new LegacyLinkDB(self::$testDatastore, true, false);
88 $this->assertFileExists(self::$testDatastore);
89 }
90
91 /**
92 * Instantiate LinkDB objects - logged out or public instance
93 */
94 public function testConstructLoggedOut()
95 {
96 new LegacyLinkDB(self::$testDatastore, false, false);
97 $this->assertFileExists(self::$testDatastore);
98 }
99
100 /**
101 * Attempt to instantiate a LinkDB whereas the datastore is not writable
102 *
103 * @expectedException Shaarli\Exceptions\IOException
104 */
105 public function testConstructDatastoreNotWriteable()
106 {
107 $this->expectExceptionMessageRegExp('/Error accessing "null"/');
108
109 new LegacyLinkDB('null/store.db', false, false);
110 }
111
112 /**
113 * The DB doesn't exist, ensure it is created with dummy content
114 */
115 public function testCheckDBNew()
116 {
117 $linkDB = new LegacyLinkDB(self::$testDatastore, false, false);
118 unlink(self::$testDatastore);
119 $this->assertFileNotExists(self::$testDatastore);
120
121 $checkDB = self::getMethod('check');
122 $checkDB->invokeArgs($linkDB, array());
123 $this->assertFileExists(self::$testDatastore);
124
125 // ensure the correct data has been written
126 $this->assertGreaterThan(0, filesize(self::$testDatastore));
127 }
128
129 /**
130 * The DB exists, don't do anything
131 */
132 public function testCheckDBLoad()
133 {
134 $linkDB = new LegacyLinkDB(self::$testDatastore, false, false);
135 $datastoreSize = filesize(self::$testDatastore);
136 $this->assertGreaterThan(0, $datastoreSize);
137
138 $checkDB = self::getMethod('check');
139 $checkDB->invokeArgs($linkDB, array());
140
141 // ensure the datastore is left unmodified
142 $this->assertEquals(
143 $datastoreSize,
144 filesize(self::$testDatastore)
145 );
146 }
147
148 /**
149 * Load an empty DB
150 */
151 public function testReadEmptyDB()
152 {
153 file_put_contents(self::$testDatastore, '<?php /* S7QysKquBQA= */ ?>');
154 $emptyDB = new LegacyLinkDB(self::$testDatastore, false, false);
155 $this->assertEquals(0, sizeof($emptyDB));
156 $this->assertEquals(0, count($emptyDB));
157 }
158
159 /**
160 * Load public bookmarks from the DB
161 */
162 public function testReadPublicDB()
163 {
164 $this->assertEquals(
165 self::$refDB->countPublicLinks(),
166 sizeof(self::$publicLinkDB)
167 );
168 }
169
170 /**
171 * Load public and private bookmarks from the DB
172 */
173 public function testReadPrivateDB()
174 {
175 $this->assertEquals(
176 self::$refDB->countLinks(),
177 sizeof(self::$privateLinkDB)
178 );
179 }
180
181 /**
182 * Save the bookmarks to the DB
183 */
184 public function testSave()
185 {
186 $testDB = new LegacyLinkDB(self::$testDatastore, true, false);
187 $dbSize = sizeof($testDB);
188
189 $link = array(
190 'id' => 43,
191 'title' => 'an additional link',
192 'url' => 'http://dum.my',
193 'description' => 'One more',
194 'private' => 0,
195 'created' => DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20150518_190000'),
196 'tags' => 'unit test'
197 );
198 $testDB[$link['id']] = $link;
199 $testDB->save('tests');
200
201 $testDB = new LegacyLinkDB(self::$testDatastore, true, false);
202 $this->assertEquals($dbSize + 1, sizeof($testDB));
203 }
204
205 /**
206 * Count existing bookmarks
207 */
208 public function testCount()
209 {
210 $this->assertEquals(
211 self::$refDB->countPublicLinks(),
212 self::$publicLinkDB->count()
213 );
214 $this->assertEquals(
215 self::$refDB->countLinks(),
216 self::$privateLinkDB->count()
217 );
218 }
219
220 /**
221 * Count existing bookmarks - public bookmarks hidden
222 */
223 public function testCountHiddenPublic()
224 {
225 $linkDB = new LegacyLinkDB(self::$testDatastore, false, true);
226
227 $this->assertEquals(
228 0,
229 $linkDB->count()
230 );
231 $this->assertEquals(
232 0,
233 $linkDB->count()
234 );
235 }
236
237 /**
238 * List the days for which bookmarks have been posted
239 */
240 public function testDays()
241 {
242 $this->assertEquals(
243 array('20100309', '20100310', '20121206', '20121207', '20130614', '20150310'),
244 self::$publicLinkDB->days()
245 );
246
247 $this->assertEquals(
248 array('20100309', '20100310', '20121206', '20121207', '20130614', '20141125', '20150310'),
249 self::$privateLinkDB->days()
250 );
251 }
252
253 /**
254 * The URL corresponds to an existing entry in the DB
255 */
256 public function testGetKnownLinkFromURL()
257 {
258 $link = self::$publicLinkDB->getLinkFromUrl('http://mediagoblin.org/');
259
260 $this->assertNotEquals(false, $link);
261 $this->assertContainsPolyfill(
262 'A free software media publishing platform',
263 $link['description']
264 );
265 }
266
267 /**
268 * The URL is not in the DB
269 */
270 public function testGetUnknownLinkFromURL()
271 {
272 $this->assertEquals(
273 false,
274 self::$publicLinkDB->getLinkFromUrl('http://dev.null')
275 );
276 }
277
278 /**
279 * Lists all tags
280 */
281 public function testAllTags()
282 {
283 $this->assertEquals(
284 array(
285 'web' => 3,
286 'cartoon' => 2,
287 'gnu' => 2,
288 'dev' => 1,
289 'samba' => 1,
290 'media' => 1,
291 'software' => 1,
292 'stallman' => 1,
293 'free' => 1,
294 '-exclude' => 1,
295 'hashtag' => 2,
296 // The DB contains a link with `sTuff` and another one with `stuff` tag.
297 // They need to be grouped with the first case found - order by date DESC: `sTuff`.
298 'sTuff' => 2,
299 'ut' => 1,
300 ),
301 self::$publicLinkDB->linksCountPerTag()
302 );
303
304 $this->assertEquals(
305 array(
306 'web' => 4,
307 'cartoon' => 3,
308 'gnu' => 2,
309 'dev' => 2,
310 'samba' => 1,
311 'media' => 1,
312 'software' => 1,
313 'stallman' => 1,
314 'free' => 1,
315 'html' => 1,
316 'w3c' => 1,
317 'css' => 1,
318 'Mercurial' => 1,
319 'sTuff' => 2,
320 '-exclude' => 1,
321 '.hidden' => 1,
322 'hashtag' => 2,
323 'tag1' => 1,
324 'tag2' => 1,
325 'tag3' => 1,
326 'tag4' => 1,
327 'ut' => 1,
328 ),
329 self::$privateLinkDB->linksCountPerTag()
330 );
331 $this->assertEquals(
332 array(
333 'web' => 4,
334 'cartoon' => 2,
335 'gnu' => 1,
336 'dev' => 1,
337 'samba' => 1,
338 'media' => 1,
339 'html' => 1,
340 'w3c' => 1,
341 'css' => 1,
342 'Mercurial' => 1,
343 '.hidden' => 1,
344 'hashtag' => 1,
345 ),
346 self::$privateLinkDB->linksCountPerTag(['web'])
347 );
348 $this->assertEquals(
349 array(
350 'web' => 1,
351 'html' => 1,
352 'w3c' => 1,
353 'css' => 1,
354 'Mercurial' => 1,
355 ),
356 self::$privateLinkDB->linksCountPerTag(['web'], 'private')
357 );
358 }
359
360 /**
361 * Test filter with string.
362 */
363 public function testFilterString()
364 {
365 $tags = 'dev cartoon';
366 $request = array('searchtags' => $tags);
367 $this->assertEquals(
368 2,
369 count(self::$privateLinkDB->filterSearch($request, true, false))
370 );
371 }
372
373 /**
374 * Test filter with string.
375 */
376 public function testFilterArray()
377 {
378 $tags = array('dev', 'cartoon');
379 $request = array('searchtags' => $tags);
380 $this->assertEquals(
381 2,
382 count(self::$privateLinkDB->filterSearch($request, true, false))
383 );
384 }
385
386 /**
387 * Test hidden tags feature:
388 * tags starting with a dot '.' are only visible when logged in.
389 */
390 public function testHiddenTags()
391 {
392 $tags = '.hidden';
393 $request = array('searchtags' => $tags);
394 $this->assertEquals(
395 1,
396 count(self::$privateLinkDB->filterSearch($request, true, false))
397 );
398
399 $this->assertEquals(
400 0,
401 count(self::$publicLinkDB->filterSearch($request, true, false))
402 );
403 }
404
405 /**
406 * Test filterHash() with a valid smallhash.
407 */
408 public function testFilterHashValid()
409 {
410 $request = smallHash('20150310_114651');
411 $this->assertEquals(
412 1,
413 count(self::$publicLinkDB->filterHash($request))
414 );
415 $request = smallHash('20150310_114633' . 8);
416 $this->assertEquals(
417 1,
418 count(self::$publicLinkDB->filterHash($request))
419 );
420 }
421
422 /**
423 * Test filterHash() with an invalid smallhash.
424 */
425 public function testFilterHashInValid1()
426 {
427 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
428
429 $request = 'blabla';
430 self::$publicLinkDB->filterHash($request);
431 }
432
433 /**
434 * Test filterHash() with an empty smallhash.
435 */
436 public function testFilterHashInValid()
437 {
438 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
439
440 self::$publicLinkDB->filterHash('');
441 }
442
443 /**
444 * Test reorder with asc/desc parameter.
445 */
446 public function testReorderLinksDesc()
447 {
448 self::$privateLinkDB->reorder('ASC');
449 $stickyIds = [11, 10];
450 $standardIds = [42, 4, 9, 1, 0, 7, 6, 8, 41];
451 $linkIds = array_merge($stickyIds, $standardIds);
452 $cpt = 0;
453 foreach (self::$privateLinkDB as $key => $value) {
454 $this->assertEquals($linkIds[$cpt++], $key);
455 }
456 self::$privateLinkDB->reorder('DESC');
457 $linkIds = array_merge(array_reverse($stickyIds), array_reverse($standardIds));
458 $cpt = 0;
459 foreach (self::$privateLinkDB as $key => $value) {
460 $this->assertEquals($linkIds[$cpt++], $key);
461 }
462 }
463
464 /**
465 * Test rename tag with a valid value present in multiple bookmarks
466 */
467 public function testRenameTagMultiple()
468 {
469 self::$refDB->write(self::$testDatastore);
470 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
471
472 $res = $linkDB->renameTag('cartoon', 'Taz');
473 $this->assertEquals(3, count($res));
474 $this->assertContainsPolyfill(' Taz ', $linkDB[4]['tags']);
475 $this->assertContainsPolyfill(' Taz ', $linkDB[1]['tags']);
476 $this->assertContainsPolyfill(' Taz ', $linkDB[0]['tags']);
477 }
478
479 /**
480 * Test rename tag with a valid value
481 */
482 public function testRenameTagCaseSensitive()
483 {
484 self::$refDB->write(self::$testDatastore);
485 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
486
487 $res = $linkDB->renameTag('sTuff', 'Taz');
488 $this->assertEquals(1, count($res));
489 $this->assertEquals('Taz', $linkDB[41]['tags']);
490 }
491
492 /**
493 * Test rename tag with invalid values
494 */
495 public function testRenameTagInvalid()
496 {
497 $linkDB = new LegacyLinkDB(self::$testDatastore, false, false);
498
499 $this->assertFalse($linkDB->renameTag('', 'test'));
500 $this->assertFalse($linkDB->renameTag('', ''));
501 // tag non existent
502 $this->assertEquals([], $linkDB->renameTag('test', ''));
503 $this->assertEquals([], $linkDB->renameTag('test', 'retest'));
504 }
505
506 /**
507 * Test delete tag with a valid value
508 */
509 public function testDeleteTag()
510 {
511 self::$refDB->write(self::$testDatastore);
512 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
513
514 $res = $linkDB->renameTag('cartoon', null);
515 $this->assertEquals(3, count($res));
516 $this->assertNotContainsPolyfill('cartoon', $linkDB[4]['tags']);
517 }
518
519 /**
520 * Test linksCountPerTag all tags without filter.
521 * Equal occurrences should be sorted alphabetically.
522 */
523 public function testCountLinkPerTagAllNoFilter()
524 {
525 $expected = [
526 'web' => 4,
527 'cartoon' => 3,
528 'dev' => 2,
529 'gnu' => 2,
530 'hashtag' => 2,
531 'sTuff' => 2,
532 '-exclude' => 1,
533 '.hidden' => 1,
534 'Mercurial' => 1,
535 'css' => 1,
536 'free' => 1,
537 'html' => 1,
538 'media' => 1,
539 'samba' => 1,
540 'software' => 1,
541 'stallman' => 1,
542 'tag1' => 1,
543 'tag2' => 1,
544 'tag3' => 1,
545 'tag4' => 1,
546 'ut' => 1,
547 'w3c' => 1,
548 ];
549 $tags = self::$privateLinkDB->linksCountPerTag();
550
551 $this->assertEquals($expected, $tags, var_export($tags, true));
552 }
553
554 /**
555 * Test linksCountPerTag all tags with filter.
556 * Equal occurrences should be sorted alphabetically.
557 */
558 public function testCountLinkPerTagAllWithFilter()
559 {
560 $expected = [
561 'gnu' => 2,
562 'hashtag' => 2,
563 '-exclude' => 1,
564 '.hidden' => 1,
565 'free' => 1,
566 'media' => 1,
567 'software' => 1,
568 'stallman' => 1,
569 'stuff' => 1,
570 'web' => 1,
571 ];
572 $tags = self::$privateLinkDB->linksCountPerTag(['gnu']);
573
574 $this->assertEquals($expected, $tags, var_export($tags, true));
575 }
576
577 /**
578 * Test linksCountPerTag public tags with filter.
579 * Equal occurrences should be sorted alphabetically.
580 */
581 public function testCountLinkPerTagPublicWithFilter()
582 {
583 $expected = [
584 'gnu' => 2,
585 'hashtag' => 2,
586 '-exclude' => 1,
587 '.hidden' => 1,
588 'free' => 1,
589 'media' => 1,
590 'software' => 1,
591 'stallman' => 1,
592 'stuff' => 1,
593 'web' => 1,
594 ];
595 $tags = self::$privateLinkDB->linksCountPerTag(['gnu'], 'public');
596
597 $this->assertEquals($expected, $tags, var_export($tags, true));
598 }
599
600 /**
601 * Test linksCountPerTag public tags with filter.
602 * Equal occurrences should be sorted alphabetically.
603 */
604 public function testCountLinkPerTagPrivateWithFilter()
605 {
606 $expected = [
607 'cartoon' => 1,
608 'dev' => 1,
609 'tag1' => 1,
610 'tag2' => 1,
611 'tag3' => 1,
612 'tag4' => 1,
613 ];
614 $tags = self::$privateLinkDB->linksCountPerTag(['dev'], 'private');
615
616 $this->assertEquals($expected, $tags, var_export($tags, true));
617 }
618
619 /**
620 * Make sure that bookmarks with the same timestamp have a consistent order:
621 * if their creation date is equal, bookmarks are sorted by ID DESC.
622 */
623 public function testConsistentOrder()
624 {
625 $nextId = 43;
626 $creation = DateTime::createFromFormat('Ymd_His', '20190807_130444');
627 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
628 for ($i = 0; $i < 4; ++$i) {
629 $linkDB[$nextId + $i] = [
630 'id' => $nextId + $i,
631 'url' => 'http://'. $i,
632 'created' => $creation,
633 'title' => true,
634 'description' => true,
635 'tags' => true,
636 ];
637 }
638
639 // Check 4 new links 4 times
640 for ($i = 0; $i < 4; ++$i) {
641 $linkDB->save('tests');
642 $linkDB = new LegacyLinkDB(self::$testDatastore, true, false);
643 $count = 3;
644 foreach ($linkDB as $link) {
645 if ($link['sticky'] === true) {
646 continue;
647 }
648 $this->assertEquals($nextId + $count, $link['id']);
649 $this->assertEquals('http://'. $count, $link['url']);
650 if (--$count < 0) {
651 break;
652 }
653 }
654 }
655 }
656 }