Files
sure/test/models/simplefin_item_test.rb
Josh d4b12d7f7a Fix SimpleFIN partial auth reconnect status (#2509)
* Fix SimpleFIN partial auth reconnect status

* Address SimpleFIN review feedback
2026-06-28 16:12:33 +02:00

229 lines
7.2 KiB
Ruby

require "test_helper"
class SimplefinItemTest < ActiveSupport::TestCase
setup do
@family = families(:dylan_family)
@simplefin_item = SimplefinItem.create!(
family: @family,
name: "Test SimpleFin Connection",
access_url: "https://example.com/access_token"
)
end
test "belongs to family" do
assert_equal @family, @simplefin_item.family
end
test "has many simplefin_accounts" do
account = @simplefin_item.simplefin_accounts.create!(
name: "Test Account",
account_id: "test_123",
currency: "USD",
account_type: "checking",
current_balance: 1000.00
)
assert_includes @simplefin_item.simplefin_accounts, account
end
test "has good status by default" do
assert_equal "good", @simplefin_item.status
end
test "setup token update is required when requires_update has no successful account sync" do
@simplefin_item.update!(status: :requires_update)
Sync.create!(
syncable: @simplefin_item,
status: "failed",
failed_at: Time.current,
error: "SimpleFIN access forbidden",
sync_stats: { "total_accounts" => 0 }
)
assert @simplefin_item.setup_token_update_required?
assert_equal "requires_update", @simplefin_item.effective_status
assert_includes @simplefin_item.attention_summary, "Connection needs update"
end
test "setup token update is not required when latest sync returned accounts" do
@simplefin_item.update!(status: :requires_update, pending_account_setup: true)
# Must be a terminal sync: a pending/in-progress sync short-circuits before
# the account-count check, so this would pass without exercising the branch.
Sync.create!(
syncable: @simplefin_item,
status: "completed",
completed_at: Time.current,
sync_stats: {
"total_accounts" => 18,
"error_buckets" => { "auth" => 1 },
"errors" => [ "Connection to Cash App may need attention. Auth required" ]
}
)
refute @simplefin_item.setup_token_update_required?
assert_equal "good", @simplefin_item.effective_status
refute_includes @simplefin_item.attention_summary, "Connection needs update"
assert_includes @simplefin_item.attention_summary, "Accounts need setup"
end
test "setup token update is not required when latest sync stats use symbol keys" do
@simplefin_item.update!(status: :requires_update)
latest_sync = Sync.new(
syncable: @simplefin_item,
status: "completed",
completed_at: Time.current,
sync_stats: {
total_accounts: 18,
error_buckets: { auth: 1 }
}
)
refute @simplefin_item.setup_token_update_required?(latest_sync:)
assert_equal "good", @simplefin_item.effective_status(latest_sync:)
end
test "setup token update is not required while latest sync is unresolved" do
@simplefin_item.update!(status: :requires_update)
Sync.create!(
syncable: @simplefin_item,
status: "pending",
sync_stats: {
"import_started" => true
}
)
refute @simplefin_item.setup_token_update_required?
assert_equal "good", @simplefin_item.effective_status
end
test "setup token update is required when latest sync is stale without accounts" do
@simplefin_item.update!(status: :requires_update)
Sync.create!(
syncable: @simplefin_item,
status: "stale",
sync_stats: {
"import_started" => true
}
)
assert @simplefin_item.setup_token_update_required?
assert_equal "requires_update", @simplefin_item.effective_status
end
test "can be marked for deletion" do
refute @simplefin_item.scheduled_for_deletion?
@simplefin_item.destroy_later
assert @simplefin_item.scheduled_for_deletion?
end
test "is syncable" do
assert_respond_to @simplefin_item, :sync_later
assert_respond_to @simplefin_item, :syncing?
end
test "scopes work correctly" do
# Create one for deletion
item_for_deletion = SimplefinItem.create!(
family: @family,
name: "Delete Me",
access_url: "https://example.com/delete_token",
scheduled_for_deletion: true
)
active_items = SimplefinItem.active
ordered_items = SimplefinItem.ordered
assert_includes active_items, @simplefin_item
refute_includes active_items, item_for_deletion
assert_equal [ @simplefin_item, item_for_deletion ].sort_by(&:created_at).reverse,
ordered_items.to_a
end
test "upserts institution data correctly" do
org_data = {
id: "bank123",
name: "Test Bank",
domain: "testbank.com",
url: "https://testbank.com",
"sfin-url": "https://sfin.testbank.com"
}
@simplefin_item.upsert_institution_data!(org_data)
assert_equal "bank123", @simplefin_item.institution_id
assert_equal "Test Bank", @simplefin_item.institution_name
assert_equal "testbank.com", @simplefin_item.institution_domain
assert_equal "https://testbank.com", @simplefin_item.institution_url
assert_equal org_data.stringify_keys, @simplefin_item.raw_institution_payload
end
test "institution display name fallback works" do
# No institution data
assert_equal @simplefin_item.name, @simplefin_item.institution_display_name
# With institution name
@simplefin_item.update!(institution_name: "Chase Bank")
assert_equal "Chase Bank", @simplefin_item.institution_display_name
# With domain fallback
@simplefin_item.update!(institution_name: nil, institution_domain: "chase.com")
assert_equal "chase.com", @simplefin_item.institution_display_name
end
test "connected institutions returns unique institutions" do
# Create accounts with different institutions
account1 = @simplefin_item.simplefin_accounts.create!(
name: "Checking",
account_id: "acc1",
currency: "USD",
account_type: "checking",
current_balance: 1000,
org_data: { "name" => "Chase Bank", "domain" => "chase.com" }
)
account2 = @simplefin_item.simplefin_accounts.create!(
name: "Savings",
account_id: "acc2",
currency: "USD",
account_type: "savings",
current_balance: 2000,
org_data: { "name" => "Wells Fargo", "domain" => "wellsfargo.com" }
)
institutions = @simplefin_item.connected_institutions
assert_equal 2, institutions.count
assert_includes institutions.map { |i| i["name"] }, "Chase Bank"
assert_includes institutions.map { |i| i["name"] }, "Wells Fargo"
end
test "institution summary with multiple institutions" do
# No institutions
assert_equal "No institutions connected", @simplefin_item.institution_summary
# One institution
@simplefin_item.simplefin_accounts.create!(
name: "Checking",
account_id: "acc1",
currency: "USD",
account_type: "checking",
current_balance: 1000,
org_data: { "name" => "Chase Bank" }
)
assert_equal "Chase Bank", @simplefin_item.institution_summary
# Multiple institutions
@simplefin_item.simplefin_accounts.create!(
name: "Savings",
account_id: "acc2",
currency: "USD",
account_type: "savings",
current_balance: 2000,
org_data: { "name" => "Wells Fargo" }
)
assert_equal "2 institutions", @simplefin_item.institution_summary
end
end