from typing import List from unittest.mock import patch from django.test import TestCase, override_settings from django.utils import timezone from bookmarks.models import Bookmark, Tag, parse_tag_string from bookmarks.services import tasks from bookmarks.services.importer import import_netscape_html, ImportOptions from bookmarks.tests.helpers import ( BookmarkFactoryMixin, ImportTestMixin, BookmarkHtmlTag, disable_logging, ) from bookmarks.utils import parse_timestamp class ImporterTestCase(TestCase, BookmarkFactoryMixin, ImportTestMixin): def assertBookmarksImported(self, html_tags: List[BookmarkHtmlTag]): for html_tag in html_tags: bookmark = Bookmark.objects.get(url=html_tag.href) self.assertIsNotNone(bookmark) self.assertEqual(bookmark.title, html_tag.title) self.assertEqual(bookmark.description, html_tag.description) self.assertEqual(bookmark.date_added, parse_timestamp(html_tag.add_date)) self.assertEqual(bookmark.unread, html_tag.to_read) self.assertEqual(bookmark.shared, not html_tag.private) tag_names = parse_tag_string(html_tag.tags) # Check assigned tags for tag_name in tag_names: tag = next( (tag for tag in bookmark.tags.all() if tag.name == tag_name), None ) self.assertIsNotNone(tag) def test_import(self): html_tags = [ BookmarkHtmlTag( href="https://example.com", title="Example title", description="Example description", add_date="1", tags="example-tag", ), BookmarkHtmlTag( href="https://example.com/foo", title="Foo title", description="", add_date="2", tags="", ), BookmarkHtmlTag( href="https://example.com/bar", title="Bar title", description="Bar description", add_date="3", tags="bar-tag, other-tag", ), BookmarkHtmlTag( href="https://example.com/baz", title="Baz title", description="Baz description", add_date="4", to_read=True, ), ] import_html = self.render_html(tags=html_tags) result = import_netscape_html(import_html, self.get_or_create_test_user()) # Check result self.assertEqual(result.total, 4) self.assertEqual(result.success, 4) self.assertEqual(result.failed, 0) # Check bookmarks bookmarks = Bookmark.objects.all() self.assertEqual(len(bookmarks), 4) self.assertBookmarksImported(html_tags) def test_synchronize(self): # Initial import html_tags = [ BookmarkHtmlTag( href="https://example.com", title="Example title", description="Example description", add_date="1", tags="example-tag", ), BookmarkHtmlTag( href="https://example.com/foo", title="Foo title", description="", add_date="2", tags="", ), BookmarkHtmlTag( href="https://example.com/bar", title="Bar title", description="Bar description", add_date="3", tags="bar-tag, other-tag", ), BookmarkHtmlTag( href="https://example.com/unread", title="Unread title", description="Unread description", add_date="3", to_read=True, ), BookmarkHtmlTag( href="https://example.com/private", title="Private title", description="Private description", add_date="4", private=True, ), ] import_html = self.render_html(tags=html_tags) import_netscape_html(import_html, self.get_or_create_test_user()) # Check bookmarks bookmarks = Bookmark.objects.all() self.assertEqual(len(bookmarks), 5) self.assertBookmarksImported(html_tags) # Change data, add some new data html_tags = [ BookmarkHtmlTag( href="https://example.com", title="Updated Example title", description="Updated Example description", add_date="111", tags="updated-example-tag", ), BookmarkHtmlTag( href="https://example.com/foo", title="Updated Foo title", description="Updated Foo description", add_date="222", tags="new-tag", ), BookmarkHtmlTag( href="https://example.com/bar", title="Updated Bar title", description="Updated Bar description", add_date="333", tags="updated-bar-tag, updated-other-tag", ), BookmarkHtmlTag( href="https://example.com/unread", title="Unread title", description="Unread description", add_date="3", to_read=False, ), BookmarkHtmlTag( href="https://example.com/private", title="Private title", description="Private description", add_date="4", private=False, ), BookmarkHtmlTag(href="https://baz.com", add_date="444", tags="baz-tag"), ] # Import updated data import_html = self.render_html(tags=html_tags) result = import_netscape_html( import_html, self.get_or_create_test_user(), ImportOptions(map_private_flag=True), ) # Check result self.assertEqual(result.total, 6) self.assertEqual(result.success, 6) self.assertEqual(result.failed, 0) # Check bookmarks bookmarks = Bookmark.objects.all() self.assertEqual(len(bookmarks), 6) self.assertBookmarksImported(html_tags) def test_import_with_some_invalid_bookmarks(self): html_tags = [ BookmarkHtmlTag(href="https://example.com"), # Invalid URL BookmarkHtmlTag(href="foo.com"), # No URL BookmarkHtmlTag(), ] import_html = self.render_html(tags=html_tags) result = import_netscape_html(import_html, self.get_or_create_test_user()) # Check result self.assertEqual(result.total, 3) self.assertEqual(result.success, 1) self.assertEqual(result.failed, 2) # Check bookmarks bookmarks = Bookmark.objects.all() self.assertEqual(len(bookmarks), 1) self.assertBookmarksImported(html_tags[1:1]) def test_import_invalid_bookmark_does_not_associate_tags(self): html_tags = [ # No URL BookmarkHtmlTag(tags="tag1, tag2, tag3"), ] import_html = self.render_html(tags=html_tags) # Sqlite silently ignores relationships that have a non-persisted bookmark, # thus testing if the bulk create receives no relationships BookmarkToTagRelationShip = Bookmark.tags.through with patch.object( BookmarkToTagRelationShip.objects, "bulk_create" ) as mock_bulk_create: import_netscape_html(import_html, self.get_or_create_test_user()) mock_bulk_create.assert_called_once_with([], ignore_conflicts=True) def test_import_tags(self): html_tags = [ BookmarkHtmlTag(href="https://example.com", tags="tag1"), BookmarkHtmlTag(href="https://foo.com", tags="tag2"), BookmarkHtmlTag(href="https://bar.com", tags="tag3"), ] import_html = self.render_html(tags=html_tags) import_netscape_html(import_html, self.get_or_create_test_user()) self.assertEqual(Tag.objects.count(), 3) def test_create_missing_tags(self): html_tags = [ BookmarkHtmlTag(href="https://example.com", tags="tag1"), BookmarkHtmlTag(href="https://foo.com", tags="tag2"), BookmarkHtmlTag(href="https://bar.com", tags="tag3"), ] import_html = self.render_html(tags=html_tags) import_netscape_html(import_html, self.get_or_create_test_user()) html_tags.append(BookmarkHtmlTag(href="https://baz.com", tags="tag4")) import_html = self.render_html(tags=html_tags) import_netscape_html(import_html, self.get_or_create_test_user()) self.assertEqual(Tag.objects.count(), 4) def test_create_missing_tags_does_not_duplicate_tags(self): html_tags = [ BookmarkHtmlTag(href="https://example.com", tags="tag1"), BookmarkHtmlTag(href="https://foo.com", tags="tag1"), BookmarkHtmlTag(href="https://bar.com", tags="tag1"), ] import_html = self.render_html(tags=html_tags) import_netscape_html(import_html, self.get_or_create_test_user()) self.assertEqual(Tag.objects.count(), 1) def test_should_append_tags_to_bookmark_when_reimporting_with_different_tags(self): html_tags = [ BookmarkHtmlTag(href="https://example.com", tags="tag1"), ] import_html = self.render_html(tags=html_tags) import_netscape_html(import_html, self.get_or_create_test_user()) html_tags.append(BookmarkHtmlTag(href="https://example.com", tags="tag2, tag3")) import_html = self.render_html(tags=html_tags) import_netscape_html(import_html, self.get_or_create_test_user()) self.assertEqual(Bookmark.objects.count(), 1) self.assertEqual(Bookmark.objects.all()[0].tags.all().count(), 3) @override_settings(USE_TZ=False) def test_use_current_date_when_no_add_date(self): test_html = self.render_html( tags_html=f"""