]> git.immae.eu Git - github/shaarli/Shaarli.git/blobdiff - tests/netscape/BookmarkImportTest.php
New plugin hook: ability to add custom filters to Shaarli search engine
[github/shaarli/Shaarli.git] / tests / netscape / BookmarkImportTest.php
index ccafc1619cf48d988fef4e4a62d44f5f81789a88..ecd33ea1af16c1c212be81e0bd7a1aab062a03bb 100644 (file)
@@ -1,26 +1,33 @@
 <?php
+
 namespace Shaarli\Netscape;
 
 use DateTime;
-use Shaarli\Bookmark\LinkDB;
+use malkusch\lock\mutex\NoMutex;
+use Psr\Http\Message\UploadedFileInterface;
+use Shaarli\Bookmark\Bookmark;
+use Shaarli\Bookmark\BookmarkFileService;
+use Shaarli\Bookmark\BookmarkFilter;
 use Shaarli\Config\ConfigManager;
 use Shaarli\History;
+use Shaarli\Plugin\PluginManager;
+use Shaarli\TestCase;
+use Slim\Http\UploadedFile;
 
 /**
  * Utility function to load a file's metadata in a $_FILES-like array
  *
  * @param string $filename Basename of the file
  *
- * @return array A $_FILES-like array
+ * @return UploadedFileInterface Upload file in PSR-7 compatible object
  */
 function file2array($filename)
 {
-    return array(
-        'filetoupload' => array(
-            'name'     => $filename,
-            'tmp_name' => __DIR__ . '/input/' . $filename,
-            'size'     => filesize(__DIR__ . '/input/' . $filename)
-        )
+    return new UploadedFile(
+        __DIR__ . '/input/' . $filename,
+        $filename,
+        null,
+        filesize(__DIR__ . '/input/' . $filename)
     );
 }
 
@@ -28,7 +35,7 @@ function file2array($filename)
 /**
  * Netscape bookmark import
  */
-class BookmarkImportTest extends \PHPUnit\Framework\TestCase
+class BookmarkImportTest extends TestCase
 {
     /**
      * @var string datastore to test write operations
@@ -41,9 +48,9 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
     protected static $historyFilePath = 'sandbox/history.php';
 
     /**
-     * @var LinkDB private LinkDB instance
+     * @var BookmarkFileService private LinkDB instance
      */
-    protected $linkDb = null;
+    protected $bookmarkService = null;
 
     /**
      * @var string Dummy page cache
@@ -60,12 +67,20 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
      */
     protected $history;
 
+    /**
+     * @var NetscapeBookmarkUtils
+     */
+    protected $netscapeBookmarkUtils;
+
+    /** @var PluginManager */
+    protected $pluginManager;
+
     /**
      * @var string Save the current timezone.
      */
     protected static $defaultTimeZone;
 
-    public static function setUpBeforeClass()
+    public static function setUpBeforeClass(): void
     {
         self::$defaultTimeZone = date_default_timezone_get();
         // Timezone without DST for test consistency
@@ -75,28 +90,39 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
     /**
      * Resets test data before each test
      */
-    protected function setUp()
+    protected function setUp(): void
     {
+        $mutex = new NoMutex();
         if (file_exists(self::$testDatastore)) {
             unlink(self::$testDatastore);
         }
         // start with an empty datastore
         file_put_contents(self::$testDatastore, '<?php /* S7QysKquBQA= */ ?>');
-        $this->linkDb = new LinkDB(self::$testDatastore, true, false);
+
         $this->conf = new ConfigManager('tests/utils/config/configJson');
         $this->conf->set('resource.page_cache', $this->pagecache);
+        $this->conf->set('resource.datastore', self::$testDatastore);
         $this->history = new History(self::$historyFilePath);
+        $this->pluginManager = new PluginManager($this->conf);
+        $this->bookmarkService = new BookmarkFileService(
+            $this->conf,
+            $this->pluginManager,
+            $this->history,
+            $mutex,
+            true
+        );
+        $this->netscapeBookmarkUtils = new NetscapeBookmarkUtils($this->bookmarkService, $this->conf, $this->history);
     }
 
     /**
      * Delete history file.
      */
-    public function tearDown()
+    protected function tearDown(): void
     {
         @unlink(self::$historyFilePath);
     }
 
-    public static function tearDownAfterClass()
+    public static function tearDownAfterClass(): void
     {
         date_default_timezone_set(self::$defaultTimeZone);
     }
@@ -110,9 +136,9 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         $this->assertEquals(
             'File empty.htm (0 bytes) has an unknown file format.'
             .' Nothing was imported.',
-            NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history)
+            $this->netscapeBookmarkUtils->import(null, $files)
         );
-        $this->assertEquals(0, count($this->linkDb));
+        $this->assertEquals(0, $this->bookmarkService->count());
     }
 
     /**
@@ -123,9 +149,9 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         $files = file2array('no_doctype.htm');
         $this->assertEquals(
             'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.',
-            NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history)
+            $this->netscapeBookmarkUtils->import(null, $files)
         );
-        $this->assertEquals(0, count($this->linkDb));
+        $this->assertEquals(0, $this->bookmarkService->count());
     }
 
     /**
@@ -136,10 +162,10 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         $files = file2array('lowercase_doctype.htm');
         $this->assertStringMatchesFormat(
             'File lowercase_doctype.htm (386 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import(null, $files, $this->linkDb, $this->conf, $this->history)
+            .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import(null, $files)
         );
-        $this->assertEquals(2, count($this->linkDb));
+        $this->assertEquals(2, $this->bookmarkService->count());
     }
 
 
@@ -151,25 +177,24 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         $files = file2array('internet_explorer_encoding.htm');
         $this->assertStringMatchesFormat(
             'File internet_explorer_encoding.htm (356 bytes) was successfully processed in %d seconds:'
-            .' 1 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history)
+            .' 1 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import([], $files)
         );
-        $this->assertEquals(1, count($this->linkDb));
-        $this->assertEquals(0, count_private($this->linkDb));
+        $this->assertEquals(1, $this->bookmarkService->count());
+        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
 
+        $bookmark = $this->bookmarkService->findByUrl('http://hginit.com/');
+        $this->assertEquals(0, $bookmark->getId());
         $this->assertEquals(
-            array(
-                'id' => 0,
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160618_203944'),
-                'title' => 'Hg Init a Mercurial tutorial by Joel Spolsky',
-                'url' => 'http://hginit.com/',
-                'description' => '',
-                'private' => 0,
-                'tags' => '',
-                'shorturl' => 'La37cg',
-            ),
-            $this->linkDb->getLinkFromUrl('http://hginit.com/')
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160618_203944'),
+            $bookmark->getCreated()
         );
+        $this->assertEquals('Hg Init a Mercurial tutorial by Joel Spolsky', $bookmark->getTitle());
+        $this->assertEquals('http://hginit.com/', $bookmark->getUrl());
+        $this->assertEquals('', $bookmark->getDescription());
+        $this->assertFalse($bookmark->isPrivate());
+        $this->assertEquals('', $bookmark->getTagsString());
+        $this->assertEquals('La37cg', $bookmark->getShortUrl());
     }
 
     /**
@@ -180,116 +205,115 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         $files = file2array('netscape_nested.htm');
         $this->assertStringMatchesFormat(
             'File netscape_nested.htm (1337 bytes) was successfully processed in %d seconds:'
-            .' 8 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history)
-        );
-        $this->assertEquals(8, count($this->linkDb));
-        $this->assertEquals(2, count_private($this->linkDb));
-
-        $this->assertEquals(
-            array(
-                'id' => 0,
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235541'),
-                'title' => 'Nested 1',
-                'url' => 'http://nest.ed/1',
-                'description' => '',
-                'private' => 0,
-                'tags' => 'tag1 tag2',
-                'shorturl' => 'KyDNKA',
-            ),
-            $this->linkDb->getLinkFromUrl('http://nest.ed/1')
-        );
-        $this->assertEquals(
-            array(
-                'id' => 1,
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235542'),
-                'title' => 'Nested 1-1',
-                'url' => 'http://nest.ed/1-1',
-                'description' => '',
-                'private' => 0,
-                'tags' => 'folder1 tag1 tag2',
-                'shorturl' => 'T2LnXg',
-            ),
-            $this->linkDb->getLinkFromUrl('http://nest.ed/1-1')
-        );
-        $this->assertEquals(
-            array(
-                'id' => 2,
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235547'),
-                'title' => 'Nested 1-2',
-                'url' => 'http://nest.ed/1-2',
-                'description' => '',
-                'private' => 0,
-                'tags' => 'folder1 tag3 tag4',
-                'shorturl' => '46SZxA',
-            ),
-            $this->linkDb->getLinkFromUrl('http://nest.ed/1-2')
-        );
-        $this->assertEquals(
-            array(
-                'id' => 3,
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'),
-                'title' => 'Nested 2-1',
-                'url' => 'http://nest.ed/2-1',
-                'description' => 'First link of the second section',
-                'private' => 1,
-                'tags' => 'folder2',
-                'shorturl' => '4UHOSw',
-            ),
-            $this->linkDb->getLinkFromUrl('http://nest.ed/2-1')
-        );
-        $this->assertEquals(
-            array(
-                'id' => 4,
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'),
-                'title' => 'Nested 2-2',
-                'url' => 'http://nest.ed/2-2',
-                'description' => 'Second link of the second section',
-                'private' => 1,
-                'tags' => 'folder2',
-                'shorturl' => 'yfzwbw',
-            ),
-            $this->linkDb->getLinkFromUrl('http://nest.ed/2-2')
-        );
-        $this->assertEquals(
-            array(
-                'id' => 5,
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'),
-                'title' => 'Nested 3-1',
-                'url' => 'http://nest.ed/3-1',
-                'description' => '',
-                'private' => 0,
-                'tags' => 'folder3 folder3-1 tag3',
-                'shorturl' => 'UwxIUQ',
-            ),
-            $this->linkDb->getLinkFromUrl('http://nest.ed/3-1')
-        );
-        $this->assertEquals(
-            array(
-                'id' => 6,
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'),
-                'title' => 'Nested 3-2',
-                'url' => 'http://nest.ed/3-2',
-                'description' => '',
-                'private' => 0,
-                'tags' => 'folder3 folder3-1',
-                'shorturl' => 'p8dyZg',
-            ),
-            $this->linkDb->getLinkFromUrl('http://nest.ed/3-2')
-        );
-        $this->assertEquals(
-            array(
-                'id' => 7,
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160229_111541'),
-                'title' => 'Nested 2',
-                'url' => 'http://nest.ed/2',
-                'description' => '',
-                'private' => 0,
-                'tags' => 'tag4',
-                'shorturl' => 'Gt3Uug',
-            ),
-            $this->linkDb->getLinkFromUrl('http://nest.ed/2')
+            .' 8 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import([], $files)
         );
+        $this->assertEquals(8, $this->bookmarkService->count());
+        $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
+
+        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/1');
+        $this->assertEquals(0, $bookmark->getId());
+        $this->assertEquals(
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235541'),
+            $bookmark->getCreated()
+        );
+        $this->assertEquals('Nested 1', $bookmark->getTitle());
+        $this->assertEquals('http://nest.ed/1', $bookmark->getUrl());
+        $this->assertEquals('', $bookmark->getDescription());
+        $this->assertFalse($bookmark->isPrivate());
+        $this->assertEquals('tag1 tag2', $bookmark->getTagsString());
+        $this->assertEquals('KyDNKA', $bookmark->getShortUrl());
+
+        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/1-1');
+        $this->assertEquals(1, $bookmark->getId());
+        $this->assertEquals(
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235542'),
+            $bookmark->getCreated()
+        );
+        $this->assertEquals('Nested 1-1', $bookmark->getTitle());
+        $this->assertEquals('http://nest.ed/1-1', $bookmark->getUrl());
+        $this->assertEquals('', $bookmark->getDescription());
+        $this->assertFalse($bookmark->isPrivate());
+        $this->assertEquals('folder1 tag1 tag2', $bookmark->getTagsString());
+        $this->assertEquals('T2LnXg', $bookmark->getShortUrl());
+
+        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/1-2');
+        $this->assertEquals(2, $bookmark->getId());
+        $this->assertEquals(
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235547'),
+            $bookmark->getCreated()
+        );
+        $this->assertEquals('Nested 1-2', $bookmark->getTitle());
+        $this->assertEquals('http://nest.ed/1-2', $bookmark->getUrl());
+        $this->assertEquals('', $bookmark->getDescription());
+        $this->assertFalse($bookmark->isPrivate());
+        $this->assertEquals('folder1 tag3 tag4', $bookmark->getTagsString());
+        $this->assertEquals('46SZxA', $bookmark->getShortUrl());
+
+        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/2-1');
+        $this->assertEquals(3, $bookmark->getId());
+        $this->assertEquals(
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160202_202222'),
+            $bookmark->getCreated()
+        );
+        $this->assertEquals('Nested 2-1', $bookmark->getTitle());
+        $this->assertEquals('http://nest.ed/2-1', $bookmark->getUrl());
+        $this->assertEquals('First link of the second section', $bookmark->getDescription());
+        $this->assertTrue($bookmark->isPrivate());
+        $this->assertEquals('folder2', $bookmark->getTagsString());
+        $this->assertEquals('4UHOSw', $bookmark->getShortUrl());
+
+        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/2-2');
+        $this->assertEquals(4, $bookmark->getId());
+        $this->assertEquals(
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160119_230227'),
+            $bookmark->getCreated()
+        );
+        $this->assertEquals('Nested 2-2', $bookmark->getTitle());
+        $this->assertEquals('http://nest.ed/2-2', $bookmark->getUrl());
+        $this->assertEquals('Second link of the second section', $bookmark->getDescription());
+        $this->assertTrue($bookmark->isPrivate());
+        $this->assertEquals('folder2', $bookmark->getTagsString());
+        $this->assertEquals('yfzwbw', $bookmark->getShortUrl());
+
+        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/3-1');
+        $this->assertEquals(5, $bookmark->getId());
+        $this->assertEquals(
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160202_202222'),
+            $bookmark->getCreated()
+        );
+        $this->assertEquals('Nested 3-1', $bookmark->getTitle());
+        $this->assertEquals('http://nest.ed/3-1', $bookmark->getUrl());
+        $this->assertEquals('', $bookmark->getDescription());
+        $this->assertFalse($bookmark->isPrivate());
+        $this->assertEquals('folder3 folder3-1 tag3', $bookmark->getTagsString());
+        $this->assertEquals('UwxIUQ', $bookmark->getShortUrl());
+
+        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/3-2');
+        $this->assertEquals(6, $bookmark->getId());
+        $this->assertEquals(
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160119_230227'),
+            $bookmark->getCreated()
+        );
+        $this->assertEquals('Nested 3-2', $bookmark->getTitle());
+        $this->assertEquals('http://nest.ed/3-2', $bookmark->getUrl());
+        $this->assertEquals('', $bookmark->getDescription());
+        $this->assertFalse($bookmark->isPrivate());
+        $this->assertEquals('folder3 folder3-1', $bookmark->getTagsString());
+        $this->assertEquals('p8dyZg', $bookmark->getShortUrl());
+
+        $bookmark = $this->bookmarkService->findByUrl('http://nest.ed/2');
+        $this->assertEquals(7, $bookmark->getId());
+        $this->assertEquals(
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160229_111541'),
+            $bookmark->getCreated()
+        );
+        $this->assertEquals('Nested 2', $bookmark->getTitle());
+        $this->assertEquals('http://nest.ed/2', $bookmark->getUrl());
+        $this->assertEquals('', $bookmark->getDescription());
+        $this->assertFalse($bookmark->isPrivate());
+        $this->assertEquals('tag4', $bookmark->getTagsString());
+        $this->assertEquals('Gt3Uug', $bookmark->getShortUrl());
     }
 
     /**
@@ -302,40 +326,38 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         $files = file2array('netscape_basic.htm');
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history)
+            .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import([], $files)
         );
 
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(1, count_private($this->linkDb));
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(1, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
 
+        $bookmark = $this->bookmarkService->findByUrl('https://private.tld');
+        $this->assertEquals(0, $bookmark->getId());
         $this->assertEquals(
-            array(
-                'id' => 0,
-                // Old link - UTC+4 (note that TZ in the import file is ignored).
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20001010_135536'),
-                'title' => 'Secret stuff',
-                'url' => 'https://private.tld',
-                'description' => "Super-secret stuff you're not supposed to know about",
-                'private' => 1,
-                'tags' => 'private secret',
-                'shorturl' => 'EokDtA',
-            ),
-            $this->linkDb->getLinkFromUrl('https://private.tld')
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20001010_135536'),
+            $bookmark->getCreated()
         );
+        $this->assertEquals('Secret stuff', $bookmark->getTitle());
+        $this->assertEquals('https://private.tld', $bookmark->getUrl());
+        $this->assertEquals('Super-secret stuff you\'re not supposed to know about', $bookmark->getDescription());
+        $this->assertTrue($bookmark->isPrivate());
+        $this->assertEquals('private secret', $bookmark->getTagsString());
+        $this->assertEquals('EokDtA', $bookmark->getShortUrl());
+
+        $bookmark = $this->bookmarkService->findByUrl('http://public.tld');
+        $this->assertEquals(1, $bookmark->getId());
         $this->assertEquals(
-            array(
-                'id' => 1,
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'),
-                'title' => 'Public stuff',
-                'url' => 'http://public.tld',
-                'description' => '',
-                'private' => 0,
-                'tags' => 'public hello world',
-                'shorturl' => 'Er9ddA',
-            ),
-            $this->linkDb->getLinkFromUrl('http://public.tld')
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235548'),
+            $bookmark->getCreated()
         );
+        $this->assertEquals('Public stuff', $bookmark->getTitle());
+        $this->assertEquals('http://public.tld', $bookmark->getUrl());
+        $this->assertEquals('', $bookmark->getDescription());
+        $this->assertFalse($bookmark->isPrivate());
+        $this->assertEquals('public hello world', $bookmark->getTagsString());
+        $this->assertEquals('Er9ddA', $bookmark->getShortUrl());
     }
 
     /**
@@ -347,43 +369,42 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         $files = file2array('netscape_basic.htm');
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
+            .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
         );
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(1, count_private($this->linkDb));
 
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(1, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
+
+        $bookmark = $this->bookmarkService->findByUrl('https://private.tld');
+        $this->assertEquals(0, $bookmark->getId());
         $this->assertEquals(
-            array(
-                'id' => 0,
-                // Note that TZ in the import file is ignored.
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20001010_135536'),
-                'title' => 'Secret stuff',
-                'url' => 'https://private.tld',
-                'description' => "Super-secret stuff you're not supposed to know about",
-                'private' => 1,
-                'tags' => 'private secret',
-                'shorturl' => 'EokDtA',
-            ),
-            $this->linkDb->getLinkFromUrl('https://private.tld')
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20001010_135536'),
+            $bookmark->getCreated()
         );
+        $this->assertEquals('Secret stuff', $bookmark->getTitle());
+        $this->assertEquals('https://private.tld', $bookmark->getUrl());
+        $this->assertEquals('Super-secret stuff you\'re not supposed to know about', $bookmark->getDescription());
+        $this->assertTrue($bookmark->isPrivate());
+        $this->assertEquals('private secret', $bookmark->getTagsString());
+        $this->assertEquals('EokDtA', $bookmark->getShortUrl());
+
+        $bookmark = $this->bookmarkService->findByUrl('http://public.tld');
+        $this->assertEquals(1, $bookmark->getId());
         $this->assertEquals(
-            array(
-                'id' => 1,
-                'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'),
-                'title' => 'Public stuff',
-                'url' => 'http://public.tld',
-                'description' => '',
-                'private' => 0,
-                'tags' => 'public hello world',
-                'shorturl' => 'Er9ddA',
-            ),
-            $this->linkDb->getLinkFromUrl('http://public.tld')
+            DateTime::createFromFormat(Bookmark::LINK_DATE_FORMAT, '20160225_235548'),
+            $bookmark->getCreated()
         );
+        $this->assertEquals('Public stuff', $bookmark->getTitle());
+        $this->assertEquals('http://public.tld', $bookmark->getUrl());
+        $this->assertEquals('', $bookmark->getDescription());
+        $this->assertFalse($bookmark->isPrivate());
+        $this->assertEquals('public hello world', $bookmark->getTagsString());
+        $this->assertEquals('Er9ddA', $bookmark->getShortUrl());
     }
 
     /**
-     * Import links as public
+     * Import bookmarks as public
      */
     public function testImportAsPublic()
     {
@@ -391,23 +412,17 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         $files = file2array('netscape_basic.htm');
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
-        );
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(0, count_private($this->linkDb));
-        $this->assertEquals(
-            0,
-            $this->linkDb[0]['private']
-        );
-        $this->assertEquals(
-            0,
-            $this->linkDb[1]['private']
+            .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
         );
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
+        $this->assertFalse($this->bookmarkService->get(0)->isPrivate());
+        $this->assertFalse($this->bookmarkService->get(1)->isPrivate());
     }
 
     /**
-     * Import links as private
+     * Import bookmarks as private
      */
     public function testImportAsPrivate()
     {
@@ -415,45 +430,34 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         $files = file2array('netscape_basic.htm');
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
-        );
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(2, count_private($this->linkDb));
-        $this->assertEquals(
-            1,
-            $this->linkDb['0']['private']
-        );
-        $this->assertEquals(
-            1,
-            $this->linkDb['1']['private']
+            .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
         );
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
+        $this->assertTrue($this->bookmarkService->get(0)->isPrivate());
+        $this->assertTrue($this->bookmarkService->get(1)->isPrivate());
     }
 
     /**
-     * Overwrite private links so they become public
+     * Overwrite private bookmarks so they become public
      */
     public function testOverwriteAsPublic()
     {
         $files = file2array('netscape_basic.htm');
 
-        // import links as private
+        // import bookmarks as private
         $post = array('privacy' => 'private');
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
-        );
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(2, count_private($this->linkDb));
-        $this->assertEquals(
-            1,
-            $this->linkDb[0]['private']
-        );
-        $this->assertEquals(
-            1,
-            $this->linkDb[1]['private']
+            .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
         );
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
+        $this->assertTrue($this->bookmarkService->get(0)->isPrivate());
+        $this->assertTrue($this->bookmarkService->get(1)->isPrivate());
+
         // re-import as public, enable overwriting
         $post = array(
             'privacy' => 'public',
@@ -461,45 +465,33 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         );
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 2 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
-        );
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(0, count_private($this->linkDb));
-        $this->assertEquals(
-            0,
-            $this->linkDb[0]['private']
-        );
-        $this->assertEquals(
-            0,
-            $this->linkDb[1]['private']
+            .' 2 bookmarks imported, 2 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
         );
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
+        $this->assertFalse($this->bookmarkService->get(0)->isPrivate());
+        $this->assertFalse($this->bookmarkService->get(1)->isPrivate());
     }
 
     /**
-     * Overwrite public links so they become private
+     * Overwrite public bookmarks so they become private
      */
     public function testOverwriteAsPrivate()
     {
         $files = file2array('netscape_basic.htm');
 
-        // import links as public
+        // import bookmarks as public
         $post = array('privacy' => 'public');
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
-        );
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(0, count_private($this->linkDb));
-        $this->assertEquals(
-            0,
-            $this->linkDb['0']['private']
-        );
-        $this->assertEquals(
-            0,
-            $this->linkDb['1']['private']
+            .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
         );
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
+        $this->assertFalse($this->bookmarkService->get(0)->isPrivate());
+        $this->assertFalse($this->bookmarkService->get(1)->isPrivate());
 
         // re-import as private, enable overwriting
         $post = array(
@@ -508,23 +500,17 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         );
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 2 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
-        );
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(2, count_private($this->linkDb));
-        $this->assertEquals(
-            1,
-            $this->linkDb['0']['private']
-        );
-        $this->assertEquals(
-            1,
-            $this->linkDb['1']['private']
+            .' 2 bookmarks imported, 2 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
         );
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(2, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
+        $this->assertTrue($this->bookmarkService->get(0)->isPrivate());
+        $this->assertTrue($this->bookmarkService->get(1)->isPrivate());
     }
 
     /**
-     * Attept to import the same links twice without enabling overwriting
+     * Attept to import the same bookmarks twice without enabling overwriting
      */
     public function testSkipOverwrite()
     {
@@ -532,21 +518,21 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
         $files = file2array('netscape_basic.htm');
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
+            .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
         );
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(0, count_private($this->linkDb));
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
 
         // re-import as private, DO NOT enable overwriting
         $post = array('privacy' => 'private');
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 0 links imported, 0 links overwritten, 2 links skipped.',
-            NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
+            .' 0 bookmarks imported, 0 bookmarks overwritten, 2 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
         );
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(0, count_private($this->linkDb));
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
     }
 
     /**
@@ -556,24 +542,18 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
     {
         $post = array(
             'privacy' => 'public',
-            'default_tags' => 'tag1,tag2 tag3'
+            'default_tags' => 'tag1 tag2 tag3'
         );
         $files = file2array('netscape_basic.htm');
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
-        );
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(0, count_private($this->linkDb));
-        $this->assertEquals(
-            'tag1 tag2 tag3 private secret',
-            $this->linkDb['0']['tags']
-        );
-        $this->assertEquals(
-            'tag1 tag2 tag3 public hello world',
-            $this->linkDb['1']['tags']
+            .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
         );
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
+        $this->assertEquals('tag1 tag2 tag3 private secret', $this->bookmarkService->get(0)->getTagsString());
+        $this->assertEquals('tag1 tag2 tag3 public hello world', $this->bookmarkService->get(1)->getTagsString());
     }
 
     /**
@@ -583,55 +563,83 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
     {
         $post = array(
             'privacy' => 'public',
-            'default_tags' => 'tag1&,tag2 "tag3"'
+            'default_tags' => 'tag1& tag2 "tag3"'
         );
         $files = file2array('netscape_basic.htm');
         $this->assertStringMatchesFormat(
             'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
-            .' 2 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
+            .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
         );
-        $this->assertEquals(2, count($this->linkDb));
-        $this->assertEquals(0, count_private($this->linkDb));
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
         $this->assertEquals(
             'tag1&amp; tag2 &quot;tag3&quot; private secret',
-            $this->linkDb['0']['tags']
+            $this->bookmarkService->get(0)->getTagsString()
         );
         $this->assertEquals(
             'tag1&amp; tag2 &quot;tag3&quot; public hello world',
-            $this->linkDb['1']['tags']
+            $this->bookmarkService->get(1)->getTagsString()
         );
     }
 
     /**
-     * Ensure each imported bookmark has a unique id
-     *
-     * See https://github.com/shaarli/Shaarli/issues/351
+     * Add user-specified tags to all imported bookmarks
      */
