Files @ c712105bed3c
Branch filter:

Location: NPO-Accounting/conservancy_beancount/tests/test_data_posting_meta.py

Brett Smith
Revise chart of accounts used throughout.

The main impetus of this change is to rename accounts that were outside
Beancount's accepted five root accounts, to move them into that
structure. This includes:

Accrued:*Payable: → Liabilities:Payable:*
Accrued:*Receivable: → Assets:Receivable:*
UneanedIncome:* → Liabilities:UnearnedIncome:*

Note the last change did inspire in a change to our validation rules. We no
longer require income-type on unearned income, because it's no longer
considered income at all. Once it's earned and converted to an Income
account, that has an income-type of course.

This did inspire another rename that was not required, but
provided more consistency with the other account names above:

Assets:Prepaid* → Assets:Prepaid:*

Where applicable, I have generally extended tests to make sure one of each
of the five account types is tested. (This mostly meant adding an Equity
account to the tests.) I also added tests for key parts of the hierarchy,
like Assets:Receivable and Liabilities:Payable, where applicable.

As part of this change, Account.is_real_asset() got renamed to
Account.is_cash_equivalent(), to better self-document its purpose.
"""Test PostingMeta class"""
# Copyright © 2020  Brett Smith
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

import pytest

from . import testutil

from conservancy_beancount import data

@pytest.fixture
def simple_txn(index=None, key=None):
    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'])

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']

# 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'