"""Test main plugin""" # 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 datetime from decimal import Decimal import pytest from . import testutil import beancount.core.data as bc_data from conservancy_beancount import beancount_types, errors as errormod, plugin HOOK_REGISTRY = plugin.HookRegistry() class NonError(errormod.Error): pass class TransactionHook: DIRECTIVE = beancount_types.Transaction HOOK_GROUPS = frozenset() def __init__(self, config): self.config = config def run(self, txn): assert False, "something called base class run method" @HOOK_REGISTRY.add_hook class ConfigurationError(TransactionHook): HOOK_GROUPS = frozenset(['unconfigured']) def __init__(self, config): raise errormod.ConfigurationError("testing error") @HOOK_REGISTRY.add_hook class TransactionError(TransactionHook): HOOK_GROUPS = frozenset(['configured']) def run(self, txn): return [NonError('txn:{}'.format(id(txn)), txn)] @HOOK_REGISTRY.add_hook class PostingError(TransactionHook): HOOK_GROUPS = frozenset(['configured', 'posting']) def run(self, txn): return [NonError('post:{}'.format(id(post)), txn) for post in txn.postings] @pytest.fixture def config_map(): return {} @pytest.fixture def easy_entries(): return [ testutil.Transaction(postings=[ ('Income:Donations', -25), ('Assets:Cash', 25), ]), testutil.Transaction(postings=[ ('Expenses:General', 10), ('Liabilites:CreditCard', -10), ]), ] def map_errors(errors): retval = {} for error in errors: key, _, errid = error.message.partition(':') retval.setdefault(key, set()).add(errid) return retval @pytest.mark.parametrize('group_str,expected', [ (None, [TransactionError, PostingError]), ('', [TransactionError, PostingError]), ('all', [TransactionError, PostingError]), ('Transaction', [TransactionError, PostingError]), ('-posting', [TransactionError]), ('-configured posting', [PostingError]), ('configured -posting', [TransactionError]), ]) def test_registry_group_by_directive(group_str, expected): args = () if group_str is None else (group_str,) actual = {hook for _, hook in HOOK_REGISTRY.group_by_directive(*args)} assert actual.issuperset(expected) if len(expected) == 1: assert not (TransactionError in actual and PostingError in actual) def test_registry_unknown_group_name(): with pytest.raises(ValueError): next(HOOK_REGISTRY.group_by_directive('UnKnownTestGroup')) def test_registry_load_included_hooks(): registry = plugin.HookRegistry() assert not list(registry.group_by_directive()) registry.load_included_hooks() actual = {hook.__name__ for key, hook in registry.group_by_directive() if key == 'Transaction'} assert len(actual) >= 5 assert 'MetaProject' in actual assert 'MetaRTLinks' in actual def test_run_with_multiple_hooks(easy_entries, config_map): out_entries, errors = plugin.run(easy_entries, config_map, '', HOOK_REGISTRY) assert len(out_entries) == 2 errmap = map_errors(errors) assert len(errmap.get('txn', '')) == 2 assert len(errmap.get('post', '')) == 4 def test_run_with_one_hook(easy_entries, config_map): out_entries, errors = plugin.run(easy_entries, config_map, 'posting', HOOK_REGISTRY) assert len(out_entries) == 2 errmap = map_errors(errors) assert len(errmap.get('txn', '')) == 0 assert len(errmap.get('post', '')) == 4 def test_run_on_all_directives(config_map): meta = { 'filename': __file__, 'lineno': 125, } date = datetime.date(2020, 3, 1) acct = 'Assets:Cash' usd = 'USD' entries = [ bc_data.Open(meta, date, acct, [usd], None), bc_data.Close(meta, date.replace(year=date.year + 1), acct), bc_data.Commodity(meta, date, usd), bc_data.Pad(meta, date, acct, 'Income:Other'), bc_data.Balance(meta, date, acct, 0, None, None), bc_data.Transaction(meta, date, None, None, 'found cash', {}, {}, []), bc_data.Note(meta, date, acct, 'test note'), bc_data.Event(meta, date, 'test event', 'Test Event 1'), bc_data.Query(meta, date, 'test query', ''), bc_data.Price(meta, date, 'EUR', (Decimal('1.10508'), usd)), bc_data.Document(meta, date, acct, '/TestDocument.txt', None, None), bc_data.Custom(meta, date, 'test custom', ['test value']), ] out_entries, errors = plugin.run(entries, config_map, '-all', HOOK_REGISTRY) assert out_entries is entries assert not errors