mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-07-24 15:49:39 +02:00
Basic Plaid Integration (#1433)
* Basic plaid data model and linking * Remove institutions, add plaid items * Improve schema and Plaid provider * Add webhook verification sketch * Webhook verification * Item accounts and balances sync setup * Provide test encryption keys * Fix test * Only provide encryption keys in prod * Try defining keys in test env * Consolidate account sync logic * Add back plaid account initialization * Plaid transaction sync * Sync UI overhaul for Plaid * Add liability and investment syncing * Handle investment webhooks and process current day holdings * Remove logs * Remove "all" period select for performance * fix amount calc * Remove todo comment * Coming soon for investment historical data * Document Plaid configuration * Listen for holding updates
This commit is contained in:
parent
3bc9da4105
commit
cbba2ba675
127 changed files with 1537 additions and 841 deletions
|
@ -1,48 +0,0 @@
|
|||
require "test_helper"
|
||||
|
||||
class Account::SyncTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@account = accounts(:depository)
|
||||
|
||||
@sync = Account::Sync.for(@account)
|
||||
|
||||
@balance_syncer = mock("Account::Balance::Syncer")
|
||||
@holding_syncer = mock("Account::Holding::Syncer")
|
||||
end
|
||||
|
||||
test "runs sync" do
|
||||
Account::Balance::Syncer.expects(:new).with(@account, start_date: nil).returns(@balance_syncer).once
|
||||
Account::Holding::Syncer.expects(:new).with(@account, start_date: nil).returns(@holding_syncer).once
|
||||
|
||||
@account.expects(:resolve_stale_issues).once
|
||||
@balance_syncer.expects(:run).once
|
||||
@holding_syncer.expects(:run).once
|
||||
|
||||
assert_equal "pending", @sync.status
|
||||
assert_nil @sync.last_ran_at
|
||||
|
||||
@sync.run
|
||||
|
||||
streams = capture_turbo_stream_broadcasts [ @account.family, :notifications ]
|
||||
|
||||
assert_equal "completed", @sync.status
|
||||
assert @sync.last_ran_at
|
||||
|
||||
assert_equal "append", streams.first["action"]
|
||||
assert_equal "remove", streams.second["action"]
|
||||
assert_equal "append", streams.third["action"]
|
||||
end
|
||||
|
||||
test "handles sync errors" do
|
||||
Account::Balance::Syncer.expects(:new).with(@account, start_date: nil).returns(@balance_syncer).once
|
||||
Account::Holding::Syncer.expects(:new).with(@account, start_date: nil).returns(@holding_syncer).never # error from balance sync halts entire sync
|
||||
|
||||
@balance_syncer.expects(:run).raises(StandardError.new("test sync error"))
|
||||
|
||||
@sync.run
|
||||
|
||||
assert @sync.last_ran_at
|
||||
assert_equal "failed", @sync.status
|
||||
assert_equal "test sync error", @sync.error
|
||||
end
|
||||
end
|
|
@ -1,34 +1,13 @@
|
|||
require "test_helper"
|
||||
|
||||
class AccountTest < ActiveSupport::TestCase
|
||||
include ActiveJob::TestHelper
|
||||
include SyncableInterfaceTest
|
||||
|
||||
setup do
|
||||
@account = accounts(:depository)
|
||||
@account = @syncable = accounts(:depository)
|
||||
@family = families(:dylan_family)
|
||||
end
|
||||
|
||||
test "can sync later" do
|
||||
assert_enqueued_with(job: AccountSyncJob, args: [ @account, start_date: Date.current ]) do
|
||||
@account.sync_later start_date: Date.current
|
||||
end
|
||||
end
|
||||
|
||||
test "can sync" do
|
||||
start_date = 10.days.ago.to_date
|
||||
|
||||
mock_sync = mock("Account::Sync")
|
||||
mock_sync.expects(:run).once
|
||||
|
||||
Account::Sync.expects(:for).with(@account, start_date: start_date).returns(mock_sync).once
|
||||
|
||||
@account.sync start_date: start_date
|
||||
end
|
||||
|
||||
test "needs sync if account has not synced today" do
|
||||
assert @account.needs_sync?
|
||||
end
|
||||
|
||||
test "groups accounts by type" do
|
||||
result = @family.accounts.by_group(period: Period.all)
|
||||
assets = result[:assets]
|
||||
|
@ -47,7 +26,7 @@ class AccountTest < ActiveSupport::TestCase
|
|||
loans = liabilities.children.find { |group| group.name == "Loan" }
|
||||
other_liabilities = liabilities.children.find { |group| group.name == "OtherLiability" }
|
||||
|
||||
assert_equal 1, depositories.children.count
|
||||
assert_equal 2, depositories.children.count
|
||||
assert_equal 1, properties.children.count
|
||||
assert_equal 1, vehicles.children.count
|
||||
assert_equal 1, investments.children.count
|
||||
|
|
|
@ -3,9 +3,28 @@ require "csv"
|
|||
|
||||
class FamilyTest < ActiveSupport::TestCase
|
||||
include Account::EntriesTestHelper
|
||||
include SyncableInterfaceTest
|
||||
|
||||
def setup
|
||||
@family = families :empty
|
||||
@family = families(:empty)
|
||||
@syncable = families(:dylan_family)
|
||||
end
|
||||
|
||||
test "syncs plaid items and manual accounts" do
|
||||
family_sync = syncs(:family)
|
||||
|
||||
manual_accounts_count = @syncable.accounts.manual.count
|
||||
items_count = @syncable.plaid_items.count
|
||||
|
||||
Account.any_instance.expects(:sync_data)
|
||||
.with(start_date: nil)
|
||||
.times(manual_accounts_count)
|
||||
|
||||
PlaidItem.any_instance.expects(:sync_data)
|
||||
.with(start_date: nil)
|
||||
.times(items_count)
|
||||
|
||||
@syncable.sync_data(start_date: family_sync.start_date)
|
||||
end
|
||||
|
||||
test "calculates assets" do
|
||||
|
@ -48,30 +67,6 @@ class FamilyTest < ActiveSupport::TestCase
|
|||
assert_equal Money.new(50000, @family.currency), @family.net_worth
|
||||
end
|
||||
|
||||
test "needs sync if last family sync was before today" do
|
||||
assert @family.needs_sync?
|
||||
|
||||
@family.update! last_synced_at: Time.now
|
||||
|
||||
assert_not @family.needs_sync?
|
||||
end
|
||||
|
||||
test "syncs active accounts" do
|
||||
account = create_account(balance: 1000, accountable: CreditCard.new, is_active: false)
|
||||
|
||||
Account.any_instance.expects(:sync_later).never
|
||||
|
||||
@family.sync
|
||||
|
||||
account.update! is_active: true
|
||||
|
||||
Account.any_instance.expects(:needs_sync?).once.returns(true)
|
||||
Account.any_instance.expects(:last_sync_date).once.returns(2.days.ago.to_date)
|
||||
Account.any_instance.expects(:sync_later).with(start_date: 2.days.ago.to_date).once
|
||||
|
||||
@family.sync
|
||||
end
|
||||
|
||||
test "calculates snapshot" do
|
||||
asset = create_account(balance: 500, accountable: Depository.new)
|
||||
liability = create_account(balance: 100, accountable: CreditCard.new)
|
||||
|
|
21
test/models/plaid_item_test.rb
Normal file
21
test/models/plaid_item_test.rb
Normal file
|
@ -0,0 +1,21 @@
|
|||
require "test_helper"
|
||||
|
||||
class PlaidItemTest < ActiveSupport::TestCase
|
||||
include SyncableInterfaceTest
|
||||
|
||||
setup do
|
||||
@plaid_item = @syncable = plaid_items(:one)
|
||||
end
|
||||
|
||||
test "removes plaid item when destroyed" do
|
||||
@plaid_provider = mock
|
||||
|
||||
PlaidItem.stubs(:plaid_provider).returns(@plaid_provider)
|
||||
|
||||
@plaid_provider.expects(:remove_item).with(@plaid_item.access_token).once
|
||||
|
||||
assert_difference "PlaidItem.count", -1 do
|
||||
@plaid_item.destroy
|
||||
end
|
||||
end
|
||||
end
|
34
test/models/sync_test.rb
Normal file
34
test/models/sync_test.rb
Normal file
|
@ -0,0 +1,34 @@
|
|||
require "test_helper"
|
||||
|
||||
class SyncTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@sync = syncs(:account)
|
||||
@sync.update(status: "pending")
|
||||
end
|
||||
|
||||
test "runs successful sync" do
|
||||
@sync.syncable.expects(:sync_data).with(start_date: @sync.start_date).once
|
||||
|
||||
assert_equal "pending", @sync.status
|
||||
|
||||
previously_ran_at = @sync.last_ran_at
|
||||
|
||||
@sync.perform
|
||||
|
||||
assert @sync.last_ran_at > previously_ran_at
|
||||
assert_equal "completed", @sync.status
|
||||
end
|
||||
|
||||
test "handles sync errors" do
|
||||
@sync.syncable.expects(:sync_data).with(start_date: @sync.start_date).raises(StandardError.new("test sync error"))
|
||||
|
||||
assert_equal "pending", @sync.status
|
||||
previously_ran_at = @sync.last_ran_at
|
||||
|
||||
@sync.perform
|
||||
|
||||
assert @sync.last_ran_at > previously_ran_at
|
||||
assert_equal "failed", @sync.status
|
||||
assert_equal "test sync error", @sync.error
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue