File diff 0431d15d685f → cd1b28ae3eb1
tests/test_cliutil_searchterm.py
Show inline comments
 
new file 100644
 
"""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)