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
|
@ -3,9 +3,6 @@ require "test_helper"
|
|||
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
|
||||
setup do
|
||||
Capybara.default_max_wait_time = 5
|
||||
|
||||
# Prevent "auto sync" from running when tests execute enqueued jobs
|
||||
families(:dylan_family).update! last_synced_at: Time.now
|
||||
end
|
||||
|
||||
driven_by :selenium, using: ENV["CI"].present? ? :headless_chrome : :chrome, screen_size: [ 1400, 1400 ]
|
||||
|
|
|
@ -19,7 +19,7 @@ class Account::EntriesControllerTest < ActionDispatch::IntegrationTest
|
|||
end
|
||||
|
||||
assert_redirected_to account_url(entry.account)
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -51,7 +51,7 @@ class Account::EntriesControllerTest < ActionDispatch::IntegrationTest
|
|||
end
|
||||
|
||||
assert_redirected_to account_entry_url(entry.account, entry)
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ class Account::TradesControllerTest < ActionDispatch::IntegrationTest
|
|||
assert created_entry.amount.positive?
|
||||
assert created_entry.account_trade.qty.positive?
|
||||
assert_equal "Transaction created successfully.", flash[:notice]
|
||||
assert_enqueued_with job: AccountSyncJob
|
||||
assert_enqueued_with job: SyncJob
|
||||
assert_redirected_to @entry.account
|
||||
end
|
||||
|
||||
|
@ -132,7 +132,7 @@ class Account::TradesControllerTest < ActionDispatch::IntegrationTest
|
|||
assert created_entry.amount.negative?
|
||||
assert created_entry.account_trade.qty.negative?
|
||||
assert_equal "Transaction created successfully.", flash[:notice]
|
||||
assert_enqueued_with job: AccountSyncJob
|
||||
assert_enqueued_with job: SyncJob
|
||||
assert_redirected_to @entry.account
|
||||
end
|
||||
end
|
||||
|
|
|
@ -35,6 +35,6 @@ class Account::TransactionsControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
assert_equal "Transaction updated successfully.", flash[:notice]
|
||||
assert_redirected_to account_entry_url(@entry.account, @entry)
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,7 +21,7 @@ class Account::TransfersControllerTest < ActionDispatch::IntegrationTest
|
|||
name: "Test Transfer"
|
||||
}
|
||||
}
|
||||
assert_enqueued_with job: AccountSyncJob
|
||||
assert_enqueued_with job: SyncJob
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ class Account::ValuationsControllerTest < ActionDispatch::IntegrationTest
|
|||
end
|
||||
|
||||
assert_equal "Valuation created successfully.", flash[:notice]
|
||||
assert_enqueued_with job: AccountSyncJob
|
||||
assert_enqueued_with job: SyncJob
|
||||
assert_redirected_to account_valuations_path(@entry.account)
|
||||
end
|
||||
|
||||
|
|
|
@ -10,9 +10,13 @@ class AccountsControllerTest < ActionDispatch::IntegrationTest
|
|||
get accounts_url
|
||||
assert_response :success
|
||||
|
||||
@user.family.accounts.each do |account|
|
||||
@user.family.accounts.manual.each do |account|
|
||||
assert_dom "#" + dom_id(account), count: 1
|
||||
end
|
||||
|
||||
@user.family.plaid_items.each do |item|
|
||||
assert_dom "#" + dom_id(item), count: 1
|
||||
end
|
||||
end
|
||||
|
||||
test "new" do
|
||||
|
@ -22,12 +26,11 @@ class AccountsControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
test "can sync an account" do
|
||||
post sync_account_path(@account)
|
||||
assert_response :no_content
|
||||
assert_redirected_to account_path(@account)
|
||||
end
|
||||
|
||||
test "can sync all accounts" do
|
||||
post sync_all_accounts_path
|
||||
assert_redirected_to accounts_url
|
||||
assert_equal "Successfully queued accounts for syncing.", flash[:notice]
|
||||
assert_redirected_to accounts_path
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,7 +43,7 @@ class CreditCardsControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
assert_redirected_to created_account
|
||||
assert_equal "Credit card account created", flash[:notice]
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
end
|
||||
|
||||
test "updates with credit card details" do
|
||||
|
@ -78,6 +78,6 @@ class CreditCardsControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
assert_redirected_to @account
|
||||
assert_equal "Credit card account updated", flash[:notice]
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
require "test_helper"
|
||||
|
||||
class InstitutionsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
sign_in users(:family_admin)
|
||||
@institution = institutions(:chase)
|
||||
end
|
||||
|
||||
test "should get new" do
|
||||
get new_institution_url
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "can create institution" do
|
||||
assert_difference("Institution.count", 1) do
|
||||
post institutions_url, params: {
|
||||
institution: {
|
||||
name: "New institution"
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
assert_redirected_to accounts_url
|
||||
assert_equal "Institution created", flash[:notice]
|
||||
end
|
||||
|
||||
test "should get edit" do
|
||||
get edit_institution_url(@institution)
|
||||
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should update institution" do
|
||||
patch institution_url(@institution), params: {
|
||||
institution: {
|
||||
name: "New Institution Name",
|
||||
logo: file_fixture_upload("square-placeholder.png", "image/png", :binary)
|
||||
}
|
||||
}
|
||||
|
||||
assert_redirected_to accounts_url
|
||||
assert_equal "Institution updated", flash[:notice]
|
||||
end
|
||||
|
||||
test "can destroy institution without destroying accounts" do
|
||||
assert @institution.accounts.count > 0
|
||||
|
||||
assert_difference -> { Institution.count } => -1, -> { Account.count } => 0 do
|
||||
delete institution_url(@institution)
|
||||
end
|
||||
|
||||
assert_redirected_to accounts_url
|
||||
assert_equal "Institution deleted", flash[:notice]
|
||||
end
|
||||
|
||||
test "can sync institution" do
|
||||
post sync_institution_url(@institution)
|
||||
|
||||
assert_redirected_to accounts_url
|
||||
assert_equal "Institution sync started", flash[:notice]
|
||||
end
|
||||
end
|
|
@ -13,7 +13,7 @@ class Issue::ExchangeRateProviderMissingsControllerTest < ActionDispatch::Integr
|
|||
}
|
||||
}
|
||||
|
||||
assert_enqueued_with job: AccountSyncJob
|
||||
assert_enqueued_with job: SyncJob
|
||||
assert_redirected_to @issue.issuable
|
||||
end
|
||||
end
|
||||
|
|
|
@ -39,7 +39,7 @@ class LoansControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
assert_redirected_to created_account
|
||||
assert_equal "Loan account created", flash[:notice]
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
end
|
||||
|
||||
test "updates with loan details" do
|
||||
|
@ -70,6 +70,6 @@ class LoansControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
assert_redirected_to @account
|
||||
assert_equal "Loan account updated", flash[:notice]
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
end
|
||||
end
|
||||
|
|
49
test/controllers/plaid_items_controller_test.rb
Normal file
49
test/controllers/plaid_items_controller_test.rb
Normal file
|
@ -0,0 +1,49 @@
|
|||
require "test_helper"
|
||||
require "ostruct"
|
||||
|
||||
class PlaidItemsControllerTest < ActionDispatch::IntegrationTest
|
||||
setup do
|
||||
sign_in @user = users(:family_admin)
|
||||
|
||||
@plaid_provider = mock
|
||||
|
||||
PlaidItem.stubs(:plaid_provider).returns(@plaid_provider)
|
||||
end
|
||||
|
||||
test "create" do
|
||||
public_token = "public-sandbox-1234"
|
||||
|
||||
@plaid_provider.expects(:exchange_public_token).with(public_token).returns(
|
||||
OpenStruct.new(access_token: "access-sandbox-1234", item_id: "item-sandbox-1234")
|
||||
)
|
||||
|
||||
assert_difference "PlaidItem.count", 1 do
|
||||
post plaid_items_url, params: {
|
||||
plaid_item: {
|
||||
public_token: public_token,
|
||||
metadata: { institution: { name: "Plaid Item Name" } }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
assert_equal "Account linked successfully. Please wait for accounts to sync.", flash[:notice]
|
||||
assert_redirected_to accounts_path
|
||||
end
|
||||
|
||||
test "destroy" do
|
||||
delete plaid_item_url(plaid_items(:one))
|
||||
|
||||
assert_equal "Accounts scheduled for deletion.", flash[:notice]
|
||||
assert_enqueued_with job: DestroyJob
|
||||
assert_redirected_to accounts_path
|
||||
end
|
||||
|
||||
test "sync" do
|
||||
plaid_item = plaid_items(:one)
|
||||
PlaidItem.any_instance.expects(:sync_later).once
|
||||
|
||||
post sync_plaid_item_url(plaid_item)
|
||||
|
||||
assert_redirected_to accounts_path
|
||||
end
|
||||
end
|
|
@ -43,7 +43,7 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
assert_redirected_to created_account
|
||||
assert_equal "Property account created", flash[:notice]
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
end
|
||||
|
||||
test "updates with property details" do
|
||||
|
@ -74,6 +74,6 @@ class PropertiesControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
assert_redirected_to @account
|
||||
assert_equal "Property account updated", flash[:notice]
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -37,7 +37,7 @@ class TransactionsControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
assert_equal entry_params[:amount].to_d, Account::Transaction.order(created_at: :desc).first.entry.amount
|
||||
assert_equal "New transaction created successfully", flash[:notice]
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
assert_redirected_to account_url(account)
|
||||
end
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ class VehiclesControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
assert_redirected_to created_account
|
||||
assert_equal "Vehicle account created", flash[:notice]
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
end
|
||||
|
||||
test "updates with vehicle details" do
|
||||
|
@ -66,6 +66,6 @@ class VehiclesControllerTest < ActionDispatch::IntegrationTest
|
|||
|
||||
assert_redirected_to @account
|
||||
assert_equal "Vehicle account updated", flash[:notice]
|
||||
assert_enqueued_with(job: AccountSyncJob)
|
||||
assert_enqueued_with(job: SyncJob)
|
||||
end
|
||||
end
|
||||
|
|
12
test/fixtures/account/syncs.yml
vendored
12
test/fixtures/account/syncs.yml
vendored
|
@ -1,12 +0,0 @@
|
|||
one:
|
||||
account: depository
|
||||
status: failed
|
||||
start_date: 2024-07-07
|
||||
last_ran_at: 2024-07-07 09:03:31
|
||||
error: test sync error
|
||||
|
||||
two:
|
||||
account: investment
|
||||
status: completed
|
||||
start_date: 2024-07-07
|
||||
last_ran_at: 2024-07-07 09:03:32
|
11
test/fixtures/accounts.yml
vendored
11
test/fixtures/accounts.yml
vendored
|
@ -21,7 +21,15 @@ depository:
|
|||
currency: USD
|
||||
accountable_type: Depository
|
||||
accountable: one
|
||||
institution: chase
|
||||
|
||||
connected:
|
||||
family: dylan_family
|
||||
name: Connected Account
|
||||
balance: 5000
|
||||
currency: USD
|
||||
accountable_type: Depository
|
||||
accountable: two
|
||||
plaid_account: one
|
||||
|
||||
credit_card:
|
||||
family: dylan_family
|
||||
|
@ -30,7 +38,6 @@ credit_card:
|
|||
currency: USD
|
||||
accountable_type: CreditCard
|
||||
accountable: one
|
||||
institution: chase
|
||||
|
||||
investment:
|
||||
family: dylan_family
|
||||
|
|
3
test/fixtures/depositories.yml
vendored
3
test/fixtures/depositories.yml
vendored
|
@ -1 +1,2 @@
|
|||
one: { }
|
||||
one: { }
|
||||
two: {}
|
2
test/fixtures/families.yml
vendored
2
test/fixtures/families.yml
vendored
|
@ -1,8 +1,10 @@
|
|||
empty:
|
||||
name: Family
|
||||
stripe_subscription_status: active
|
||||
last_synced_at: <%= Time.now %>
|
||||
|
||||
dylan_family:
|
||||
name: The Dylan Family
|
||||
stripe_subscription_status: active
|
||||
last_synced_at: <%= Time.now %>
|
||||
|
||||
|
|
8
test/fixtures/institutions.yml
vendored
8
test/fixtures/institutions.yml
vendored
|
@ -1,8 +0,0 @@
|
|||
chase:
|
||||
name: Chase
|
||||
family: dylan_family
|
||||
|
||||
revolut:
|
||||
name: Revolut
|
||||
family: dylan_family
|
||||
logo_url: <%= "file://" + Rails.root.join('test/fixtures/files/square-placeholder.png').to_s %>
|
3
test/fixtures/plaid_accounts.yml
vendored
Normal file
3
test/fixtures/plaid_accounts.yml
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
one:
|
||||
plaid_item: one
|
||||
plaid_id: "1234567890"
|
5
test/fixtures/plaid_items.yml
vendored
Normal file
5
test/fixtures/plaid_items.yml
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
one:
|
||||
family: dylan_family
|
||||
plaid_id: "1234567890"
|
||||
access_token: encrypted_token_1
|
||||
name: "Test Bank"
|
17
test/fixtures/syncs.yml
vendored
Normal file
17
test/fixtures/syncs.yml
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
account:
|
||||
syncable_type: Account
|
||||
syncable: depository
|
||||
last_ran_at: <%= Time.now %>
|
||||
status: completed
|
||||
|
||||
plaid_item:
|
||||
syncable_type: PlaidItem
|
||||
syncable: one
|
||||
last_ran_at: <%= Time.now %>
|
||||
status: completed
|
||||
|
||||
family:
|
||||
syncable_type: Family
|
||||
syncable: dylan_family
|
||||
last_ran_at: <%= Time.now %>
|
||||
status: completed
|
|
@ -4,6 +4,10 @@ module AccountableResourceInterfaceTest
|
|||
extend ActiveSupport::Testing::Declarative
|
||||
|
||||
test "shows new form" do
|
||||
Plaid::PlaidApi.any_instance.stubs(:link_token_create).returns(
|
||||
Plaid::LinkTokenCreateResponse.new(link_token: "test-link-token")
|
||||
)
|
||||
|
||||
get new_polymorphic_url(@account.accountable)
|
||||
assert_response :success
|
||||
end
|
||||
|
@ -21,14 +25,14 @@ module AccountableResourceInterfaceTest
|
|||
test "destroys account" do
|
||||
delete account_url(@account)
|
||||
assert_redirected_to accounts_path
|
||||
assert_equal "#{@account.accountable_name.humanize} account deleted", flash[:notice]
|
||||
assert_enqueued_with job: DestroyJob
|
||||
assert_equal "#{@account.accountable_name.underscore.humanize} account scheduled for deletion", flash[:notice]
|
||||
end
|
||||
|
||||
test "updates basic account balances" do
|
||||
assert_no_difference [ "Account.count", "@account.accountable_class.count" ] do
|
||||
patch account_url(@account), params: {
|
||||
account: {
|
||||
institution_id: institutions(:chase).id,
|
||||
name: "Updated name",
|
||||
balance: 10000,
|
||||
currency: "USD"
|
||||
|
@ -37,7 +41,7 @@ module AccountableResourceInterfaceTest
|
|||
end
|
||||
|
||||
assert_redirected_to @account
|
||||
assert_equal "#{@account.accountable_name.humanize} account updated", flash[:notice]
|
||||
assert_equal "#{@account.accountable_name.underscore.humanize} account updated", flash[:notice]
|
||||
end
|
||||
|
||||
test "creates with basic attributes" do
|
||||
|
@ -45,7 +49,6 @@ module AccountableResourceInterfaceTest
|
|||
post "/#{@account.accountable_name.pluralize}", params: {
|
||||
account: {
|
||||
accountable_type: @account.accountable_class,
|
||||
institution_id: institutions(:chase).id,
|
||||
name: "New accountable",
|
||||
balance: 10000,
|
||||
currency: "USD",
|
||||
|
@ -68,7 +71,7 @@ module AccountableResourceInterfaceTest
|
|||
end
|
||||
|
||||
assert_redirected_to @account
|
||||
assert_enqueued_with job: AccountSyncJob
|
||||
assert_enqueued_with job: SyncJob
|
||||
assert_equal "#{@account.accountable_name.humanize} account updated", flash[:notice]
|
||||
end
|
||||
|
||||
|
@ -84,7 +87,7 @@ module AccountableResourceInterfaceTest
|
|||
end
|
||||
|
||||
assert_redirected_to @account
|
||||
assert_enqueued_with job: AccountSyncJob
|
||||
assert_enqueued_with job: SyncJob
|
||||
assert_equal "#{@account.accountable_name.humanize} account updated", flash[:notice]
|
||||
end
|
||||
end
|
||||
|
|
24
test/interfaces/syncable_interface_test.rb
Normal file
24
test/interfaces/syncable_interface_test.rb
Normal file
|
@ -0,0 +1,24 @@
|
|||
require "test_helper"
|
||||
|
||||
module SyncableInterfaceTest
|
||||
extend ActiveSupport::Testing::Declarative
|
||||
include ActiveJob::TestHelper
|
||||
|
||||
test "can sync later" do
|
||||
assert_difference "@syncable.syncs.count", 1 do
|
||||
assert_enqueued_with job: SyncJob do
|
||||
@syncable.sync_later
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
test "can sync" do
|
||||
assert_difference "@syncable.syncs.count", 1 do
|
||||
@syncable.sync(start_date: 2.days.ago.to_date)
|
||||
end
|
||||
end
|
||||
|
||||
test "implements sync_data" do
|
||||
assert_respond_to @syncable, :sync_data
|
||||
end
|
||||
end
|
|
@ -1,7 +0,0 @@
|
|||
require "test_helper"
|
||||
|
||||
class Account::BalanceSyncJobTest < ActiveJob::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
|
@ -1,7 +0,0 @@
|
|||
require "test_helper"
|
||||
|
||||
class ConvertCurrencyJobTest < ActiveJob::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
|
@ -1,7 +0,0 @@
|
|||
require "test_helper"
|
||||
|
||||
class DailyExchangeRateJobTest < ActiveJob::TestCase
|
||||
# test "the truth" do
|
||||
# assert true
|
||||
# end
|
||||
end
|
13
test/jobs/sync_job_test.rb
Normal file
13
test/jobs/sync_job_test.rb
Normal file
|
@ -0,0 +1,13 @@
|
|||
require "test_helper"
|
||||
|
||||
class SyncJobTest < ActiveJob::TestCase
|
||||
test "sync is performed" do
|
||||
syncable = accounts(:depository)
|
||||
|
||||
sync = syncable.syncs.create!(start_date: 2.days.ago.to_date)
|
||||
|
||||
sync.expects(:perform).once
|
||||
|
||||
SyncJob.perform_now(sync)
|
||||
end
|
||||
end
|
|
@ -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
|
|
@ -4,6 +4,8 @@ class AccountsTest < ApplicationSystemTestCase
|
|||
setup do
|
||||
sign_in @user = users(:family_admin)
|
||||
|
||||
Family.any_instance.stubs(:get_link_token).returns("test-link-token")
|
||||
|
||||
visit root_url
|
||||
open_new_account_modal
|
||||
end
|
||||
|
@ -67,7 +69,7 @@ class AccountsTest < ApplicationSystemTestCase
|
|||
assert_account_created("OtherLiability")
|
||||
end
|
||||
|
||||
test "can sync all acounts on accounts page" do
|
||||
test "can sync all accounts on accounts page" do
|
||||
visit accounts_url
|
||||
assert_button "Sync all"
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue