test_meta_receipt: Prepare for multiple fallback meta keys.
This commit is contained in:
		
							parent
							
								
									c1ecc48297
								
							
						
					
					
						commit
						51db04dc20
					
				
					 1 changed files with 66 additions and 65 deletions
				
			
		| 
						 | 
				
			
			@ -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))
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue