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