From 4c79ce234b0ce3758cd97e5b7553bcd67a941115 Mon Sep 17 00:00:00 2001 From: Siddhartha Date: Tue, 14 Jan 2025 21:33:05 -0700 Subject: [PATCH] unit tests for equity module These are quite rough. It would be good to review the interfaces to see if they could be further simplified so that these tests could be leaner as well. --- oldabe/money_in/equity.py | 5 +- tests/unit/accounting_test.py | 1 - tests/unit/equity_test.py | 154 ++++++++++++++++++++++++++++++++++ 3 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 tests/unit/equity_test.py diff --git a/oldabe/money_in/equity.py b/oldabe/money_in/equity.py index d42992a..7f58417 100644 --- a/oldabe/money_in/equity.py +++ b/oldabe/money_in/equity.py @@ -32,9 +32,10 @@ def calculate_incoming_investment(payment, price, new_itemized_payments): If the payment brings the aggregate amount paid by the payee above the price, then that excess is treated as investment. """ + prior_itemized_payments = ItemizedPaymentsRepo() total_attributable_payments = sum( p.project_amount - for p in [*ItemizedPaymentsRepo(), *new_itemized_payments] + for p in [*prior_itemized_payments, *new_itemized_payments] if p.attributable and p.email == payment.email ) @@ -56,7 +57,7 @@ def calculate_incoming_attribution( share = incoming_investment / posterior_valuation return Attribution(email, share) else: - return None + return Attribution(email, Decimal('0')) def dilute_attributions(incoming_attribution, attributions): diff --git a/tests/unit/accounting_test.py b/tests/unit/accounting_test.py index 869b904..35b337a 100644 --- a/tests/unit/accounting_test.py +++ b/tests/unit/accounting_test.py @@ -2,7 +2,6 @@ from decimal import Decimal from oldabe.accounting import ( get_rounding_difference, - correct_rounding_error, ROUNDING_TOLERANCE, ) from .fixtures import normalized_attributions # noqa diff --git a/tests/unit/equity_test.py b/tests/unit/equity_test.py new file mode 100644 index 0000000..5e534a2 --- /dev/null +++ b/tests/unit/equity_test.py @@ -0,0 +1,154 @@ +from decimal import Decimal +import pytest +from unittest.mock import patch +from oldabe.models import Payment, ItemizedPayment, Attribution +from oldabe.money_in.equity import calculate_incoming_investment, calculate_incoming_attribution, dilute_attributions, handle_investment +from .fixtures import normalized_attributions # noqa + + +class TestCalculateIncomingInvestment: + @pytest.mark.parametrize( + "prior_contribution, incoming_amount, price, expected_investment", + [ + (Decimal("0"), Decimal("0"), Decimal("100"), Decimal("0")), + (Decimal("0"), Decimal("20"), Decimal("100"), Decimal("0")), + (Decimal("0"), Decimal("100"), Decimal("100"), Decimal("0")), + (Decimal("0"), Decimal("120"), Decimal("100"), Decimal("20")), + (Decimal("20"), Decimal("0"), Decimal("100"), Decimal("0")), + (Decimal("20"), Decimal("20"), Decimal("100"), Decimal("0")), + (Decimal("20"), Decimal("80"), Decimal("100"), Decimal("0")), + (Decimal("20"), Decimal("100"), Decimal("100"), Decimal("20")), + (Decimal("100"), Decimal("0"), Decimal("100"), Decimal("0")), + (Decimal("100"), Decimal("20"), Decimal("100"), Decimal("20")), + (Decimal("120"), Decimal("0"), Decimal("100"), Decimal("0")), + (Decimal("120"), Decimal("20"), Decimal("100"), Decimal("20")), + ], + ) + @patch('oldabe.money_in.equity.ItemizedPaymentsRepo') + def test_matrix( + self, + prior_itemized_payments, + prior_contribution, + incoming_amount, + price, + expected_investment, + ): + email = 'dummy@abe.org' + payment = Payment(email, email, incoming_amount) + # this is the total amount paid _including_ the incoming amount + prior_itemized_payments.return_value = ( + [ItemizedPayment(email, 0, prior_contribution, True, 'dummy.file')] + ) + new_itemized_payments = [ItemizedPayment(email, 0, incoming_amount, True, 'dummy.file')] + result = calculate_incoming_investment(payment, price, new_itemized_payments) + assert result == expected_investment + + +class TestCalculateIncomingAttribution: + @pytest.mark.parametrize( + "incoming_investment, expected_attribution", + [ + (Decimal("0"), Decimal('0')), + (Decimal("50"), Decimal('0.005')), + (Decimal("5000"), Decimal('0.5')), + ], + ) + def test_matrix( + self, + incoming_investment, + expected_attribution, + ): + email = 'a@b.co' + posterior_valuation = Decimal('10000') + + attribution = calculate_incoming_attribution( + email, incoming_investment, posterior_valuation + ) + assert ( + attribution.share == expected_attribution + ) + + +class TestDiluteAttributions: + + @pytest.mark.parametrize( + "incoming_attribution, expected_attribution", + [ + (Decimal("0"), Decimal('0.2')), + (Decimal("0.1"), Decimal('0.28')), + (Decimal("0.5"), Decimal('0.60')), + ], + ) + def test_matrix( + self, + incoming_attribution, + expected_attribution, + normalized_attributions, + ): + email = 'a@b.com' + incoming_attribution = Attribution(email, incoming_attribution) + + dilute_attributions( + incoming_attribution, normalized_attributions + ) + a_share = normalized_attributions[email] + b_share = normalized_attributions['b@c.com'] + assert ( + a_share == expected_attribution + ) + assert ( + b_share == 1 - expected_attribution + ) + + +class TestHandleInvestment: + + def test_dilutes_attributions( + self, + normalized_attributions, + ): + apriori_share = normalized_attributions['a@b.com'] + email = 'dummy@abe.org' + payment = Payment(email, email, Decimal('90')) + itemized = ItemizedPayment( + email, + 10, + 90, + payment.attributable, + payment.file, + ) + new_itemized_payments = [itemized] + price = Decimal('10') + prior_valuation = Decimal('50') + + _valuation = handle_investment(payment, + new_itemized_payments, + normalized_attributions, + price, + prior_valuation) + assert normalized_attributions['a@b.com'] < apriori_share + assert sum(normalized_attributions.values()) == Decimal('1') + + def test_inflates_valuation( + self, + normalized_attributions, + ): + email = 'dummy@abe.org' + payment = Payment(email, email, Decimal('90')) + itemized = ItemizedPayment( + email, + 10, + 90, + payment.attributable, + payment.file, + ) + new_itemized_payments = [itemized] + price = Decimal('10') + prior_valuation = Decimal('50') + + valuation = handle_investment(payment, + new_itemized_payments, + normalized_attributions, + price, + prior_valuation) + assert valuation == (prior_valuation + 80)