diff --git a/tests/test_meta_project.py b/tests/test_meta_project.py new file mode 100644 index 0000000000000000000000000000000000000000..cbf834f2e77cec5e3a7ef694f34630ff61131866 --- /dev/null +++ b/tests/test_meta_project.py @@ -0,0 +1,151 @@ +"""Test handling of project 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 . + +from pathlib import Path + +import pytest + +from . import testutil + +from conservancy_beancount import errors as errormod +from conservancy_beancount.plugin import meta_project + +VALID_VALUES = { + 'Conservancy': 'Conservancy', + 'Alpha': 'Alpha', + 'Bravo': 'Bravo', + 'Charles': 'Charlie', + 'Chuck': 'Charlie', +} + +INVALID_VALUES = { + 'Alhpa', + 'Yankee', + '', +} + +TEST_KEY = 'project' +DEFAULT_VALUE = 'Conservancy' + +@pytest.fixture(scope='module') +def hook(): + config = testutil.TestConfig(repo_path='repository') + return meta_project.MetaProject(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,required', [ + ('Accrued:AccountsReceivable', True), + ('Assets:Cash', False), + ('Expenses:General', True), + ('Income:Donations', True), + ('Liabilities:CreditCard', False), + ('UnearnedIncome:Donations', True), +]) +def test_which_accounts_required_on(hook, account, required): + txn = testutil.Transaction(postings=[ + ('Assets:Checking', 25), + (account, 25), + ]) + errors = list(hook.run(txn)) + assert required == any(errors) + +@pytest.mark.parametrize('account', [ + 'Accrued:VacationPayable', + 'Expenses:Payroll:Salary', + 'Expenses:Payroll:Tax', +]) +def test_default_values(hook, account): + txn = testutil.Transaction(postings=[ + ('Assets:Checking', -25), + (account, 25), + ]) + errors = list(hook.run(txn)) + assert not errors + testutil.check_post_meta(txn, None, {TEST_KEY: DEFAULT_VALUE}) + +@pytest.mark.parametrize('date,required', [ + (testutil.EXTREME_FUTURE_DATE, False), + (testutil.FUTURE_DATE, True), + (testutil.FY_START_DATE, True), + (testutil.FY_MID_DATE, True), + (testutil.PAST_DATE, None), +]) +def test_default_value_set_in_date_range(hook, date, required): + txn = testutil.Transaction(date=date, postings=[ + ('Expenses:Payroll:Benefits', 25), + ('Accrued:VacationPayable', -25), + ]) + errors = list(hook.run(txn)) + assert not errors + expect_meta = {TEST_KEY: DEFAULT_VALUE} if required else None + testutil.check_post_meta(txn, expect_meta, expect_meta) + +@pytest.mark.parametrize('repo_path', [ + None, + '..', +]) +def test_missing_project_data(repo_path): + config = testutil.TestConfig(repo_path=repo_path) + with pytest.raises(errormod.ConfigurationError): + meta_project.MetaProject(config) + +@pytest.mark.parametrize('repo_path_s,data_path_s', [ + ('repository', 'Projects/project-list.yml'), + ('..', 'LICENSE.txt'), +]) +def test_invalid_project_data(repo_path_s, data_path_s): + config = testutil.TestConfig(repo_path=repo_path_s) + with pytest.raises(errormod.ConfigurationError): + meta_project.MetaProject(config, Path(data_path_s))