Files @ 6deaacb11bdd
Branch filter:

Location: NPO-Accounting/conservancy_beancount/tests/test_cliutil_searchterm.py - annotation

bkuhn
Add US:TN:Unemployment as a valid `payroll-type` metadata for taxes
cd1b28ae3eb1
cd1b28ae3eb1
1b7fdf4f3b00
cd1b28ae3eb1
1b7fdf4f3b00
1b7fdf4f3b00
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
cd1b28ae3eb1
"""test_cliutil_searchterm - Unit tests for cliutil.SearchTerm"""
# Copyright © 2020  Brett Smith
# License: AGPLv3-or-later WITH Beancount-Plugin-Additional-Permission-1.0
#
# Full copyright and licensing details can be found at toplevel file
# LICENSE.txt in the repository.

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)