]> git.immae.eu Git - github/shaarli/Shaarli.git/blame - tests/bookmark/BookmarkFilterTest.php
Feature: support any tag separator
[github/shaarli/Shaarli.git] / tests / bookmark / BookmarkFilterTest.php
CommitLineData
822bffce
A
1<?php
2
6696729b 3namespace Shaarli\Bookmark;
f24896b2 4
fd1ddad9 5use malkusch\lock\mutex\NoMutex;
6696729b 6use ReferenceLinkDB;
e26e2060 7use Shaarli\Config\ConfigManager;
e26e2060 8use Shaarli\History;
a5a9cf23 9use Shaarli\TestCase;
822bffce
A
10
11/**
e26e2060 12 * Class BookmarkFilterTest.
822bffce 13 */
e26e2060 14class BookmarkFilterTest extends TestCase
822bffce 15{
9ec0a611
A
16 /**
17 * @var string Test datastore path.
18 */
19 protected static $testDatastore = 'sandbox/datastore.php';
822bffce 20 /**
e26e2060 21 * @var BookmarkFilter instance.
822bffce
A
22 */
23 protected static $linkFilter;
24
7f96d9ec
A
25 /**
26 * @var ReferenceLinkDB instance
27 */
28 protected static $refDB;
29
9ec0a611 30 /**
e26e2060 31 * @var BookmarkFileService instance
9ec0a611 32 */
e26e2060 33 protected static $bookmarkService;
9ec0a611 34
822bffce 35 /**
6696729b 36 * Instantiate linkFilter with ReferenceLinkDB data.
822bffce 37 */
27ddfec3 38 public static function setUpBeforeClass(): void
822bffce 39 {
fd1ddad9 40 $mutex = new NoMutex();
e26e2060
A
41 $conf = new ConfigManager('tests/utils/config/configJson');
42 $conf->set('resource.datastore', self::$testDatastore);
43 self::$refDB = new \ReferenceLinkDB();
9ec0a611 44 self::$refDB->write(self::$testDatastore);
e26e2060 45 $history = new History('sandbox/history.php');
fd1ddad9 46 self::$bookmarkService = new \FakeBookmarkService($conf, $history, $mutex, true);
b3bd8c3e 47 self::$linkFilter = new BookmarkFilter(self::$bookmarkService->getBookmarks(), $conf);
822bffce
A
48 }
49
50 /**
51 * Blank filter.
52 */
53 public function testFilter()
54 {
55 $this->assertEquals(
7f96d9ec 56 self::$refDB->countLinks(),
822bffce
A
57 count(self::$linkFilter->filter('', ''))
58 );
59
7f96d9ec
A
60 $this->assertEquals(
61 self::$refDB->countLinks(),
62 count(self::$linkFilter->filter('', '', 'all'))
63 );
64
65 $this->assertEquals(
66 self::$refDB->countLinks(),
67 count(self::$linkFilter->filter('', '', 'randomstr'))
68 );
69
822bffce
A
70 // Private only.
71 $this->assertEquals(
7f96d9ec
A
72 self::$refDB->countPrivateLinks(),
73 count(self::$linkFilter->filter('', '', false, 'private'))
74 );
75
76 // Public only.
77 $this->assertEquals(
78 self::$refDB->countPublicLinks(),
79 count(self::$linkFilter->filter('', '', false, 'public'))
822bffce 80 );
c51fae92
A
81
82 $this->assertEquals(
89baf23d 83 ReferenceLinkDB::$NB_LINKS_TOTAL,
e26e2060 84 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, ''))
c51fae92
A
85 );
86
7d86f40b
A
87 $this->assertEquals(
88 self::$refDB->countUntaggedLinks(),
9d9f6d75
V
89 count(
90 self::$linkFilter->filter(
e26e2060 91 BookmarkFilter::$FILTER_TAG,
6696729b
V
92 /*$request=*/
93 '',
94 /*$casesensitive=*/
95 false,
96 /*$visibility=*/
97 'all',
98 /*$untaggedonly=*/
99 true
9d9f6d75
V
100 )
101 )
7d86f40b
A
102 );
103
c51fae92 104 $this->assertEquals(
89baf23d 105 ReferenceLinkDB::$NB_LINKS_TOTAL,
e26e2060 106 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, ''))
c51fae92 107 );
822bffce
A
108 }
109
110 /**
e26e2060 111 * Filter bookmarks using a tag
822bffce
A
112 */
113 public function testFilterOneTag()
114 {
115 $this->assertEquals(
116 4,
e26e2060 117 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false))
822bffce
A
118 );
119
7f96d9ec
A
120 $this->assertEquals(
121 4,
e26e2060 122 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'all'))
7f96d9ec
A
123 );
124
125 $this->assertEquals(
126 4,
e26e2060 127 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'default-blabla'))
7f96d9ec
A
128 );
129
822bffce
A
130 // Private only.
131 $this->assertEquals(
132 1,
e26e2060 133 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'private'))
7f96d9ec
A
134 );
135
136 // Public only.
137 $this->assertEquals(
138 3,
e26e2060 139 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'web', false, 'public'))
822bffce
A
140 );
141 }
142
143 /**
e26e2060 144 * Filter bookmarks using a tag - case-sensitive
822bffce
A
145 */
146 public function testFilterCaseSensitiveTag()
147 {
148 $this->assertEquals(
149 0,
e26e2060 150 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'mercurial', true))
822bffce
A
151 );
152
153 $this->assertEquals(
154 1,
e26e2060 155 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'Mercurial', true))
822bffce
A
156 );
157 }
158
159 /**
e26e2060 160 * Filter bookmarks using a tag combination
822bffce
A
161 */
162 public function testFilterMultipleTags()
163 {
164 $this->assertEquals(
165 2,
e26e2060 166 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'dev cartoon', false))
822bffce
A
167 );
168 }
169
170 /**
e26e2060 171 * Filter bookmarks using a non-existent tag
822bffce
A
172 */
173 public function testFilterUnknownTag()
174 {
175 $this->assertEquals(
176 0,
e26e2060 177 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'null', false))
822bffce
A
178 );
179 }
180
181 /**
e26e2060 182 * Return bookmarks for a given day
822bffce
A
183 */
184 public function testFilterDay()
185 {
186 $this->assertEquals(
7d86f40b 187 4,
e26e2060 188 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, '20121206'))
822bffce
A
189 );
190 }
191
27ddfec3
A
192 /**
193 * Return bookmarks for a given day
194 */
195 public function testFilterDayRestrictedVisibility(): void
196 {
197 $this->assertEquals(
198 3,
199 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, '20121206', false, BookmarkFilter::$PUBLIC))
200 );
201 }
202
822bffce
A
203 /**
204 * 404 - day not found
205 */
206 public function testFilterUnknownDay()
207 {
208 $this->assertEquals(
209 0,
e26e2060 210 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, '19700101'))
822bffce
A
211 );
212 }
213
214 /**
215 * Use an invalid date format
822bffce
A
216 */
217 public function testFilterInvalidDayWithChars()
218 {
f447edb7 219 $this->expectException(\Exception::class);
b1baca99
A
220 $this->expectExceptionMessageRegExp('/Invalid date format/');
221
e26e2060 222 self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, 'Rainy day, dream away');
822bffce
A
223 }
224
225 /**
226 * Use an invalid date format
822bffce
A
227 */
228 public function testFilterInvalidDayDigits()
229 {
f447edb7 230 $this->expectException(\Exception::class);
b1baca99
A
231 $this->expectExceptionMessageRegExp('/Invalid date format/');
232
e26e2060 233 self::$linkFilter->filter(BookmarkFilter::$FILTER_DAY, '20');
822bffce
A
234 }
235
236 /**
237 * Retrieve a link entry with its hash
238 */
239 public function testFilterSmallHash()
240 {
e26e2060 241 $links = self::$linkFilter->filter(BookmarkFilter::$FILTER_HASH, 'IuWvgA');
822bffce
A
242
243 $this->assertEquals(
244 1,
245 count($links)
246 );
247
248 $this->assertEquals(
249 'MediaGoblin',
e26e2060 250 $links[7]->getTitle()
822bffce
A
251 );
252 }
253
254 /**
255 * No link for this hash
256 */
257 public function testFilterUnknownSmallHash()
258 {
b1baca99
A
259 $this->expectException(\Shaarli\Bookmark\Exception\BookmarkNotFoundException::class);
260
e26e2060 261 self::$linkFilter->filter(BookmarkFilter::$FILTER_HASH, 'Iblaah');
822bffce
A
262 }
263
522b278b
A
264 /**
265 * Full-text search - no result found.
266 */
267 public function testFilterFullTextNoResult()
268 {
269 $this->assertEquals(
270 0,
e26e2060 271 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'azertyuiop'))
522b278b
A
272 );
273 }
274
822bffce
A
275 /**
276 * Full-text search - result from a link's URL
277 */
278 public function testFilterFullTextURL()
279 {
280 $this->assertEquals(
281 2,
e26e2060 282 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'ars.userfriendly.org'))
822bffce 283 );
9d9f6d75 284
ebd8075a
FV
285 $this->assertEquals(
286 2,
e26e2060 287 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'ars org'))
ebd8075a 288 );
822bffce
A
289 }
290
291 /**
292 * Full-text search - result from a link's title only
293 */
294 public function testFilterFullTextTitle()
295 {
296 // use miscellaneous cases
297 $this->assertEquals(
298 2,
e26e2060 299 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'userfriendly -'))
822bffce
A
300 );
301 $this->assertEquals(
302 2,
e26e2060 303 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'UserFriendly -'))
822bffce
A
304 );
305 $this->assertEquals(
306 2,
e26e2060 307 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'uSeRFrIendlY -'))
822bffce
A
308 );
309
310 // use miscellaneous case and offset
311 $this->assertEquals(
312 2,
e26e2060 313 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'RFrIendL'))
822bffce
A
314 );
315 }
316
317 /**
318 * Full-text search - result from the link's description only
319 */
320 public function testFilterFullTextDescription()
321 {
322 $this->assertEquals(
323 1,
e26e2060 324 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'publishing media'))
ebd8075a 325 );
9d9f6d75 326
ebd8075a
FV
327 $this->assertEquals(
328 1,
e26e2060 329 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'mercurial w3c'))
822bffce 330 );
9d9f6d75 331
ebd8075a 332 $this->assertEquals(
bedd176a 333 3,
e26e2060 334 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, '"free software"'))
067c2dd8 335 );
822bffce
A
336 }
337
338 /**
339 * Full-text search - result from the link's tags only
340 */
341 public function testFilterFullTextTags()
342 {
343 $this->assertEquals(
7f96d9ec 344 6,
e26e2060 345 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web'))
7f96d9ec
A
346 );
347
348 $this->assertEquals(
349 6,
e26e2060 350 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', 'all'))
7f96d9ec
A
351 );
352
353 $this->assertEquals(
354 6,
e26e2060 355 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', 'bla'))
822bffce
A
356 );
357
358 // Private only.
359 $this->assertEquals(
360 1,
e26e2060 361 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', false, 'private'))
7f96d9ec
A
362 );
363
364 // Public only.
365 $this->assertEquals(
366 5,
e26e2060 367 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'web', false, 'public'))
822bffce
A
368 );
369 }
370
371 /**
372 * Full-text search - result set from mixed sources
373 */
374 public function testFilterFullTextMixed()
375 {
376 $this->assertEquals(
bedd176a 377 3,
e26e2060 378 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'free software'))
822bffce
A
379 );
380 }
21979ff1 381
bedd176a
A
382 /**
383 * Full-text search - test exclusion with '-'.
384 */
385 public function testExcludeSearch()
386 {
387 $this->assertEquals(
388 1,
e26e2060 389 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, 'free -gnu'))
bedd176a
A
390 );
391
392 $this->assertEquals(
7d86f40b 393 ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
e26e2060 394 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TEXT, '-revolution'))
bedd176a
A
395 );
396 }
397
398 /**
522b278b 399 * Full-text search - test AND, exact terms and exclusion combined, across fields.
bedd176a
A
400 */
401 public function testMultiSearch()
402 {
403 $this->assertEquals(
404 2,
522b278b 405 count(self::$linkFilter->filter(
e26e2060 406 BookmarkFilter::$FILTER_TEXT,
522b278b
A
407 '"Free Software " stallman "read this" @website stuff'
408 ))
bedd176a
A
409 );
410
411 $this->assertEquals(
412 1,
522b278b 413 count(self::$linkFilter->filter(
e26e2060 414 BookmarkFilter::$FILTER_TEXT,
522b278b
A
415 '"free software " stallman "read this" -beard @website stuff'
416 ))
417 );
418 }
419
420 /**
421 * Full-text search - make sure that exact search won't work across fields.
422 */
423 public function testSearchExactTermMultiFieldsKo()
424 {
425 $this->assertEquals(
426 0,
427 count(self::$linkFilter->filter(
e26e2060 428 BookmarkFilter::$FILTER_TEXT,
522b278b
A
429 '"designer naming"'
430 ))
431 );
432
433 $this->assertEquals(
434 0,
435 count(self::$linkFilter->filter(
e26e2060 436 BookmarkFilter::$FILTER_TEXT,
522b278b
A
437 '"designernaming"'
438 ))
bedd176a
A
439 );
440 }
441
21979ff1
A
442 /**
443 * Tag search with exclusion.
444 */
445 public function testTagFilterWithExclusion()
446 {
447 $this->assertEquals(
448 1,
e26e2060 449 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, 'gnu -free'))
21979ff1
A
450 );
451
452 $this->assertEquals(
7d86f40b 453 ReferenceLinkDB::$NB_LINKS_TOTAL - 1,
e26e2060 454 count(self::$linkFilter->filter(BookmarkFilter::$FILTER_TAG, '-free'))
21979ff1
A
455 );
456 }
c51fae92
A
457
458 /**
459 * Test crossed search (terms + tags).
460 */
461 public function testFilterCrossedSearch()
462 {
463 $terms = '"Free Software " stallman "read this" @website stuff';
464 $tags = 'free';
465 $this->assertEquals(
466 1,
467 count(self::$linkFilter->filter(
e26e2060 468 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
c51fae92
A
469 array($tags, $terms)
470 ))
471 );
472 $this->assertEquals(
473 2,
474 count(self::$linkFilter->filter(
e26e2060 475 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
c51fae92
A
476 array('', $terms)
477 ))
478 );
479 $this->assertEquals(
7d86f40b
A
480 1,
481 count(self::$linkFilter->filter(
e26e2060 482 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
7d86f40b
A
483 array(false, 'PSR-2')
484 ))
485 );
486 $this->assertEquals(
c51fae92
A
487 1,
488 count(self::$linkFilter->filter(
e26e2060 489 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
c51fae92
A
490 array($tags, '')
491 ))
492 );
493 $this->assertEquals(
89baf23d 494 ReferenceLinkDB::$NB_LINKS_TOTAL,
c51fae92 495 count(self::$linkFilter->filter(
e26e2060 496 BookmarkFilter::$FILTER_TAG | BookmarkFilter::$FILTER_TEXT,
c51fae92
A
497 ''
498 ))
499 );
500 }
9ccca401
A
501
502 /**
e26e2060 503 * Filter bookmarks by #hashtag.
9ccca401
A
504 */
505 public function testFilterByHashtag()
506 {
507 $hashtag = 'hashtag';
508 $this->assertEquals(
509 3,
510 count(self::$linkFilter->filter(
e26e2060 511 BookmarkFilter::$FILTER_TAG,
9ccca401
A
512 $hashtag
513 ))
514 );
515
516 $hashtag = 'private';
517 $this->assertEquals(
518 1,
519 count(self::$linkFilter->filter(
e26e2060 520 BookmarkFilter::$FILTER_TAG,
9ccca401
A
521 $hashtag,
522 false,
7f96d9ec 523 'private'
9ccca401
A
524 ))
525 );
526 }
f1a148ab
A
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 }
822bffce 566}