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