"""test_meta_payroll_type.py - Unit tests for payroll-type validation hook""" # Copyright © 2021 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 typing import pytest from . import testutil from conservancy_beancount.plugin import meta_payroll_type class HookData(typing.NamedTuple): account: str hook_type: typing.Type valid_values: typing.Set[str] invalid_values: typing.Set[str] @classmethod def from_hook(cls, hook, *valid_values): return cls(hook.ACCOUNT, hook, set(valid_values), set()) @classmethod def set_invalid_values(cls, datas): all_values = frozenset(v for d in datas for v in d.valid_values) for data in datas: data.invalid_values.update(all_values.difference(data.valid_values)) TEST_KEY = 'payroll-type' HOOK_DATA = [ HookData.from_hook(meta_payroll_type.HealthInsuranceHook, 'US:HRA:Fees', 'US:HRA:Usage', 'US:Premium:Main'), HookData.from_hook(meta_payroll_type.OtherBenefitsHook, 'US:403b:Fees', 'US:Education', 'US:ProfessionalMembership'), HookData.from_hook( meta_payroll_type.SalaryHook, 'CA:Tax:EI', 'US:NY:Tax:NYC', 'US:Taxes:Medicare', 'CA:General', 'US:403b:Match', 'US:PTO:Earned', 'US:PTO:Taken', ), HookData.from_hook(meta_payroll_type.TaxHook, 'CA:PP', 'US:IL:Unemployment', 'US:SocialSecurity'), ] HookData.set_invalid_values(HOOK_DATA) @pytest.fixture(scope='module') def config(): return testutil.TestConfig() def norm_value(value): return value.replace(':Taxes:', ':Tax:', 1) @pytest.mark.parametrize('hook_type,account,value', ( (data.hook_type, data.account, value) for data in HOOK_DATA for value in data.valid_values )) def test_valid_value_on_post(config, hook_type, account, value): txn = testutil.Transaction(postings=[ (account, 55, {TEST_KEY: value}), ('Assets:Checking', -55), ]) errors = list(hook_type(config).run(txn)) assert not errors testutil.check_post_meta(txn, {TEST_KEY: norm_value(value)}, None) @pytest.mark.parametrize('hook_type,account,value', ( (data.hook_type, data.account, value) for data in HOOK_DATA for value in data.valid_values )) def test_valid_value_on_txn(config, hook_type, account, value): txn = testutil.Transaction(**{TEST_KEY: value}, postings=[ (account, 80, {TEST_KEY: value}), ('Assets:Checking', -80), ]) errors = list(hook_type(config).run(txn)) assert not errors testutil.check_post_meta(txn, {TEST_KEY: norm_value(value)}, None) @pytest.mark.parametrize('hook_type,account,value', ( (data.hook_type, data.account, value) for data in HOOK_DATA for value in data.invalid_values )) def test_invalid_value_on_post(config, hook_type, account, value): txn = testutil.Transaction(flag='!', postings=[ (account, 90, {TEST_KEY: value}), ('Assets:Checking', -90), ]) errors = list(hook_type(config).run(txn)) assert errors testutil.check_post_meta(txn, {TEST_KEY: value}, None) @pytest.mark.parametrize('hook_type,account,value', ( (data.hook_type, data.account, value) for data in HOOK_DATA for value in data.invalid_values )) def test_invalid_value_on_txn(config, hook_type, account, value): txn = testutil.Transaction(flag='!', **{TEST_KEY: value}, postings=[ (account, 105), ('Assets:Checking', -105), ]) errors = list(hook_type(config).run(txn)) assert errors testutil.check_post_meta(txn, None, None) @pytest.mark.parametrize('hook_type,account', ( (data.hook_type, data.account) for data in HOOK_DATA )) def test_missing_value(config, hook_type, account): txn = testutil.Transaction(flag='!', postings=[ (account, 115), ('Assets:Checking', -115), ]) errors = list(hook_type(config).run(txn)) assert errors @pytest.mark.parametrize('hook_type,account', ( (hook_data.hook_type, other_data.account) for hook_data in HOOK_DATA for other_data in HOOK_DATA if other_data is not hook_data )) def test_no_overlapping_account_checks(config, hook_type, account): txn = testutil.Transaction(postings=[ (account, 120, {TEST_KEY: 'Test:Overlap'}), ('Assets:Checking', -120), ]) errors = list(hook_type(config).run(txn)) assert not errors @pytest.mark.parametrize('hook_data,value', testutil.combine_values( HOOK_DATA, testutil.FIXME_VALUES, )) def test_flagged_fixme_ok(config, hook_data, value): txn = testutil.Transaction(flag='!', postings=[ (hook_data.account, 120, {TEST_KEY: value}), ('Assets:Checking', -120), ]) errors = list(hook_data.hook_type(config).run(txn)) assert not errors testutil.check_post_meta(txn, {TEST_KEY: value}, None)