diff --git a/src/test/app/LendingHelpers_test.cpp b/src/test/app/LendingHelpers_test.cpp index baed15b868..ec2d4e99a2 100644 --- a/src/test/app/LendingHelpers_test.cpp +++ b/src/test/app/LendingHelpers_test.cpp @@ -620,10 +620,130 @@ class LendingHelpers_test : public beast::unit_test::suite } } + void + testTryOverpaymentValueChange() + { + // This test ensures that overpayment value change is computed + // correctly. I am sorry, this unit test will be a pain in the ass. + testcase("tryOverpay - Value Change is the decrease in interest"); + + using namespace jtx; + using namespace ripple::detail; + + Env env{*this}; + Account const issuer{"issuer"}; + PrettyAsset const asset = issuer["USD"]; + + // Interest delta is 40 (100 - 50 - 10) + ExtendedPaymentComponents const overpaymentComponents = { + PaymentComponents{ + .trackedValueDelta = Number{50, 0}, + .trackedPrincipalDelta = Number{50, 0}, + .trackedManagementFeeDelta = Number{0, 0}, + .specialCase = PaymentSpecialCase::extra, + }, + numZero, + numZero, + }; + + TenthBips16 managementFeeRate{20'000}; // 10% + TenthBips32 loanInterestRate{10'000}; // 20% + Number loanPrincipal{1'000}; + std::uint32_t paymentInterval = 30 * 24 * 60 * 60; + std::uint32_t paymentsRemaining = 10; + std::int32_t loanScale = -5; + auto const periodicRate = + loanPeriodicRate(loanInterestRate, paymentInterval); + + auto loanProperites = computeLoanProperties( + asset, + loanPrincipal, + loanInterestRate, + paymentInterval, + paymentsRemaining, + managementFeeRate, + loanScale); + std::cout << loanProperites.periodicPayment << std::endl; + std::cout << loanProperites.loanState.valueOutstanding << std::endl; + std::cout << loanProperites.loanState.interestOutstanding() + << std::endl; + + Number totalValueOutstanding = + loanProperites.loanState.valueOutstanding; + Number principalOutstanding = + loanProperites.loanState.principalOutstanding; + Number managementFeeOutstanding = + loanProperites.loanState.managementFeeDue; + Number periodicPayment = loanProperites.periodicPayment; + + auto const ret = tryOverpayment( + asset, + loanScale, + overpaymentComponents, + totalValueOutstanding, + principalOutstanding, + managementFeeOutstanding, + periodicPayment, + paymentInterval, + periodicRate, + paymentsRemaining, + managementFeeRate, + env.journal); + + BEAST_EXPECT(ret); + + auto const& actualPaymentParts = *ret; + + BEAST_EXPECTS( + actualPaymentParts.valueChange == + ((totalValueOutstanding - principalOutstanding - + managementFeeOutstanding)) - + loanProperites.loanState.interestDue, + " valueChange mismatch: expected " + + to_string( + (totalValueOutstanding - principalOutstanding - + managementFeeOutstanding) - + loanProperites.loanState.interestDue) + + ", got " + to_string(actualPaymentParts.valueChange)); + + BEAST_EXPECTS( + actualPaymentParts.feePaid == + loanProperites.loanState.managementFeeDue - + managementFeeOutstanding, + " feePaid mismatch: expected " + + to_string( + loanProperites.loanState.managementFeeDue - + managementFeeOutstanding) + + ", got " + to_string(actualPaymentParts.feePaid)); + + BEAST_EXPECTS( + actualPaymentParts.principalPaid == + loanProperites.loanState.principalOutstanding - + principalOutstanding, + " principalPaid mismatch: expected " + + to_string( + loanProperites.loanState.principalOutstanding - + principalOutstanding) + + ", got " + to_string(actualPaymentParts.principalPaid)); + + BEAST_EXPECTS( + actualPaymentParts.interestPaid == + loanProperites.loanState.interestDue - + (totalValueOutstanding - principalOutstanding - + managementFeeOutstanding), + " interestPaid mismatch: expected " + + to_string( + loanProperites.loanState.interestDue - + (totalValueOutstanding - principalOutstanding - + managementFeeOutstanding)) + + ", got " + to_string(actualPaymentParts.interestPaid)); + } + public: void run() override { + testTryOverpaymentValueChange(); testComputeFullPaymentInterest(); testLoanAccruedInterest(); testLoanLatePaymentInterest();