]> git.immae.eu Git - github/shaarli/Shaarli.git/blob - tests/NetscapeBookmarkUtils/BookmarkImportTest.php
namespacing: \Shaarli\Bookmark\LinkDB
[github/shaarli/Shaarli.git] / tests / NetscapeBookmarkUtils / BookmarkImportTest.php
1 <?php
2
3 require_once 'application/NetscapeBookmarkUtils.php';
4
5 use Shaarli\Bookmark\LinkDB;
6 use Shaarli\Config\ConfigManager;
7 use Shaarli\History;
8
9 /**
10 * Utility function to load a file's metadata in a $_FILES-like array
11 *
12 * @param string $filename Basename of the file
13 *
14 * @return array A $_FILES-like array
15 */
16 function file2array($filename)
17 {
18 return array(
19 'filetoupload' => array(
20 'name' => $filename,
21 'tmp_name' => __DIR__ . '/input/' . $filename,
22 'size' => filesize(__DIR__ . '/input/' . $filename)
23 )
24 );
25 }
26
27
28 /**
29 * Netscape bookmark import
30 */
31 class BookmarkImportTest extends PHPUnit_Framework_TestCase
32 {
33 /**
34 * @var string datastore to test write operations
35 */
36 protected static $testDatastore = 'sandbox/datastore.php';
37
38 /**
39 * @var string History file path
40 */
41 protected static $historyFilePath = 'sandbox/history.php';
42
43 /**
44 * @var LinkDB private LinkDB instance
45 */
46 protected $linkDb = null;
47
48 /**
49 * @var string Dummy page cache
50 */
51 protected $pagecache = 'tests';
52
53 /**
54 * @var ConfigManager instance.
55 */
56 protected $conf;
57
58 /**
59 * @var History instance.
60 */
61 protected $history;
62
63 /**
64 * @var string Save the current timezone.
65 */
66 protected static $defaultTimeZone;
67
68 public static function setUpBeforeClass()
69 {
70 self::$defaultTimeZone = date_default_timezone_get();
71 // Timezone without DST for test consistency
72 date_default_timezone_set('Africa/Nairobi');
73 }
74
75 /**
76 * Resets test data before each test
77 */
78 protected function setUp()
79 {
80 if (file_exists(self::$testDatastore)) {
81 unlink(self::$testDatastore);
82 }
83 // start with an empty datastore
84 file_put_contents(self::$testDatastore, '<?php /* S7QysKquBQA= */ ?>');
85 $this->linkDb = new LinkDB(self::$testDatastore, true, false);
86 $this->conf = new ConfigManager('tests/utils/config/configJson');
87 $this->conf->set('resource.page_cache', $this->pagecache);
88 $this->history = new History(self::$historyFilePath);
89 }
90
91 /**
92 * Delete history file.
93 */
94 public function tearDown()
95 {
96 @unlink(self::$historyFilePath);
97 }
98
99 public static function tearDownAfterClass()
100 {
101 date_default_timezone_set(self::$defaultTimeZone);
102 }
103
104 /**
105 * Attempt to import bookmarks from an empty file
106 */
107 public function testImportEmptyData()
108 {
109 $files = file2array('empty.htm');
110 $this->assertEquals(
111 'File empty.htm (0 bytes) has an unknown file format.'
112 .' Nothing was imported.',
113 NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history)
114 );
115 $this->assertEquals(0, count($this->linkDb));
116 }
117
118 /**
119 * Attempt to import bookmarks from a file with no Doctype
120 */
121 public function testImportNoDoctype()
122 {
123 $files = file2array('no_doctype.htm');
124 $this->assertEquals(
125 'File no_doctype.htm (350 bytes) has an unknown file format. Nothing was imported.',
126 NetscapeBookmarkUtils::import(null, $files, null, $this->conf, $this->history)
127 );
128 $this->assertEquals(0, count($this->linkDb));
129 }
130
131 /**
132 * Attempt to import bookmarks from a file with a lowercase Doctype
133 */
134 public function testImportLowecaseDoctype()
135 {
136 $files = file2array('lowercase_doctype.htm');
137 $this->assertStringMatchesFormat(
138 'File lowercase_doctype.htm (386 bytes) was successfully processed in %d seconds:'
139 .' 2 links imported, 0 links overwritten, 0 links skipped.',
140 NetscapeBookmarkUtils::import(null, $files, $this->linkDb, $this->conf, $this->history)
141 );
142 $this->assertEquals(2, count($this->linkDb));
143 }
144
145
146 /**
147 * Ensure IE dumps are supported
148 */
149 public function testImportInternetExplorerEncoding()
150 {
151 $files = file2array('internet_explorer_encoding.htm');
152 $this->assertStringMatchesFormat(
153 'File internet_explorer_encoding.htm (356 bytes) was successfully processed in %d seconds:'
154 .' 1 links imported, 0 links overwritten, 0 links skipped.',
155 NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history)
156 );
157 $this->assertEquals(1, count($this->linkDb));
158 $this->assertEquals(0, count_private($this->linkDb));
159
160 $this->assertEquals(
161 array(
162 'id' => 0,
163 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160618_203944'),
164 'title' => 'Hg Init a Mercurial tutorial by Joel Spolsky',
165 'url' => 'http://hginit.com/',
166 'description' => '',
167 'private' => 0,
168 'tags' => '',
169 'shorturl' => 'La37cg',
170 ),
171 $this->linkDb->getLinkFromUrl('http://hginit.com/')
172 );
173 }
174
175 /**
176 * Import bookmarks nested in a folder hierarchy
177 */
178 public function testImportNested()
179 {
180 $files = file2array('netscape_nested.htm');
181 $this->assertStringMatchesFormat(
182 'File netscape_nested.htm (1337 bytes) was successfully processed in %d seconds:'
183 .' 8 links imported, 0 links overwritten, 0 links skipped.',
184 NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history)
185 );
186 $this->assertEquals(8, count($this->linkDb));
187 $this->assertEquals(2, count_private($this->linkDb));
188
189 $this->assertEquals(
190 array(
191 'id' => 0,
192 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235541'),
193 'title' => 'Nested 1',
194 'url' => 'http://nest.ed/1',
195 'description' => '',
196 'private' => 0,
197 'tags' => 'tag1 tag2',
198 'shorturl' => 'KyDNKA',
199 ),
200 $this->linkDb->getLinkFromUrl('http://nest.ed/1')
201 );
202 $this->assertEquals(
203 array(
204 'id' => 1,
205 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235542'),
206 'title' => 'Nested 1-1',
207 'url' => 'http://nest.ed/1-1',
208 'description' => '',
209 'private' => 0,
210 'tags' => 'folder1 tag1 tag2',
211 'shorturl' => 'T2LnXg',
212 ),
213 $this->linkDb->getLinkFromUrl('http://nest.ed/1-1')
214 );
215 $this->assertEquals(
216 array(
217 'id' => 2,
218 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235547'),
219 'title' => 'Nested 1-2',
220 'url' => 'http://nest.ed/1-2',
221 'description' => '',
222 'private' => 0,
223 'tags' => 'folder1 tag3 tag4',
224 'shorturl' => '46SZxA',
225 ),
226 $this->linkDb->getLinkFromUrl('http://nest.ed/1-2')
227 );
228 $this->assertEquals(
229 array(
230 'id' => 3,
231 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'),
232 'title' => 'Nested 2-1',
233 'url' => 'http://nest.ed/2-1',
234 'description' => 'First link of the second section',
235 'private' => 1,
236 'tags' => 'folder2',
237 'shorturl' => '4UHOSw',
238 ),
239 $this->linkDb->getLinkFromUrl('http://nest.ed/2-1')
240 );
241 $this->assertEquals(
242 array(
243 'id' => 4,
244 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'),
245 'title' => 'Nested 2-2',
246 'url' => 'http://nest.ed/2-2',
247 'description' => 'Second link of the second section',
248 'private' => 1,
249 'tags' => 'folder2',
250 'shorturl' => 'yfzwbw',
251 ),
252 $this->linkDb->getLinkFromUrl('http://nest.ed/2-2')
253 );
254 $this->assertEquals(
255 array(
256 'id' => 5,
257 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160202_202222'),
258 'title' => 'Nested 3-1',
259 'url' => 'http://nest.ed/3-1',
260 'description' => '',
261 'private' => 0,
262 'tags' => 'folder3 folder3-1 tag3',
263 'shorturl' => 'UwxIUQ',
264 ),
265 $this->linkDb->getLinkFromUrl('http://nest.ed/3-1')
266 );
267 $this->assertEquals(
268 array(
269 'id' => 6,
270 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160119_230227'),
271 'title' => 'Nested 3-2',
272 'url' => 'http://nest.ed/3-2',
273 'description' => '',
274 'private' => 0,
275 'tags' => 'folder3 folder3-1',
276 'shorturl' => 'p8dyZg',
277 ),
278 $this->linkDb->getLinkFromUrl('http://nest.ed/3-2')
279 );
280 $this->assertEquals(
281 array(
282 'id' => 7,
283 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160229_111541'),
284 'title' => 'Nested 2',
285 'url' => 'http://nest.ed/2',
286 'description' => '',
287 'private' => 0,
288 'tags' => 'tag4',
289 'shorturl' => 'Gt3Uug',
290 ),
291 $this->linkDb->getLinkFromUrl('http://nest.ed/2')
292 );
293 }
294
295 /**
296 * Import bookmarks with the default privacy setting (reuse from file)
297 *
298 * The $_POST array is not set.
299 */
300 public function testImportDefaultPrivacyNoPost()
301 {
302 $files = file2array('netscape_basic.htm');
303 $this->assertStringMatchesFormat(
304 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
305 .' 2 links imported, 0 links overwritten, 0 links skipped.',
306 NetscapeBookmarkUtils::import([], $files, $this->linkDb, $this->conf, $this->history)
307 );
308
309 $this->assertEquals(2, count($this->linkDb));
310 $this->assertEquals(1, count_private($this->linkDb));
311
312 $this->assertEquals(
313 array(
314 'id' => 0,
315 // Old link - UTC+4 (note that TZ in the import file is ignored).
316 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20001010_135536'),
317 'title' => 'Secret stuff',
318 'url' => 'https://private.tld',
319 'description' => "Super-secret stuff you're not supposed to know about",
320 'private' => 1,
321 'tags' => 'private secret',
322 'shorturl' => 'EokDtA',
323 ),
324 $this->linkDb->getLinkFromUrl('https://private.tld')
325 );
326 $this->assertEquals(
327 array(
328 'id' => 1,
329 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'),
330 'title' => 'Public stuff',
331 'url' => 'http://public.tld',
332 'description' => '',
333 'private' => 0,
334 'tags' => 'public hello world',
335 'shorturl' => 'Er9ddA',
336 ),
337 $this->linkDb->getLinkFromUrl('http://public.tld')
338 );
339 }
340
341 /**
342 * Import bookmarks with the default privacy setting (reuse from file)
343 */
344 public function testImportKeepPrivacy()
345 {
346 $post = array('privacy' => 'default');
347 $files = file2array('netscape_basic.htm');
348 $this->assertStringMatchesFormat(
349 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
350 .' 2 links imported, 0 links overwritten, 0 links skipped.',
351 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
352 );
353 $this->assertEquals(2, count($this->linkDb));
354 $this->assertEquals(1, count_private($this->linkDb));
355
356 $this->assertEquals(
357 array(
358 'id' => 0,
359 // Note that TZ in the import file is ignored.
360 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20001010_135536'),
361 'title' => 'Secret stuff',
362 'url' => 'https://private.tld',
363 'description' => "Super-secret stuff you're not supposed to know about",
364 'private' => 1,
365 'tags' => 'private secret',
366 'shorturl' => 'EokDtA',
367 ),
368 $this->linkDb->getLinkFromUrl('https://private.tld')
369 );
370 $this->assertEquals(
371 array(
372 'id' => 1,
373 'created' => DateTime::createFromFormat(LinkDB::LINK_DATE_FORMAT, '20160225_235548'),
374 'title' => 'Public stuff',
375 'url' => 'http://public.tld',
376 'description' => '',
377 'private' => 0,
378 'tags' => 'public hello world',
379 'shorturl' => 'Er9ddA',
380 ),
381 $this->linkDb->getLinkFromUrl('http://public.tld')
382 );
383 }
384
385 /**
386 * Import links as public
387 */
388 public function testImportAsPublic()
389 {
390 $post = array('privacy' => 'public');
391 $files = file2array('netscape_basic.htm');
392 $this->assertStringMatchesFormat(
393 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
394 .' 2 links imported, 0 links overwritten, 0 links skipped.',
395 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
396 );
397 $this->assertEquals(2, count($this->linkDb));
398 $this->assertEquals(0, count_private($this->linkDb));
399 $this->assertEquals(
400 0,
401 $this->linkDb[0]['private']
402 );
403 $this->assertEquals(
404 0,
405 $this->linkDb[1]['private']
406 );
407 }
408
409 /**
410 * Import links as private
411 */
412 public function testImportAsPrivate()
413 {
414 $post = array('privacy' => 'private');
415 $files = file2array('netscape_basic.htm');
416 $this->assertStringMatchesFormat(
417 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
418 .' 2 links imported, 0 links overwritten, 0 links skipped.',
419 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
420 );
421 $this->assertEquals(2, count($this->linkDb));
422 $this->assertEquals(2, count_private($this->linkDb));
423 $this->assertEquals(
424 1,
425 $this->linkDb['0']['private']
426 );
427 $this->assertEquals(
428 1,
429 $this->linkDb['1']['private']
430 );
431 }
432
433 /**
434 * Overwrite private links so they become public
435 */
436 public function testOverwriteAsPublic()
437 {
438 $files = file2array('netscape_basic.htm');
439
440 // import links as private
441 $post = array('privacy' => 'private');
442 $this->assertStringMatchesFormat(
443 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
444 .' 2 links imported, 0 links overwritten, 0 links skipped.',
445 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
446 );
447 $this->assertEquals(2, count($this->linkDb));
448 $this->assertEquals(2, count_private($this->linkDb));
449 $this->assertEquals(
450 1,
451 $this->linkDb[0]['private']
452 );
453 $this->assertEquals(
454 1,
455 $this->linkDb[1]['private']
456 );
457 // re-import as public, enable overwriting
458 $post = array(
459 'privacy' => 'public',
460 'overwrite' => 'true'
461 );
462 $this->assertStringMatchesFormat(
463 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
464 .' 2 links imported, 2 links overwritten, 0 links skipped.',
465 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
466 );
467 $this->assertEquals(2, count($this->linkDb));
468 $this->assertEquals(0, count_private($this->linkDb));
469 $this->assertEquals(
470 0,
471 $this->linkDb[0]['private']
472 );
473 $this->assertEquals(
474 0,
475 $this->linkDb[1]['private']
476 );
477 }
478
479 /**
480 * Overwrite public links so they become private
481 */
482 public function testOverwriteAsPrivate()
483 {
484 $files = file2array('netscape_basic.htm');
485
486 // import links as public
487 $post = array('privacy' => 'public');
488 $this->assertStringMatchesFormat(
489 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
490 .' 2 links imported, 0 links overwritten, 0 links skipped.',
491 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
492 );
493 $this->assertEquals(2, count($this->linkDb));
494 $this->assertEquals(0, count_private($this->linkDb));
495 $this->assertEquals(
496 0,
497 $this->linkDb['0']['private']
498 );
499 $this->assertEquals(
500 0,
501 $this->linkDb['1']['private']
502 );
503
504 // re-import as private, enable overwriting
505 $post = array(
506 'privacy' => 'private',
507 'overwrite' => 'true'
508 );
509 $this->assertStringMatchesFormat(
510 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
511 .' 2 links imported, 2 links overwritten, 0 links skipped.',
512 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
513 );
514 $this->assertEquals(2, count($this->linkDb));
515 $this->assertEquals(2, count_private($this->linkDb));
516 $this->assertEquals(
517 1,
518 $this->linkDb['0']['private']
519 );
520 $this->assertEquals(
521 1,
522 $this->linkDb['1']['private']
523 );
524 }
525
526 /**
527 * Attept to import the same links twice without enabling overwriting
528 */
529 public function testSkipOverwrite()
530 {
531 $post = array('privacy' => 'public');
532 $files = file2array('netscape_basic.htm');
533 $this->assertStringMatchesFormat(
534 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
535 .' 2 links imported, 0 links overwritten, 0 links skipped.',
536 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
537 );
538 $this->assertEquals(2, count($this->linkDb));
539 $this->assertEquals(0, count_private($this->linkDb));
540
541 // re-import as private, DO NOT enable overwriting
542 $post = array('privacy' => 'private');
543 $this->assertStringMatchesFormat(
544 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
545 .' 0 links imported, 0 links overwritten, 2 links skipped.',
546 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
547 );
548 $this->assertEquals(2, count($this->linkDb));
549 $this->assertEquals(0, count_private($this->linkDb));
550 }
551
552 /**
553 * Add user-specified tags to all imported bookmarks
554 */
555 public function testSetDefaultTags()
556 {
557 $post = array(
558 'privacy' => 'public',
559 'default_tags' => 'tag1,tag2 tag3'
560 );
561 $files = file2array('netscape_basic.htm');
562 $this->assertStringMatchesFormat(
563 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
564 .' 2 links imported, 0 links overwritten, 0 links skipped.',
565 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
566 );
567 $this->assertEquals(2, count($this->linkDb));
568 $this->assertEquals(0, count_private($this->linkDb));
569 $this->assertEquals(
570 'tag1 tag2 tag3 private secret',
571 $this->linkDb['0']['tags']
572 );
573 $this->assertEquals(
574 'tag1 tag2 tag3 public hello world',
575 $this->linkDb['1']['tags']
576 );
577 }
578
579 /**
580 * The user-specified tags contain characters to be escaped
581 */
582 public function testSanitizeDefaultTags()
583 {
584 $post = array(
585 'privacy' => 'public',
586 'default_tags' => 'tag1&,tag2 "tag3"'
587 );
588 $files = file2array('netscape_basic.htm');
589 $this->assertStringMatchesFormat(
590 'File netscape_basic.htm (482 bytes) was successfully processed in %d seconds:'
591 .' 2 links imported, 0 links overwritten, 0 links skipped.',
592 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history)
593 );
594 $this->assertEquals(2, count($this->linkDb));
595 $this->assertEquals(0, count_private($this->linkDb));
596 $this->assertEquals(
597 'tag1&amp; tag2 &quot;tag3&quot; private secret',
598 $this->linkDb['0']['tags']
599 );
600 $this->assertEquals(
601 'tag1&amp; tag2 &quot;tag3&quot; public hello world',
602 $this->linkDb['1']['tags']
603 );
604 }
605
606 /**
607 * Ensure each imported bookmark has a unique id
608 *
609 * See https://github.com/shaarli/Shaarli/issues/351
610 */
611 public function testImportSameDate()
612 {
613 $files = file2array('same_date.htm');
614 $this->assertStringMatchesFormat(
615 'File same_date.htm (453 bytes) was successfully processed in %d seconds:'
616 .' 3 links imported, 0 links overwritten, 0 links skipped.',
617 NetscapeBookmarkUtils::import(array(), $files, $this->linkDb, $this->conf, $this->history)
618 );
619 $this->assertEquals(3, count($this->linkDb));
620 $this->assertEquals(0, count_private($this->linkDb));
621 $this->assertEquals(
622 0,
623 $this->linkDb[0]['id']
624 );
625 $this->assertEquals(
626 1,
627 $this->linkDb[1]['id']
628 );
629 $this->assertEquals(
630 2,
631 $this->linkDb[2]['id']
632 );
633 }
634
635 public function testImportCreateUpdateHistory()
636 {
637 $post = [
638 'privacy' => 'public',
639 'overwrite' => 'true',
640 ];
641 $files = file2array('netscape_basic.htm');
642 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history);
643 $history = $this->history->getHistory();
644 $this->assertEquals(1, count($history));
645 $this->assertEquals(History::IMPORT, $history[0]['event']);
646 $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']);
647
648 // re-import as private, enable overwriting
649 NetscapeBookmarkUtils::import($post, $files, $this->linkDb, $this->conf, $this->history);
650 $history = $this->history->getHistory();
651 $this->assertEquals(2, count($history));
652 $this->assertEquals(History::IMPORT, $history[0]['event']);
653 $this->assertTrue(new DateTime('-5 seconds') < $history[0]['datetime']);
654 $this->assertEquals(History::IMPORT, $history[1]['event']);
655 $this->assertTrue(new DateTime('-5 seconds') < $history[1]['datetime']);
656 }
657 }