Changeset - 250b14954ffb
[Not reviewed]
0 0 1
Brett Smith - 4 years ago 2021-01-29 16:28:29
brettcsmith@brettcsmith.org
tests: Add tests for plugin.meta_payroll_type.
1 file changed with 152 insertions and 0 deletions:
0 comments (0 inline, 0 general)
tests/test_meta_payroll_type.py
Show inline comments
 
new file 100644
 
"""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',
 
    ),
 
    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)
0 comments (0 inline, 0 general)