def test_highest_interest_first(current_date): """ If there are 2+ loans, the loan with highest interest receives the payment. """ init_account_balance = Money(10000.00) account = Account(init_account_balance) init_loans = [] init_loan_balance = Money(100.00) bill_info = BillInfo( day=10, amount=Money(1.00)) # days after dates covered in this test loan_info = LoanInfo(init_loan_balance, interest=1.00) init_loans.append(Loan(loan_info, bill_info)) loan_info = LoanInfo(init_loan_balance, interest=loan_info.interest + 1.00) init_loans.append(Loan(loan_info, bill_info)) loans = copy.deepcopy(init_loans) payer = HighestInterestFirstPayer(loans, account, next_day(current_date.date), Money(100.00)) current_date.register(payer) current_date.increment_day() # second loan, the higher interest one, should have a different balance assert init_loans[1].balance != loans[1].balance # but lower interest one should be unchanged in this test assert init_loans[0].balance == loans[0].balance
def test_highest_interest_accruing(current_date): """ If there are multiple loans, it's the highest interest loans who are accruing that are addressed first. """ init_account_balance = Money(10000.00) account = Account(init_account_balance) init_loans = [] init_loan_balance = Money(100.00) bill_info = BillInfo( day=10, amount=Money(1.00)) # days after dates covered in this test accrue_start_date = current_date.date + datetime.timedelta(days=100) loan_info = LoanInfo(init_loan_balance, 1.00, accrue_start_date) init_loans.append(Loan(loan_info, bill_info)) loan_info = LoanInfo(init_loan_balance, 1.00) init_loans.append(Loan(loan_info, bill_info)) loans = copy.deepcopy(init_loans) payer = HighestInterestFirstPayer(loans, account, next_day(current_date.date), Money(50.00)) current_date.register(payer) current_date.increment_day() # first loan, nonaccruing, should not have a changed balance assert loans[0].balance == init_loans[0].balance # second loan should have a decreased balance assert loans[1].balance + Money(50.00) == init_loans[1].balance
def test_account_loan_balance_adjusts(current_date): """ The total_owed on a loan and change in account balance should be changed in a specific way. """ init_account_balance = Money(10000.00) account = Account(init_account_balance) init_loan_balance = Money(100.00) bill_info = BillInfo( day=10, amount=Money(1.00)) # days after dates covered in this test loan_info = LoanInfo(init_loan_balance, interest=1.00) loans = [Loan(loan_info, bill_info)] # pay day after current_date (5) pay_amount = Money(10.00) payer = HighestInterestFirstPayer(loans, account, next_day(current_date.date), pay_amount) init_total_owed = payer.total_owed current_date.register(payer) current_date.increment_day() assert account.balance + pay_amount == init_account_balance assert loans[0].total_owed + pay_amount == init_loan_balance
def test_dont_overpay_small_balance_loan(current_date): """ If a loan has only a small balance left, and the HighestInterestFirstPayer's amount is greater than that balance do not overpay. """ init_account_balance = Money(10000.00) account = Account(init_account_balance) init_loan_balance = Money(100.00) bill_info = BillInfo( day=10, amount=Money(1.00)) # days after dates covered in this test loan_info = LoanInfo(init_loan_balance, interest=1.00) loans = [Loan(loan_info, bill_info)] # pay day after current_date (5) pay_amount = Money(200.00) payer = HighestInterestFirstPayer(loans, account, next_day(current_date.date), pay_amount) init_total_owed = payer.total_owed current_date.register(payer) current_date.increment_day() assert account.balance + init_loan_balance == init_account_balance assert loans[0].total_owed == Money(0.00)
def test_order_observers_regiester(): """ The order in which two observers are registered should not affect the update cycle. """ start = datetime.date(2020, 7, 4) date = DateSubject(start) end = date.date + datetime.timedelta(days=10) bill_info = BillInfo(day=end.day, amount=Money(10.00)) loan_info = LoanInfo(Money(1000.00), interest=1.00) loan = Loan(loan_info, bill_info) loan_copy = copy.deepcopy(loan) account = Account(Money(10000.00)) # a reserve of money date.register(MinPaymentPayer(loan, account)) date.register(InterestAccruer(loan)) date.register(InterestAccruer(loan_copy)) date.register(MinPaymentPayer(loan_copy, account)) for d in date_range(start, end): date.increment_day() # \todo These are off by a penny. Is that acceptable? assert almost_equal(loan.balance, loan_copy.balance, 0.01) assert almost_equal(loan.total_owed, loan_copy.total_owed, 0.01)
def test_create_from_string(self): """ For user's convenience we wish to be able to construct a Money instance from a string. """ result = Money.from_string("10.00") self.assertEqual(result.amount, Decimal("10.00")) result = Money.from_string("-15.34") self.assertEqual(result.amount, Decimal("-15.34"))
def test_create_from_float(self): """ For user's convenience we expect to be able to construct a Money instance from a float or int. """ result = Money(10) self.assertEqual(result.amount, 10) result = Money(-10) self.assertEqual(result.amount, -10) result = Money(1.23) self.assertEqual(result.amount, 1.23)
def test_payment_reduces_balance(current_date, min_payment): """ When bill day is hit, loan total_owed reduces to zero when minimum payment exceed amount owed on loan. """ initial_balance = Money(100.00) loan = create_loan(initial_balance, min_payment) initial_amount = Money(1000.00) account = Account(initial_amount) # a reserve of money payer = MinPaymentPayer(loan, account) current_date.register(payer) current_date.increment_day() assert account.balance == initial_amount - initial_balance assert loan.total_owed == Money(0.00)
def test_in_progress(current_date): """ If min payments are in progress account balance should change, and loan balance should change. """ initial_balance = Money(100.00) loan = create_loan(initial_balance, Money(10.00)) initial_amount = Money(1000.00) account = Account(initial_amount) # a reserve of money payer = MinPaymentPayer(loan, account) current_date.register(payer) current_date.increment_day() assert account.balance != initial_amount assert loan.total_owed != initial_balance
def test_payment_reduces_balance(current_date, min_payment): """ When bill day is hit, loan total_owed reduces by the minimum payment amount. Same with account balance. """ initial_balance = Money(100.00) loan = create_loan(initial_balance, min_payment) initial_amount = Money(1000.00) account = Account(initial_amount) # a reserve of money payer = MinPaymentPayer(loan, account) current_date.register(payer) current_date.increment_day() assert account.balance == initial_amount - min_payment assert loan.total_owed == initial_balance - min_payment
def test_not_in_progress(current_date): """ If min payments are not in progress hitting bill date should not change account balance or total owed on loan. """ initial_balance = Money(100.00) bill_start_date = current_date.date + datetime.timedelta(days=100) loan = create_loan(initial_balance, Money(10.00), bill_start_date) initial_amount = Money(1000.00) account = Account(initial_amount) # a reserve of money payer = MinPaymentPayer(loan, account) current_date.register(payer) current_date.increment_day() assert account.balance == initial_amount assert loan.total_owed == initial_balance
def test_daily_accrued(zero_accured_loan, num_days): """ If you have a 1% APR and accrue the daily interest rate 365 times on a $100 balance loan, the accured interest should be $1. """ date = datetime.date(2011, 1, 1) for i in range(num_days): zero_accured_loan.accrue_daily(date.timetuple().tm_year) assert almost_equal(zero_accured_loan.total_owed, Money(100.00 + num_days / 365))
def test_loan_accrues_multiple_days(current_date): loan = create_loan() initial_principal = loan.balance accruer = InterestAccruer(loan) current_date.register(accruer) for i in range(10): current_date.increment_day() expected_interest = 10 * 1.00 * Money(10000.00) / 366. / 100 assert almost_equal(loan.total_owed, initial_principal + expected_interest)
def test_nonzero_balance_loan(current_date): """ If there is a nonzero balance loan total owed of highinterestpayer should change, and account amount should change. """ init_account_balance = Money(10000.00) account = Account(init_account_balance) bill_info = BillInfo(day=10, amount=Money(1.00)) loan_info = LoanInfo(Money(100.00), interest=1.00) loans = [Loan(loan_info, bill_info)] payer = HighestInterestFirstPayer(loans, account, 8, Money(10.00)) init_total_owed = payer.total_owed current_date.register(payer) for i in range(31): current_date.increment_day() assert init_account_balance != account.balance assert init_total_owed != payer.total_owed
def test_multiple_loans_highest_interest_first(current_date): """ If there are 2+ loans, where payer's amount covers at least one of the loan's remaining balance, and then some, the second highest interest loan is the loan whose balance gets paid down. """ init_account_balance = Money(10000.00) account = Account(init_account_balance) init_loans = [] init_loan_balance = Money(100.00) bill_info = BillInfo( day=10, amount=Money(1.00)) # days after dates covered in this test loan_info = LoanInfo(init_loan_balance, interest=1.00) init_loans.append(Loan(loan_info, bill_info)) loan_info = LoanInfo(init_loan_balance, interest=loan_info.interest + 1.00) init_loans.append(Loan(loan_info, bill_info)) loan_info = LoanInfo(init_loan_balance, interest=loan_info.interest - 0.50) init_loans.append(Loan(loan_info, bill_info)) loans = copy.deepcopy(init_loans) payer = HighestInterestFirstPayer(loans, account, next_day(current_date.date), init_loan_balance + Money(50.00)) current_date.register(payer) current_date.increment_day() # first loan in this test should remain unchanged. assert loans[0].balance == init_loans[0].balance # Second loan should have zero money left assert loans[1].balance == Money(0.00) # Third loan should have a reduced balance assert loans[2].balance == init_loans[2].balance - Money(50.00)
def test_neg(self): self.assertAlmostEqual(-Money(3.67), Money(-3.67))
def test_abs(self): self.assertAlmostEqual(Money(1.23), abs(Money(-1.23)))
def zero_accured_loan(): """Return a loan with zero accrued interest with 1% interest rate (APR)""" bill_info = BillInfo(day=1, amount=Money()) loan_info = LoanInfo(Money(100.00), interest=1.00) return Loan(loan_info, bill_info)
def test_eq(self): a = Money(1.45) b = Money(1.45) self.assertEqual(a, b)
def test_add(self): a = Money(1.30) b = Money(2.60) self.assertAlmostEqual(a + b, Money(3.90))
def test_mul(self): a = Money(2.20) b = 4 * a self.assertAlmostEqual(b, Money(8.80)) c = 0.1 * a self.assertAlmostEqual(c, Money(0.22))
initial_balance = Money(100.00) loan = create_loan(initial_balance, Money(10.00)) initial_amount = Money(1000.00) account = Account(initial_amount) # a reserve of money payer = MinPaymentPayer(loan, account) current_date.register(payer) current_date.increment_day() assert account.balance != initial_amount assert loan.total_owed != initial_balance @pytest.mark.parametrize( "min_payment", [Money(1.00), Money(5.34), Money(25.00)]) def test_payment_reduces_balance(current_date, min_payment): """ When bill day is hit, loan total_owed reduces by the minimum payment amount. Same with account balance. """ initial_balance = Money(100.00) loan = create_loan(initial_balance, min_payment) initial_amount = Money(1000.00) account = Account(initial_amount) # a reserve of money payer = MinPaymentPayer(loan, account) current_date.register(payer) current_date.increment_day() assert account.balance == initial_amount - min_payment
def test_bool_false(self): self.assertFalse(Money(0))
def test_bool_true(self): self.assertTrue(Money(1))
def test_sub(self): a = Money(3.45) b = Money(4.60) self.assertAlmostEqual(a - b, Money(-1.15))
def test_lt(self): one = Money(1) two = Money(2) self.assertTrue(one < two)
def create_loan(accrue_start_date=None): # create loan where minimum payment is the day after ptest fixture's date. bill_info = BillInfo(day=20, amount=Money(1.00)) loan_info = LoanInfo(Money(10000.00), interest=1.00, start_date=accrue_start_date) return Loan(loan_info, bill_info)
def test_gt(self): one = Money(1) two = Money(2) self.assertTrue(two > one)
from finance.finance.billinfo import BillInfo from finance.finance.loaninfo import LoanInfo from finance.finance.money import Money, almost_equal from finance.finance.loan import Loan @pytest.fixture def zero_accured_loan(): """Return a loan with zero accrued interest with 1% interest rate (APR)""" bill_info = BillInfo(day=1, amount=Money()) loan_info = LoanInfo(Money(100.00), interest=1.00) return Loan(loan_info, bill_info) @pytest.mark.parametrize("amount, expected", [(Money(1.00), Money(99.00)), (Money(5.00), Money(95.00)), (Money(67), Money(33))]) def test_make_payment(zero_accured_loan, amount, expected): """If you apply $1 to a loan, its balance should descrease by $1""" zero_accured_loan.make_payment(amount) assert zero_accured_loan.balance == expected @pytest.mark.parametrize("num_days", [randint(0, 1000) for i in range(10)]) def test_daily_accrued(zero_accured_loan, num_days): """ If you have a 1% APR and accrue the daily interest rate 365 times on a $100 balance loan, the accured interest should be $1. """ date = datetime.date(2011, 1, 1)
def test_div(self): a = Money(3.33) b = a / 3 self.assertAlmostEqual(b, Money(1.11))