Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unit tests for equity module #36

Merged
merged 1 commit into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions oldabe/money_in/equity.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand All @@ -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):
Expand Down
1 change: 0 additions & 1 deletion tests/unit/accounting_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
154 changes: 154 additions & 0 deletions tests/unit/equity_test.py
Original file line number Diff line number Diff line change
@@ -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")),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: are all of these cases actually testing different logic / use cases? Do we need them all? Might be helpful to add a comment for each one so that if one fails, we can easily identify what is supposed to happen and what logic is broken.

(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')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While discussing, we decided to pass Repos into functions instead of patching them in tests.

def test_matrix(
self,
prior_itemized_payments,
prior_contribution,
incoming_amount,
price,
expected_investment,
):
email = '[email protected]'
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 = '[email protected]'
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 = '[email protected]'
incoming_attribution = Attribution(email, incoming_attribution)

dilute_attributions(
incoming_attribution, normalized_attributions
)
a_share = normalized_attributions[email]
b_share = normalized_attributions['[email protected]']
assert (
a_share == expected_attribution
)
assert (
b_share == 1 - expected_attribution
)


class TestHandleInvestment:

def test_dilutes_attributions(
self,
normalized_attributions,
):
apriori_share = normalized_attributions['[email protected]']
email = '[email protected]'
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['[email protected]'] < apriori_share
assert sum(normalized_attributions.values()) == Decimal('1')

def test_inflates_valuation(
self,
normalized_attributions,
):
email = '[email protected]'
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)