-    public function testImportSameDate()
+    public function testSetDefaultTagsWithCustomSeparator()
     {
-        $files = file2array('same_date.htm');
+        $separator = '@';
+        $this->conf->set('general.tags_separator', $separator);
+        $post = [
+            'privacy' => 'public',
+            'default_tags' => 'tag1@tag2@tag3@multiple words tag'
+        ];
+        $files = file2array('netscape_basic.htm');
         $this->assertStringMatchesFormat(
-            'File same_date.htm (453 bytes) was successfully processed in %d seconds:'
-            .' 3 links imported, 0 links overwritten, 0 links skipped.',
-            NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf, $this->history)
+            'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
+            .' 2 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import($post, $files)
+        );
+        $this->assertEquals(2, $this->bookmarkService->count());
+        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
+        $this->assertEquals(
+            'tag1@tag2@tag3@multiple words tag@private@secret',
+            $this->bookmarkService->get(0)->getTagsString($separator)
         );
-        $this->assertEquals(3, count($this->linkDb));
-        $this->assertEquals(0, count_private($this->linkDb));
         $this->assertEquals(
-            0,
-            $this->linkDb[0]['id']
+            ['tag1', 'tag2', 'tag3', 'multiple words tag', 'private', 'secret'],
+            $this->bookmarkService->get(0)->getTags()
         );
         $this->assertEquals(
-            1,
-            $this->linkDb[1]['id']
+            'tag1@tag2@tag3@multiple words tag@public@hello@world',
+            $this->bookmarkService->get(1)->getTagsString($separator)
         );
         $this->assertEquals(
-            2,
-            $this->linkDb[2]['id']
+            ['tag1', 'tag2', 'tag3', 'multiple words tag', 'public', 'hello', 'world'],
+            $this->bookmarkService->get(1)->getTags()
         );
     }
 
