test_meta_receipt: Prepare for multiple fallback meta keys.

This commit is contained in:
Brett Smith 2020-05-15 16:46:20 -04:00
parent c1ecc48297
commit 51db04dc20

View file

@ -38,14 +38,15 @@ class PostType(enum.IntFlag):
class AccountForTesting(typing.NamedTuple): class AccountForTesting(typing.NamedTuple):
name: str name: str
required_types: PostType required_types: PostType
fallback_meta: typing.Optional[str] fallback_meta: typing.Sequence[str] = ()
def missing_message(self, include_fallback=True): def missing_message(self, include_fallback=True):
if self.fallback_meta is None or not include_fallback: return "{} missing {}{}{}".format(
rest = "" self.name,
else: TEST_KEY,
rest = f"/{self.fallback_meta}" '/' if self.fallback_meta else '',
return f"{self.name} missing {TEST_KEY}{rest}" '/'.join(self.fallback_meta),
)
def wrong_type_message(self, wrong_value, key=TEST_KEY): def wrong_type_message(self, wrong_value, key=TEST_KEY):
expect_type = 'Decimal' if key == 'check-id' else 'str' expect_type = 'Decimal' if key == 'check-id' else 'str'
@ -58,22 +59,25 @@ class AccountForTesting(typing.NamedTuple):
ACCOUNTS = [AccountForTesting._make(t) for t in [ ACCOUNTS = [AccountForTesting._make(t) for t in [
('Assets:Bank:CheckCard', PostType.CREDIT, 'check'), ('Assets:Bank:CheckCard', PostType.CREDIT, ('check',)),
('Assets:Bank:CheckCard', PostType.DEBIT, 'check-id'), ('Assets:Bank:CheckCard', PostType.DEBIT, ('check-id',)),
('Assets:Cash', PostType.BOTH, None), ('Assets:Cash', PostType.BOTH, ()),
('Assets:Checking', PostType.CREDIT, 'check'), ('Assets:Checking', PostType.CREDIT, ('check',)),
('Assets:Checking', PostType.DEBIT, 'check-id'), ('Assets:Checking', PostType.DEBIT, ('check-id',)),
('Assets:Savings', PostType.BOTH, None), ('Assets:Savings', PostType.BOTH, ()),
('Liabilities:CreditCard', PostType.CREDIT, None), ('Liabilities:CreditCard', PostType.CREDIT, ()),
('Liabilities:CreditCard', PostType.DEBIT, 'invoice'), ('Liabilities:CreditCard', PostType.DEBIT, ('invoice',)),
]] ]]
ACCOUNTS_WITH_LINK_FALLBACK = [acct for acct in ACCOUNTS ACCOUNTS_WITH_LINK_FALLBACK = [
if acct.fallback_meta and acct.fallback_meta != 'check-id'] (acct, fallback_key)
ACCOUNTS_WITH_CHECK_ID_FALLBACK = [acct for acct in ACCOUNTS for acct in ACCOUNTS
if acct.fallback_meta == 'check-id'] for fallback_key in acct.fallback_meta
ACCOUNTS_WITHOUT_FALLBACKS = [acct for acct in ACCOUNTS if not acct.fallback_meta] if fallback_key != 'check-id'
KNOWN_FALLBACKS = {acct.fallback_meta for acct in ACCOUNTS if acct.fallback_meta} ]
ACCOUNTS_WITH_CHECK_ID_FALLBACK = [
acct for acct in ACCOUNTS if 'check-id' in acct.fallback_meta
]
# These are mostly fill-in values. # These are mostly fill-in values.
# We don't need to run every test on every value for these, just enough to # We don't need to run every test on every value for these, just enough to
@ -181,67 +185,69 @@ def test_bad_type_receipt_on_txn(hook, test_acct, other_acct, value):
check(hook, test_acct, other_acct, test_acct.wrong_type_message(value), check(hook, test_acct, other_acct, test_acct.wrong_type_message(value),
txn_meta={TEST_KEY: value}) txn_meta={TEST_KEY: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_pair,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_LINK_FALLBACK, ACCOUNTS_WITH_LINK_FALLBACK,
NOT_REQUIRED_ACCOUNTS, NOT_REQUIRED_ACCOUNTS,
testutil.LINK_METADATA_STRINGS, testutil.LINK_METADATA_STRINGS,
)) ))
def test_valid_fallback_on_post(hook, test_acct, other_acct, value): def test_valid_fallback_on_post(hook, test_pair, other_acct, value):
check(hook, test_acct, other_acct, None, test_acct, meta_key = test_pair
post_meta={test_acct.fallback_meta: value}) check(hook, test_acct, other_acct, None, post_meta={meta_key: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_pair,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_LINK_FALLBACK, ACCOUNTS_WITH_LINK_FALLBACK,
NOT_REQUIRED_ACCOUNTS, NOT_REQUIRED_ACCOUNTS,
testutil.NON_LINK_METADATA_STRINGS, testutil.NON_LINK_METADATA_STRINGS,
)) ))
def test_invalid_fallback_on_post(hook, test_acct, other_acct, value): def test_invalid_fallback_on_post(hook, test_pair, other_acct, value):
test_acct, meta_key = test_pair
check(hook, test_acct, other_acct, {test_acct.missing_message()}, check(hook, test_acct, other_acct, {test_acct.missing_message()},
post_meta={test_acct.fallback_meta: value}) post_meta={meta_key: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_pair,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_LINK_FALLBACK, ACCOUNTS_WITH_LINK_FALLBACK,
NOT_REQUIRED_ACCOUNTS, NOT_REQUIRED_ACCOUNTS,
testutil.NON_STRING_METADATA_VALUES, testutil.NON_STRING_METADATA_VALUES,
)) ))
def test_bad_type_fallback_on_post(hook, test_acct, other_acct, value): def test_bad_type_fallback_on_post(hook, test_pair, other_acct, value):
test_acct, meta_key = test_pair
expected = { expected = {
test_acct.missing_message(), test_acct.missing_message(),
test_acct.wrong_type_message(value, test_acct.fallback_meta), test_acct.wrong_type_message(value, meta_key),
} }
check(hook, test_acct, other_acct, expected, check(hook, test_acct, other_acct, expected, post_meta={meta_key: value})
post_meta={test_acct.fallback_meta: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_pair,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_LINK_FALLBACK, ACCOUNTS_WITH_LINK_FALLBACK,
NOT_REQUIRED_ACCOUNTS, NOT_REQUIRED_ACCOUNTS,
testutil.LINK_METADATA_STRINGS, testutil.LINK_METADATA_STRINGS,
)) ))
def test_valid_fallback_on_txn(hook, test_acct, other_acct, value): def test_valid_fallback_on_txn(hook, test_pair, other_acct, value):
check(hook, test_acct, other_acct, None, test_acct, meta_key = test_pair
txn_meta={test_acct.fallback_meta: value}) check(hook, test_acct, other_acct, None, txn_meta={meta_key: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_pair,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_LINK_FALLBACK, ACCOUNTS_WITH_LINK_FALLBACK,
NOT_REQUIRED_ACCOUNTS, NOT_REQUIRED_ACCOUNTS,
testutil.NON_LINK_METADATA_STRINGS, testutil.NON_LINK_METADATA_STRINGS,
)) ))
def test_invalid_fallback_on_txn(hook, test_acct, other_acct, value): def test_invalid_fallback_on_txn(hook, test_pair, other_acct, value):
test_acct, meta_key = test_pair
check(hook, test_acct, other_acct, {test_acct.missing_message()}, check(hook, test_acct, other_acct, {test_acct.missing_message()},
txn_meta={test_acct.fallback_meta: value}) txn_meta={meta_key: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_pair,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_LINK_FALLBACK, ACCOUNTS_WITH_LINK_FALLBACK,
NOT_REQUIRED_ACCOUNTS, NOT_REQUIRED_ACCOUNTS,
testutil.NON_STRING_METADATA_VALUES, testutil.NON_STRING_METADATA_VALUES,
)) ))
def test_bad_type_fallback_on_txn(hook, test_acct, other_acct, value): def test_bad_type_fallback_on_txn(hook, test_pair, other_acct, value):
test_acct, meta_key = test_pair
expected = { expected = {
test_acct.missing_message(), test_acct.missing_message(),
test_acct.wrong_type_message(value, test_acct.fallback_meta), test_acct.wrong_type_message(value, meta_key),
} }
check(hook, test_acct, other_acct, expected, check(hook, test_acct, other_acct, expected, txn_meta={meta_key: value})
txn_meta={test_acct.fallback_meta: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_CHECK_ID_FALLBACK, ACCOUNTS_WITH_CHECK_ID_FALLBACK,
@ -249,8 +255,7 @@ def test_bad_type_fallback_on_txn(hook, test_acct, other_acct, value):
CHECK_IDS, CHECK_IDS,
)) ))
def test_valid_check_id_on_post(hook, test_acct, other_acct, value): def test_valid_check_id_on_post(hook, test_acct, other_acct, value):
check(hook, test_acct, other_acct, None, check(hook, test_acct, other_acct, None, post_meta={'check-id': value})
post_meta={test_acct.fallback_meta: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_CHECK_ID_FALLBACK, ACCOUNTS_WITH_CHECK_ID_FALLBACK,
@ -260,10 +265,9 @@ def test_valid_check_id_on_post(hook, test_acct, other_acct, value):
def test_invalid_check_id_on_post(hook, test_acct, other_acct, value): def test_invalid_check_id_on_post(hook, test_acct, other_acct, value):
expected = { expected = {
test_acct.missing_message(), test_acct.missing_message(),
f"{test_acct.name} has invalid {test_acct.fallback_meta}: {value}", f"{test_acct.name} has invalid check-id: {value}",
} }
check(hook, test_acct, other_acct, expected, check(hook, test_acct, other_acct, expected, post_meta={'check-id': value})
post_meta={test_acct.fallback_meta: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_CHECK_ID_FALLBACK, ACCOUNTS_WITH_CHECK_ID_FALLBACK,
@ -275,10 +279,9 @@ def test_bad_type_check_id_on_post(hook, test_acct, other_acct, value):
value = '' value = ''
expected = { expected = {
test_acct.missing_message(), test_acct.missing_message(),
test_acct.wrong_type_message(value, test_acct.fallback_meta), test_acct.wrong_type_message(value, 'check-id'),
} }
check(hook, test_acct, other_acct, expected, check(hook, test_acct, other_acct, expected, post_meta={'check-id': value})
post_meta={test_acct.fallback_meta: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_CHECK_ID_FALLBACK, ACCOUNTS_WITH_CHECK_ID_FALLBACK,
@ -286,8 +289,7 @@ def test_bad_type_check_id_on_post(hook, test_acct, other_acct, value):
CHECK_IDS, CHECK_IDS,
)) ))
def test_valid_check_id_on_txn(hook, test_acct, other_acct, value): def test_valid_check_id_on_txn(hook, test_acct, other_acct, value):
check(hook, test_acct, other_acct, None, check(hook, test_acct, other_acct, None, txn_meta={'check-id': value})
txn_meta={test_acct.fallback_meta: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_CHECK_ID_FALLBACK, ACCOUNTS_WITH_CHECK_ID_FALLBACK,
@ -297,10 +299,9 @@ def test_valid_check_id_on_txn(hook, test_acct, other_acct, value):
def test_invalid_check_id_on_txn(hook, test_acct, other_acct, value): def test_invalid_check_id_on_txn(hook, test_acct, other_acct, value):
expected = { expected = {
test_acct.missing_message(), test_acct.missing_message(),
f"{test_acct.name} has invalid {test_acct.fallback_meta}: {value}", f"{test_acct.name} has invalid check-id: {value}",
} }
check(hook, test_acct, other_acct, expected, check(hook, test_acct, other_acct, expected, txn_meta={'check-id': value})
txn_meta={test_acct.fallback_meta: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_CHECK_ID_FALLBACK, ACCOUNTS_WITH_CHECK_ID_FALLBACK,
@ -312,34 +313,34 @@ def test_bad_type_check_id_on_txn(hook, test_acct, other_acct, value):
value = '' value = ''
expected = { expected = {
test_acct.missing_message(), test_acct.missing_message(),
test_acct.wrong_type_message(value, test_acct.fallback_meta), test_acct.wrong_type_message(value, 'check-id'),
} }
check(hook, test_acct, other_acct, expected, check(hook, test_acct, other_acct, expected, txn_meta={'check-id': value})
txn_meta={test_acct.fallback_meta: value})
@pytest.mark.parametrize('test_acct,other_acct,key,value', testutil.combine_values( @pytest.mark.parametrize('test_acct,other_acct,key,value', testutil.combine_values(
ACCOUNTS_WITHOUT_FALLBACKS, [acct for acct in ACCOUNTS if not acct.fallback_meta],
NOT_REQUIRED_ACCOUNTS, NOT_REQUIRED_ACCOUNTS,
KNOWN_FALLBACKS, {key for acct in ACCOUNTS for key in acct.fallback_meta},
testutil.LINK_METADATA_STRINGS, testutil.LINK_METADATA_STRINGS,
)) ))
def test_fallback_not_accepted_on_other_accounts(hook, test_acct, other_acct, key, value): def test_fallback_not_accepted_on_other_accounts(hook, test_acct, other_acct, key, value):
check(hook, test_acct, other_acct, {test_acct.missing_message()}, check(hook, test_acct, other_acct, {test_acct.missing_message()},
post_meta={key: value}) post_meta={key: value})
@pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( @pytest.mark.parametrize('test_pair,other_acct,value', testutil.combine_values(
ACCOUNTS_WITH_LINK_FALLBACK, ACCOUNTS_WITH_LINK_FALLBACK,
NOT_REQUIRED_ACCOUNTS, NOT_REQUIRED_ACCOUNTS,
testutil.LINK_METADATA_STRINGS, testutil.LINK_METADATA_STRINGS,
)) ))
def test_fallback_on_zero_amount_postings(hook, test_acct, other_acct, value): def test_fallback_on_zero_amount_postings(hook, test_pair, other_acct, value):
# Unfortunately it does happen that we get donations that go 100% to # Unfortunately it does happen that we get donations that go 100% to
# banking fees, and our importer writes a zero-amount posting to the # banking fees, and our importer writes a zero-amount posting to the
# Assets account. # Assets account.
test_acct, meta_key = test_pair
txn = testutil.Transaction(postings=[ txn = testutil.Transaction(postings=[
('Income:Donations', '-.1'), ('Income:Donations', '-.1'),
('Expenses:BankingFees', '.1'), ('Expenses:BankingFees', '.1'),
(test_acct.name, 0, {test_acct.fallback_meta: value}), (test_acct.name, 0, {meta_key: value}),
]) ])
assert not list(hook.run(txn)) assert not list(hook.run(txn))