"Fossies" - the Fresh Open Source Software Archive

Member "buku-4.3/tests/test_bukuDb.py" (31 Jan 2020, 49349 Bytes) of package /linux/privat/buku-4.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "test_bukuDb.py": 4.2.2_vs_4.3.

    1 #!/usr/bin/env python3
    2 #
    3 # Unit test cases for buku
    4 #
    5 import logging
    6 import math
    7 import os
    8 import re
    9 import shutil
   10 import sqlite3
   11 import sys
   12 import urllib
   13 import zipfile
   14 from genericpath import exists
   15 from tempfile import TemporaryDirectory, NamedTemporaryFile
   16 
   17 from unittest import mock
   18 import unittest
   19 import pytest
   20 import yaml
   21 from hypothesis import given, example, settings
   22 from hypothesis import strategies as st
   23 import vcr
   24 
   25 from buku import BukuDb, parse_tags, prompt
   26 
   27 
   28 logging.basicConfig()  # you need to initialize logging, otherwise you will not see anything from vcrpy
   29 vcr_log = logging.getLogger("vcr")
   30 vcr_log.setLevel(logging.INFO)
   31 
   32 TEST_TEMP_DIR_OBJ = TemporaryDirectory(prefix='bukutest_')
   33 TEST_TEMP_DIR_PATH = TEST_TEMP_DIR_OBJ.name
   34 TEST_TEMP_DBDIR_PATH = os.path.join(TEST_TEMP_DIR_PATH, 'buku')
   35 TEST_TEMP_DBFILE_PATH = os.path.join(TEST_TEMP_DBDIR_PATH, 'bookmarks.db')
   36 MAX_SQLITE_INT = int(math.pow(2, 63) - 1)
   37 
   38 TEST_BOOKMARKS = [
   39     ['http://slashdot.org',
   40      'SLASHDOT',
   41      parse_tags(['old,news']),
   42      "News for old nerds, stuff that doesn't matter"],
   43     ['http://www.zażółćgęśląjaźń.pl/',
   44      'ZAŻÓŁĆ',
   45      parse_tags(['zażółć,gęślą,jaźń']),
   46      "Testing UTF-8, zażółć gęślą jaźń."],
   47     ['http://example.com/',
   48      'test',
   49      parse_tags(['test,tes,est,es']),
   50      "a case for replace_tag test"],
   51 ]
   52 
   53 only_python_3_5 = pytest.mark.skipif(
   54     sys.version_info < (3, 5), reason="requires Python 3.5 or later")
   55 
   56 
   57 @pytest.fixture()
   58 def setup():
   59     os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
   60 
   61     # start every test from a clean state
   62     if exists(TEST_TEMP_DBFILE_PATH):
   63         os.remove(TEST_TEMP_DBFILE_PATH)
   64 
   65 
   66 class PrettySafeLoader(yaml.SafeLoader):   # pylint: disable=too-many-ancestors,too-few-public-methods
   67     def construct_python_tuple(self, node):
   68         return tuple(self.construct_sequence(node))
   69 
   70 
   71 PrettySafeLoader.add_constructor(
   72     u'tag:yaml.org,2002:python/tuple',
   73     PrettySafeLoader.construct_python_tuple)
   74 
   75 
   76 class TestBukuDb(unittest.TestCase):
   77 
   78     def setUp(self):
   79         os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
   80 
   81         # start every test from a clean state
   82         if exists(TEST_TEMP_DBFILE_PATH):
   83             os.remove(TEST_TEMP_DBFILE_PATH)
   84 
   85         self.bookmarks = TEST_BOOKMARKS
   86         self.bdb = BukuDb()
   87 
   88     def tearDown(self):
   89         os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
   90 
   91     @pytest.mark.non_tox
   92     def test_get_default_dbdir(self):
   93         dbdir_expected = TEST_TEMP_DBDIR_PATH
   94         dbdir_local_expected = os.path.join(os.path.expanduser('~'), '.local', 'share', 'buku')
   95         dbdir_relative_expected = os.path.abspath('.')
   96 
   97         # desktop linux
   98         self.assertEqual(dbdir_expected, BukuDb.get_default_dbdir())
   99 
  100         # desktop generic
  101         os.environ.pop('XDG_DATA_HOME')
  102         self.assertEqual(dbdir_local_expected, BukuDb.get_default_dbdir())
  103 
  104         # no desktop
  105 
  106         # -- home is defined differently on various platforms.
  107         # -- keep a copy and set it back once done
  108         originals = {}
  109         for env_var in ['HOME', 'HOMEPATH', 'HOMEDIR']:
  110             try:
  111                 originals[env_var] = os.environ.pop(env_var)
  112             except KeyError:
  113                 pass
  114         self.assertEqual(dbdir_relative_expected, BukuDb.get_default_dbdir())
  115         for key, value in list(originals.items()):
  116             os.environ[key] = value
  117 
  118     # # not sure how to test this in nondestructive manner
  119     # def test_move_legacy_dbfile(self):
  120     #     self.fail()
  121 
  122     def test_initdb(self):
  123         if exists(TEST_TEMP_DBFILE_PATH):
  124             os.remove(TEST_TEMP_DBFILE_PATH)
  125         self.assertIs(False, exists(TEST_TEMP_DBFILE_PATH))
  126         conn, curr = BukuDb.initdb()
  127         self.assertIsInstance(conn, sqlite3.Connection)
  128         self.assertIsInstance(curr, sqlite3.Cursor)
  129         self.assertIs(True, exists(TEST_TEMP_DBFILE_PATH))
  130         curr.close()
  131         conn.close()
  132 
  133     def test_get_rec_by_id(self):
  134         for bookmark in self.bookmarks:
  135             # adding bookmark from self.bookmarks
  136             self.bdb.add_rec(*bookmark)
  137 
  138         # the expected bookmark
  139         expected = (1, 'http://slashdot.org', 'SLASHDOT', ',news,old,',
  140                     "News for old nerds, stuff that doesn't matter", 0)
  141         bookmark_from_db = self.bdb.get_rec_by_id(1)
  142         # asserting bookmark matches expected
  143         self.assertEqual(expected, bookmark_from_db)
  144         # asserting None returned if index out of range
  145         self.assertIsNone(self.bdb.get_rec_by_id(len(self.bookmarks[0]) + 1))
  146 
  147     def test_get_rec_id(self):
  148         for idx, bookmark in enumerate(self.bookmarks):
  149             # adding bookmark from self.bookmarks to database
  150             self.bdb.add_rec(*bookmark)
  151             # asserting index is in order
  152             idx_from_db = self.bdb.get_rec_id(bookmark[0])
  153             self.assertEqual(idx + 1, idx_from_db)
  154 
  155         # asserting -1 is returned for nonexistent url
  156         idx_from_db = self.bdb.get_rec_id("http://nonexistent.url")
  157         self.assertEqual(-1, idx_from_db)
  158 
  159     def test_add_rec(self):
  160         for bookmark in self.bookmarks:
  161             # adding bookmark from self.bookmarks to database
  162             self.bdb.add_rec(*bookmark)
  163             # retrieving bookmark from database
  164             index = self.bdb.get_rec_id(bookmark[0])
  165             from_db = self.bdb.get_rec_by_id(index)
  166             self.assertIsNotNone(from_db)
  167             # comparing data
  168             for pair in zip(from_db[1:], bookmark):
  169                 self.assertEqual(*pair)
  170 
  171         # TODO: tags should be passed to the api as a sequence...
  172 
  173     def test_suggest_tags(self):
  174         for bookmark in self.bookmarks:
  175             self.bdb.add_rec(*bookmark)
  176 
  177         tagstr = ',test,old,'
  178         with mock.patch('builtins.input', return_value='1 2 3'):
  179             expected_results = ',es,est,news,old,test,'
  180             suggested_results = self.bdb.suggest_similar_tag(tagstr)
  181             self.assertEqual(expected_results, suggested_results)
  182 
  183         # returns user supplied tags if none are in the DB
  184         tagstr = ',uniquetag1,uniquetag2,'
  185         expected_results = tagstr
  186         suggested_results = self.bdb.suggest_similar_tag(tagstr)
  187         self.assertEqual(expected_results, suggested_results)
  188 
  189     def test_update_rec(self):
  190         old_values = self.bookmarks[0]
  191         new_values = self.bookmarks[1]
  192 
  193         # adding bookmark and getting index
  194         self.bdb.add_rec(*old_values)
  195         index = self.bdb.get_rec_id(old_values[0])
  196         # updating with new values
  197         self.bdb.update_rec(index, *new_values)
  198         # retrieving bookmark from database
  199         from_db = self.bdb.get_rec_by_id(index)
  200         self.assertIsNotNone(from_db)
  201         # checking if values are updated
  202         for pair in zip(from_db[1:], new_values):
  203             self.assertEqual(*pair)
  204 
  205     def test_append_tag_at_index(self):
  206         for bookmark in self.bookmarks:
  207             self.bdb.add_rec(*bookmark)
  208 
  209         # tags to add
  210         old_tags = self.bdb.get_rec_by_id(1)[3]
  211         new_tags = ",foo,bar,baz"
  212         self.bdb.append_tag_at_index(1, new_tags)
  213         # updated list of tags
  214         from_db = self.bdb.get_rec_by_id(1)[3]
  215 
  216         # checking if new tags were added to the bookmark
  217         self.assertTrue(split_and_test_membership(new_tags, from_db))
  218         # checking if old tags still exist
  219         self.assertTrue(split_and_test_membership(old_tags, from_db))
  220 
  221     def test_append_tag_at_all_indices(self):
  222         for bookmark in self.bookmarks:
  223             self.bdb.add_rec(*bookmark)
  224 
  225         # tags to add
  226         new_tags = ",foo,bar,baz"
  227         # record of original tags for each bookmark
  228         old_tagsets = {i: self.bdb.get_rec_by_id(i)[3] for i in inclusive_range(1, len(self.bookmarks))}
  229 
  230         with mock.patch('builtins.input', return_value='y'):
  231             self.bdb.append_tag_at_index(0, new_tags)
  232             # updated tags for each bookmark
  233             from_db = [(i, self.bdb.get_rec_by_id(i)[3]) for i in inclusive_range(1, len(self.bookmarks))]
  234             for index, tagset in from_db:
  235                 # checking if new tags added to bookmark
  236                 self.assertTrue(split_and_test_membership(new_tags, tagset))
  237                 # checking if old tags still exist for boomark
  238                 self.assertTrue(split_and_test_membership(old_tagsets[index], tagset))
  239 
  240     def test_delete_tag_at_index(self):
  241         # adding bookmarks
  242         for bookmark in self.bookmarks:
  243             self.bdb.add_rec(*bookmark)
  244 
  245         get_tags_at_idx = lambda i: self.bdb.get_rec_by_id(i)[3]
  246         # list of two-tuples, each containg bookmark index and corresponding tags
  247         tags_by_index = [(i, get_tags_at_idx(i)) for i in inclusive_range(1, len(self.bookmarks))]
  248 
  249         for i, tags in tags_by_index:
  250             # get the first tag from the bookmark
  251             to_delete = re.match(',.*?,', tags).group(0)
  252             self.bdb.delete_tag_at_index(i, to_delete)
  253             # get updated tags from db
  254             from_db = get_tags_at_idx(i)
  255             self.assertNotIn(to_delete, from_db)
  256 
  257     def test_search_keywords_and_filter_by_tags(self):
  258         # adding bookmark
  259         for bookmark in self.bookmarks:
  260             self.bdb.add_rec(*bookmark)
  261 
  262         with mock.patch('buku.prompt'):
  263             expected = [(3,
  264                          'http://example.com/',
  265                          'test',
  266                          ',es,est,tes,test,',
  267                          'a case for replace_tag test', 0)]
  268             results = self.bdb.search_keywords_and_filter_by_tags(
  269                 ['News', 'case'],
  270                 False,
  271                 False,
  272                 False,
  273                 ['est'],
  274             )
  275             self.assertIn(expected[0], results)
  276             expected = [(3,
  277                          'http://example.com/',
  278                          'test',
  279                          ',es,est,tes,test,',
  280                          'a case for replace_tag test', 0),
  281                         (2,
  282                          'http://www.zażółćgęśląjaźń.pl/',
  283                          'ZAŻÓŁĆ',
  284                          ',gęślą,jaźń,zażółć,',
  285                          'Testing UTF-8, zażółć gęślą jaźń.', 0)]
  286             results = self.bdb.search_keywords_and_filter_by_tags(
  287                 ['UTF-8', 'case'],
  288                 False,
  289                 False,
  290                 False,
  291                 'jaźń, test',
  292             )
  293             self.assertIn(expected[0], results)
  294             self.assertIn(expected[1], results)
  295 
  296     def test_searchdb(self):
  297         # adding bookmarks
  298         for bookmark in self.bookmarks:
  299             self.bdb.add_rec(*bookmark)
  300 
  301         get_first_tag = lambda x: ''.join(x[2].split(',')[:2])
  302         for i, bookmark in enumerate(self.bookmarks):
  303             tag_search = get_first_tag(bookmark)
  304             # search by the domain name for url
  305             url_search = re.match(r'https?://(.*)?\..*', bookmark[0]).group(1)
  306             title_search = bookmark[1]
  307             # Expect a five-tuple containing all bookmark data
  308             # db index, URL, title, tags, description
  309             expected = [(i + 1,) + tuple(bookmark)]
  310             expected[0] += tuple([0])
  311             # search db by tag, url (domain name), and title
  312             for keyword in (tag_search, url_search, title_search):
  313                 with mock.patch('buku.prompt'):
  314                     # search by keyword
  315                     results = self.bdb.searchdb([keyword])
  316                     self.assertEqual(results, expected)
  317 
  318     def test_search_by_tag(self):
  319         # adding bookmarks
  320         for bookmark in self.bookmarks:
  321             self.bdb.add_rec(*bookmark)
  322 
  323         with mock.patch('buku.prompt'):
  324             get_first_tag = lambda x: ''.join(x[2].split(',')[:2])
  325             for i in range(len(self.bookmarks)):
  326                 # search for bookmark with a tag that is known to exist
  327                 results = self.bdb.search_by_tag(get_first_tag(self.bookmarks[i]))
  328                 # Expect a five-tuple containing all bookmark data
  329                 # db index, URL, title, tags, description
  330                 expected = [(i + 1,) + tuple(self.bookmarks[i])]
  331                 expected[0] += tuple([0])
  332                 self.assertEqual(results, expected)
  333 
  334     @vcr.use_cassette('tests/vcr_cassettes/test_search_by_multiple_tags_search_any.yaml')
  335     def test_search_by_multiple_tags_search_any(self):
  336         # adding bookmarks
  337         for bookmark in self.bookmarks:
  338             self.bdb.add_rec(*bookmark)
  339 
  340         new_bookmark = ['https://newbookmark.com',
  341                         'New Bookmark',
  342                         parse_tags(['test,old,new']),
  343                         'additional bookmark to test multiple tag search', 0]
  344 
  345         self.bdb.add_rec(*new_bookmark)
  346 
  347         with mock.patch('buku.prompt'):
  348             # search for bookmarks matching ANY of the supplied tags
  349             results = self.bdb.search_by_tag('test, old')
  350             # Expect a list of five-element tuples containing all bookmark data
  351             # db index, URL, title, tags, description, ordered by records with
  352             # the most number of matches.
  353             expected = [
  354                 (4, 'https://newbookmark.com', 'New Bookmark',
  355                  parse_tags([',test,old,new,']),
  356                  'additional bookmark to test multiple tag search', 0),
  357                 (1, 'http://slashdot.org', 'SLASHDOT',
  358                  parse_tags([',news,old,']),
  359                  "News for old nerds, stuff that doesn't matter", 0),
  360                 (3, 'http://example.com/', 'test', ',es,est,tes,test,', 'a case for replace_tag test', 0)
  361             ]
  362             self.assertEqual(results, expected)
  363 
  364     @vcr.use_cassette('tests/vcr_cassettes/test_search_by_multiple_tags_search_all.yaml')
  365     def test_search_by_multiple_tags_search_all(self):
  366         # adding bookmarks
  367         for bookmark in self.bookmarks:
  368             self.bdb.add_rec(*bookmark)
  369 
  370         new_bookmark = ['https://newbookmark.com',
  371                         'New Bookmark',
  372                         parse_tags(['test,old,new']),
  373                         'additional bookmark to test multiple tag search']
  374 
  375         self.bdb.add_rec(*new_bookmark)
  376 
  377         with mock.patch('buku.prompt'):
  378             # search for bookmarks matching ALL of the supplied tags
  379             results = self.bdb.search_by_tag('test + old')
  380             # Expect a list of five-element tuples containing all bookmark data
  381             # db index, URL, title, tags, description
  382             expected = [
  383                 (4, 'https://newbookmark.com', 'New Bookmark',
  384                  parse_tags([',test,old,new,']),
  385                  'additional bookmark to test multiple tag search', 0)
  386             ]
  387             self.assertEqual(results, expected)
  388 
  389     def test_search_by_tags_enforces_space_seprations_search_all(self):
  390 
  391         bookmark1 = ['https://bookmark1.com',
  392                      'Bookmark One',
  393                      parse_tags(['tag, two,tag+two']),
  394                      "test case for bookmark with '+' in tag"]
  395 
  396         bookmark2 = ['https://bookmark2.com',
  397                      'Bookmark Two',
  398                      parse_tags(['tag,two, tag-two']),
  399                      "test case for bookmark with hyphenated tag"]
  400 
  401         self.bdb.add_rec(*bookmark1)
  402         self.bdb.add_rec(*bookmark2)
  403 
  404         with mock.patch('buku.prompt'):
  405             # check that space separation for ' + ' operator is enforced
  406             results = self.bdb.search_by_tag('tag+two')
  407             # Expect a list of five-element tuples containing all bookmark data
  408             # db index, URL, title, tags, description
  409             expected = [
  410                 (1, 'https://bookmark1.com', 'Bookmark One',
  411                  parse_tags([',tag,two,tag+two,']),
  412                  "test case for bookmark with '+' in tag", 0)
  413             ]
  414             self.assertEqual(results, expected)
  415             results = self.bdb.search_by_tag('tag + two')
  416             # Expect a list of five-element tuples containing all bookmark data
  417             # db index, URL, title, tags, description
  418             expected = [
  419                 (1, 'https://bookmark1.com', 'Bookmark One',
  420                  parse_tags([',tag,two,tag+two,']),
  421                  "test case for bookmark with '+' in tag", 0),
  422                 (2, 'https://bookmark2.com', 'Bookmark Two',
  423                  parse_tags([',tag,two,tag-two,']),
  424                  "test case for bookmark with hyphenated tag", 0),
  425             ]
  426             self.assertEqual(results, expected)
  427 
  428     def test_search_by_tags_exclusion(self):
  429         # adding bookmarks
  430         for bookmark in self.bookmarks:
  431             self.bdb.add_rec(*bookmark)
  432 
  433         new_bookmark = ['https://newbookmark.com',
  434                         'New Bookmark',
  435                         parse_tags(['test,old,new']),
  436                         'additional bookmark to test multiple tag search']
  437 
  438         self.bdb.add_rec(*new_bookmark)
  439 
  440         with mock.patch('buku.prompt'):
  441             # search for bookmarks matching ANY of the supplied tags
  442             # while excluding bookmarks from results that match a given tag
  443             results = self.bdb.search_by_tag('test, old - est')
  444             # Expect a list of five-element tuples containing all bookmark data
  445             # db index, URL, title, tags, description
  446             expected = [
  447                 (4, 'https://newbookmark.com', 'New Bookmark',
  448                  parse_tags([',test,old,new,']),
  449                  'additional bookmark to test multiple tag search', 0),
  450                 (1, 'http://slashdot.org', 'SLASHDOT',
  451                  parse_tags([',news,old,']),
  452                  "News for old nerds, stuff that doesn't matter", 0),
  453             ]
  454             self.assertEqual(results, expected)
  455 
  456     @vcr.use_cassette('tests/vcr_cassettes/test_search_by_tags_enforces_space_seprations_exclusion.yaml')
  457     def test_search_by_tags_enforces_space_seprations_exclusion(self):
  458 
  459         bookmark1 = ['https://bookmark1.com',
  460                      'Bookmark One',
  461                      parse_tags(['tag, two,tag+two']),
  462                      "test case for bookmark with '+' in tag"]
  463 
  464         bookmark2 = ['https://bookmark2.com',
  465                      'Bookmark Two',
  466                      parse_tags(['tag,two, tag-two']),
  467                      "test case for bookmark with hyphenated tag"]
  468 
  469         bookmark3 = ['https://bookmark3.com',
  470                      'Bookmark Three',
  471                      parse_tags(['tag, tag three']),
  472                      "second test case for bookmark with hyphenated tag"]
  473 
  474         self.bdb.add_rec(*bookmark1)
  475         self.bdb.add_rec(*bookmark2)
  476         self.bdb.add_rec(*bookmark3)
  477 
  478         with mock.patch('buku.prompt'):
  479             # check that space separation for ' - ' operator is enforced
  480             results = self.bdb.search_by_tag('tag-two')
  481             # Expect a list of five-element tuples containing all bookmark data
  482             # db index, URL, title, tags, description
  483             expected = [
  484                 (2, 'https://bookmark2.com', 'Bookmark Two',
  485                  parse_tags([',tag,two,tag-two,']),
  486                  "test case for bookmark with hyphenated tag", 0),
  487             ]
  488             self.assertEqual(results, expected)
  489             results = self.bdb.search_by_tag('tag - two')
  490             # Expect a list of five-element tuples containing all bookmark data
  491             # db index, URL, title, tags, description
  492             expected = [
  493                 (3, 'https://bookmark3.com', 'Bookmark Three',
  494                  parse_tags([',tag,tag three,']),
  495                  "second test case for bookmark with hyphenated tag", 0),
  496             ]
  497             self.assertEqual(results, expected)
  498 
  499     def test_search_and_open_in_broswer_by_range(self):
  500         # adding bookmarks
  501         for bookmark in self.bookmarks:
  502             self.bdb.add_rec(*bookmark)
  503 
  504         # simulate user input, select range of indices 1-3
  505         index_range = '1-%s' % len(self.bookmarks)
  506         with mock.patch('builtins.input', side_effect=[index_range]):
  507             with mock.patch('buku.browse') as mock_browse:
  508                 try:
  509                     # search the db with keywords from each bookmark
  510                     # searching using the first tag from bookmarks
  511                     get_first_tag = lambda x: x[2].split(',')[1]
  512                     results = self.bdb.searchdb([get_first_tag(bm) for bm in self.bookmarks])
  513                     prompt(self.bdb, results)
  514                 except StopIteration:
  515                     # catch exception thrown by reaching the end of the side effect iterable
  516                     pass
  517 
  518                 # collect arguments passed to browse
  519                 arg_list = [args[0] for args, _ in mock_browse.call_args_list]
  520                 # expect a list of one-tuples that are bookmark URLs
  521                 expected = [x[0] for x in self.bookmarks]
  522                 # checking if browse called with expected arguments
  523                 self.assertEqual(arg_list, expected)
  524 
  525     @vcr.use_cassette('tests/vcr_cassettes/test_search_and_open_all_in_browser.yaml')
  526     def test_search_and_open_all_in_browser(self):
  527         # adding bookmarks
  528         for bookmark in self.bookmarks:
  529             self.bdb.add_rec(*bookmark)
  530 
  531         # simulate user input, select 'a' to open all bookmarks in results
  532         with mock.patch('builtins.input', side_effect=['a']):
  533             with mock.patch('buku.browse') as mock_browse:
  534                 try:
  535                     # search the db with keywords from each bookmark
  536                     # searching using the first tag from bookmarks
  537                     get_first_tag = lambda x: x[2].split(',')[1]
  538                     results = self.bdb.searchdb([get_first_tag(bm) for bm in self.bookmarks[:2]])
  539                     prompt(self.bdb, results)
  540                 except StopIteration:
  541                     # catch exception thrown by reaching the end of the side effect iterable
  542                     pass
  543 
  544                 # collect arguments passed to browse
  545                 arg_list = [args[0] for args, _ in mock_browse.call_args_list]
  546                 # expect a list of one-tuples that are bookmark URLs
  547                 expected = [x[0] for x in self.bookmarks][:2]
  548                 # checking if browse called with expected arguments
  549                 self.assertEqual(arg_list, expected)
  550 
  551     def test_delete_rec(self):
  552         # adding bookmark and getting index
  553         self.bdb.add_rec(*self.bookmarks[0])
  554         index = self.bdb.get_rec_id(self.bookmarks[0][0])
  555         # deleting bookmark
  556         self.bdb.delete_rec(index)
  557         # asserting it doesn't exist
  558         from_db = self.bdb.get_rec_by_id(index)
  559         self.assertIsNone(from_db)
  560 
  561     def test_delete_rec_yes(self):
  562         # checking that "y" response causes delete_rec to return True
  563         with mock.patch('builtins.input', return_value='y'):
  564             self.assertTrue(self.bdb.delete_rec(0))
  565 
  566     def test_delete_rec_no(self):
  567         # checking that non-"y" response causes delete_rec to return None
  568         with mock.patch('builtins.input', return_value='n'):
  569             self.assertFalse(self.bdb.delete_rec(0))
  570 
  571     def test_cleardb(self):
  572         # adding bookmarks
  573         self.bdb.add_rec(*self.bookmarks[0])
  574         # deleting all bookmarks
  575         with mock.patch('builtins.input', return_value='y'):
  576             self.bdb.cleardb()
  577         # assert table has been dropped
  578         assert self.bdb.get_rec_by_id(0) is None
  579 
  580     def test_replace_tag(self):
  581         indices = []
  582         for bookmark in self.bookmarks:
  583             # adding bookmark, getting index
  584             self.bdb.add_rec(*bookmark)
  585             index = self.bdb.get_rec_id(bookmark[0])
  586             indices += [index]
  587 
  588         # replacing tags
  589         with mock.patch('builtins.input', return_value='y'):
  590             self.bdb.replace_tag("news", ["__01"])
  591         with mock.patch('builtins.input', return_value='y'):
  592             self.bdb.replace_tag("zażółć", ["__02,__03"])
  593 
  594         # replacing tag which is also a substring of other tag
  595         with mock.patch('builtins.input', return_value='y'):
  596             self.bdb.replace_tag("es", ["__04"])
  597 
  598         # removing tags
  599         with mock.patch('builtins.input', return_value='y'):
  600             self.bdb.replace_tag("gęślą")
  601         with mock.patch('builtins.input', return_value='y'):
  602             self.bdb.replace_tag("old")
  603 
  604         # removing non-existent tag
  605         with mock.patch('builtins.input', return_value='y'):
  606             self.bdb.replace_tag("_")
  607 
  608         # removing nonexistent tag which is also a substring of other tag
  609         with mock.patch('builtins.input', return_value='y'):
  610             self.bdb.replace_tag("e")
  611 
  612         for url, title, _, _ in self.bookmarks:
  613             # retrieving from db
  614             index = self.bdb.get_rec_id(url)
  615             from_db = self.bdb.get_rec_by_id(index)
  616             # asserting tags were replaced
  617             if title == "SLASHDOT":
  618                 self.assertEqual(from_db[3], parse_tags(["__01"]))
  619             elif title == "ZAŻÓŁĆ":
  620                 self.assertEqual(from_db[3], parse_tags(["__02,__03,jaźń"]))
  621             elif title == "test":
  622                 self.assertEqual(from_db[3], parse_tags(["test,tes,est,__04"]))
  623 
  624     def test_tnyfy_url(self):
  625         # shorten a well-known url
  626         shorturl = self.bdb.tnyfy_url(url='https://www.google.com', shorten=True)
  627         self.assertEqual(shorturl, 'http://tny.im/yt')
  628 
  629         # expand a well-known short url
  630         url = self.bdb.tnyfy_url(url='http://tny.im/yt', shorten=False)
  631         self.assertEqual(url, 'https://www.google.com')
  632 
  633     # def test_browse_by_index(self):
  634     # self.fail()
  635 
  636     def test_close_quit(self):
  637         # quitting with no args
  638         try:
  639             self.bdb.close_quit()
  640         except SystemExit as err:
  641             self.assertEqual(err.args[0], 0)
  642         # quitting with custom arg
  643         try:
  644             self.bdb.close_quit(1)
  645         except SystemExit as err:
  646             self.assertEqual(err.args[0], 1)
  647 
  648     # def test_import_bookmark(self):
  649     # self.fail()
  650 
  651 
  652 @pytest.fixture(scope='function')
  653 def refreshdb_fixture():
  654     # Setup
  655     os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
  656 
  657     # start every test from a clean state
  658     if exists(TEST_TEMP_DBFILE_PATH):
  659         os.remove(TEST_TEMP_DBFILE_PATH)
  660 
  661     bdb = BukuDb()
  662 
  663     yield bdb
  664 
  665     # Teardown
  666     os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
  667 
  668 
  669 @pytest.mark.parametrize(
  670     "title_in, exp_res",
  671     [
  672         ['?', 'Example Domain'],
  673         [None, 'Example Domain'],
  674         ['', 'Example Domain'],
  675         ['random title', 'Example Domain'],
  676     ]
  677 )
  678 def test_refreshdb(refreshdb_fixture, title_in, exp_res):
  679     bdb = refreshdb_fixture
  680     args = ["http://example.com"]
  681     if title_in:
  682         args.append(title_in)
  683     bdb.add_rec(*args)
  684     bdb.refreshdb(1, 1)
  685     from_db = bdb.get_rec_by_id(1)
  686     assert from_db[2] == exp_res, 'from_db: {}'.format(from_db)
  687 
  688 
  689 @given(
  690     index=st.integers(min_value=-10, max_value=10),
  691     low=st.integers(min_value=-10, max_value=10),
  692     high=st.integers(min_value=-10, max_value=10),
  693     is_range=st.booleans(),
  694 )
  695 @settings(deadline=None)
  696 def test_print_rec_hypothesis(caplog, setup, index, low, high, is_range):
  697     """test when index, low or high is less than 0."""
  698     # setup
  699     caplog.handler.records.clear()
  700     caplog.records.clear()
  701 
  702     bdb = BukuDb()
  703     # clear all record first before testing
  704     bdb.delete_rec_all()
  705     bdb.add_rec("http://one.com", "", parse_tags(['cat,ant,bee,1']), "")
  706     db_len = 1
  707     bdb.print_rec(index=index, low=low, high=high, is_range=is_range)
  708 
  709     check_print = False
  710     err_msg = ['Actual log:']
  711     err_msg.extend(['{}:{}'.format(x.levelname, x.getMessage()) for x in caplog.records])
  712 
  713     if index < 0 or (0 <= index <= db_len and not is_range):
  714         check_print = True
  715     # negative index/range on is_range
  716     elif (is_range and any([low < 0, high < 0])):
  717         assert any([x.levelname == "ERROR" for x in caplog.records]), \
  718             '\n'.join(err_msg)
  719         assert any([x.getMessage() == "Negative range boundary" for x in caplog.records]), \
  720             '\n'.join(err_msg)
  721     elif is_range:
  722         check_print = True
  723     else:
  724         assert any([x.levelname == "ERROR" for x in caplog.records]), \
  725             '\n'.join(err_msg)
  726         assert any([x.getMessage().startswith("No matching index") for x in caplog.records]), \
  727             '\n'.join(err_msg)
  728 
  729     if check_print:
  730         assert not any([x.levelname == "ERROR" for x in caplog.records]), \
  731             '\n'.join(err_msg)
  732 
  733     # teardown
  734     bdb.delete_rec(index=1)
  735     caplog.handler.records.clear()
  736     caplog.records.clear()
  737 
  738 
  739 def test_list_tags(capsys, setup):
  740     bdb = BukuDb()
  741 
  742     # adding bookmarks
  743     bdb.add_rec("http://one.com", "", parse_tags(['cat,ant,bee,1']), "")
  744     bdb.add_rec("http://two.com", "", parse_tags(['Cat,Ant,bee,1']), "")
  745     bdb.add_rec("http://three.com", "", parse_tags(['Cat,Ant,3,Bee,2']), "")
  746 
  747     # listing tags, asserting output
  748     out, err = capsys.readouterr()
  749     prompt(bdb, None, True, listtags=True)
  750     out, err = capsys.readouterr()
  751     assert out == "     1. 1 (2)\n     2. 2 (1)\n     3. 3 (1)\n     4. ant (3)\n     5. bee (3)\n     6. cat (3)\n\n"
  752     assert err == ''
  753 
  754 
  755 def test_compactdb(setup):
  756     bdb = BukuDb()
  757 
  758     # adding bookmarks
  759     for bookmark in TEST_BOOKMARKS:
  760         bdb.add_rec(*bookmark)
  761 
  762     # manually deleting 2nd index from db, calling compactdb
  763     bdb.cur.execute('DELETE FROM bookmarks WHERE id = ?', (2,))
  764     bdb.compactdb(2)
  765 
  766     # asserting bookmarks have correct indices
  767     assert bdb.get_rec_by_id(1) == (
  768         1, 'http://slashdot.org', 'SLASHDOT', ',news,old,', "News for old nerds, stuff that doesn't matter", 0)
  769     assert bdb.get_rec_by_id(2) == (
  770         2, 'http://example.com/', 'test', ',es,est,tes,test,', 'a case for replace_tag test', 0)
  771     assert bdb.get_rec_by_id(3) is None
  772 
  773 
  774 @vcr.use_cassette('tests/vcr_cassettes/test_delete_rec_range_and_delay_commit.yaml')
  775 @given(
  776     low=st.integers(min_value=-10, max_value=10),
  777     high=st.integers(min_value=-10, max_value=10),
  778     delay_commit=st.booleans(),
  779     input_retval=st.characters()
  780 )
  781 @example(low=0, high=0, delay_commit=False, input_retval='y')
  782 @settings(max_examples=2, deadline=None)
  783 def test_delete_rec_range_and_delay_commit(setup, low, high, delay_commit, input_retval):
  784     """test delete rec, range and delay commit."""
  785     bdb = BukuDb()
  786     bdb_dc = BukuDb()  # instance for delay_commit check.
  787     index = 0
  788     is_range = True
  789 
  790     # Fill bookmark
  791     for bookmark in TEST_BOOKMARKS:
  792         bdb.add_rec(*bookmark)
  793     db_len = len(TEST_BOOKMARKS)
  794 
  795     # use normalized high and low variable
  796     n_low, n_high = normalize_range(db_len=db_len, low=low, high=high)
  797 
  798     exp_res = True
  799     if n_high > db_len >= n_low:
  800         exp_db_len = db_len - (db_len + 1 - n_low)
  801     elif n_high == n_low > db_len:
  802         exp_db_len = db_len
  803         exp_res = False
  804     elif n_high == n_low <= db_len:
  805         exp_db_len = db_len - 1
  806     else:
  807         exp_db_len = db_len - (n_high + 1 - n_low)
  808 
  809     with mock.patch('builtins.input', return_value=input_retval):
  810         res = bdb.delete_rec(
  811             index=index, low=low, high=high, is_range=is_range, delay_commit=delay_commit)
  812 
  813     if n_low < 0:
  814         assert not res
  815         assert len(bdb_dc.get_rec_all()) == db_len
  816         # teardown
  817         os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
  818         return
  819     if (low == 0 or high == 0) and input_retval != 'y':
  820         assert not res
  821         assert len(bdb_dc.get_rec_all()) == db_len
  822         # teardown
  823         os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
  824         return
  825     if (low == 0 or high == 0) and input_retval == 'y':
  826         assert res == exp_res
  827         assert len(bdb_dc.get_rec_all()) == 0
  828         # teardown
  829         os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
  830         return
  831     if n_low > db_len and n_low > 0:
  832         assert not res
  833         assert len(bdb_dc.get_rec_all()) == db_len
  834         # teardown
  835         os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
  836         return
  837     assert res == exp_res
  838     assert len(bdb.get_rec_all()) == exp_db_len
  839     if delay_commit:
  840         assert len(bdb_dc.get_rec_all()) == db_len
  841     else:
  842         assert len(bdb_dc.get_rec_all()) == exp_db_len
  843 
  844     # teardown
  845     os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
  846 
  847 
  848 @pytest.mark.parametrize(
  849     'index, delay_commit, input_retval',
  850     [
  851         [-1, False, False],
  852         [0, False, False],
  853         [1, False, True],
  854         [1, False, False],
  855         [1, True, True],
  856         [1, True, False],
  857         [100, False, True],
  858     ]
  859 )
  860 def test_delete_rec_index_and_delay_commit(index, delay_commit, input_retval):
  861     """test delete rec, index and delay commit."""
  862     bdb = BukuDb()
  863     bdb_dc = BukuDb()  # instance for delay_commit check.
  864 
  865     # Fill bookmark
  866     for bookmark in TEST_BOOKMARKS:
  867         bdb.add_rec(*bookmark)
  868     db_len = len(TEST_BOOKMARKS)
  869 
  870     n_index = index
  871 
  872     with mock.patch('builtins.input', return_value=input_retval):
  873         res = bdb.delete_rec(index=index, delay_commit=delay_commit)
  874 
  875     if n_index < 0:
  876         assert not res
  877     elif n_index > db_len:
  878         assert not res
  879         assert len(bdb.get_rec_all()) == db_len
  880     elif index == 0 and input_retval != 'y':
  881         assert not res
  882         assert len(bdb.get_rec_all()) == db_len
  883     else:
  884         assert res
  885         assert len(bdb.get_rec_all()) == db_len - 1
  886         if delay_commit:
  887             assert len(bdb_dc.get_rec_all()) == db_len
  888         else:
  889             assert len(bdb_dc.get_rec_all()) == db_len - 1
  890 
  891     # teardown
  892     os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
  893 
  894 
  895 @pytest.mark.parametrize(
  896     'index, is_range, low, high',
  897     [
  898         # range on non zero index
  899         (0, True, 1, 1),
  900         # range on zero index
  901         (0, True, 0, 0),
  902         # zero index only
  903         (0, False, 0, 0),
  904     ]
  905 )
  906 def test_delete_rec_on_empty_database(setup, index, is_range, low, high):
  907     """test delete rec, on empty database."""
  908     bdb = BukuDb()
  909     with mock.patch('builtins.input', return_value='y'):
  910         res = bdb.delete_rec(index, is_range, low, high)
  911 
  912     if (is_range and any([low == 0, high == 0])) or (not is_range and index == 0):
  913         assert res
  914         # teardown
  915         os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
  916         return
  917 
  918     if is_range and low > 1 and high > 1:
  919         assert not res
  920 
  921     # teardown
  922     os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH
  923 
  924 
  925 @pytest.mark.parametrize(
  926     'index, low, high, is_range',
  927     [
  928         ['a', 'a', 1, True],
  929         ['a', 'a', 1, False],
  930         ['a', 1, 'a', True],
  931     ]
  932 )
  933 def test_delete_rec_on_non_interger(index, low, high, is_range):
  934     """test delete rec on non integer arg."""
  935     bdb = BukuDb()
  936 
  937     for bookmark in TEST_BOOKMARKS:
  938         bdb.add_rec(*bookmark)
  939     db_len = len(TEST_BOOKMARKS)
  940 
  941     if is_range and not (isinstance(low, int) and isinstance(high, int)):
  942         with pytest.raises(TypeError):
  943             bdb.delete_rec(index=index, low=low, high=high, is_range=is_range)
  944         return
  945     if not is_range and not isinstance(index, int):
  946         res = bdb.delete_rec(index=index, low=low, high=high, is_range=is_range)
  947         assert not res
  948         assert len(bdb.get_rec_all()) == db_len
  949     else:
  950         assert bdb.delete_rec(index=index, low=low, high=high, is_range=is_range)
  951 
  952 
  953 @pytest.mark.parametrize('url', ['', False, None, 0])
  954 def test_add_rec_add_invalid_url(caplog, url):
  955     """test method."""
  956     bdb = BukuDb()
  957     res = bdb.add_rec(url=url)
  958     assert res == -1
  959     caplog.records[0].levelname == 'ERROR'
  960     caplog.records[0].getMessage() == 'Invalid URL'
  961 
  962 
  963 @pytest.mark.parametrize(
  964     "kwargs, exp_arg",
  965     [
  966         [
  967             {'url': 'example.com'},
  968             ('example.com', 'Example Domain', ',', '', 0)
  969         ],
  970         [
  971             {'url': 'http://example.com'},
  972             ('http://example.com', 'Example Domain', ',', '', 0)
  973         ],
  974         [
  975             {'url': 'http://example.com', 'immutable': 1},
  976             ('http://example.com', 'Example Domain', ',', '', 1)
  977         ],
  978         [
  979             {'url': 'http://example.com', 'desc': 'randomdesc'},
  980             ('http://example.com', 'Example Domain', ',', 'randomdesc', 0)
  981         ],
  982         [
  983             {'url': 'http://example.com', 'title_in': 'randomtitle'},
  984             ('http://example.com', 'randomtitle', ',', '', 0)
  985         ],
  986         [
  987             {'url': 'http://example.com', 'tags_in': 'tag1'},
  988             ('http://example.com', 'Example Domain', ',tag1,', '', 0),
  989         ],
  990         [
  991             {'url': 'http://example.com', 'tags_in': ',tag1'},
  992             ('http://example.com', 'Example Domain', ',tag1,', '', 0),
  993         ],
  994         [
  995             {'url': 'http://example.com', 'tags_in': ',tag1,'},
  996             ('http://example.com', 'Example Domain', ',tag1,', '', 0),
  997         ],
  998     ]
  999 )
 1000 def test_add_rec_exec_arg(kwargs, exp_arg):
 1001     """test func."""
 1002     bdb = BukuDb()
 1003     bdb.cur = mock.Mock()
 1004     bdb.get_rec_id = mock.Mock(return_value=-1)
 1005     bdb.add_rec(**kwargs)
 1006     assert bdb.cur.execute.call_args[0][1] == exp_arg
 1007 
 1008 
 1009 def test_update_rec_index_0(caplog):
 1010     """test method."""
 1011     bdb = BukuDb()
 1012     res = bdb.update_rec(index=0, url='http://example.com')
 1013     assert not res
 1014     assert caplog.records[0].getMessage() == 'All URLs cannot be same'
 1015     assert caplog.records[0].levelname == 'ERROR'
 1016 
 1017 
 1018 def test_update_rec_only_index():
 1019     """test method."""
 1020     bdb = BukuDb()
 1021     res = bdb.update_rec(index=1)
 1022     assert res
 1023 
 1024 
 1025 @pytest.mark.parametrize('url', [None, ''])
 1026 def test_update_rec_invalid_url(url):
 1027     """test method."""
 1028     bdb = BukuDb()
 1029     res = bdb.update_rec(index=1, url=url)
 1030     assert res
 1031 
 1032 
 1033 @pytest.mark.parametrize('invalid_tag', ['+,', '-,'])
 1034 def test_update_rec_invalid_tag(caplog, invalid_tag):
 1035     """test method."""
 1036     url = 'http://example.com'
 1037     bdb = BukuDb()
 1038     res = bdb.update_rec(index=1, url=url, tags_in=invalid_tag)
 1039     assert not res
 1040     try:
 1041         assert caplog.records[0].getMessage() == 'Please specify a tag'
 1042         assert caplog.records[0].levelname == 'ERROR'
 1043     except IndexError as e:
 1044         if (sys.version_info.major, sys.version_info.minor) == (3, 4):
 1045             print('caplog records: {}'.format(caplog.records))
 1046             for idx, record in enumerate(caplog.records):
 1047                 print('idx:{};{};message:{};levelname:{}'.format(
 1048                     idx, record, record.getMessage(), record.levelname))
 1049         else:
 1050             raise e
 1051 
 1052 
 1053 @pytest.mark.parametrize('read_in_retval', ['y', 'n', ''])
 1054 def test_update_rec_update_all_bookmark(caplog, read_in_retval):
 1055     """test method."""
 1056     if (sys.version_info.major, sys.version_info.minor) == (3, 8):
 1057         caplog.set_level(logging.DEBUG)
 1058     with mock.patch('buku.read_in', return_value=read_in_retval):
 1059         import buku
 1060         bdb = buku.BukuDb()
 1061         res = bdb.update_rec(index=0, tags_in='tags1')
 1062         if read_in_retval != 'y':
 1063             assert not res
 1064             return
 1065         assert res
 1066         try:
 1067             if (sys.version_info.major, sys.version_info.minor) == (3, 8):
 1068                 assert caplog.records[0].getMessage() == \
 1069                        'update_rec query: "UPDATE bookmarks SET tags = ?", args: [\',tags1,\']'
 1070             else:
 1071                 assert caplog.records[0].getMessage() == \
 1072                        'query: "UPDATE bookmarks SET tags = ?", args: [\',tags1\']'
 1073             assert caplog.records[0].levelname == 'DEBUG'
 1074         except IndexError as e:
 1075             # TODO: fix test
 1076             if (sys.version_info.major, sys.version_info.minor) in [(3, 4), (3, 5), (3, 6), (3, 7)]:
 1077                 print('caplog records: {}'.format(caplog.records))
 1078                 for idx, record in enumerate(caplog.records):
 1079                     print('idx:{};{};message:{};levelname:{}'.format(
 1080                         idx, record, record.getMessage(), record.levelname))
 1081             else:
 1082                 raise e
 1083 
 1084 
 1085 @pytest.mark.parametrize(
 1086     'get_system_editor_retval, index, exp_res',
 1087     [
 1088         ['none', 0, False],
 1089         ['nano', -2, False],
 1090     ]
 1091 )
 1092 def test_edit_update_rec_with_invalid_input(get_system_editor_retval, index, exp_res):
 1093     """test method."""
 1094     with mock.patch('buku.get_system_editor', return_value=get_system_editor_retval):
 1095         import buku
 1096         bdb = buku.BukuDb()
 1097         res = bdb.edit_update_rec(index=index)
 1098         assert res == exp_res
 1099 
 1100 
 1101 @vcr.use_cassette('tests/vcr_cassettes/test_browse_by_index.yaml')
 1102 @given(
 1103     low=st.integers(min_value=-2, max_value=3),
 1104     high=st.integers(min_value=-2, max_value=3),
 1105     index=st.integers(min_value=-2, max_value=3),
 1106     is_range=st.booleans(),
 1107     empty_database=st.booleans(),
 1108 )
 1109 @example(low=0, high=0, index=0, is_range=False, empty_database=True)
 1110 @settings(max_examples=2, deadline=None)
 1111 def test_browse_by_index(low, high, index, is_range, empty_database):
 1112     """test method."""
 1113     n_low, n_high = (high, low) if low > high else (low, high)
 1114     with mock.patch('buku.browse'):
 1115         import buku
 1116         bdb = buku.BukuDb()
 1117         bdb.delete_rec_all()
 1118         db_len = 0
 1119         if not empty_database:
 1120             bdb.add_rec("https://www.google.com/ncr", "?")
 1121             db_len += 1
 1122         res = bdb.browse_by_index(index=index, low=low, high=high, is_range=is_range)
 1123         if is_range and (low < 0 or high < 0):
 1124             assert not res
 1125         elif is_range and n_low > 0 and n_high > 0:
 1126             assert res
 1127         elif is_range:
 1128             assert not res
 1129         elif not is_range and index < 0:
 1130             assert not res
 1131         elif not is_range and index > db_len:
 1132             assert not res
 1133         elif not is_range and index >= 0 and empty_database:
 1134             assert not res
 1135         elif not is_range and 0 <= index <= db_len and not empty_database:
 1136             assert res
 1137         else:
 1138             raise ValueError
 1139         bdb.delete_rec_all()
 1140 
 1141 
 1142 @pytest.fixture()
 1143 def chrome_db():
 1144     # compatibility
 1145     dir_path = os.path.dirname(os.path.realpath(__file__))
 1146     res_yaml_file = os.path.join(dir_path, 'test_bukuDb', '25491522_res.yaml')
 1147     res_nopt_yaml_file = os.path.join(dir_path, 'test_bukuDb', '25491522_res_nopt.yaml')
 1148     json_file = os.path.join(dir_path, 'test_bukuDb', 'Bookmarks')
 1149     return json_file, res_yaml_file, res_nopt_yaml_file
 1150 
 1151 
 1152 @pytest.mark.parametrize('add_pt', [True, False])
 1153 def test_load_chrome_database(chrome_db, add_pt):
 1154     """test method."""
 1155     # compatibility
 1156     json_file = chrome_db[0]
 1157     res_yaml_file = chrome_db[1] if add_pt else chrome_db[2]
 1158     dump_data = False  # NOTE: change this value to dump data
 1159     if not dump_data:
 1160         with open(res_yaml_file, 'r') as f:
 1161             try:
 1162                 res_yaml = yaml.load(f, Loader=yaml.FullLoader)
 1163             except RuntimeError:
 1164                 res_yaml = yaml.load(f, Loader=PrettySafeLoader)
 1165     # init
 1166     import buku
 1167     bdb = buku.BukuDb()
 1168     bdb.add_rec = mock.Mock()
 1169     bdb.load_chrome_database(json_file, None, add_pt)
 1170     call_args_list_dict = dict(bdb.add_rec.call_args_list)
 1171     # test
 1172     if not dump_data:
 1173         assert call_args_list_dict == res_yaml
 1174     # dump data for new test
 1175     if dump_data:
 1176         with open(res_yaml_file, 'w') as f:
 1177             yaml.dump(call_args_list_dict, f)
 1178         print('call args list dict dumped to:{}'.format(res_yaml_file))
 1179 
 1180 
 1181 @pytest.fixture()
 1182 def firefox_db(tmpdir):
 1183     zip_url = 'https://github.com/jarun/buku/files/1319933/bookmarks.zip'
 1184     dir_path = os.path.dirname(os.path.realpath(__file__))
 1185     res_yaml_file = os.path.join(dir_path, 'test_bukuDb', 'firefox_res.yaml')
 1186     res_nopt_yaml_file = os.path.join(dir_path, 'test_bukuDb', 'firefox_res_nopt.yaml')
 1187     ff_db_path = os.path.join(dir_path, 'test_bukuDb', 'places.sqlite')
 1188     if not os.path.isfile(ff_db_path):
 1189         tmp_zip = tmpdir.join('bookmarks.zip')
 1190         with urllib.request.urlopen(zip_url) as response, open(tmp_zip.strpath, 'wb') as out_file:
 1191             shutil.copyfileobj(response, out_file)
 1192         zip_obj = zipfile.ZipFile(tmp_zip.strpath)
 1193         zip_obj.extractall(path=os.path.join(dir_path, 'test_bukuDb'))
 1194     return ff_db_path, res_yaml_file, res_nopt_yaml_file
 1195 
 1196 
 1197 @pytest.mark.parametrize('add_pt', [True, False])
 1198 def test_load_firefox_database(firefox_db, add_pt):
 1199     # compatibility
 1200     ff_db_path = firefox_db[0]
 1201     dump_data = False  # NOTE: change this value to dump data
 1202     res_yaml_file = firefox_db[1] if add_pt else firefox_db[2]
 1203     if not dump_data:
 1204         with open(res_yaml_file, 'r') as f:
 1205             try:
 1206                 res_yaml = yaml.load(f)
 1207             except RuntimeError:
 1208                 res_yaml = yaml.load(f, Loader=PrettySafeLoader)
 1209     # init
 1210     import buku
 1211     bdb = buku.BukuDb()
 1212     bdb.add_rec = mock.Mock()
 1213     bdb.load_firefox_database(ff_db_path, None, add_pt)
 1214     call_args_list_dict = dict(bdb.add_rec.call_args_list)
 1215     # test
 1216     if not dump_data:
 1217         assert call_args_list_dict == res_yaml
 1218     if dump_data:
 1219         with open(res_yaml_file, 'w') as f:
 1220             yaml.dump(call_args_list_dict, f)
 1221         print('call args list dict dumped to:{}'.format(res_yaml_file))
 1222 
 1223 
 1224 @pytest.mark.parametrize(
 1225     'keyword_results, stag_results, exp_res',
 1226     [
 1227         ([], [], []),
 1228         (['item1'], ['item1', 'item2'], ['item1']),
 1229         (['item2'], ['item1'], []),
 1230     ]
 1231 )
 1232 def test_search_keywords_and_filter_by_tags(keyword_results, stag_results, exp_res):
 1233     """test method."""
 1234     # init
 1235     import buku
 1236     bdb = buku.BukuDb()
 1237     bdb.searchdb = mock.Mock(return_value=keyword_results)
 1238     bdb.search_by_tag = mock.Mock(return_value=stag_results)
 1239     # test
 1240     res = bdb.search_keywords_and_filter_by_tags(
 1241         mock.Mock(), mock.Mock(), mock.Mock(), mock.Mock(), [])
 1242     assert exp_res == res
 1243 
 1244 
 1245 @pytest.mark.parametrize(
 1246     'search_results, exclude_results, exp_res',
 1247     [
 1248         ([], [], []),
 1249         (['item1', 'item2'], ['item2'], ['item1']),
 1250         (['item2'], ['item1'], ['item2']),
 1251         (['item1', 'item2'], ['item1', 'item2'], []),
 1252     ]
 1253 )
 1254 def test_exclude_results_from_search(search_results, exclude_results, exp_res):
 1255     """test method."""
 1256     # init
 1257     import buku
 1258     bdb = buku.BukuDb()
 1259     bdb.searchdb = mock.Mock(return_value=exclude_results)
 1260     # test
 1261     res = bdb.exclude_results_from_search(
 1262         search_results, [], True)
 1263     assert exp_res == res
 1264 
 1265 
 1266 def test_exportdb_empty_db():
 1267     with NamedTemporaryFile(delete=False) as f:
 1268         db = BukuDb(dbfile=f.name)
 1269         with NamedTemporaryFile(delete=False) as f2:
 1270             res = db.exportdb(f2.name)
 1271             assert not res
 1272 
 1273 
 1274 def test_exportdb_single_rec(tmpdir):
 1275     with NamedTemporaryFile(delete=False) as f:
 1276         db = BukuDb(dbfile=f.name)
 1277         db.add_rec('http://example.com')
 1278         exp_file = tmpdir.join('export')
 1279         db.exportdb(exp_file.strpath)
 1280         with open(exp_file.strpath) as f:
 1281             assert f.read()
 1282 
 1283 
 1284 def test_exportdb_to_db():
 1285     with NamedTemporaryFile(delete=False) as f1, NamedTemporaryFile(delete=False, suffix='.db') as f2:
 1286         db = BukuDb(dbfile=f1.name)
 1287         db.add_rec('http://example.com')
 1288         db.add_rec('http://google.com')
 1289         with mock.patch('builtins.input', return_value='y'):
 1290             db.exportdb(f2.name)
 1291         db2 = BukuDb(dbfile=f2.name)
 1292         assert db.get_rec_all() == db2.get_rec_all()
 1293 
 1294 
 1295 @pytest.mark.parametrize(
 1296     'urls, exp_res',
 1297     [
 1298         [[], -1],
 1299         [['http://example.com'], 1],
 1300         [['htttp://example.com', 'http://google.com'], 2],
 1301     ])
 1302 def test_get_max_id(urls, exp_res):
 1303     with NamedTemporaryFile(delete=False) as f:
 1304         db = BukuDb(dbfile=f.name)
 1305         if urls:
 1306             list(map(lambda x: db.add_rec(x), urls))
 1307         assert db.get_max_id() == exp_res
 1308 
 1309 
 1310 # Helper functions for testcases
 1311 
 1312 
 1313 def split_and_test_membership(a, b):
 1314     # :param a, b: comma separated strings to split
 1315     # test everything in a in b
 1316     return all(x in b.split(',') for x in a.split(','))
 1317 
 1318 
 1319 def inclusive_range(start, end):
 1320     return list(range(start, end + 1))
 1321 
 1322 
 1323 def normalize_range(db_len, low, high):
 1324     """normalize index and range.
 1325 
 1326     Args:
 1327         db_len (int): database length.
 1328         low (int): low limit.
 1329         high (int): high limit.
 1330 
 1331     Returns:
 1332         Tuple contain following normalized variables (low, high)
 1333     """
 1334     require_comparison = True
 1335     # don't deal with non instance of the variable.
 1336     if not isinstance(low, int):
 1337         n_low = low
 1338         require_comparison = False
 1339     if not isinstance(high, int):
 1340         n_high = high
 1341         require_comparison = False
 1342 
 1343     max_value = db_len
 1344     if low == 'max' and high == 'max':
 1345         n_low = db_len
 1346         n_high = max_value
 1347     elif low == 'max' and high != 'max':
 1348         n_low = high
 1349         n_high = max_value
 1350     elif low != 'max' and high == 'max':
 1351         n_low = low
 1352         n_high = max_value
 1353     else:
 1354         n_low = low
 1355         n_high = high
 1356 
 1357     if require_comparison:
 1358         if n_high < n_low:
 1359             n_high, n_low = n_low, n_high
 1360 
 1361     return (n_low, n_high)
 1362 
 1363 
 1364 if __name__ == "__main__":
 1365     unittest.main()