Beispiel #1
0
def test_compactdb(setup):
    bdb = BukuDb()

    # adding bookmarks
    for bookmark in TEST_BOOKMARKS:
        bdb.add_rec(*bookmark)

    # manually deleting 2nd index from db, calling compactdb
    bdb.cur.execute("DELETE FROM bookmarks WHERE id = ?", (2,))
    bdb.compactdb(2)

    # asserting bookmarks have correct indices
    assert bdb.get_rec_by_id(1) == (
        1,
        "http://slashdot.org",
        "SLASHDOT",
        ",news,old,",
        "News for old nerds, stuff that doesn't matter",
        0,
    )
    assert bdb.get_rec_by_id(2) == (
        2,
        "http://example.com/",
        "test",
        ",es,est,tes,test,",
        "a case for replace_tag test",
        0,
    )
    assert bdb.get_rec_by_id(3) is None
Beispiel #2
0
def test_compactdb(setup):
    bdb = BukuDb()

    # adding bookmarks
    for bookmark in TEST_BOOKMARKS:
        bdb.add_rec(*bookmark)

    # manually deleting 2nd index from db, calling compactdb
    bdb.cur.execute('DELETE FROM bookmarks WHERE id = ?', (2,))
    bdb.compactdb(2)

    # asserting bookmarks have correct indices
    assert bdb.get_rec_by_id(1) == (1, 'http://slashdot.org', 'SLASHDOT', ',news,old,', "News for old nerds, stuff that doesn't matter", 0)
    assert bdb.get_rec_by_id(2) == (2, 'https://test.com:8080', 'test', ',es,est,tes,test,', 'a case for replace_tag test', 0)
    assert bdb.get_rec_by_id(3) is None
Beispiel #3
0
def test_compactdb(setup):
    bdb = BukuDb()

    # adding bookmarks
    for bookmark in TEST_BOOKMARKS:
        bdb.add_rec(*bookmark)

    # manually deleting 2nd index from db, calling compactdb
    bdb.cur.execute('DELETE FROM bookmarks WHERE id = ?', (2,))
    bdb.compactdb(2)

    # asserting bookmarks have correct indices
    assert bdb.get_rec_by_id(1) == (1, 'http://slashdot.org', 'SLASHDOT', ',news,old,', "News for old nerds, stuff that doesn't matter", 0)
    assert bdb.get_rec_by_id(2) == (2, 'https://test.com:8080', 'test', ',es,est,tes,test,', 'a case for replace_tag test', 0)
    assert bdb.get_rec_by_id(3) is None
