Feature/search multiple tags (#187)

* Implement feature "Search multiple tags, exclusion in tag search"

Add failing test for searching any multiple set of tags

Search any tags works; passing tests

Add failing test for searching all multiple tags

Search all tags works

Do not allow combination of search logics

Adds support for tag exclusion search

Update Search Options Group

Update README

Clean up

Fix man/readme language

Fix search_by_tag docstring

Split tags by ' - ' not '-': allows for hyphenation

Remove unnecessary else

Change search_logic lang to search_operator

Update exclusion language in readme and man

Print warning if ',' and '+' are both used in a search

* Add documentation and examples to manpage, improve documentation in README and buku.py

* Enforce space separation of '+' and document

* Fix under-indent in tests that causes build failure

* Add test to check that search_by_tag constructs correct sqlite query; also remove unittest.skips from search_by_tag tests

* Move tag search preparation work into prep_tag_search helper function; add test to test_buku.py

* Use pytest.mark.parametrize for test_prep_tag_search
This commit is contained in:
Alex 2017-08-14 17:07:01 -04:00 committed by Arun Prakash Jana
parent 281613bc40
commit 2b7f142245
5 changed files with 365 additions and 47 deletions

View File

@ -189,9 +189,12 @@ SEARCH OPTIONS:
"immutable": entries with locked title
--deep match substrings ('pen' matches 'opens')
-r, --sreg run a regex search
-t, --stag search bookmarks by a tag
-t, --stag [tag [,|+] ...] [- tag, ...]
search bookmarks by tags
use ',' to find entries matching ANY tag
use '+' to find entries matching ALL tags
excludes entries matching tags following ' - '
list all tags, if no search keywords
ENCRYPTION OPTIONS:
-l, --lock [N] encrypt DB file with N (> 0, default 8)
hash iterations to generate key
@ -242,7 +245,7 @@ PROMPT KEYS:
S keyword [...] search for records with ALL keywords
d match substrings ('pen' matches 'opened')
r expression run a regex search
t [...] search bookmarks by a tag or show taglist
t [...] search bookmarks by tags or show taglist
list index after a tag listing shows records with the tag
o id|range [...] browse bookmarks by indices and/or ranges
p id|range [...] print bookmarks by indices and/or ranges
@ -284,7 +287,7 @@ PROMPT KEYS:
- --sall : match all the keywords in URL, title or tags.
- --deep : match **substrings** (`match` matches `rematched`) in URL, title and tags.
- --sreg : match a regular expression (ignores --deep).
- --stag : search bookmarks by a tag, or list all tags alphabetically with usage count (if no arguments).
- --stag : search bookmarks by tags, or list all tags alphabetically with usage count (if no arguments). Delimit the list of tags in the query with `,` to search for bookmarks that match ANY of the listed tags. Delimit tags with `+` to search for bookmarks that match ALL of the listed tags. Note that `,` and `+` cannot be used together in the same search. Exclude bookmarks matching certain tags from the results by using ` - ` followed by the tags. Note that the ` - ` operator and the ` + ` delimiter must be space separated: ` - ` instead of `-` and ` + ` instead of `+`. This is to distinguish them from hyphenated tags (e.g., `some-tag-name`) and tags with '+'s (e.g., `some+tag+name`).
- Search results are indexed serially. This index is different from actual database index of a bookmark record which is shown within `[]` after the title.
- **Import**:
- URLs starting with `place:`, `file://` and `apt:` are ignored during import.
@ -498,54 +501,63 @@ for resp, url in zip(gr_results, urls):
18. **Search** bookmarks **tagged** `general kernel concepts`:
$ buku --stag general kernel concepts
19. List **all unique tags** alphabetically:
19. **Search** for bookmarks matching **ANY** of the tags `kernel`, `debugging`, `general kernel concepts`:
$ buku --stag kernel, debugging, general kernel concepts
20. **Search** for bookmarks matching **ALL** of the tags `kernel`, `debugging`, `general kernel concepts`:
$ buku --stag kernel + debugging + general kernel concepts
21. **Search** for bookmarks matching both the tags `kernel` and `debugging`, but **excluding** bookmarks matching the tag `general kernel concepts`:
$ buku --stag kernel + debugging - general kernel concepts
22. List **all unique tags** alphabetically:
$ buku --stag
20. Run a **search and update** the results:
23. Run a **search and update** the results:
$ buku -s kernel debugging -u --tag + linux kernel
21. Run a **search and delete** the results:
24. Run a **search and delete** the results:
$ buku -s kernel debugging -d
22. **Encrypt or decrypt** DB with **custom number of iterations** (15) to generate key:
25. **Encrypt or decrypt** DB with **custom number of iterations** (15) to generate key:
$ buku -l 15
$ buku -k 15
The same number of iterations must be specified for one lock & unlock instance. Default is 8, if omitted.
23. **Show details** of bookmarks at index 15012014 and ranges 20-30, 40-50:
26. **Show details** of bookmarks at index 15012014 and ranges 20-30, 40-50:
$ buku -p 20-30 15012014 40-50
24. Show details of the **last 10 bookmarks**:
27. Show details of the **last 10 bookmarks**:
$ buku -p -10
25. **Show all** bookmarks with real index from database:
28. **Show all** bookmarks with real index from database:
$ buku -p
$ buku -p | more
26. **Replace tag** 'old tag' with 'new tag':
29. **Replace tag** 'old tag' with 'new tag':
$ buku --replace 'old tag' 'new tag'
27. **Delete tag** 'old tag' from DB:
30. **Delete tag** 'old tag' from DB:
$ buku --replace 'old tag'
28. **Append (or delete) tags** 'tag 1', 'tag 2' to (or from) existing tags of bookmark at index 15012014:
31. **Append (or delete) tags** 'tag 1', 'tag 2' to (or from) existing tags of bookmark at index 15012014:
$ buku -u 15012014 --tag + tag 1, tag 2
$ buku -u 15012014 --tag - tag 1, tag 2
29. **Open URL** at index 15012014 in browser:
32. **Open URL** at index 15012014 in browser:
$ buku -o 15012014
30. List bookmarks with **no title or tags** for bookkeeping:
33. List bookmarks with **no title or tags** for bookkeeping:
$ buku -S blank
31. List bookmarks with **immutable title**:
34. List bookmarks with **immutable title**:
$ buku -S immutable
32. **Shorten URL** www.google.com and the URL at index 20:
35. **Shorten URL** www.google.com and the URL at index 20:
$ buku --shorten www.google.com
$ buku --shorten 20
33. **Append, remove tags at prompt** (taglist index to the left, bookmark index to the right):
36. **Append, remove tags at prompt** (taglist index to the left, bookmark index to the right):
// append tags at taglist indices 4 and 6-9 to existing tags in bookmarks at indices 5 and 2-3
buku (? for help) g 4 9-6 >> 5 3-2
@ -555,7 +567,7 @@ for resp, url in zip(gr_results, urls):
buku (? for help) g > 5 3-2
// remove tags at taglist indices 4 and 6-9 from tags in bookmarks at indices 5 and 2-3
buku (? for help) g 4 9-6 << 5 3-2
34. More **help**:
37. More **help**:
$ buku -h
$ man buku

66
buku.1
View File

@ -69,7 +69,7 @@ Bookmarks with immutable titles are listed with '(L)' after the title.
- --sall : match all the keywords in URL, title or tags.
- --deep : match \fBsubstrings\fR (`match` matches `rematched`) in URL, title and tags.
- --sreg : match a regular expression (ignores --deep).
- --stag : search bookmarks by a tag, or list all tags alphabetically with usage count (if no arguments).
- --stag : search bookmarks by tags, or list all tags alphabetically with usage count (if no arguments). Delimit the list of tags in the query with `,` to search for bookmarks that match ANY of the listed tags. Delimit tags with `+` to search for bookmarks that match ALL of the listed tags. Note that `,` and `+` cannot be used together in the same search. Exclude bookmarks matching certain tags from the results by using ` - ` followed by the tags. Note that the ` - ` operator and the ` + ` delimiter must be space separated: ` - ` instead of `-` and ` + ` instead of `+`. This is to distinguish them from hyphenated tags (e.g., `some-tag-name`) and tags with '+'s (e.g., `some+tag+name`).
- Search results are indexed serially. This index is different from actual database index of a bookmark record which is shown within '[]' after the title.
.PP
.IP 9. 4
@ -154,8 +154,18 @@ Search modifier to match substrings. Works with --sany, --sall.
.BI \-r " " \--sreg " expression"
Scan for a regular expression match.
.TP
.BI \-t " " \--stag " [...]"
Search bookmarks by a tag. List all tags alphabetically, if no arguments. The usage count (number of bookmarks having the tag) is shown within first brackets.
.BI \-t " " \--stag " [tag [,|+] ...] [\- tag, ...]"
Search bookmarks by tags.
.br
Use ',' delimiter to find entries matching ANY of the tags
.br
Use ' + ' delimiter to find entries matching ALL of the tags. (Note that the ' + ' delimiter must be space separated)
.br
NOTE: Cannot combine ',' and '+' in the same search
.br
Use ' - ' to exclude bookmarks that match the tags that follow. (Note that the '-' operator must be space separated).
.br
List all tags alphabetically, if no arguments. The usage count (number of bookmarks having the tag) is shown within first brackets.
.SH ENCRYPTION OPTIONS
.TP
.BI \-l " " \--lock " [N]"
@ -506,6 +516,28 @@ The last index is moved to the deleted index to keep the DB compact.
.EE
.PP
.IP 19. 4
\fBSearch\fR for bookmarks matching \fBANY\fR of the tags 'kernel', 'debugging', 'general kernel concepts':
.PP
.EX
.IP
.B buku --stag kernel, debugging, general kernel concepts
.EE
.PP
.IP 20.4
\fBSearch\fR for bookmarks matching \fBALL\fR of the tags 'kernel', 'debugging', 'general kernel concepts':
.PP
.EX
.IP
.B buku --stag kernel + debugging + general kernel concepts
.EE
.PP
.IP 21.4
\fBSearch\fR for bookmarks matching both the tags `kernel` and `debugging`, but \fBexcluding\fR bookmarks matching the tag 'general kernel concepts':
.PP
.EX
.IP
.B buku --stag kernel + debugging - general kernel concepts
.IP 22.4
List \fBall unique tags\fR alphabetically:
.PP
.EX
@ -513,7 +545,7 @@ List \fBall unique tags\fR alphabetically:
.B buku --stag
.EE
.PP
.IP 20. 4
.IP 23. 4
Run a \fBsearch and update\fR the results:
.PP
.EX
@ -521,7 +553,7 @@ Run a \fBsearch and update\fR the results:
.B buku -s kernel debugging -u --tag + linux kernel
.EE
.PP
.IP 21. 4
.IP 24. 4
Run a \fBsearch and delete\fR the results:
.PP
.EX
@ -529,7 +561,7 @@ Run a \fBsearch and delete\fR the results:
.B buku -s kernel debugging -d
.EE
.PP
.IP 22. 4
.IP 25. 4
\fBEncrypt or decrypt\fR DB with \fBcustom number of iterations\fR (15) to generate key:
.PP
.EX
@ -542,7 +574,7 @@ Run a \fBsearch and delete\fR the results:
.IP "" 4
The same number of iterations must be specified for one lock & unlock instance. Default is 8, if omitted.
.PP
.IP 23. 4
.IP 26. 4
\fBShow details\fR of bookmarks at index 15012014 and ranges 20-30, 40-50:
.PP
.EX
@ -550,7 +582,7 @@ The same number of iterations must be specified for one lock & unlock instance.
.B buku -p 20-30 15012014 40-50
.EE
.PP
.IP 24. 4
.IP 27. 4
Show details of the \fBlast 10 bookmarks\fR:
.PP
.EX
@ -558,7 +590,7 @@ Show details of the \fBlast 10 bookmarks\fR:
.B buku -p -10
.EE
.PP
.IP 25. 4
.IP 28. 4
\fBShow all\fR bookmarks with real index from database:
.PP
.EX
@ -567,7 +599,7 @@ Show details of the \fBlast 10 bookmarks\fR:
.B buku -p | more
.EE
.PP
.IP 26. 4
.IP 29. 4
\fBReplace tag\fR 'old tag' with 'new tag':
.PP
.EX
@ -575,7 +607,7 @@ Show details of the \fBlast 10 bookmarks\fR:
.B buku --replace 'old tag' 'new tag'
.EE
.PP
.IP 27. 4
.IP 30. 4
\fBDelete tag\fR 'old tag' from DB:
.PP
.EX
@ -583,7 +615,7 @@ Show details of the \fBlast 10 bookmarks\fR:
.B buku --replace 'old tag'
.EE
.PP
.IP 28. 4
.IP 31. 4
\fBAppend (or delete) tags\fR 'tag 1', 'tag 2' to (or from) existing tags of bookmark at index 15012014:
.PP
.EX
@ -592,7 +624,7 @@ Show details of the \fBlast 10 bookmarks\fR:
.B buku -u 15012014 --tag - tag 1, tag 2
.EE
.PP
.IP 29. 4
.IP 32. 4
\fBOpen URL\fR at index 15012014 in browser:
.PP
.EX
@ -600,7 +632,7 @@ Show details of the \fBlast 10 bookmarks\fR:
.B buku -o 15012014
.EE
.PP
.IP 30. 4
.IP 33. 4
List bookmarks with \fBno title or tags\fR for bookkeeping:
.PP
.EX
@ -608,7 +640,7 @@ List bookmarks with \fBno title or tags\fR for bookkeeping:
.B buku -S blank
.EE
.PP
.IP 31. 4
.IP 34. 4
List bookmarks with \fBimmutable title\fR:
.PP
.EX
@ -616,7 +648,7 @@ List bookmarks with \fBimmutable title\fR:
.B buku -S immutable
.EE
.PP
.IP 32. 4
.IP 35. 4
\fBShorten\fR the URL www.google.com and the URL at index 20:
.PP
.EX
@ -625,7 +657,7 @@ List bookmarks with \fBimmutable title\fR:
.B buku --shorten 20
.EE
.PP
.IP 33. 4
.IP 36. 4
\fBAppend, remove tags at prompt\fR (taglist index to the left, bookmark index to the right):
.PP
.EX

65
buku.py
View File

@ -1079,18 +1079,32 @@ class BukuDb:
return self.cur.fetchall()
def search_by_tag(self, tag):
def search_by_tag(self, tags):
'''Search and list bookmarks with a tag
:param tag: a tag to search as string
:param tags: list of tags to search as string
:return: search results, or None, if no matches
'''
tag = delim_wrap(tag.strip(DELIM))
query = "SELECT id, url, metadata, tags, desc FROM bookmarks WHERE tags LIKE '%' || ? || '%' ORDER BY id ASC"
logdbg('query: "%s", args: %s', query, tag)
# do not allow combination of search logics
if ' + ' in tags and ',' in tags:
logerr("Cannot use both '+' and ',' in same search")
return
self.cur.execute(query, (tag,))
tags, search_operator, excluded_tags = prep_tag_search(tags)
query = "SELECT id, url, metadata, tags, desc FROM bookmarks WHERE tags LIKE '%' || ? || '%' "
for tag in tags[1:]:
query += "{} tags LIKE '%' || ? || '%' ".format(search_operator)
if excluded_tags:
tags.append(excluded_tags)
query = query.replace('WHERE tags', 'WHERE (tags')
query += ') AND tags NOT REGEXP ? '
query += 'ORDER BY id ASC'
logdbg('query: "%s", args: %s', query, tags)
self.cur.execute(query, tuple(tags, ))
return self.cur.fetchall()
def compactdb(self, index, delay_commit=False):
@ -2042,7 +2056,7 @@ PROMPT KEYS:
S keyword [...] search for records with ALL keywords
d match substrings ('pen' matches 'opened')
r expression run a regex search
t [...] search bookmarks by a tag or show taglist
t [...] search bookmarks by tags or show taglist
list index after a tag listing shows records with the tag
o id|range [...] browse bookmarks by indices and/or ranges
p id|range [...] print bookmarks by indices and/or ranges
@ -2326,6 +2340,37 @@ def parse_tags(keywords=[]):
return delim_wrap(DELIM.join(sorted_tags))
def prep_tag_search(tags):
"""Prepare list of tags to search and determine search operator
:param tags: list of tags to search as string
:return: tuple (
list of formatted tags to search,
a string indicating query search operator (either OR or AND),
a regex string of tags (used to exclude matching bookmarks),
),
None, if ' - ' operator is not in the tags argument
"""
excluded_tags = None
if ' - ' in tags:
tags, excluded_tags = tags.split(' - ', 1)
excluded_taglist = [delim_wrap(t.strip()) for t in excluded_tags.split(',')]
# join with pipe to construct regex string
excluded_tags = '|'.join(excluded_taglist)
search_operator = 'OR'
tag_delim = ','
if ' + ' in tags:
search_operator = 'AND'
tag_delim = ' + '
tags = [delim_wrap(t.strip()) for t in tags.split(tag_delim)]
return tags, search_operator, excluded_tags
def edit_at_prompt(obj, nav):
'''Edit and add or update a bookmark
@ -3101,7 +3146,11 @@ POSITIONAL ARGUMENTS:
"immutable": entries with locked title
--deep match substrings ('pen' matches 'opens')
-r, --sreg run a regex search
-t, --stag search bookmarks by a tag
-t, --stag [tag [,|+] ...] [- tag, ...]
search bookmarks by tags
use ',' to find entries matching ANY tag
use '+' to find entries matching ALL tags
excludes entries matching tags following ' - '
list all tags, if no search keywords''')
addarg = search_grp.add_argument
addarg('-s', '--sany', action='store_true', help=HIDE)