+    /**
+     * Ensure each imported bookmark has a unique id
+     *
+     * See https://github.com/shaarli/Shaarli/issues/351
+     */
+    public function testImportSameDate()
+    {
+        $files = file2array('same_date.htm');
+        $this->assertStringMatchesFormat(
+            'File same_date.htm (453 bytes) was successfully processed in %d seconds:'
+            .' 3 bookmarks imported, 0 bookmarks overwritten, 0 bookmarks skipped.',
+            $this->netscapeBookmarkUtils->import(array(), $files)
+        );
+        $this->assertEquals(3, $this->bookmarkService->count());
+        $this->assertEquals(0, $this->bookmarkService->count(BookmarkFilter::$PRIVATE));
+        $this->assertEquals(0, $this->bookmarkService->get(0)->getId());
+        $this->assertEquals(1, $this->bookmarkService->get(1)->getId());
+        $this->assertEquals(2, $this->bookmarkService->get(2)->getId());
+    }
+
     public function testImportCreateUpdateHistory()
     {
         $post = [
@@ -639,14 +647,14 @@ class BookmarkImportTest extends \PHPUnit\Framework\TestCase
             'overwrite' => 'true',
         ];
         $files = file2array('netscape_basic.htm');
-        NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history);
+        $this->netscapeBookmarkUtils->import($post, $files);
         $history = $this->history->getHistory();
         $this->assertEquals(1, count($history));
         $this->assertEquals(History::IMPORT, $history[0]['event']);
         $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']);
 
         // re-import as private, enable overwriting
-        NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history);
+        $this->netscapeBookmarkUtils->import($post, $files);
         $history = $this->history->getHistory();
         $this->assertEquals(2, count($history));
         $this->assertEquals(History::IMPORT, $history[0]['event']);