"""Test main plugin""" # 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 . import pytest from . import testutil 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_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