View File

@ -8,7 +8,7 @@ import unittest
import pytest
from buku import is_int, parse_tags
from buku import is_int, parse_tags, prep_tag_search
only_python_3_5 = pytest.mark.skipif(
sys.version_info < (3, 5), reason="requires Python 3.5 or later")
@ -107,6 +107,30 @@ def test_parse_tags(keywords, exp_res):
assert res == exp_res
@pytest.mark.parametrize(
'taglist, exp_res',
[
[
'tag1, tag2+3',
([',tag1,', ',tag2+3,'], 'OR', None)
],
[
'tag1 + tag2-3 + tag4',
([',tag1,', ',tag2-3,', ',tag4,'], 'AND', None)
],
[
'tag1, tag2-3 - tag4, tag5',
([',tag1,', ',tag2-3,'], 'OR', ',tag4,|,tag5,')
]
]
)
def test_prep_tag_search(taglist, exp_res):
"""test prep_tag_search helper function"""
results = prep_tag_search(taglist)
assert results == exp_res
@pytest.mark.parametrize(
'nav, is_editor_valid_retval, edit_rec_retval',
product(

View File

@ -273,6 +273,169 @@ class TestBukuDb(unittest.TestCase):
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
@ -427,7 +590,6 @@ class TestBukuDb(unittest.TestCase):
# def test_import_bookmark(self):
# self.fail()
@given(
index=st.integers(min_value=-10, max_value=10),
low=st.integers(min_value=-10, max_value=10),
@ -824,6 +986,45 @@ def test_update_rec_exec_arg(caplog, kwargs, exp_query, exp_arguments):
assert caplog.records[-1].levelname == 'DEBUG'
@pytest.mark.parametrize(
'tags_to_search, exp_query, exp_arguments',
[
[
'tag1, tag2',
"SELECT id, url, metadata, tags, desc FROM bookmarks WHERE tags LIKE '%' || ? || '%' "
"OR tags LIKE '%' || ? || '%' ORDER BY id ASC",
[',tag1,', ',tag2,']
],
[
'tag1+tag2,tag3, tag4',
"SELECT id, url, metadata, tags, desc FROM bookmarks WHERE tags LIKE '%' || ? || '%' "
"OR tags LIKE '%' || ? || '%' OR tags LIKE '%' || ? || '%' ORDER BY id ASC",
[',tag1+tag2,', ',tag3,', ',tag4,']
],
[
'tag1 + tag2+tag3',
"SELECT id, url, metadata, tags, desc FROM bookmarks WHERE tags LIKE '%' || ? || '%' "
"AND tags LIKE '%' || ? || '%' ORDER BY id ASC",
[',tag1,', ',tag2+tag3,']
],
[
'tag1-tag2 + tag 3 - tag4',
"SELECT id, url, metadata, tags, desc FROM bookmarks WHERE (tags LIKE '%' || ? || '%' "
"AND tags LIKE '%' || ? || '%' ) AND tags NOT REGEXP ? ORDER BY id ASC",
[',tag1-tag2,', ',tag 3,', ',tag4,']
]
]
)
def test_search_by_tag_query(caplog, tags_to_search, exp_query, exp_arguments):
"""test that the correct query and argments are constructed"""
bdb = BukuDb()
bdb.search_by_tag(tags_to_search)
exp_log = 'query: "{}", args: {}'.format(exp_query, exp_arguments)
assert caplog.records[-1].getMessage() == exp_log
assert caplog.records[-1].levelname == 'DEBUG'
def test_update_rec_only_index():
"""test method."""
bdb = BukuDb()