Beispiel #4
0
class TestBukuDb(unittest.TestCase):
    def setUp(self):
        os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH

        # start every test from a clean state
        if exists(TEST_TEMP_DBFILE_PATH):
            os.remove(TEST_TEMP_DBFILE_PATH)

        self.bookmarks = TEST_BOOKMARKS
        self.bdb = BukuDb()

    def tearDown(self):
        os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH

    # @unittest.skip('skipping')
    @pytest.mark.non_tox
    def test_get_default_dbdir(self):
        dbdir_expected = TEST_TEMP_DBDIR_PATH
        dbdir_local_expected = os.path.join(os.path.expanduser('~'), '.local',
                                            'share', 'buku')
        dbdir_relative_expected = os.path.abspath('.')

        # desktop linux
        self.assertEqual(dbdir_expected, BukuDb.get_default_dbdir())

        # desktop generic
        os.environ.pop('XDG_DATA_HOME')
        self.assertEqual(dbdir_local_expected, BukuDb.get_default_dbdir())

        # no desktop

        # -- home is defined differently on various platforms.
        # -- keep a copy and set it back once done
        originals = {}
        for env_var in ['HOME', 'HOMEPATH', 'HOMEDIR']:
            try:
                originals[env_var] = os.environ.pop(env_var)
            except KeyError:
                pass
        self.assertEqual(dbdir_relative_expected, BukuDb.get_default_dbdir())
        for key, value in list(originals.items()):
            os.environ[key] = value

    # # not sure how to test this in nondestructive manner
    # def test_move_legacy_dbfile(self):
    #     self.fail()

    # @unittest.skip('skipping')
    def test_initdb(self):
        if exists(TEST_TEMP_DBFILE_PATH):
            os.remove(TEST_TEMP_DBFILE_PATH)
        self.assertIs(False, exists(TEST_TEMP_DBFILE_PATH))
        conn, curr = BukuDb.initdb()
        self.assertIsInstance(conn, sqlite3.Connection)
        self.assertIsInstance(curr, sqlite3.Cursor)
        self.assertIs(True, exists(TEST_TEMP_DBFILE_PATH))
        curr.close()
        conn.close()

    # @unittest.skip('skipping')
    def test_get_rec_by_id(self):
        for bookmark in self.bookmarks:
            # adding bookmark from self.bookmarks
            self.bdb.add_rec(*bookmark)

        # the expected bookmark
        expected = (1, 'http://slashdot.org', 'SLASHDOT', ',news,old,',
                    "News for old nerds, stuff that doesn't matter", 0)
        bookmark_from_db = self.bdb.get_rec_by_id(1)
        # asserting bookmark matches expected
        self.assertEqual(expected, bookmark_from_db)
        # asserting None returned if index out of range
        self.assertIsNone(self.bdb.get_rec_by_id(len(self.bookmarks[0]) + 1))

    # @unittest.skip('skipping')
    def test_get_rec_id(self):
        for idx, bookmark in enumerate(self.bookmarks):
            # adding bookmark from self.bookmarks to database
            self.bdb.add_rec(*bookmark)
            # asserting index is in order
            idx_from_db = self.bdb.get_rec_id(bookmark[0])
            self.assertEqual(idx + 1, idx_from_db)

        # asserting -1 is returned for nonexistent url
        idx_from_db = self.bdb.get_rec_id("http://nonexistent.url")
        self.assertEqual(-1, idx_from_db)

    # @unittest.skip('skipping')
    def test_add_rec(self):
        for bookmark in self.bookmarks:
            # adding bookmark from self.bookmarks to database
            self.bdb.add_rec(*bookmark)
            # retrieving bookmark from database
            index = self.bdb.get_rec_id(bookmark[0])
            from_db = self.bdb.get_rec_by_id(index)
            self.assertIsNotNone(from_db)
            # comparing data
            for pair in zip(from_db[1:], bookmark):
                self.assertEqual(*pair)

        # TODO: tags should be passed to the api as a sequence...

    # @unittest.skip('skipping')
    def test_update_rec(self):
        old_values = self.bookmarks[0]
        new_values = self.bookmarks[1]

        # adding bookmark and getting index
        self.bdb.add_rec(*old_values)
        index = self.bdb.get_rec_id(old_values[0])
        # updating with new values
        self.bdb.update_rec(index, *new_values)
        # retrieving bookmark from database
        from_db = self.bdb.get_rec_by_id(index)
        self.assertIsNotNone(from_db)
        # checking if values are updated
        for pair in zip(from_db[1:], new_values):
            self.assertEqual(*pair)

    # @unittest.skip('skipping')
    def test_append_tag_at_index(self):
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # tags to add
        old_tags = self.bdb.get_rec_by_id(1)[3]
        new_tags = ",foo,bar,baz"
        self.bdb.append_tag_at_index(1, new_tags)
        # updated list of tags
        from_db = self.bdb.get_rec_by_id(1)[3]

        # checking if new tags were added to the bookmark
        self.assertTrue(split_and_test_membership(new_tags, from_db))
        # checking if old tags still exist
        self.assertTrue(split_and_test_membership(old_tags, from_db))

    # @unittest.skip('skipping')
    def test_append_tag_at_all_indices(self):
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # tags to add
        new_tags = ",foo,bar,baz"
        # record of original tags for each bookmark
        old_tagsets = {
            i: self.bdb.get_rec_by_id(i)[3]
            for i in inclusive_range(1, len(self.bookmarks))
        }

        with mock.patch('builtins.input', return_value='y'):
            self.bdb.append_tag_at_index(0, new_tags)
            # updated tags for each bookmark
            from_db = [(i, self.bdb.get_rec_by_id(i)[3])
                       for i in inclusive_range(1, len(self.bookmarks))]
            for index, tagset in from_db:
                # checking if new tags added to bookmark
                self.assertTrue(split_and_test_membership(new_tags, tagset))
                # checking if old tags still exist for boomark
                self.assertTrue(
                    split_and_test_membership(old_tagsets[index], tagset))

    # @unittest.skip('skipping')
    def test_delete_tag_at_index(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        get_tags_at_idx = lambda i: self.bdb.get_rec_by_id(i)[3]
        # list of two-tuples, each containg bookmark index and corresponding tags
        tags_by_index = [(i, get_tags_at_idx(i))
                         for i in inclusive_range(1, len(self.bookmarks))]

        for i, tags in tags_by_index:
            # get the first tag from the bookmark
            to_delete = re.match(',.*?,', tags).group(0)
            self.bdb.delete_tag_at_index(i, to_delete)
            # get updated tags from db
            from_db = get_tags_at_idx(i)
            self.assertNotIn(to_delete, from_db)

    # @unittest.skip('skipping')
    @pytest.mark.slowtest
    def test_refreshdb(self):
        self.bdb.add_rec("https://www.google.com/ncr", "?")
        self.bdb.refreshdb(1, 1)
        from_db = self.bdb.get_rec_by_id(1)
        self.assertEqual(from_db[2], "Google")

    # @unittest.skip('skipping')
    def test_searchdb(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        get_first_tag = lambda x: ''.join(x[2].split(',')[:2])
        for i, bookmark in enumerate(self.bookmarks):
            tag_search = get_first_tag(bookmark)
            # search by the domain name for url
            url_search = re.match('https?://(.*)?\..*', bookmark[0]).group(1)
            title_search = bookmark[1]
            # Expect a five-tuple containing all bookmark data
            # db index, URL, title, tags, description
            expected = [(i + 1, ) + tuple(bookmark)]
            # search db by tag, url (domain name), and title
            for keyword in (tag_search, url_search, title_search):
                with mock.patch('buku.prompt'):
                    # search by keyword
                    results = self.bdb.searchdb([keyword])
                    self.assertEqual(results, expected)

    # @unittest.skip('skipping')
    def test_search_by_tag(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        with mock.patch('buku.prompt'):
            get_first_tag = lambda x: ''.join(x[2].split(',')[:2])
            for i in range(len(self.bookmarks)):
                # search for bookmark with a tag that is known to exist
                results = self.bdb.search_by_tag(
                    get_first_tag(self.bookmarks[i]))
                # Expect a five-tuple containing all bookmark data
                # db index, URL, title, tags, description
                expected = [(i + 1, ) + tuple(self.bookmarks[i])]
                self.assertEqual(results, expected)

    def test_search_by_multiple_tags_search_any(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        new_bookmark = [
            'https://newbookmark.com', 'New Bookmark',
            parse_tags(['test,old,new']),
            'additional bookmark to test multiple tag search'
        ]

        self.bdb.add_rec(*new_bookmark)

        with mock.patch('buku.prompt'):
            # search for bookmarks matching ANY of the supplied tags
            results = self.bdb.search_by_tag('test, old')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [(1, 'http://slashdot.org', 'SLASHDOT',
                         parse_tags([',news,old,']),
                         "News for old nerds, stuff that doesn't matter"),
                        (3, 'https://test.com:8080', 'test',
                         parse_tags([',test,tes,est,es,']),
                         "a case for replace_tag test"),
                        (4, 'https://newbookmark.com', 'New Bookmark',
                         parse_tags([',test,old,new,']),
                         'additional bookmark to test multiple tag search')]
            self.assertEqual(results, expected)

    def test_search_by_multiple_tags_search_all(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        new_bookmark = [
            'https://newbookmark.com', 'New Bookmark',
            parse_tags(['test,old,new']),
            'additional bookmark to test multiple tag search'
        ]

        self.bdb.add_rec(*new_bookmark)

        with mock.patch('buku.prompt'):
            # search for bookmarks matching ALL of the supplied tags
            results = self.bdb.search_by_tag('test + old')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [(4, 'https://newbookmark.com', 'New Bookmark',
                         parse_tags([',test,old,new,']),
                         'additional bookmark to test multiple tag search')]
            self.assertEqual(results, expected)

    def test_search_by_tags_enforces_space_seprations_search_all(self):

        bookmark1 = [
            'https://bookmark1.com', 'Bookmark One',
            parse_tags(['tag, two,tag+two']),
            "test case for bookmark with '+' in tag"
        ]

        bookmark2 = [
            'https://bookmark2.com', 'Bookmark Two',
            parse_tags(['tag,two, tag-two']),
            "test case for bookmark with hyphenated tag"
        ]

        self.bdb.add_rec(*bookmark1)
        self.bdb.add_rec(*bookmark2)

        with mock.patch('buku.prompt'):
            # check that space separation for ' + ' operator is enforced
            results = self.bdb.search_by_tag('tag+two')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [(1, 'https://bookmark1.com', 'Bookmark One',
                         parse_tags([',tag,two,tag+two,']),
                         "test case for bookmark with '+' in tag")]
            self.assertEqual(results, expected)
            results = self.bdb.search_by_tag('tag + two')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (1, 'https://bookmark1.com', 'Bookmark One',
                 parse_tags([',tag,two,tag+two,']),
                 "test case for bookmark with '+' in tag"),
                (2, 'https://bookmark2.com', 'Bookmark Two',
                 parse_tags([',tag,two,tag-two,']),
                 "test case for bookmark with hyphenated tag"),
            ]
            self.assertEqual(results, expected)

    def test_search_by_tags_exclusion(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        new_bookmark = [
            'https://newbookmark.com', 'New Bookmark',
            parse_tags(['test,old,new']),
            'additional bookmark to test multiple tag search'
        ]

        self.bdb.add_rec(*new_bookmark)

        with mock.patch('buku.prompt'):
            # search for bookmarks matching ANY of the supplied tags
            # while excluding bookmarks from results that match a given tag
            results = self.bdb.search_by_tag('test, old - est')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [(1, 'http://slashdot.org', 'SLASHDOT',
                         parse_tags([',news,old,']),
                         "News for old nerds, stuff that doesn't matter"),
                        (4, 'https://newbookmark.com', 'New Bookmark',
                         parse_tags([',test,old,new,']),
                         'additional bookmark to test multiple tag search')]
            self.assertEqual(results, expected)

    def test_search_by_tags_enforces_space_seprations_exclusion(self):

        bookmark1 = [
            'https://bookmark1.com', 'Bookmark One',
            parse_tags(['tag, two,tag+two']),
            "test case for bookmark with '+' in tag"
        ]

        bookmark2 = [
            'https://bookmark2.com', 'Bookmark Two',
            parse_tags(['tag,two, tag-two']),
            "test case for bookmark with hyphenated tag"
        ]

        bookmark3 = [
            'https://bookmark3.com', 'Bookmark Three',
            parse_tags(['tag, tag three']),
            "second test case for bookmark with hyphenated tag"
        ]

        self.bdb.add_rec(*bookmark1)
        self.bdb.add_rec(*bookmark2)
        self.bdb.add_rec(*bookmark3)

        with mock.patch('buku.prompt'):
            # check that space separation for ' - ' operator is enforced
            results = self.bdb.search_by_tag('tag-two')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (2, 'https://bookmark2.com', 'Bookmark Two',
                 parse_tags([',tag,two,tag-two,']),
                 "test case for bookmark with hyphenated tag"),
            ]
            self.assertEqual(results, expected)
            results = self.bdb.search_by_tag('tag - two')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (3, 'https://bookmark3.com', 'Bookmark Three',
                 parse_tags([',tag,tag three,']),
                 "second test case for bookmark with hyphenated tag"),
            ]
            self.assertEqual(results, expected)

    # @unittest.skip('skipping')
    def test_search_and_open_in_broswer_by_range(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # simulate user input, select range of indices 1-3
        index_range = '1-%s' % len(self.bookmarks)
        with mock.patch('builtins.input', side_effect=[index_range]):
            with mock.patch('buku.browse') as mock_browse:
                try:
                    # search the db with keywords from each bookmark
                    # searching using the first tag from bookmarks
                    get_first_tag = lambda x: x[2].split(',')[1]
                    results = self.bdb.searchdb(
                        [get_first_tag(bm) for bm in self.bookmarks])
                    prompt(self.bdb, results)
                except StopIteration:
                    # catch exception thrown by reaching the end of the side effect iterable
                    pass

                # collect arguments passed to browse
                arg_list = [args[0] for args, _ in mock_browse.call_args_list]
                # expect a list of one-tuples that are bookmark URLs
                expected = [x[0] for x in self.bookmarks]
                # checking if browse called with expected arguments
                self.assertEqual(arg_list, expected)

    # @unittest.skip('skipping')
    def test_search_and_open_all_in_browser(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # simulate user input, select 'a' to open all bookmarks in results
        with mock.patch('builtins.input', side_effect=['a']):
            with mock.patch('buku.browse') as mock_browse:
                try:
                    # search the db with keywords from each bookmark
                    # searching using the first tag from bookmarks
                    get_first_tag = lambda x: x[2].split(',')[1]
                    results = self.bdb.searchdb(
                        [get_first_tag(bm) for bm in self.bookmarks[:2]])
                    prompt(self.bdb, results)
                except StopIteration:
                    # catch exception thrown by reaching the end of the side effect iterable
                    pass

                # collect arguments passed to browse
                arg_list = [args[0] for args, _ in mock_browse.call_args_list]
                # expect a list of one-tuples that are bookmark URLs
                expected = [x[0] for x in self.bookmarks][:2]
                # checking if browse called with expected arguments
                self.assertEqual(arg_list, expected)

    # @unittest.skip('skipping')
    def test_delete_rec(self):
        # adding bookmark and getting index
        self.bdb.add_rec(*self.bookmarks[0])
        index = self.bdb.get_rec_id(self.bookmarks[0][0])
        # deleting bookmark
        self.bdb.delete_rec(index)
        # asserting it doesn't exist
        from_db = self.bdb.get_rec_by_id(index)
        self.assertIsNone(from_db)

    # @unittest.skip('skipping')
    def test_delete_rec_yes(self):
        # checking that "y" response causes delete_rec to return True
        with mock.patch('builtins.input', return_value='y'):
            self.assertTrue(self.bdb.delete_rec(0))

    # @unittest.skip('skipping')
    def test_delete_rec_no(self):
        # checking that non-"y" response causes delete_rec to return None
        with mock.patch('builtins.input', return_value='n'):
            self.assertFalse(self.bdb.delete_rec(0))

    # @unittest.skip('skipping')
    def test_cleardb(self):
        # adding bookmarks
        self.bdb.add_rec(*self.bookmarks[0])
        # deleting all bookmarks
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.cleardb()
        # assert table has been dropped
        with self.assertRaises(sqlite3.OperationalError) as ctx_man:
            self.bdb.get_rec_by_id(0)

        err_msg = str(ctx_man.exception)
        self.assertEqual(err_msg, 'no such table: bookmarks')

    # @unittest.skip('skipping')
    def test_replace_tag(self):
        indices = []
        for bookmark in self.bookmarks:
            # adding bookmark, getting index
            self.bdb.add_rec(*bookmark)
            index = self.bdb.get_rec_id(bookmark[0])
            indices += [index]

        # replacing tags
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("news", ["__01"])
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("zażółć", ["__02,__03"])

        # replacing tag which is also a substring of other tag
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("es", ["__04"])

        # removing tags
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("gęślą")
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("old")

        # removing non-existent tag
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("_")

        # removing nonexistent tag which is also a substring of other tag
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("e")

        for url, title, _, _ in self.bookmarks:
            # retrieving from db
            index = self.bdb.get_rec_id(url)
            from_db = self.bdb.get_rec_by_id(index)
            # asserting tags were replaced
            if title == "SLASHDOT":
                self.assertEqual(from_db[3], parse_tags(["__01"]))
            elif title == "ZAŻÓŁĆ":
                self.assertEqual(from_db[3], parse_tags(["__02,__03,jaźń"]))
            elif title == "test":
                self.assertEqual(from_db[3], parse_tags(["test,tes,est,__04"]))

    # def test_browse_by_index(self):
    # self.fail()

    # @unittest.skip('skipping')
    def test_close_quit(self):
        # quitting with no args
        try:
            self.bdb.close_quit()
        except SystemExit as err:
            self.assertEqual(err.args[0], 0)
        # quitting with custom arg
        try:
            self.bdb.close_quit(1)
        except SystemExit as err:
            self.assertEqual(err.args[0], 1)
Beispiel #5
0
class TestBukuDb(unittest.TestCase):

    def setUp(self):
        os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH

        # start every test from a clean state
        if exists(TEST_TEMP_DBFILE_PATH):
            os.remove(TEST_TEMP_DBFILE_PATH)

        self.bookmarks = TEST_BOOKMARKS
        self.bdb = BukuDb()

    def tearDown(self):
        os.environ['XDG_DATA_HOME'] = TEST_TEMP_DIR_PATH

    # @unittest.skip('skipping')
    @pytest.mark.non_tox
    def test_get_default_dbdir(self):
        dbdir_expected = TEST_TEMP_DBDIR_PATH
        dbdir_local_expected = os.path.join(os.path.expanduser('~'), '.local', 'share', 'buku')
        dbdir_relative_expected = os.path.abspath('.')

        # desktop linux
        self.assertEqual(dbdir_expected, BukuDb.get_default_dbdir())

        # desktop generic
        os.environ.pop('XDG_DATA_HOME')
        self.assertEqual(dbdir_local_expected, BukuDb.get_default_dbdir())

        # no desktop

        # -- home is defined differently on various platforms.
        # -- keep a copy and set it back once done
        originals = {}
        for env_var in ['HOME', 'HOMEPATH', 'HOMEDIR']:
            try:
                originals[env_var] = os.environ.pop(env_var)
            except KeyError:
                pass
        self.assertEqual(dbdir_relative_expected, BukuDb.get_default_dbdir())
        for key, value in list(originals.items()):
            os.environ[key] = value

    # # not sure how to test this in nondestructive manner
    # def test_move_legacy_dbfile(self):
    #     self.fail()

    # @unittest.skip('skipping')
    def test_initdb(self):
        if exists(TEST_TEMP_DBFILE_PATH):
            os.remove(TEST_TEMP_DBFILE_PATH)
        self.assertIs(False, exists(TEST_TEMP_DBFILE_PATH))
        conn, curr = BukuDb.initdb()
        self.assertIsInstance(conn, sqlite3.Connection)
        self.assertIsInstance(curr, sqlite3.Cursor)
        self.assertIs(True, exists(TEST_TEMP_DBFILE_PATH))
        curr.close()
        conn.close()

    # @unittest.skip('skipping')
    def test_get_rec_by_id(self):
        for bookmark in self.bookmarks:
            # adding bookmark from self.bookmarks
            self.bdb.add_rec(*bookmark)

        # the expected bookmark
        expected = (1, 'http://slashdot.org', 'SLASHDOT', ',news,old,',
                    "News for old nerds, stuff that doesn't matter", 0)
        bookmark_from_db = self.bdb.get_rec_by_id(1)
        # asserting bookmark matches expected
        self.assertEqual(expected, bookmark_from_db)
        # asserting None returned if index out of range
        self.assertIsNone(self.bdb.get_rec_by_id(len(self.bookmarks[0]) + 1))

    # @unittest.skip('skipping')
    def test_get_rec_id(self):
        for idx, bookmark in enumerate(self.bookmarks):
            # adding bookmark from self.bookmarks to database
            self.bdb.add_rec(*bookmark)
            # asserting index is in order
            idx_from_db = self.bdb.get_rec_id(bookmark[0])
            self.assertEqual(idx + 1, idx_from_db)

        # asserting -1 is returned for nonexistent url
        idx_from_db = self.bdb.get_rec_id("http://nonexistent.url")
        self.assertEqual(-1, idx_from_db)

    # @unittest.skip('skipping')
    def test_add_rec(self):
        for bookmark in self.bookmarks:
            # adding bookmark from self.bookmarks to database
            self.bdb.add_rec(*bookmark)
            # retrieving bookmark from database
            index = self.bdb.get_rec_id(bookmark[0])
            from_db = self.bdb.get_rec_by_id(index)
            self.assertIsNotNone(from_db)
            # comparing data
            for pair in zip(from_db[1:], bookmark):
                self.assertEqual(*pair)

        # TODO: tags should be passed to the api as a sequence...

    # @unittest.skip('skipping')
    def test_update_rec(self):
        old_values = self.bookmarks[0]
        new_values = self.bookmarks[1]

        # adding bookmark and getting index
        self.bdb.add_rec(*old_values)
        index = self.bdb.get_rec_id(old_values[0])
        # updating with new values
        self.bdb.update_rec(index, *new_values)
        # retrieving bookmark from database
        from_db = self.bdb.get_rec_by_id(index)
        self.assertIsNotNone(from_db)
        # checking if values are updated
        for pair in zip(from_db[1:], new_values):
            self.assertEqual(*pair)

    # @unittest.skip('skipping')
    def test_append_tag_at_index(self):
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # tags to add
        old_tags = self.bdb.get_rec_by_id(1)[3]
        new_tags = ",foo,bar,baz"
        self.bdb.append_tag_at_index(1, new_tags)
        # updated list of tags
        from_db = self.bdb.get_rec_by_id(1)[3]

        # checking if new tags were added to the bookmark
        self.assertTrue(split_and_test_membership(new_tags, from_db))
        # checking if old tags still exist
        self.assertTrue(split_and_test_membership(old_tags, from_db))

    # @unittest.skip('skipping')
    def test_append_tag_at_all_indices(self):
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # tags to add
        new_tags = ",foo,bar,baz"
        # record of original tags for each bookmark
        old_tagsets = {i: self.bdb.get_rec_by_id(i)[3] for i in inclusive_range(1, len(self.bookmarks))}

        with mock.patch('builtins.input', return_value='y'):
            self.bdb.append_tag_at_index(0, new_tags)
            # updated tags for each bookmark
            from_db = [(i, self.bdb.get_rec_by_id(i)[3]) for i in inclusive_range(1, len(self.bookmarks))]
            for index, tagset in from_db:
                # checking if new tags added to bookmark
                self.assertTrue(split_and_test_membership(new_tags, tagset))
                # checking if old tags still exist for boomark
                self.assertTrue(split_and_test_membership(old_tagsets[index], tagset))

    # @unittest.skip('skipping')
    def test_delete_tag_at_index(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        get_tags_at_idx = lambda i: self.bdb.get_rec_by_id(i)[3]
        # list of two-tuples, each containg bookmark index and corresponding tags
        tags_by_index = [(i, get_tags_at_idx(i)) for i in inclusive_range(1, len(self.bookmarks))]

        for i, tags in tags_by_index:
            # get the first tag from the bookmark
            to_delete = re.match(',.*?,', tags).group(0)
            self.bdb.delete_tag_at_index(i, to_delete)
            # get updated tags from db
            from_db = get_tags_at_idx(i)
            self.assertNotIn(to_delete, from_db)

    # @unittest.skip('skipping')
    @pytest.mark.slowtest
    def test_refreshdb(self):
        self.bdb.add_rec("https://www.google.com/ncr", "?")
        self.bdb.refreshdb(1, 1)
        from_db = self.bdb.get_rec_by_id(1)
        self.assertEqual(from_db[2], "Google")

    # @unittest.skip('skipping')
    def test_searchdb(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        get_first_tag = lambda x: ''.join(x[2].split(',')[:2])
        for i, bookmark in enumerate(self.bookmarks):
            tag_search = get_first_tag(bookmark)
            # search by the domain name for url
            url_search = re.match('https?://(.*)?\..*', bookmark[0]).group(1)
            title_search = bookmark[1]
            # Expect a five-tuple containing all bookmark data
            # db index, URL, title, tags, description
            expected = [(i + 1,) + tuple(bookmark)]
            # search db by tag, url (domain name), and title
            for keyword in (tag_search, url_search, title_search):
                with mock.patch('buku.prompt'):
                    # search by keyword
                    results = self.bdb.searchdb([keyword])
                    self.assertEqual(results, expected)

    # @unittest.skip('skipping')
    def test_search_by_tag(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        with mock.patch('buku.prompt'):
            get_first_tag = lambda x: ''.join(x[2].split(',')[:2])
            for i in range(len(self.bookmarks)):
                # search for bookmark with a tag that is known to exist
                results = self.bdb.search_by_tag(get_first_tag(self.bookmarks[i]))
                # Expect a five-tuple containing all bookmark data
                # db index, URL, title, tags, description
                expected = [(i + 1,) + tuple(self.bookmarks[i])]
                self.assertEqual(results, expected)

    def test_search_by_multiple_tags_search_any(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        new_bookmark = ['https://newbookmark.com',
                        'New Bookmark',
                        parse_tags(['test,old,new']),
                        'additional bookmark to test multiple tag search']

        self.bdb.add_rec(*new_bookmark)

        with mock.patch('buku.prompt'):
            # search for bookmarks matching ANY of the supplied tags
            results = self.bdb.search_by_tag('test, old')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (1, 'http://slashdot.org', 'SLASHDOT',
                 parse_tags([',news,old,']),
                 "News for old nerds, stuff that doesn't matter"),
                (3, 'https://test.com:8080', 'test',
                 parse_tags([',test,tes,est,es,']),
                 "a case for replace_tag test"),
                (4, 'https://newbookmark.com', 'New Bookmark',
                 parse_tags([',test,old,new,']),
                 'additional bookmark to test multiple tag search')
            ]
            self.assertEqual(results, expected)

    def test_search_by_multiple_tags_search_all(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        new_bookmark = ['https://newbookmark.com',
                        'New Bookmark',
                        parse_tags(['test,old,new']),
                        'additional bookmark to test multiple tag search']

        self.bdb.add_rec(*new_bookmark)

        with mock.patch('buku.prompt'):
            # search for bookmarks matching ALL of the supplied tags
            results = self.bdb.search_by_tag('test + old')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (4, 'https://newbookmark.com', 'New Bookmark',
                 parse_tags([',test,old,new,']),
                 'additional bookmark to test multiple tag search')
            ]
            self.assertEqual(results, expected)

    def test_search_by_tags_enforces_space_seprations_search_all(self):

        bookmark1 = ['https://bookmark1.com',
                     'Bookmark One',
                     parse_tags(['tag, two,tag+two']),
                     "test case for bookmark with '+' in tag"]

        bookmark2 = ['https://bookmark2.com',
                     'Bookmark Two',
                     parse_tags(['tag,two, tag-two']),
                     "test case for bookmark with hyphenated tag"]

        self.bdb.add_rec(*bookmark1)
        self.bdb.add_rec(*bookmark2)

        with mock.patch('buku.prompt'):
            # check that space separation for ' + ' operator is enforced
            results = self.bdb.search_by_tag('tag+two')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (1, 'https://bookmark1.com', 'Bookmark One',
                 parse_tags([',tag,two,tag+two,']),
                 "test case for bookmark with '+' in tag")
            ]
            self.assertEqual(results, expected)
            results = self.bdb.search_by_tag('tag + two')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (1, 'https://bookmark1.com', 'Bookmark One',
                 parse_tags([',tag,two,tag+two,']),
                 "test case for bookmark with '+' in tag"),
                (2, 'https://bookmark2.com', 'Bookmark Two',
                 parse_tags([',tag,two,tag-two,']),
                 "test case for bookmark with hyphenated tag"),
            ]
            self.assertEqual(results, expected)

    def test_search_by_tags_exclusion(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        new_bookmark = ['https://newbookmark.com',
                        'New Bookmark',
                        parse_tags(['test,old,new']),
                        'additional bookmark to test multiple tag search']

        self.bdb.add_rec(*new_bookmark)

        with mock.patch('buku.prompt'):
            # search for bookmarks matching ANY of the supplied tags
            # while excluding bookmarks from results that match a given tag
            results = self.bdb.search_by_tag('test, old - est')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (1, 'http://slashdot.org', 'SLASHDOT',
                 parse_tags([',news,old,']),
                 "News for old nerds, stuff that doesn't matter"),
                (4, 'https://newbookmark.com', 'New Bookmark',
                 parse_tags([',test,old,new,']),
                 'additional bookmark to test multiple tag search')
            ]
            self.assertEqual(results, expected)

    def test_search_by_tags_enforces_space_seprations_exclusion(self):

        bookmark1 = ['https://bookmark1.com',
                     'Bookmark One',
                     parse_tags(['tag, two,tag+two']),
                     "test case for bookmark with '+' in tag"]

        bookmark2 = ['https://bookmark2.com',
                     'Bookmark Two',
                     parse_tags(['tag,two, tag-two']),
                     "test case for bookmark with hyphenated tag"]

        bookmark3 = ['https://bookmark3.com',
                     'Bookmark Three',
                     parse_tags(['tag, tag three']),
                     "second test case for bookmark with hyphenated tag"]

        self.bdb.add_rec(*bookmark1)
        self.bdb.add_rec(*bookmark2)
        self.bdb.add_rec(*bookmark3)

        with mock.patch('buku.prompt'):
            # check that space separation for ' - ' operator is enforced
            results = self.bdb.search_by_tag('tag-two')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (2, 'https://bookmark2.com', 'Bookmark Two',
                 parse_tags([',tag,two,tag-two,']),
                 "test case for bookmark with hyphenated tag"),
            ]
            self.assertEqual(results, expected)
            results = self.bdb.search_by_tag('tag - two')
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (3, 'https://bookmark3.com', 'Bookmark Three',
                 parse_tags([',tag,tag three,']),
                 "second test case for bookmark with hyphenated tag"),
            ]
            self.assertEqual(results, expected)

    # @unittest.skip('skipping')
    def test_search_and_open_in_broswer_by_range(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # simulate user input, select range of indices 1-3
        index_range = '1-%s' % len(self.bookmarks)
        with mock.patch('builtins.input', side_effect=[index_range]):
            with mock.patch('buku.browse') as mock_browse:
                try:
                    # search the db with keywords from each bookmark
                    # searching using the first tag from bookmarks
                    get_first_tag = lambda x: x[2].split(',')[1]
                    results = self.bdb.searchdb([get_first_tag(bm) for bm in self.bookmarks])
                    prompt(self.bdb, results)
                except StopIteration:
                    # catch exception thrown by reaching the end of the side effect iterable
                    pass

                # collect arguments passed to browse
                arg_list = [args[0] for args, _ in mock_browse.call_args_list]
                # expect a list of one-tuples that are bookmark URLs
                expected = [x[0] for x in self.bookmarks]
                # checking if browse called with expected arguments
                self.assertEqual(arg_list, expected)

    # @unittest.skip('skipping')
    def test_search_and_open_all_in_browser(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # simulate user input, select 'a' to open all bookmarks in results
        with mock.patch('builtins.input', side_effect=['a']):
            with mock.patch('buku.browse') as mock_browse:
                try:
                    # search the db with keywords from each bookmark
                    # searching using the first tag from bookmarks
                    get_first_tag = lambda x: x[2].split(',')[1]
                    results = self.bdb.searchdb([get_first_tag(bm) for bm in self.bookmarks[:2]])
                    prompt(self.bdb, results)
                except StopIteration:
                    # catch exception thrown by reaching the end of the side effect iterable
                    pass

                # collect arguments passed to browse
                arg_list = [args[0] for args, _ in mock_browse.call_args_list]
                # expect a list of one-tuples that are bookmark URLs
                expected = [x[0] for x in self.bookmarks][:2]
                # checking if browse called with expected arguments
                self.assertEqual(arg_list, expected)

    # @unittest.skip('skipping')
    def test_delete_rec(self):
        # adding bookmark and getting index
        self.bdb.add_rec(*self.bookmarks[0])
        index = self.bdb.get_rec_id(self.bookmarks[0][0])
        # deleting bookmark
        self.bdb.delete_rec(index)
        # asserting it doesn't exist
        from_db = self.bdb.get_rec_by_id(index)
        self.assertIsNone(from_db)

    # @unittest.skip('skipping')
    def test_delete_rec_yes(self):
        # checking that "y" response causes delete_rec to return True
        with mock.patch('builtins.input', return_value='y'):
            self.assertTrue(self.bdb.delete_rec(0))

    # @unittest.skip('skipping')
    def test_delete_rec_no(self):
        # checking that non-"y" response causes delete_rec to return None
        with mock.patch('builtins.input', return_value='n'):
            self.assertFalse(self.bdb.delete_rec(0))

    # @unittest.skip('skipping')
    def test_cleardb(self):
        # adding bookmarks
        self.bdb.add_rec(*self.bookmarks[0])
        # deleting all bookmarks
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.cleardb()
        # assert table has been dropped
        with self.assertRaises(sqlite3.OperationalError) as ctx_man:
            self.bdb.get_rec_by_id(0)

        err_msg = str(ctx_man.exception)
        self.assertEqual(err_msg, 'no such table: bookmarks')

    # @unittest.skip('skipping')
    def test_replace_tag(self):
        indices = []
        for bookmark in self.bookmarks:
            # adding bookmark, getting index
            self.bdb.add_rec(*bookmark)
            index = self.bdb.get_rec_id(bookmark[0])
            indices += [index]

        # replacing tags
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("news", ["__01"])
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("zażółć", ["__02,__03"])

        # replacing tag which is also a substring of other tag
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("es", ["__04"])

        # removing tags
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("gęślą")
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("old")

        # removing non-existent tag
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("_")

        # removing nonexistent tag which is also a substring of other tag
        with mock.patch('builtins.input', return_value='y'):
            self.bdb.replace_tag("e")

        for url, title, _, _ in self.bookmarks:
            # retrieving from db
            index = self.bdb.get_rec_id(url)
            from_db = self.bdb.get_rec_by_id(index)
            # asserting tags were replaced
            if title == "SLASHDOT":
                self.assertEqual(from_db[3], parse_tags(["__01"]))
            elif title == "ZAŻÓŁĆ":
                self.assertEqual(from_db[3], parse_tags(["__02,__03,jaźń"]))
            elif title == "test":
                self.assertEqual(from_db[3], parse_tags(["test,tes,est,__04"]))

    # def test_browse_by_index(self):
        # self.fail()

    # @unittest.skip('skipping')
    def test_close_quit(self):
        # quitting with no args
        try:
            self.bdb.close_quit()
        except SystemExit as err:
            self.assertEqual(err.args[0], 0)
        # quitting with custom arg
        try:
            self.bdb.close_quit(1)
        except SystemExit as err:
            self.assertEqual(err.args[0], 1)
Beispiel #6
0
class TestBukuDb(unittest.TestCase):
    def setUp(self):
        os.environ["XDG_DATA_HOME"] = TEST_TEMP_DIR_PATH

        # start every test from a clean state
        if exists(TEST_TEMP_DBFILE_PATH):
            os.remove(TEST_TEMP_DBFILE_PATH)

        self.bookmarks = TEST_BOOKMARKS
        self.bdb = BukuDb()

    def tearDown(self):
        os.environ["XDG_DATA_HOME"] = TEST_TEMP_DIR_PATH

    @pytest.mark.non_tox
    def test_get_default_dbdir(self):
        dbdir_expected = TEST_TEMP_DBDIR_PATH
        dbdir_local_expected = os.path.join(
            os.path.expanduser("~"), ".local", "share", "buku"
        )
        dbdir_relative_expected = os.path.abspath(".")

        # desktop linux
        self.assertEqual(dbdir_expected, BukuDb.get_default_dbdir())

        # desktop generic
        os.environ.pop("XDG_DATA_HOME")
        self.assertEqual(dbdir_local_expected, BukuDb.get_default_dbdir())

        # no desktop

        # -- home is defined differently on various platforms.
        # -- keep a copy and set it back once done
        originals = {}
        for env_var in ["HOME", "HOMEPATH", "HOMEDIR"]:
            try:
                originals[env_var] = os.environ.pop(env_var)
            except KeyError:
                pass
        self.assertEqual(dbdir_relative_expected, BukuDb.get_default_dbdir())
        for key, value in list(originals.items()):
            os.environ[key] = value

    # # not sure how to test this in nondestructive manner
    # def test_move_legacy_dbfile(self):
    #     self.fail()

    def test_initdb(self):
        if exists(TEST_TEMP_DBFILE_PATH):
            os.remove(TEST_TEMP_DBFILE_PATH)
        self.assertIs(False, exists(TEST_TEMP_DBFILE_PATH))
        conn, curr = BukuDb.initdb()
        self.assertIsInstance(conn, sqlite3.Connection)
        self.assertIsInstance(curr, sqlite3.Cursor)
        self.assertIs(True, exists(TEST_TEMP_DBFILE_PATH))
        curr.close()
        conn.close()

    def test_get_rec_by_id(self):
        for bookmark in self.bookmarks:
            # adding bookmark from self.bookmarks
            self.bdb.add_rec(*bookmark)

        # the expected bookmark
        expected = (
            1,
            "http://slashdot.org",
            "SLASHDOT",
            ",news,old,",
            "News for old nerds, stuff that doesn't matter",
            0,
        )
        bookmark_from_db = self.bdb.get_rec_by_id(1)
        # asserting bookmark matches expected
        self.assertEqual(expected, bookmark_from_db)
        # asserting None returned if index out of range
        self.assertIsNone(self.bdb.get_rec_by_id(len(self.bookmarks[0]) + 1))

    def test_get_rec_id(self):
        for idx, bookmark in enumerate(self.bookmarks):
            # adding bookmark from self.bookmarks to database
            self.bdb.add_rec(*bookmark)
            # asserting index is in order
            idx_from_db = self.bdb.get_rec_id(bookmark[0])
            self.assertEqual(idx + 1, idx_from_db)

        # asserting -1 is returned for nonexistent url
        idx_from_db = self.bdb.get_rec_id("http://nonexistent.url")
        self.assertEqual(-1, idx_from_db)

    def test_add_rec(self):
        for bookmark in self.bookmarks:
            # adding bookmark from self.bookmarks to database
            self.bdb.add_rec(*bookmark)
            # retrieving bookmark from database
            index = self.bdb.get_rec_id(bookmark[0])
            from_db = self.bdb.get_rec_by_id(index)
            self.assertIsNotNone(from_db)
            # comparing data
            for pair in zip(from_db[1:], bookmark):
                self.assertEqual(*pair)

        # TODO: tags should be passed to the api as a sequence...

    def test_suggest_tags(self):
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        tagstr = ",test,old,"
        with mock.patch("builtins.input", return_value="1 2 3"):
            expected_results = ",es,est,news,old,test,"
            suggested_results = self.bdb.suggest_similar_tag(tagstr)
            self.assertEqual(expected_results, suggested_results)

        # returns user supplied tags if none are in the DB
        tagstr = ",uniquetag1,uniquetag2,"
        expected_results = tagstr
        suggested_results = self.bdb.suggest_similar_tag(tagstr)
        self.assertEqual(expected_results, suggested_results)

    def test_update_rec(self):
        old_values = self.bookmarks[0]
        new_values = self.bookmarks[1]

        # adding bookmark and getting index
        self.bdb.add_rec(*old_values)
        index = self.bdb.get_rec_id(old_values[0])
        # updating with new values
        self.bdb.update_rec(index, *new_values)
        # retrieving bookmark from database
        from_db = self.bdb.get_rec_by_id(index)
        self.assertIsNotNone(from_db)
        # checking if values are updated
        for pair in zip(from_db[1:], new_values):
            self.assertEqual(*pair)

    def test_append_tag_at_index(self):
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # tags to add
        old_tags = self.bdb.get_rec_by_id(1)[3]
        new_tags = ",foo,bar,baz"
        self.bdb.append_tag_at_index(1, new_tags)
        # updated list of tags
        from_db = self.bdb.get_rec_by_id(1)[3]

        # checking if new tags were added to the bookmark
        self.assertTrue(split_and_test_membership(new_tags, from_db))
        # checking if old tags still exist
        self.assertTrue(split_and_test_membership(old_tags, from_db))

    def test_append_tag_at_all_indices(self):
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # tags to add
        new_tags = ",foo,bar,baz"
        # record of original tags for each bookmark
        old_tagsets = {
            i: self.bdb.get_rec_by_id(i)[3]
            for i in inclusive_range(1, len(self.bookmarks))
        }

        with mock.patch("builtins.input", return_value="y"):
            self.bdb.append_tag_at_index(0, new_tags)
            # updated tags for each bookmark
            from_db = [
                (i, self.bdb.get_rec_by_id(i)[3])
                for i in inclusive_range(1, len(self.bookmarks))
            ]
            for index, tagset in from_db:
                # checking if new tags added to bookmark
                self.assertTrue(split_and_test_membership(new_tags, tagset))
                # checking if old tags still exist for boomark
                self.assertTrue(split_and_test_membership(old_tagsets[index], tagset))

    def test_delete_tag_at_index(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        get_tags_at_idx = lambda i: self.bdb.get_rec_by_id(i)[3]
        # list of two-tuples, each containg bookmark index and corresponding tags
        tags_by_index = [
            (i, get_tags_at_idx(i)) for i in inclusive_range(1, len(self.bookmarks))
        ]

        for i, tags in tags_by_index:
            # get the first tag from the bookmark
            to_delete = re.match(",.*?,", tags).group(0)
            self.bdb.delete_tag_at_index(i, to_delete)
            # get updated tags from db
            from_db = get_tags_at_idx(i)
            self.assertNotIn(to_delete, from_db)

    def test_search_keywords_and_filter_by_tags(self):
        # adding bookmark
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        with mock.patch("buku.prompt"):
            expected = [
                (
                    3,
                    "http://example.com/",
                    "test",
                    ",es,est,tes,test,",
                    "a case for replace_tag test",
                    0,
                )
            ]
            results = self.bdb.search_keywords_and_filter_by_tags(
                ["News", "case"],
                False,
                False,
                False,
                ["est"],
            )
            self.assertIn(expected[0], results)
            expected = [
                (
                    3,
                    "http://example.com/",
                    "test",
                    ",es,est,tes,test,",
                    "a case for replace_tag test",
                    0,
                ),
                (
                    2,
                    "http://www.zażółćgęśląjaźń.pl/",
                    "ZAŻÓŁĆ",
                    ",gęślą,jaźń,zażółć,",
                    "Testing UTF-8, zażółć gęślą jaźń.",
                    0,
                ),
            ]
            results = self.bdb.search_keywords_and_filter_by_tags(
                ["UTF-8", "case"],
                False,
                False,
                False,
                "jaźń, test",
            )
            self.assertIn(expected[0], results)
            self.assertIn(expected[1], results)

    def test_searchdb(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        get_first_tag = lambda x: "".join(x[2].split(",")[:2])
        for i, bookmark in enumerate(self.bookmarks):
            tag_search = get_first_tag(bookmark)
            # search by the domain name for url
            url_search = re.match(r"https?://(.*)?\..*", bookmark[0]).group(1)
            title_search = bookmark[1]
            # Expect a five-tuple containing all bookmark data
            # db index, URL, title, tags, description
            expected = [(i + 1,) + tuple(bookmark)]
            expected[0] += tuple([0])
            # search db by tag, url (domain name), and title
            for keyword in (tag_search, url_search, title_search):
                with mock.patch("buku.prompt"):
                    # search by keyword
                    results = self.bdb.searchdb([keyword])
                    self.assertEqual(results, expected)

    def test_search_by_tag(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        with mock.patch("buku.prompt"):
            get_first_tag = lambda x: "".join(x[2].split(",")[:2])
            for i, bookmark in enumerate(self.bookmarks):
                # search for bookmark with a tag that is known to exist
                results = self.bdb.search_by_tag(get_first_tag(bookmark))
                # Expect a five-tuple containing all bookmark data
                # db index, URL, title, tags, description
                expected = [(i + 1,) + tuple(bookmark)]
                expected[0] += tuple([0])
                self.assertEqual(results, expected)

    @vcr.use_cassette(
        "tests/vcr_cassettes/test_search_by_multiple_tags_search_any.yaml"
    )
    def test_search_by_multiple_tags_search_any(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        new_bookmark = [
            "https://newbookmark.com",
            "New Bookmark",
            parse_tags(["test,old,new"]),
            "additional bookmark to test multiple tag search",
            0,
        ]

        self.bdb.add_rec(*new_bookmark)

        with mock.patch("buku.prompt"):
            # search for bookmarks matching ANY of the supplied tags
            results = self.bdb.search_by_tag("test, old")
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description, ordered by records with
            # the most number of matches.
            expected = [
                (
                    4,
                    "https://newbookmark.com",
                    "New Bookmark",
                    parse_tags([",test,old,new,"]),
                    "additional bookmark to test multiple tag search",
                    0,
                ),
                (
                    1,
                    "http://slashdot.org",
                    "SLASHDOT",
                    parse_tags([",news,old,"]),
                    "News for old nerds, stuff that doesn't matter",
                    0,
                ),
                (
                    3,
                    "http://example.com/",
                    "test",
                    ",es,est,tes,test,",
                    "a case for replace_tag test",
                    0,
                ),
            ]
            self.assertEqual(results, expected)

    @vcr.use_cassette(
        "tests/vcr_cassettes/test_search_by_multiple_tags_search_all.yaml"
    )
    def test_search_by_multiple_tags_search_all(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        new_bookmark = [
            "https://newbookmark.com",
            "New Bookmark",
            parse_tags(["test,old,new"]),
            "additional bookmark to test multiple tag search",
        ]

        self.bdb.add_rec(*new_bookmark)

        with mock.patch("buku.prompt"):
            # search for bookmarks matching ALL of the supplied tags
            results = self.bdb.search_by_tag("test + old")
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (
                    4,
                    "https://newbookmark.com",
                    "New Bookmark",
                    parse_tags([",test,old,new,"]),
                    "additional bookmark to test multiple tag search",
                    0,
                )
            ]
            self.assertEqual(results, expected)

    def test_search_by_tags_enforces_space_seprations_search_all(self):

        bookmark1 = [
            "https://bookmark1.com",
            "Bookmark One",
            parse_tags(["tag, two,tag+two"]),
            "test case for bookmark with '+' in tag",
        ]

        bookmark2 = [
            "https://bookmark2.com",
            "Bookmark Two",
            parse_tags(["tag,two, tag-two"]),
            "test case for bookmark with hyphenated tag",
        ]

        self.bdb.add_rec(*bookmark1)
        self.bdb.add_rec(*bookmark2)

        with mock.patch("buku.prompt"):
            # check that space separation for ' + ' operator is enforced
            results = self.bdb.search_by_tag("tag+two")
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (
                    1,
                    "https://bookmark1.com",
                    "Bookmark One",
                    parse_tags([",tag,two,tag+two,"]),
                    "test case for bookmark with '+' in tag",
                    0,
                )
            ]
            self.assertEqual(results, expected)
            results = self.bdb.search_by_tag("tag + two")
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (
                    1,
                    "https://bookmark1.com",
                    "Bookmark One",
                    parse_tags([",tag,two,tag+two,"]),
                    "test case for bookmark with '+' in tag",
                    0,
                ),
                (
                    2,
                    "https://bookmark2.com",
                    "Bookmark Two",
                    parse_tags([",tag,two,tag-two,"]),
                    "test case for bookmark with hyphenated tag",
                    0,
                ),
            ]
            self.assertEqual(results, expected)

    def test_search_by_tags_exclusion(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        new_bookmark = [
            "https://newbookmark.com",
            "New Bookmark",
            parse_tags(["test,old,new"]),
            "additional bookmark to test multiple tag search",
        ]

        self.bdb.add_rec(*new_bookmark)

        with mock.patch("buku.prompt"):
            # search for bookmarks matching ANY of the supplied tags
            # while excluding bookmarks from results that match a given tag
            results = self.bdb.search_by_tag("test, old - est")
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (
                    4,
                    "https://newbookmark.com",
                    "New Bookmark",
                    parse_tags([",test,old,new,"]),
                    "additional bookmark to test multiple tag search",
                    0,
                ),
                (
                    1,
                    "http://slashdot.org",
                    "SLASHDOT",
                    parse_tags([",news,old,"]),
                    "News for old nerds, stuff that doesn't matter",
                    0,
                ),
            ]
            self.assertEqual(results, expected)

    @vcr.use_cassette(
        "tests/vcr_cassettes/test_search_by_tags_enforces_space_seprations_exclusion.yaml"
    )
    def test_search_by_tags_enforces_space_seprations_exclusion(self):

        bookmark1 = [
            "https://bookmark1.com",
            "Bookmark One",
            parse_tags(["tag, two,tag+two"]),
            "test case for bookmark with '+' in tag",
        ]

        bookmark2 = [
            "https://bookmark2.com",
            "Bookmark Two",
            parse_tags(["tag,two, tag-two"]),
            "test case for bookmark with hyphenated tag",
        ]

        bookmark3 = [
            "https://bookmark3.com",
            "Bookmark Three",
            parse_tags(["tag, tag three"]),
            "second test case for bookmark with hyphenated tag",
        ]

        self.bdb.add_rec(*bookmark1)
        self.bdb.add_rec(*bookmark2)
        self.bdb.add_rec(*bookmark3)

        with mock.patch("buku.prompt"):
            # check that space separation for ' - ' operator is enforced
            results = self.bdb.search_by_tag("tag-two")
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (
                    2,
                    "https://bookmark2.com",
                    "Bookmark Two",
                    parse_tags([",tag,two,tag-two,"]),
                    "test case for bookmark with hyphenated tag",
                    0,
                ),
            ]
            self.assertEqual(results, expected)
            results = self.bdb.search_by_tag("tag - two")
            # Expect a list of five-element tuples containing all bookmark data
            # db index, URL, title, tags, description
            expected = [
                (
                    3,
                    "https://bookmark3.com",
                    "Bookmark Three",
                    parse_tags([",tag,tag three,"]),
                    "second test case for bookmark with hyphenated tag",
                    0,
                ),
            ]
            self.assertEqual(results, expected)

    def test_search_and_open_in_broswer_by_range(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # simulate user input, select range of indices 1-3
        index_range = "1-%s" % len(self.bookmarks)
        with mock.patch("builtins.input", side_effect=[index_range]):
            with mock.patch("buku.browse") as mock_browse:
                try:
                    # search the db with keywords from each bookmark
                    # searching using the first tag from bookmarks
                    get_first_tag = lambda x: x[2].split(",")[1]
                    results = self.bdb.searchdb(
                        [get_first_tag(bm) for bm in self.bookmarks]
                    )
                    prompt(self.bdb, results)
                except StopIteration:
                    # catch exception thrown by reaching the end of the side effect iterable
                    pass

                # collect arguments passed to browse
                arg_list = [args[0] for args, _ in mock_browse.call_args_list]
                # expect a list of one-tuples that are bookmark URLs
                expected = [x[0] for x in self.bookmarks]
                # checking if browse called with expected arguments
                self.assertEqual(arg_list, expected)

    @vcr.use_cassette("tests/vcr_cassettes/test_search_and_open_all_in_browser.yaml")
    def test_search_and_open_all_in_browser(self):
        # adding bookmarks
        for bookmark in self.bookmarks:
            self.bdb.add_rec(*bookmark)

        # simulate user input, select 'a' to open all bookmarks in results
        with mock.patch("builtins.input", side_effect=["a"]):
            with mock.patch("buku.browse") as mock_browse:
                try:
                    # search the db with keywords from each bookmark
                    # searching using the first tag from bookmarks
                    get_first_tag = lambda x: x[2].split(",")[1]
                    results = self.bdb.searchdb(
                        [get_first_tag(bm) for bm in self.bookmarks[:2]]
                    )
                    prompt(self.bdb, results)
                except StopIteration:
                    # catch exception thrown by reaching the end of the side effect iterable
                    pass

                # collect arguments passed to browse
                arg_list = [args[0] for args, _ in mock_browse.call_args_list]
                # expect a list of one-tuples that are bookmark URLs
                expected = [x[0] for x in self.bookmarks][:2]
                # checking if browse called with expected arguments
                self.assertEqual(arg_list, expected)

    def test_delete_rec(self):
        # adding bookmark and getting index
        self.bdb.add_rec(*self.bookmarks[0])
        index = self.bdb.get_rec_id(self.bookmarks[0][0])
        # deleting bookmark
        self.bdb.delete_rec(index)
        # asserting it doesn't exist
        from_db = self.bdb.get_rec_by_id(index)
        self.assertIsNone(from_db)

    def test_delete_rec_yes(self):
        # checking that "y" response causes delete_rec to return True
        with mock.patch("builtins.input", return_value="y"):
            self.assertTrue(self.bdb.delete_rec(0))

    def test_delete_rec_no(self):
        # checking that non-"y" response causes delete_rec to return None
        with mock.patch("builtins.input", return_value="n"):
            self.assertFalse(self.bdb.delete_rec(0))

    def test_cleardb(self):
        # adding bookmarks
        self.bdb.add_rec(*self.bookmarks[0])
        # deleting all bookmarks
        with mock.patch("builtins.input", return_value="y"):
            self.bdb.cleardb()
        # assert table has been dropped
        assert self.bdb.get_rec_by_id(0) is None

    def test_replace_tag(self):
        indices = []
        for bookmark in self.bookmarks:
            # adding bookmark, getting index
            self.bdb.add_rec(*bookmark)
            index = self.bdb.get_rec_id(bookmark[0])
            indices += [index]

        # replacing tags
        with mock.patch("builtins.input", return_value="y"):
            self.bdb.replace_tag("news", ["__01"])
        with mock.patch("builtins.input", return_value="y"):
            self.bdb.replace_tag("zażółć", ["__02,__03"])

        # replacing tag which is also a substring of other tag
        with mock.patch("builtins.input", return_value="y"):
            self.bdb.replace_tag("es", ["__04"])

        # removing tags
        with mock.patch("builtins.input", return_value="y"):
            self.bdb.replace_tag("gęślą")
        with mock.patch("builtins.input", return_value="y"):
            self.bdb.replace_tag("old")

        # removing non-existent tag
        with mock.patch("builtins.input", return_value="y"):
            self.bdb.replace_tag("_")

        # removing nonexistent tag which is also a substring of other tag
        with mock.patch("builtins.input", return_value="y"):
            self.bdb.replace_tag("e")

        for url, title, _, _ in self.bookmarks:
            # retrieving from db
            index = self.bdb.get_rec_id(url)
            from_db = self.bdb.get_rec_by_id(index)
            # asserting tags were replaced
            if title == "SLASHDOT":
                self.assertEqual(from_db[3], parse_tags(["__01"]))
            elif title == "ZAŻÓŁĆ":
                self.assertEqual(from_db[3], parse_tags(["__02,__03,jaźń"]))
            elif title == "test":
                self.assertEqual(from_db[3], parse_tags(["test,tes,est,__04"]))

    def test_tnyfy_url(self):
        # shorten a well-known url
        shorturl = self.bdb.tnyfy_url(url="https://www.google.com", shorten=True)
        self.assertEqual(shorturl, "http://tny.im/yt")

        # expand a well-known short url
        url = self.bdb.tnyfy_url(url="http://tny.im/yt", shorten=False)
        self.assertEqual(url, "https://www.google.com")

    # def test_browse_by_index(self):
    # self.fail()

    def test_close_quit(self):
        # quitting with no args
        try:
            self.bdb.close_quit()
        except SystemExit as err:
            self.assertEqual(err.args[0], 0)
        # quitting with custom arg
        try:
            self.bdb.close_quit(1)
        except SystemExit as err:
            self.assertEqual(err.args[0], 1)