165 lines
5.1 KiB
Python
165 lines
5.1 KiB
Python
|
"""test_cliutil_searchterm - Unit tests for cliutil.SearchTerm"""
|
||
|
# Copyright © 2020 Brett Smith
|
||
|
#
|
||
|
# This program is free software: you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU Affero General Public License as published by
|
||
|
# the Free Software Foundation, either version 3 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU Affero General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU Affero General Public License
|
||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
|
||
|
import re
|
||
|
|
||
|
import pytest
|
||
|
|
||
|
from . import testutil
|
||
|
|
||
|
from conservancy_beancount import cliutil
|
||
|
from conservancy_beancount import data
|
||
|
|
||
|
TICKET_LINKS = [
|
||
|
'123',
|
||
|
'rt:123',
|
||
|
'rt://ticket/123',
|
||
|
]
|
||
|
|
||
|
ATTACHMENT_LINKS = [
|
||
|
'123/456',
|
||
|
'rt:123/456',
|
||
|
'rt://ticket/123/attachments/456',
|
||
|
]
|
||
|
|
||
|
REPOSITORY_LINKS = [
|
||
|
'789.pdf',
|
||
|
'Documents/789.pdf',
|
||
|
]
|
||
|
|
||
|
RT_LINKS = TICKET_LINKS + ATTACHMENT_LINKS
|
||
|
ALL_LINKS = RT_LINKS + REPOSITORY_LINKS
|
||
|
|
||
|
INVALID_METADATA_KEYS = [
|
||
|
# Must start with a-z
|
||
|
';key',
|
||
|
' key',
|
||
|
'ákey',
|
||
|
'Key',
|
||
|
# Must only contain alphanumerics, -, and _
|
||
|
'key;',
|
||
|
'key ',
|
||
|
'a.key',
|
||
|
]
|
||
|
|
||
|
@pytest.fixture(scope='module')
|
||
|
def defaults_parser():
|
||
|
return cliutil.SearchTerm.arg_parser('document', 'rt-id')
|
||
|
|
||
|
@pytest.fixture(scope='module')
|
||
|
def one_default_parser():
|
||
|
return cliutil.SearchTerm.arg_parser('default')
|
||
|
|
||
|
@pytest.fixture(scope='module')
|
||
|
def no_default_parser():
|
||
|
return cliutil.SearchTerm.arg_parser()
|
||
|
|
||
|
def check_link_regexp(regexp, match_s, first_link_only=False):
|
||
|
assert regexp
|
||
|
assert re.search(regexp, match_s)
|
||
|
assert re.search(regexp, match_s + ' postlink')
|
||
|
assert re.search(regexp, match_s + '0') is None
|
||
|
assert re.search(regexp, '1' + match_s) is None
|
||
|
end_match = re.search(regexp, 'prelink ' + match_s)
|
||
|
if first_link_only:
|
||
|
assert end_match is None
|
||
|
else:
|
||
|
assert end_match
|
||
|
|
||
|
@pytest.mark.parametrize('arg', TICKET_LINKS)
|
||
|
def test_search_term_parse_ticket(defaults_parser, arg):
|
||
|
key, regexp = defaults_parser(arg)
|
||
|
assert key == 'rt-id'
|
||
|
check_link_regexp(regexp, 'rt:123', first_link_only=True)
|
||
|
check_link_regexp(regexp, 'rt://ticket/123', first_link_only=True)
|
||
|
|
||
|
@pytest.mark.parametrize('arg', ATTACHMENT_LINKS)
|
||
|
def test_search_term_parse_attachment(defaults_parser, arg):
|
||
|
key, regexp = defaults_parser(arg)
|
||
|
assert key == 'document'
|
||
|
check_link_regexp(regexp, 'rt:123/456')
|
||
|
check_link_regexp(regexp, 'rt://ticket/123/attachments/456')
|
||
|
|
||
|
@pytest.mark.parametrize('key,query', testutil.combine_values(
|
||
|
['approval', 'contract', 'invoice'],
|
||
|
RT_LINKS,
|
||
|
))
|
||
|
def test_search_term_parse_metadata_rt_shortcut(defaults_parser, key, query):
|
||
|
actual_key, regexp = defaults_parser(f'{key}={query}')
|
||
|
assert actual_key == key
|
||
|
if query.endswith('/456'):
|
||
|
check_link_regexp(regexp, 'rt:123/456')
|
||
|
check_link_regexp(regexp, 'rt://ticket/123/attachments/456')
|
||
|
else:
|
||
|
check_link_regexp(regexp, 'rt:123')
|
||
|
check_link_regexp(regexp, 'rt://ticket/123')
|
||
|
|
||
|
@pytest.mark.parametrize('search_prefix', [
|
||
|
'',
|
||
|
'approval=',
|
||
|
'contract=',
|
||
|
'invoice=',
|
||
|
])
|
||
|
def test_search_term_parse_repo_link(defaults_parser, search_prefix):
|
||
|
document = '1234.pdf'
|
||
|
actual_key, regexp = defaults_parser(f'{search_prefix}{document}')
|
||
|
if search_prefix:
|
||
|
expect_key = search_prefix.rstrip('=')
|
||
|
else:
|
||
|
expect_key = 'document'
|
||
|
assert actual_key == expect_key
|
||
|
check_link_regexp(regexp, document)
|
||
|
|
||
|
@pytest.mark.parametrize('search,unmatched', [
|
||
|
('1234.pdf', '1234_pdf'),
|
||
|
])
|
||
|
def test_search_term_parse_regexp_escaping(defaults_parser, search, unmatched):
|
||
|
_, regexp = defaults_parser(search)
|
||
|
assert re.search(regexp, unmatched) is None
|
||
|
|
||
|
@pytest.mark.parametrize('key', INVALID_METADATA_KEYS)
|
||
|
def test_non_metadata_key(one_default_parser, key):
|
||
|
document = f'{key}=890'
|
||
|
key, pattern = one_default_parser(document)
|
||
|
assert key == 'default'
|
||
|
check_link_regexp(pattern, document)
|
||
|
|
||
|
@pytest.mark.parametrize('arg', ALL_LINKS)
|
||
|
def test_default_parser(one_default_parser, arg):
|
||
|
key, _ = one_default_parser(arg)
|
||
|
assert key == 'default'
|
||
|
|
||
|
@pytest.mark.parametrize('arg', ALL_LINKS + [
|
||
|
f'{nonkey}={link}' for nonkey, link in testutil.combine_values(
|
||
|
INVALID_METADATA_KEYS, ALL_LINKS,
|
||
|
)
|
||
|
])
|
||
|
def test_no_key(no_default_parser, arg):
|
||
|
with pytest.raises(ValueError):
|
||
|
key, pattern = no_default_parser(arg)
|
||
|
|
||
|
@pytest.mark.parametrize('key', ['zero', 'one', 'two'])
|
||
|
def test_filter_postings(key):
|
||
|
txn = testutil.Transaction(postings=[
|
||
|
('Income:Other', 3, {'one': '1', 'two': '2'}),
|
||
|
('Income:Other', 2, {'two': '2'}),
|
||
|
('Income:Other', 1, {'one': '1'}),
|
||
|
])
|
||
|
search = cliutil.SearchTerm(key, '.')
|
||
|
actual = list(search.filter_postings(data.Posting.from_txn(txn)))
|
||
|
assert len(actual) == 0 if key == 'zero' else 2
|
||
|
assert all(post.meta.get(key) for post in actual)
|