"""Test PostingMeta class""" # Copyright © 2020 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 pytest from . import testutil from conservancy_beancount import data @pytest.fixture def simple_txn(): return testutil.Transaction(note='txn note', postings=[ ('Assets:Cash', 5), ('Income:Donations', -5, {'note': 'donation love', 'extra': 'Extra'}), ]) SIMPLE_TXN_METAKEYS = frozenset(['filename', 'lineno', 'note']) @pytest.fixture def payee_txn(): return testutil.Transaction(payee='SampleCo', postings=[ ('Assets:Receivable:Accounts', -100), ('Assets:Checking', 95), ('Expenses:BankingFees', 5, {'entity': 'MyBank'}), ]) def test_getitem_transaction(simple_txn): assert data.PostingMeta(simple_txn, 0)['note'] == 'txn note' def test_getitem_posting(simple_txn): assert data.PostingMeta(simple_txn, 1)['note'] == 'donation love' def test_getitem_keyerror(simple_txn): with pytest.raises(KeyError): data.PostingMeta(simple_txn, 1)['InvalidMetadata'] def test_setitem_overwrite(simple_txn): meta = data.PostingMeta(simple_txn, 1) meta['note'] = 'overwritten' assert meta['note'] == 'overwritten' assert data.PostingMeta(simple_txn, 0)['note'] == 'txn note' def test_setitem_over_txn(simple_txn): meta = data.PostingMeta(simple_txn, 0) meta['note'] = 'overwritten' assert meta['note'] == 'overwritten' assert simple_txn.meta['note'] == 'txn note' assert data.PostingMeta(simple_txn, 1)['note'] == 'donation love' def test_setitem_new_meta(simple_txn): meta = data.PostingMeta(simple_txn, 0) meta['newkey'] = 'testvalue' assert meta['newkey'] == 'testvalue' assert 'newkey' not in simple_txn.meta assert 'newkey' not in simple_txn.postings[1].meta def test_delitem(simple_txn): meta = data.PostingMeta(simple_txn, 1) del meta['note'] assert 'note' not in simple_txn.postings[1].meta def test_delitem_fails_on_txn_meta(simple_txn): meta = data.PostingMeta(simple_txn, 0) with pytest.raises(KeyError): del meta['note'] def test_len_with_empty_post_meta(simple_txn): assert len(data.PostingMeta(simple_txn, 0)) == len(SIMPLE_TXN_METAKEYS) def test_len_with_post_meta_over_txn(simple_txn): assert len(data.PostingMeta(simple_txn, 1)) == len(SIMPLE_TXN_METAKEYS) + 1 def test_iter_with_empty_post_meta(simple_txn): assert set(data.PostingMeta(simple_txn, 0)) == SIMPLE_TXN_METAKEYS def test_iter_with_post_meta_over_txn(simple_txn): assert set(data.PostingMeta(simple_txn, 1)) == SIMPLE_TXN_METAKEYS.union(['extra']) def test_get_links_from_txn(simple_txn): meta = data.PostingMeta(simple_txn, 0) assert list(meta.get_links('note')) == ['txn', 'note'] def test_get_links_from_post_override(simple_txn): meta = data.PostingMeta(simple_txn, 1) assert list(meta.get_links('note')) == ['donation', 'love'] def test_payee_used_as_entity(payee_txn): actual = [data.PostingMeta(payee_txn, n, p)['entity'] for n, p in enumerate(payee_txn.postings)] assert actual == ['SampleCo', 'SampleCo', 'MyBank'] def test_entity_metadata_has_precedence_over_payee(payee_txn): payee_txn.meta['entity'] = 'ExampleCo' actual = [data.PostingMeta(payee_txn, n, p)['entity'] for n, p in enumerate(payee_txn.postings)] assert actual == ['ExampleCo', 'ExampleCo', 'MyBank'] def test_keyerror_when_no_entity_or_payee(simple_txn): meta = data.PostingMeta(simple_txn, 1) with pytest.raises(KeyError): meta['entity'] @pytest.mark.parametrize('date', [ testutil.FUTURE_DATE, testutil.FY_START_DATE, testutil.FY_MID_DATE, testutil.PAST_DATE, ]) def test_date(date): txn = testutil.Transaction(date=date, postings=[ ('Income:Donations', -15), ('Assets:Cash', 15), ]) for index, post in enumerate(txn.postings): assert data.PostingMeta(txn, index, post).date == date def test_mutable_copy(): txn = testutil.Transaction( filename='f', lineno=130, txnkey='one', postings=[ ('Assets:Cash', 18), ('Income:Donations', -18), ]) meta = data.PostingMeta(txn, 1).detached() meta['layerkey'] = 'two' assert dict(meta) == { 'filename': 'f', 'lineno': 130, 'txnkey': 'one', 'layerkey': 'two', } assert 'layerkey' not in txn.meta assert all(post.meta is None for post in txn.postings) assert meta.date == txn.date def test_double_detached(): txn = testutil.Transaction(filename='f', lineno=140, postings=[ ('Income:Donations', -19), ]) meta1 = data.PostingMeta(txn, 0).detached() meta1['metakey'] = 'meta' meta1['layerkey'] = 'one' meta2 = meta1.detached() meta2['layerkey'] = 'two' expected = { 'filename': 'f', 'lineno': 140, 'metakey': 'meta', 'layerkey': 'two', } assert dict(meta2) == expected expected['layerkey'] = 'one' assert dict(meta1) == expected assert not any(post.meta for post in txn.postings) # The .get() tests are arguably testing the stdlib, but they're short and # they confirm that we're using the stdlib as we intend. def test_get_with_meta_value(simple_txn): assert data.PostingMeta(simple_txn, 1).get('note') == 'donation love' def test_get_with_txn_value(simple_txn): assert data.PostingMeta(simple_txn, 0).get('note') == 'txn note' def test_get_with_no_value(simple_txn): assert data.PostingMeta(simple_txn, 0).get('extra') is None def test_get_with_specified_default(simple_txn): assert data.PostingMeta(simple_txn, 0).get('extra', 'blank') == 'blank'