mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-09 07:25:19 +02:00
Plaid sync domain improvements (#2267)
Breaks our Plaid sync process out into more manageable classes. Notably, this moves the sync process to a distinct, 2-step flow: 1. Import stage - we first make API calls and import Plaid data to "mirror" tables 2. Processing stage - read the raw data, apply business rules, build internal domain models and sync balances This provides several benefits: - Plaid syncs can now be "replayed" without fetching API data again - Mirror tables provide better audit and debugging capabilities - Eliminates the "all or nothing" sync behavior that is currently in place, which is brittle
This commit is contained in:
parent
5c82af0e8c
commit
03a146222d
72 changed files with 3763 additions and 706 deletions
|
@ -0,0 +1,39 @@
|
|||
require "test_helper"
|
||||
|
||||
class PlaidAccount::Liabilities::CreditProcessorTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@plaid_account = plaid_accounts(:one)
|
||||
@plaid_account.update!(
|
||||
plaid_type: "credit",
|
||||
plaid_subtype: "credit_card"
|
||||
)
|
||||
|
||||
@plaid_account.account.update!(
|
||||
accountable: CreditCard.new,
|
||||
)
|
||||
end
|
||||
|
||||
test "updates credit card minimum payment and APR from Plaid data" do
|
||||
@plaid_account.update!(raw_liabilities_payload: {
|
||||
credit: {
|
||||
minimum_payment_amount: 100,
|
||||
aprs: [ { apr_percentage: 15.0 } ]
|
||||
}
|
||||
})
|
||||
|
||||
processor = PlaidAccount::Liabilities::CreditProcessor.new(@plaid_account)
|
||||
processor.process
|
||||
|
||||
assert_equal 100, @plaid_account.account.credit_card.minimum_payment
|
||||
assert_equal 15.0, @plaid_account.account.credit_card.apr
|
||||
end
|
||||
|
||||
test "does nothing when liability data absent" do
|
||||
@plaid_account.update!(raw_liabilities_payload: {})
|
||||
processor = PlaidAccount::Liabilities::CreditProcessor.new(@plaid_account)
|
||||
processor.process
|
||||
|
||||
assert_nil @plaid_account.account.credit_card.minimum_payment
|
||||
assert_nil @plaid_account.account.credit_card.apr
|
||||
end
|
||||
end
|
|
@ -0,0 +1,44 @@
|
|||
require "test_helper"
|
||||
|
||||
class PlaidAccount::Liabilities::MortgageProcessorTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@plaid_account = plaid_accounts(:one)
|
||||
@plaid_account.update!(
|
||||
plaid_type: "loan",
|
||||
plaid_subtype: "mortgage"
|
||||
)
|
||||
|
||||
@plaid_account.account.update!(accountable: Loan.new)
|
||||
end
|
||||
|
||||
test "updates loan interest rate and type from Plaid data" do
|
||||
@plaid_account.update!(raw_liabilities_payload: {
|
||||
mortgage: {
|
||||
interest_rate: {
|
||||
type: "fixed",
|
||||
percentage: 4.25
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
processor = PlaidAccount::Liabilities::MortgageProcessor.new(@plaid_account)
|
||||
processor.process
|
||||
|
||||
loan = @plaid_account.account.loan
|
||||
|
||||
assert_equal "fixed", loan.rate_type
|
||||
assert_equal 4.25, loan.interest_rate
|
||||
end
|
||||
|
||||
test "does nothing when mortgage data absent" do
|
||||
@plaid_account.update!(raw_liabilities_payload: {})
|
||||
|
||||
processor = PlaidAccount::Liabilities::MortgageProcessor.new(@plaid_account)
|
||||
processor.process
|
||||
|
||||
loan = @plaid_account.account.loan
|
||||
|
||||
assert_nil loan.rate_type
|
||||
assert_nil loan.interest_rate
|
||||
end
|
||||
end
|
|
@ -0,0 +1,68 @@
|
|||
require "test_helper"
|
||||
|
||||
class PlaidAccount::Liabilities::StudentLoanProcessorTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@plaid_account = plaid_accounts(:one)
|
||||
@plaid_account.update!(
|
||||
plaid_type: "loan",
|
||||
plaid_subtype: "student"
|
||||
)
|
||||
|
||||
# Change the underlying accountable to a Loan so the helper method `loan` is available
|
||||
@plaid_account.account.update!(accountable: Loan.new)
|
||||
end
|
||||
|
||||
test "updates loan details including term months from Plaid data" do
|
||||
@plaid_account.update!(raw_liabilities_payload: {
|
||||
student: {
|
||||
interest_rate_percentage: 5.5,
|
||||
origination_principal_amount: 20000,
|
||||
origination_date: Date.new(2020, 1, 1),
|
||||
expected_payoff_date: Date.new(2022, 1, 1)
|
||||
}
|
||||
})
|
||||
|
||||
processor = PlaidAccount::Liabilities::StudentLoanProcessor.new(@plaid_account)
|
||||
processor.process
|
||||
|
||||
loan = @plaid_account.account.loan
|
||||
|
||||
assert_equal "fixed", loan.rate_type
|
||||
assert_equal 5.5, loan.interest_rate
|
||||
assert_equal 20000, loan.initial_balance
|
||||
assert_equal 24, loan.term_months
|
||||
end
|
||||
|
||||
test "handles missing payoff dates gracefully" do
|
||||
@plaid_account.update!(raw_liabilities_payload: {
|
||||
student: {
|
||||
interest_rate_percentage: 4.8,
|
||||
origination_principal_amount: 15000,
|
||||
origination_date: Date.new(2021, 6, 1)
|
||||
# expected_payoff_date omitted
|
||||
}
|
||||
})
|
||||
|
||||
processor = PlaidAccount::Liabilities::StudentLoanProcessor.new(@plaid_account)
|
||||
processor.process
|
||||
|
||||
loan = @plaid_account.account.loan
|
||||
|
||||
assert_nil loan.term_months
|
||||
assert_equal 4.8, loan.interest_rate
|
||||
assert_equal 15000, loan.initial_balance
|
||||
end
|
||||
|
||||
test "does nothing when loan data absent" do
|
||||
@plaid_account.update!(raw_liabilities_payload: {})
|
||||
|
||||
processor = PlaidAccount::Liabilities::StudentLoanProcessor.new(@plaid_account)
|
||||
processor.process
|
||||
|
||||
loan = @plaid_account.account.loan
|
||||
|
||||
assert_nil loan.interest_rate
|
||||
assert_nil loan.initial_balance
|
||||
assert_nil loan.term_months
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue