diff --git a/tests/test_meta_receipt.py b/tests/test_meta_receipt.py index daf2a79f94f658417be9afcf8eb0eec6aa3d5314..e480ff5142ecfa2bda117633db0a8193b4130b6e 100644 --- a/tests/test_meta_receipt.py +++ b/tests/test_meta_receipt.py @@ -38,14 +38,15 @@ class PostType(enum.IntFlag): class AccountForTesting(typing.NamedTuple): name: str required_types: PostType - fallback_meta: typing.Optional[str] + fallback_meta: typing.Sequence[str] = () def missing_message(self, include_fallback=True): - if self.fallback_meta is None or not include_fallback: - rest = "" - else: - rest = f"/{self.fallback_meta}" - return f"{self.name} missing {TEST_KEY}{rest}" + return "{} missing {}{}{}".format( + self.name, + TEST_KEY, + '/' if self.fallback_meta else '', + '/'.join(self.fallback_meta), + ) def wrong_type_message(self, wrong_value, key=TEST_KEY): expect_type = 'Decimal' if key == 'check-id' else 'str' @@ -58,22 +59,25 @@ class AccountForTesting(typing.NamedTuple): ACCOUNTS = [AccountForTesting._make(t) for t in [ - ('Assets:Bank:CheckCard', PostType.CREDIT, 'check'), - ('Assets:Bank:CheckCard', PostType.DEBIT, 'check-id'), - ('Assets:Cash', PostType.BOTH, None), - ('Assets:Checking', PostType.CREDIT, 'check'), - ('Assets:Checking', PostType.DEBIT, 'check-id'), - ('Assets:Savings', PostType.BOTH, None), - ('Liabilities:CreditCard', PostType.CREDIT, None), - ('Liabilities:CreditCard', PostType.DEBIT, 'invoice'), + ('Assets:Bank:CheckCard', PostType.CREDIT, ('check',)), + ('Assets:Bank:CheckCard', PostType.DEBIT, ('check-id',)), + ('Assets:Cash', PostType.BOTH, ()), + ('Assets:Checking', PostType.CREDIT, ('check',)), + ('Assets:Checking', PostType.DEBIT, ('check-id',)), + ('Assets:Savings', PostType.BOTH, ()), + ('Liabilities:CreditCard', PostType.CREDIT, ()), + ('Liabilities:CreditCard', PostType.DEBIT, ('invoice',)), ]] -ACCOUNTS_WITH_LINK_FALLBACK = [acct for acct in ACCOUNTS - if acct.fallback_meta and acct.fallback_meta != 'check-id'] -ACCOUNTS_WITH_CHECK_ID_FALLBACK = [acct for acct in ACCOUNTS - if acct.fallback_meta == 'check-id'] -ACCOUNTS_WITHOUT_FALLBACKS = [acct for acct in ACCOUNTS if not acct.fallback_meta] -KNOWN_FALLBACKS = {acct.fallback_meta for acct in ACCOUNTS if acct.fallback_meta} +ACCOUNTS_WITH_LINK_FALLBACK = [ + (acct, fallback_key) + for acct in ACCOUNTS + for fallback_key in acct.fallback_meta + if fallback_key != 'check-id' +] +ACCOUNTS_WITH_CHECK_ID_FALLBACK = [ + acct for acct in ACCOUNTS if 'check-id' in acct.fallback_meta +] # These are mostly fill-in values. # 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), 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, NOT_REQUIRED_ACCOUNTS, testutil.LINK_METADATA_STRINGS, )) -def test_valid_fallback_on_post(hook, test_acct, other_acct, value): - check(hook, test_acct, other_acct, None, - post_meta={test_acct.fallback_meta: value}) +def test_valid_fallback_on_post(hook, test_pair, other_acct, value): + test_acct, meta_key = test_pair + 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, NOT_REQUIRED_ACCOUNTS, 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()}, - 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, NOT_REQUIRED_ACCOUNTS, 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 = { 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, - post_meta={test_acct.fallback_meta: value}) + check(hook, test_acct, other_acct, expected, 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, NOT_REQUIRED_ACCOUNTS, testutil.LINK_METADATA_STRINGS, )) -def test_valid_fallback_on_txn(hook, test_acct, other_acct, value): - check(hook, test_acct, other_acct, None, - txn_meta={test_acct.fallback_meta: value}) +def test_valid_fallback_on_txn(hook, test_pair, other_acct, value): + test_acct, meta_key = test_pair + 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, NOT_REQUIRED_ACCOUNTS, 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()}, - 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, NOT_REQUIRED_ACCOUNTS, 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 = { 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, - txn_meta={test_acct.fallback_meta: value}) + check(hook, test_acct, other_acct, expected, txn_meta={meta_key: value}) @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( ACCOUNTS_WITH_CHECK_ID_FALLBACK, @@ -249,8 +255,7 @@ def test_bad_type_fallback_on_txn(hook, test_acct, other_acct, value): CHECK_IDS, )) def test_valid_check_id_on_post(hook, test_acct, other_acct, value): - check(hook, test_acct, other_acct, None, - post_meta={test_acct.fallback_meta: value}) + check(hook, test_acct, other_acct, None, post_meta={'check-id': value}) @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( 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): expected = { 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, - post_meta={test_acct.fallback_meta: value}) + check(hook, test_acct, other_acct, expected, post_meta={'check-id': value}) @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( ACCOUNTS_WITH_CHECK_ID_FALLBACK, @@ -275,10 +279,9 @@ def test_bad_type_check_id_on_post(hook, test_acct, other_acct, value): value = '' expected = { 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, - post_meta={test_acct.fallback_meta: value}) + check(hook, test_acct, other_acct, expected, post_meta={'check-id': value}) @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( 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, )) def test_valid_check_id_on_txn(hook, test_acct, other_acct, value): - check(hook, test_acct, other_acct, None, - txn_meta={test_acct.fallback_meta: value}) + check(hook, test_acct, other_acct, None, txn_meta={'check-id': value}) @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( 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): expected = { 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, - txn_meta={test_acct.fallback_meta: value}) + check(hook, test_acct, other_acct, expected, txn_meta={'check-id': value}) @pytest.mark.parametrize('test_acct,other_acct,value', testutil.combine_values( ACCOUNTS_WITH_CHECK_ID_FALLBACK, @@ -312,34 +313,34 @@ def test_bad_type_check_id_on_txn(hook, test_acct, other_acct, value): value = '' expected = { 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, - txn_meta={test_acct.fallback_meta: value}) + check(hook, test_acct, other_acct, expected, txn_meta={'check-id': value}) @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, - KNOWN_FALLBACKS, + {key for acct in ACCOUNTS for key in acct.fallback_meta}, testutil.LINK_METADATA_STRINGS, )) 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()}, 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, NOT_REQUIRED_ACCOUNTS, 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 # banking fees, and our importer writes a zero-amount posting to the # Assets account. + test_acct, meta_key = test_pair txn = testutil.Transaction(postings=[ ('Income:Donations', '-.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))