3 require_once 'application/NetscapeBookmarkUtils.php';
5 use Shaarli\Config\ConfigManager
;
8 * Utility function to load a file's metadata in a $_FILES-like array
10 * @param string $filename Basename of the file
12 * @return array A $_FILES-like array
14 function file2array($filename)
17 'filetoupload' => array(
19 'tmp_name' => __DIR__
. '/input/' . $filename,
20 'size' => filesize(__DIR__
. '/input/' . $filename)
27 * Netscape bookmark import
29 class BookmarkImportTest
extends PHPUnit_Framework_TestCase
32 * @var string datastore to test write operations
34 protected static $testDatastore = 'sandbox/datastore.php';
37 * @var string History file path
39 protected static $historyFilePath = 'sandbox/history.php';
42 * @var LinkDB private LinkDB instance
44 protected $linkDb = null;
47 * @var string Dummy page cache
49 protected $pagecache = 'tests';
52 * @var ConfigManager instance.
57 * @var History instance.
62 * @var string Save the current timezone.
64 protected static $defaultTimeZone;
66 public static function setUpBeforeClass()
68 self
::$defaultTimeZone = date_default_timezone_get();
69 // Timezone without DST for test consistency
70 date_default_timezone_set('Africa/Nairobi');
74 * Resets test data before each test
76 protected function setUp()
78 if (file_exists(self
::$testDatastore)) {
79 unlink(self
::$testDatastore);
81 // start with an empty datastore
82 file_put_contents(self
::$testDatastore, '<?php /* S7QysKquBQA= */ ?>');
83 $this->linkDb
= new LinkDB(self
::$testDatastore, true, false);
84 $this->conf
= new ConfigManager('tests/utils/config/configJson');
85 $this->conf
->set('resource.page_cache', $this->pagecache
);
86 $this->history
= new History(self
::$historyFilePath);
90 * Delete history file.
92 public function tearDown()
94 @unlink(self
::$historyFilePath);
97 public static function tearDownAfterClass()
99 date_default_timezone_set(self
::$defaultTimeZone);
103 * Attempt to import bookmarks from an empty file
105 public function testImportEmptyData()
107 $files = file2array('empty.htm');
109 'File empty.htm (0 bytes) has an unknown file format.'
110 .' Nothing was imported.',
111 NetscapeBookmarkUtils
::import(null, $files, null, $this->conf
, $this->history
)
113 $this->assertEquals(0, count($this->linkDb
));
117 * Attempt to import bookmarks from a file with no Doctype
119 public function testImportNoDoctype()
121 $files = file2array('no_doctype.htm');
123 'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.',
124 NetscapeBookmarkUtils
::import(null, $files, null, $this->conf
, $this->history
)
126 $this->assertEquals(0, count($this->linkDb
));
130 * Ensure IE dumps are supported
132 public function testImportInternetExplorerEncoding()
134 $files = file2array('internet_explorer_encoding.htm');
135 $this->assertStringMatchesFormat(
136 'File internet_explorer_encoding.htm (356 bytes) was successfully processed in %d seconds:'
137 .' 1 links imported, 0 links overwritten, 0 links skipped.',
138 NetscapeBookmarkUtils
::import([], $files, $this->linkDb
, $this->conf
, $this->history
)
140 $this->assertEquals(1, count($this->linkDb
));
141 $this->assertEquals(0, count_private($this->linkDb
));
146 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20160618_203944'),
147 'title' => 'Hg Init a Mercurial tutorial by Joel Spolsky',
148 'url' => 'http://hginit.com/',
152 'shorturl' => 'La37cg',
154 $this->linkDb
->getLinkFromUrl('http://hginit.com/')
159 * Import bookmarks nested in a folder hierarchy
161 public function testImportNested()
163 $files = file2array('netscape_nested.htm');
164 $this->assertStringMatchesFormat(
165 'File netscape_nested.htm (1337 bytes) was successfully processed in %d seconds:'
166 .' 8 links imported, 0 links overwritten, 0 links skipped.',
167 NetscapeBookmarkUtils
::import([], $files, $this->linkDb
, $this->conf
, $this->history
)
169 $this->assertEquals(8, count($this->linkDb
));
170 $this->assertEquals(2, count_private($this->linkDb
));
175 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20160225_235541'),
176 'title' => 'Nested 1',
177 'url' => 'http://nest.ed/1',
180 'tags' => 'tag1 tag2',
181 'shorturl' => 'KyDNKA',
183 $this->linkDb
->getLinkFromUrl('http://nest.ed/1')
188 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20160225_235542'),
189 'title' => 'Nested 1-1',
190 'url' => 'http://nest.ed/1-1',
193 'tags' => 'folder1 tag1 tag2',
194 'shorturl' => 'T2LnXg',
196 $this->linkDb
->getLinkFromUrl('http://nest.ed/1-1')
201 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20160225_235547'),
202 'title' => 'Nested 1-2',
203 'url' => 'http://nest.ed/1-2',
206 'tags' => 'folder1 tag3 tag4',
207 'shorturl' => '46SZxA',
209 $this->linkDb
->getLinkFromUrl('http://nest.ed/1-2')
214 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20160202_202222'),
215 'title' => 'Nested 2-1',
216 'url' => 'http://nest.ed/2-1',
217 'description' => 'First link of the second section',
220 'shorturl' => '4UHOSw',
222 $this->linkDb
->getLinkFromUrl('http://nest.ed/2-1')
227 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20160119_230227'),
228 'title' => 'Nested 2-2',
229 'url' => 'http://nest.ed/2-2',
230 'description' => 'Second link of the second section',
233 'shorturl' => 'yfzwbw',
235 $this->linkDb
->getLinkFromUrl('http://nest.ed/2-2')
240 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20160202_202222'),
241 'title' => 'Nested 3-1',
242 'url' => 'http://nest.ed/3-1',
245 'tags' => 'folder3 folder3-1 tag3',
246 'shorturl' => 'UwxIUQ',
248 $this->linkDb
->getLinkFromUrl('http://nest.ed/3-1')
253 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20160119_230227'),
254 'title' => 'Nested 3-2',
255 'url' => 'http://nest.ed/3-2',
258 'tags' => 'folder3 folder3-1',
259 'shorturl' => 'p8dyZg',
261 $this->linkDb
->getLinkFromUrl('http://nest.ed/3-2')
266 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20160229_111541'),
267 'title' => 'Nested 2',
268 'url' => 'http://nest.ed/2',
272 'shorturl' => 'Gt3Uug',
274 $this->linkDb
->getLinkFromUrl('http://nest.ed/2')
279 * Import bookmarks with the default privacy setting (reuse from file)
281 * The $_POST array is not set.
283 public function testImportDefaultPrivacyNoPost()
285 $files = file2array('netscape_basic.htm');
286 $this->assertStringMatchesFormat(
287 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
288 .' 2 links imported, 0 links overwritten, 0 links skipped.',
289 NetscapeBookmarkUtils
::import([], $files, $this->linkDb
, $this->conf
, $this->history
)
292 $this->assertEquals(2, count($this->linkDb
));
293 $this->assertEquals(1, count_private($this->linkDb
));
298 // Old link - UTC+4 (note that TZ in the import file is ignored).
299 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20001010_135536'),
300 'title' => 'Secret stuff',
301 'url' => 'https://private.tld',
302 'description' => "Super-secret stuff you're not supposed to know about",
304 'tags' => 'private secret',
305 'shorturl' => 'EokDtA',
307 $this->linkDb
->getLinkFromUrl('https://private.tld')
312 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20160225_235548'),
313 'title' => 'Public stuff',
314 'url' => 'http://public.tld',
317 'tags' => 'public hello world',
318 'shorturl' => 'Er9ddA',
320 $this->linkDb
->getLinkFromUrl('http://public.tld')
325 * Import bookmarks with the default privacy setting (reuse from file)
327 public function testImportKeepPrivacy()
329 $post = array('privacy' => 'default');
330 $files = file2array('netscape_basic.htm');
331 $this->assertStringMatchesFormat(
332 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
333 .' 2 links imported, 0 links overwritten, 0 links skipped.',
334 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
)
336 $this->assertEquals(2, count($this->linkDb
));
337 $this->assertEquals(1, count_private($this->linkDb
));
342 // Note that TZ in the import file is ignored.
343 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20001010_135536'),
344 'title' => 'Secret stuff',
345 'url' => 'https://private.tld',
346 'description' => "Super-secret stuff you're not supposed to know about",
348 'tags' => 'private secret',
349 'shorturl' => 'EokDtA',
351 $this->linkDb
->getLinkFromUrl('https://private.tld')
356 'created' => DateTime
::createFromFormat(LinkDB
::LINK_DATE_FORMAT
, '20160225_235548'),
357 'title' => 'Public stuff',
358 'url' => 'http://public.tld',
361 'tags' => 'public hello world',
362 'shorturl' => 'Er9ddA',
364 $this->linkDb
->getLinkFromUrl('http://public.tld')
369 * Import links as public
371 public function testImportAsPublic()
373 $post = array('privacy' => 'public');
374 $files = file2array('netscape_basic.htm');
375 $this->assertStringMatchesFormat(
376 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
377 .' 2 links imported, 0 links overwritten, 0 links skipped.',
378 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
)
380 $this->assertEquals(2, count($this->linkDb
));
381 $this->assertEquals(0, count_private($this->linkDb
));
384 $this->linkDb
[0]['private']
388 $this->linkDb
[1]['private']
393 * Import links as private
395 public function testImportAsPrivate()
397 $post = array('privacy' => 'private');
398 $files = file2array('netscape_basic.htm');
399 $this->assertStringMatchesFormat(
400 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
401 .' 2 links imported, 0 links overwritten, 0 links skipped.',
402 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
)
404 $this->assertEquals(2, count($this->linkDb
));
405 $this->assertEquals(2, count_private($this->linkDb
));
408 $this->linkDb
['0']['private']
412 $this->linkDb
['1']['private']
417 * Overwrite private links so they become public
419 public function testOverwriteAsPublic()
421 $files = file2array('netscape_basic.htm');
423 // import links as private
424 $post = array('privacy' => 'private');
425 $this->assertStringMatchesFormat(
426 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
427 .' 2 links imported, 0 links overwritten, 0 links skipped.',
428 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
)
430 $this->assertEquals(2, count($this->linkDb
));
431 $this->assertEquals(2, count_private($this->linkDb
));
434 $this->linkDb
[0]['private']
438 $this->linkDb
[1]['private']
440 // re-import as public, enable overwriting
442 'privacy' => 'public',
443 'overwrite' => 'true'
445 $this->assertStringMatchesFormat(
446 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
447 .' 2 links imported, 2 links overwritten, 0 links skipped.',
448 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
)
450 $this->assertEquals(2, count($this->linkDb
));
451 $this->assertEquals(0, count_private($this->linkDb
));
454 $this->linkDb
[0]['private']
458 $this->linkDb
[1]['private']
463 * Overwrite public links so they become private
465 public function testOverwriteAsPrivate()
467 $files = file2array('netscape_basic.htm');
469 // import links as public
470 $post = array('privacy' => 'public');
471 $this->assertStringMatchesFormat(
472 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
473 .' 2 links imported, 0 links overwritten, 0 links skipped.',
474 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
)
476 $this->assertEquals(2, count($this->linkDb
));
477 $this->assertEquals(0, count_private($this->linkDb
));
480 $this->linkDb
['0']['private']
484 $this->linkDb
['1']['private']
487 // re-import as private, enable overwriting
489 'privacy' => 'private',
490 'overwrite' => 'true'
492 $this->assertStringMatchesFormat(
493 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
494 .' 2 links imported, 2 links overwritten, 0 links skipped.',
495 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
)
497 $this->assertEquals(2, count($this->linkDb
));
498 $this->assertEquals(2, count_private($this->linkDb
));
501 $this->linkDb
['0']['private']
505 $this->linkDb
['1']['private']
510 * Attept to import the same links twice without enabling overwriting
512 public function testSkipOverwrite()
514 $post = array('privacy' => 'public');
515 $files = file2array('netscape_basic.htm');
516 $this->assertStringMatchesFormat(
517 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
518 .' 2 links imported, 0 links overwritten, 0 links skipped.',
519 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
)
521 $this->assertEquals(2, count($this->linkDb
));
522 $this->assertEquals(0, count_private($this->linkDb
));
524 // re-import as private, DO NOT enable overwriting
525 $post = array('privacy' => 'private');
526 $this->assertStringMatchesFormat(
527 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
528 .' 0 links imported, 0 links overwritten, 2 links skipped.',
529 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
)
531 $this->assertEquals(2, count($this->linkDb
));
532 $this->assertEquals(0, count_private($this->linkDb
));
536 * Add user-specified tags to all imported bookmarks
538 public function testSetDefaultTags()
541 'privacy' => 'public',
542 'default_tags' => 'tag1,tag2 tag3'
544 $files = file2array('netscape_basic.htm');
545 $this->assertStringMatchesFormat(
546 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
547 .' 2 links imported, 0 links overwritten, 0 links skipped.',
548 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
)
550 $this->assertEquals(2, count($this->linkDb
));
551 $this->assertEquals(0, count_private($this->linkDb
));
553 'tag1 tag2 tag3 private secret',
554 $this->linkDb
['0']['tags']
557 'tag1 tag2 tag3 public hello world',
558 $this->linkDb
['1']['tags']
563 * The user-specified tags contain characters to be escaped
565 public function testSanitizeDefaultTags()
568 'privacy' => 'public',
569 'default_tags' => 'tag1&,tag2 "tag3"'
571 $files = file2array('netscape_basic.htm');
572 $this->assertStringMatchesFormat(
573 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
574 .' 2 links imported, 0 links overwritten, 0 links skipped.',
575 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
)
577 $this->assertEquals(2, count($this->linkDb
));
578 $this->assertEquals(0, count_private($this->linkDb
));
580 'tag1& tag2 "tag3" private secret',
581 $this->linkDb
['0']['tags']
584 'tag1& tag2 "tag3" public hello world',
585 $this->linkDb
['1']['tags']
590 * Ensure each imported bookmark has a unique id
592 * See https://github.com/shaarli/Shaarli/issues/351
594 public function testImportSameDate()
596 $files = file2array('same_date.htm');
597 $this->assertStringMatchesFormat(
598 'File same_date.htm (453 bytes) was successfully processed in %d seconds:'
599 .' 3 links imported, 0 links overwritten, 0 links skipped.',
600 NetscapeBookmarkUtils
::import(array(), $files, $this->linkDb
, $this->conf
, $this->history
)
602 $this->assertEquals(3, count($this->linkDb
));
603 $this->assertEquals(0, count_private($this->linkDb
));
606 $this->linkDb
[0]['id']
610 $this->linkDb
[1]['id']
614 $this->linkDb
[2]['id']
618 public function testImportCreateUpdateHistory()
621 'privacy' => 'public',
622 'overwrite' => 'true',
624 $files = file2array('netscape_basic.htm');
625 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
);
626 $history = $this->history
->getHistory();
627 $this->assertEquals(1, count($history));
628 $this->assertEquals(History
::IMPORT
, $history[0]['event']);
629 $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']);
631 // re-import as private, enable overwriting
632 NetscapeBookmarkUtils
::import($post, $files, $this->linkDb
, $this->conf
, $this->history
);
633 $history = $this->history
->getHistory();
634 $this->assertEquals(2, count($history));
635 $this->assertEquals(History
::IMPORT
, $history[0]['event']);
636 $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']);
637 $this->assertEquals(History
::IMPORT
, $history[1]['event']);
638 $this->assertTrue(new DateTime('-5 seconds') < $history[1]['datetime']);