diff --git a/tests/test_meta_expense_type.py b/tests/test_meta_expense_type.py new file mode 100644 index 0000000000000000000000000000000000000000..c8289a8288a48bd55031c398935b89106b009fa1 --- /dev/null +++ b/tests/test_meta_expense_type.py @@ -0,0 +1,146 @@ +"""Test handling of expense-type metadata""" +# 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.plugin import meta_expense_type + +VALID_VALUES = { + 'program': 'program', + 'fundraising': 'fundraising', + 'management': 'management', + 'admin': 'management', + 'administration': 'management', + 'mgmt': 'management', +} + +INVALID_VALUES = { + 'porgram', + 'mangement', + 'fundrasing', + '', + *testutil.NON_STRING_METADATA_VALUES, +} + +TEST_KEY = 'expense-type' + +@pytest.fixture(scope='module') +def hook(): + config = testutil.TestConfig() + return meta_expense_type.MetaExpenseType(config) + +@pytest.mark.parametrize('src_value,set_value', VALID_VALUES.items()) +def test_valid_values_on_postings(hook, src_value, set_value): + txn = testutil.Transaction(postings=[ + ('Assets:Cash', -25), + ('Expenses:General', 25, {TEST_KEY: src_value}), + ]) + errors = list(hook.run(txn)) + assert not errors + testutil.check_post_meta(txn, None, {TEST_KEY: set_value}) + +@pytest.mark.parametrize('src_value', INVALID_VALUES) +def test_invalid_values_on_postings(hook, src_value): + txn = testutil.Transaction(postings=[ + ('Assets:Cash', -25), + ('Expenses:General', 25, {TEST_KEY: src_value}), + ]) + errors = list(hook.run(txn)) + assert errors + testutil.check_post_meta(txn, None, {TEST_KEY: src_value}) + +@pytest.mark.parametrize('src_value,set_value', VALID_VALUES.items()) +def test_valid_values_on_transactions(hook, src_value, set_value): + txn = testutil.Transaction(**{TEST_KEY: src_value}, postings=[ + ('Assets:Cash', -25), + ('Expenses:General', 25), + ]) + errors = list(hook.run(txn)) + assert not errors + testutil.check_post_meta(txn, None, {TEST_KEY: set_value}) + +@pytest.mark.parametrize('src_value', INVALID_VALUES) +def test_invalid_values_on_transactions(hook, src_value): + txn = testutil.Transaction(**{TEST_KEY: src_value}, postings=[ + ('Assets:Cash', -25), + ('Expenses:General', 25), + ]) + errors = list(hook.run(txn)) + assert errors + testutil.check_post_meta(txn, None, None) + +@pytest.mark.parametrize('account', [ + 'Assets:Cash', + 'Assets:Receivable:Accounts', + 'Equity:OpeningBalance', + 'Income:Other', + 'Liabilities:CreditCard', + 'Liabilities:Payable:Vacation', +]) +def test_non_expense_accounts_skipped(hook, account): + meta = {TEST_KEY: 'program'} + txn = testutil.Transaction(postings=[ + (account, -25), + ('Expenses:General', 25, meta.copy()), + ]) + errors = list(hook.run(txn)) + assert not errors + testutil.check_post_meta(txn, None, meta) + +@pytest.mark.parametrize('account,set_value', [ + ('Expenses:Services:Accounting', 'management'), + ('Expenses:Services:Administration', 'management'), + ('Expenses:Services:Advocacy', 'program'), + ('Expenses:Services:Development', 'program'), + ('Expenses:Services:Fundraising', 'fundraising'), +]) +def test_default_values(hook, account, set_value): + txn = testutil.Transaction(postings=[ + ('Liabilites:CreditCard', -25), + (account, 25), + ]) + errors = list(hook.run(txn)) + assert not errors + testutil.check_post_meta(txn, None, {TEST_KEY: set_value}) + +@pytest.mark.parametrize('date,set_value', [ + (testutil.EXTREME_FUTURE_DATE, None), + (testutil.FUTURE_DATE, 'program'), + (testutil.FY_START_DATE, 'program'), + (testutil.FY_MID_DATE, 'program'), + (testutil.PAST_DATE, None), +]) +def test_default_value_set_in_date_range(hook, date, set_value): + txn = testutil.Transaction(date=date, postings=[ + ('Liabilites:CreditCard', -25), + ('Expenses:General', 25), + ]) + errors = list(hook.run(txn)) + assert not errors + expect_meta = None if set_value is None else {TEST_KEY: set_value} + testutil.check_post_meta(txn, None, expect_meta) + +@pytest.mark.parametrize('src_value', INVALID_VALUES) +def test_flagged_txn_checked(hook, src_value): + txn = testutil.Transaction(flag='!', postings=[ + ('Assets:Cash', -25), + ('Expenses:General', 25, {TEST_KEY: src_value}), + ]) + errors = list(hook.run(txn)) + assert errors + testutil.check_post_meta(txn, None, {TEST_KEY: src_value})