Files @ 21c7646b4192
Branch filter:

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

Brett Smith
README: Add basic installation instructions.
"""Test validation of entity metadata"""
# 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.plugin import meta_entity

VALID_VALUES = {
    # Classic entity: LastName-FirstName
    'Smith-Alex',
    # Various people and companies have one-word names
    # Digits are allowed, as part of a name or standalone
    'Company19',
    'Company-19',
    # No case requirements
    'boyd-danah',
    # No limit on the number of parts of the name
    'B-van-der-A',
    # Names that have no ASCII are allowed, with or without dash separators
    '田中流星',
    '田中-流星',
    'スミスダコタ',
    'スミス-ダコタ',
    'Яшин-Данила',
    # The PayPal importer produces . in entity metadata
    'Du-Bois-W.-E.-B.',
    # import2ledger produces entities that end with -
    # That's probably a bug, but allow it for now.
    'foo-',
}

INVALID_VALUES = {
    # Starting with a - is not allowed
    '-foo',
    '-',
    # Names that can be reduced to ASCII should be
    # Producers should change this to Uberentity or Ueberentity
    # I am not wild about this rule and would like to relax it—it's mostly
    # based on an expectation that entities are typed in by an American. That's
    # true less and less and it seems like we should reduce the amount of
    # mangling producers are expected to do. But it's the rule for today.
    'Überentity',
    # Whitespace is never allowed
    ' ',
    'Alex Smith',
    '田中\u00A0流星',  # Non-breaking space
    # The only punctuation allowed is - and .
    'スミス_ダコタ',
    'Яшин—Данила',  # em dash
    # An empty string is not valid
    '',
}

TEST_KEY = 'entity'

@pytest.fixture(scope='module')
def hook():
    config = testutil.TestConfig()
    return meta_entity.MetaEntity(config)

@pytest.mark.parametrize('src_value', VALID_VALUES)
def test_valid_values_on_postings(hook, src_value):
    txn = testutil.Transaction(postings=[
        ('Assets:Cash', -25),
        ('Expenses:General', 25, {TEST_KEY: src_value}),
    ])
    assert not any(hook.run(txn))

@pytest.mark.parametrize('src_value', INVALID_VALUES)
def test_invalid_values_on_postings(hook, src_value):
    txn = testutil.Transaction(postings=[
        ('Assets:Cash', -25),
        ('Expenses:General', 25, {TEST_KEY: src_value}),
    ])
    errors = list(hook.run(txn))
    assert len(errors) == 1
    assert errors[0].message == "Expenses:General has invalid entity: {}".format(src_value)

@pytest.mark.parametrize('src_value', VALID_VALUES)
def test_valid_values_on_transactions(hook, src_value):
    txn = testutil.Transaction(**{TEST_KEY: src_value}, postings=[
        ('Assets:Cash', -25),
        ('Expenses:General', 25),
    ])
    assert not any(hook.run(txn))

@pytest.mark.parametrize('src_value', INVALID_VALUES)
def test_invalid_values_on_transactions(hook, src_value):
    txn = testutil.Transaction(**{TEST_KEY: src_value}, postings=[
        ('Assets:Cash', -25),
        ('Expenses:General', 25),
    ])
    errors = list(hook.run(txn))
    assert 1 <= len(errors) <= 2
    assert all(error.message == "transaction has invalid entity: {}".format(src_value)
               for error in hook.run(txn))

@pytest.mark.parametrize('account,required', [
    ('Accrued:AccountsReceivable', True),
    ('Assets:Cash', False),
    ('Equity:OpeningBalances', False),
    ('Expenses:General', True),
    ('Income:Donations', True),
    ('Liabilities:CreditCard', False),
    ('UnearnedIncome:Donations', True),
])
def test_which_accounts_required_on(hook, account, required):
    txn = testutil.Transaction(postings=[
        ('Assets:Checking', 25),
        (account, 25),
    ])
    errors = list(hook.run(txn))
    if not required:
        assert not errors
    else:
        assert errors
        assert any(error.message == "{} missing entity".format(account)
                   for error in errors)