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.php622
1 files changed, 622 insertions, 0 deletions
diff --git a/tests/bookmark/LinkDBTest.php b/tests/bookmark/LinkDBTest.php
new file mode 100644
index 00000000..2990a6b5
--- /dev/null
+++ b/tests/bookmark/LinkDBTest.php
@@ -0,0 +1,622 @@
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 filter with string.
366 */
367 public function testFilterString()
368 {
369 $tags = 'dev cartoon';
370 $request = array('searchtags' => $tags);
371 $this->assertEquals(
372 2,
373 count(self::$privateLinkDB->filterSearch($request, true, false))
374 );
375 }
376
377 /**
378 * Test filter with string.
379 */
380 public function testFilterArray()
381 {
382 $tags = array('dev', 'cartoon');
383 $request = array('searchtags' => $tags);
384 $this->assertEquals(
385 2,
386 count(self::$privateLinkDB->filterSearch($request, true, false))
387 );
388 }
389
390 /**
391 * Test hidden tags feature:
392 * tags starting with a dot '.' are only visible when logged in.
393 */
394 public function testHiddenTags()
395 {
396 $tags = '.hidden';
397 $request = array('searchtags' => $tags);
398 $this->assertEquals(
399 1,
400 count(self::$privateLinkDB->filterSearch($request, true, false))
401 );
402
403 $this->assertEquals(
404 0,
405 count(self::$publicLinkDB->filterSearch($request, true, false))
406 );
407 }
408
409 /**
410 * Test filterHash() with a valid smallhash.
411 */
412 public function testFilterHashValid()
413 {
414 $request = smallHash('20150310_114651');
415 $this->assertEquals(
416 1,
417 count(self::$publicLinkDB->filterHash($request))
418 );
419 $request = smallHash('20150310_114633' . 8);
420 $this->assertEquals(
421 1,
422 count(self::$publicLinkDB->filterHash($request))
423 );
424 }
425
426 /**
427 * Test filterHash() with an invalid smallhash.
428 *
429 * @expectedException \Shaarli\Bookmark\Exception\LinkNotFoundException
430 */
431 public function testFilterHashInValid1()
432 {
433 $request = 'blabla';
434 self::$publicLinkDB->filterHash($request);
435 }
436
437 /**
438 * Test filterHash() with an empty smallhash.
439 *
440 * @expectedException \Shaarli\Bookmark\Exception\LinkNotFoundException
441 */
442 public function testFilterHashInValid()
443 {
444 self::$publicLinkDB->filterHash('');
445 }
446
447 /**
448 * Test reorder with asc/desc parameter.
449 */
450 public function testReorderLinksDesc()
451 {
452 self::$privateLinkDB->reorder('ASC');
453 $stickyIds = [11, 10];
454 $standardIds = [42, 4, 9, 1, 0, 7, 6, 8, 41];
455 $linkIds = array_merge($stickyIds, $standardIds);
456 $cpt = 0;
457 foreach (self::$privateLinkDB as $key => $value) {
458 $this->assertEquals($linkIds[$cpt++], $key);
459 }
460 self::$privateLinkDB->reorder('DESC');
461 $linkIds = array_merge(array_reverse($stickyIds), array_reverse($standardIds));
462 $cpt = 0;
463 foreach (self::$privateLinkDB as $key => $value) {
464 $this->assertEquals($linkIds[$cpt++], $key);
465 }
466 }
467
468 /**
469 * Test rename tag with a valid value present in multiple links
470 */
471 public function testRenameTagMultiple()
472 {
473 self::$refDB->write(self::$testDatastore);
474 $linkDB = new LinkDB(self::$testDatastore, true, false);
475
476 $res = $linkDB->renameTag('cartoon', 'Taz');
477 $this->assertEquals(3, count($res));
478 $this->assertContains(' Taz ', $linkDB[4]['tags']);
479 $this->assertContains(' Taz ', $linkDB[1]['tags']);
480 $this->assertContains(' Taz ', $linkDB[0]['tags']);
481 }
482
483 /**
484 * Test rename tag with a valid value
485 */
486 public function testRenameTagCaseSensitive()
487 {
488 self::$refDB->write(self::$testDatastore);
489 $linkDB = new LinkDB(self::$testDatastore, true, false);
490
491 $res = $linkDB->renameTag('sTuff', 'Taz');
492 $this->assertEquals(1, count($res));
493 $this->assertEquals('Taz', $linkDB[41]['tags']);
494 }
495
496 /**
497 * Test rename tag with invalid values
498 */
499 public function testRenameTagInvalid()
500 {
501 $linkDB = new LinkDB(self::$testDatastore, false, false);
502
503 $this->assertFalse($linkDB->renameTag('', 'test'));
504 $this->assertFalse($linkDB->renameTag('', ''));
505 // tag non existent
506 $this->assertEquals([], $linkDB->renameTag('test', ''));
507 $this->assertEquals([], $linkDB->renameTag('test', 'retest'));
508 }
509
510 /**
511 * Test delete tag with a valid value
512 */
513 public function testDeleteTag()
514 {
515 self::$refDB->write(self::$testDatastore);
516 $linkDB = new LinkDB(self::$testDatastore, true, false);
517
518 $res = $linkDB->renameTag('cartoon', null);
519 $this->assertEquals(3, count($res));
520 $this->assertNotContains('cartoon', $linkDB[4]['tags']);
521 }
522
523 /**
524 * Test linksCountPerTag all tags without filter.
525 * Equal occurrences should be sorted alphabetically.
526 */
527 public function testCountLinkPerTagAllNoFilter()
528 {
529 $expected = [
530 'web' => 4,
531 'cartoon' => 3,
532 'dev' => 2,
533 'gnu' => 2,
534 'hashtag' => 2,
535 'sTuff' => 2,
536 '-exclude' => 1,
537 '.hidden' => 1,
538 'Mercurial' => 1,
539 'css' => 1,
540 'free' => 1,
541 'html' => 1,
542 'media' => 1,
543 'samba' => 1,
544 'software' => 1,
545 'stallman' => 1,
546 'tag1' => 1,
547 'tag2' => 1,
548 'tag3' => 1,
549 'tag4' => 1,
550 'ut' => 1,
551 'w3c' => 1,
552 ];
553 $tags = self::$privateLinkDB->linksCountPerTag();
554
555 $this->assertEquals($expected, $tags, var_export($tags, true));
556 }
557
558 /**
559 * Test linksCountPerTag all tags with filter.
560 * Equal occurrences should be sorted alphabetically.
561 */
562 public function testCountLinkPerTagAllWithFilter()
563 {
564 $expected = [
565 'gnu' => 2,
566 'hashtag' => 2,
567 '-exclude' => 1,
568 '.hidden' => 1,
569 'free' => 1,
570 'media' => 1,
571 'software' => 1,
572 'stallman' => 1,
573 'stuff' => 1,
574 'web' => 1,
575 ];
576 $tags = self::$privateLinkDB->linksCountPerTag(['gnu']);
577
578 $this->assertEquals($expected, $tags, var_export($tags, true));
579 }
580
581 /**
582 * Test linksCountPerTag public tags with filter.
583 * Equal occurrences should be sorted alphabetically.
584 */
585 public function testCountLinkPerTagPublicWithFilter()
586 {
587 $expected = [
588 'gnu' => 2,
589 'hashtag' => 2,
590 '-exclude' => 1,
591 '.hidden' => 1,
592 'free' => 1,
593 'media' => 1,
594 'software' => 1,
595 'stallman' => 1,
596 'stuff' => 1,
597 'web' => 1,
598 ];
599 $tags = self::$privateLinkDB->linksCountPerTag(['gnu'], 'public');
600
601 $this->assertEquals($expected, $tags, var_export($tags, true));
602 }
603
604 /**
605 * Test linksCountPerTag public tags with filter.
606 * Equal occurrences should be sorted alphabetically.
607 */
608 public function testCountLinkPerTagPrivateWithFilter()
609 {
610 $expected = [
611 'cartoon' => 1,
612 'dev' => 1,
613 'tag1' => 1,
614 'tag2' => 1,
615 'tag3' => 1,
616 'tag4' => 1,
617 ];
618 $tags = self::$privateLinkDB->linksCountPerTag(['dev'], 'private');
619
620 $this->assertEquals($expected, $tags, var_export($tags, true));
621 }
622}