2020-06-06 14:29:44 +00:00
|
|
|
"""test_cliutil_searchterm - Unit tests for cliutil.SearchTerm"""
|
|
|
|
# Copyright © 2020 Brett Smith
|
2021-01-08 21:57:43 +00:00
|
|
|
# License: AGPLv3-or-later WITH Beancount-Plugin-Additional-Permission-1.0
|
2020-06-06 14:29:44 +00:00
|
|
|
#
|
2021-01-08 21:57:43 +00:00
|
|
|
# Full copyright and licensing details can be found at toplevel file
|
|
|
|
# LICENSE.txt in the repository.
|
2020-06-06 14:29:44 +00:00
|
|
|
|
|
|
|
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)
|