Files
sure/test/models/snaptrade_account_test.rb
LPW a83f70425f Add SnapTrade brokerage integration with full trade history support (#737)
* Introduce SnapTrade integration with models, migrations, views, and activity processing logic.

* Refactor SnapTrade activities processing: improve activity fetching flow, handle pending states, and update UI elements for enhanced user feedback.

* Update Brakeman ignore file to include intentional redirect for SnapTrade OAuth portal.

* Refactor SnapTrade models, views, and processing logic: add currency extraction helper, improve pending state handling, optimize migration checks, and enhance user feedback in UI.

* Remove encryption for SnapTrade `snaptrade_user_id`, as it is an identifier, not a secret.

* Introduce `SnaptradeConnectionCleanupJob` to asynchronously handle SnapTrade connection cleanup and improve i18n for SnapTrade item status messages.

* Update SnapTrade encryption: make `snaptrade_user_secret` non-deterministic to enhance security.

---------

Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
Co-authored-by: luckyPipewrench <luckypipewrench@proton.me>
Co-authored-by: Juan José Mata <juanjo.mata@gmail.com>
2026-01-22 20:52:49 +01:00

140 lines
4.5 KiB
Ruby

require "test_helper"
class SnaptradeAccountTest < ActiveSupport::TestCase
fixtures :families, :snaptrade_items, :snaptrade_accounts
setup do
@family = families(:dylan_family)
@snaptrade_item = snaptrade_items(:configured_item)
@snaptrade_account = snaptrade_accounts(:fidelity_401k)
end
test "validates presence of name" do
@snaptrade_account.name = nil
assert_not @snaptrade_account.valid?
assert_includes @snaptrade_account.errors[:name], "can't be blank"
end
test "validates presence of currency" do
@snaptrade_account.currency = nil
assert_not @snaptrade_account.valid?
assert_includes @snaptrade_account.errors[:currency], "can't be blank"
end
test "ensure_account_provider! creates link when account provided" do
account = @family.accounts.create!(
name: "Test Investment",
balance: 10000,
currency: "USD",
accountable: Investment.new
)
assert_nil @snaptrade_account.account_provider
@snaptrade_account.ensure_account_provider!(account)
@snaptrade_account.reload
assert_not_nil @snaptrade_account.account_provider
assert_equal account, @snaptrade_account.current_account
end
test "ensure_account_provider! updates link when account changes" do
account1 = @family.accounts.create!(
name: "First Account",
balance: 10000,
currency: "USD",
accountable: Investment.new
)
account2 = @family.accounts.create!(
name: "Second Account",
balance: 20000,
currency: "USD",
accountable: Investment.new
)
@snaptrade_account.ensure_account_provider!(account1)
assert_equal account1, @snaptrade_account.reload.current_account
@snaptrade_account.ensure_account_provider!(account2)
assert_equal account2, @snaptrade_account.reload.current_account
end
test "ensure_account_provider! is idempotent" do
account = @family.accounts.create!(
name: "Test Investment",
balance: 10000,
currency: "USD",
accountable: Investment.new
)
@snaptrade_account.ensure_account_provider!(account)
provider1 = @snaptrade_account.reload.account_provider
@snaptrade_account.ensure_account_provider!(account)
provider2 = @snaptrade_account.reload.account_provider
assert_equal provider1.id, provider2.id
end
test "upsert_holdings_snapshot! stores holdings and updates timestamp" do
holdings = [
{ "symbol" => { "symbol" => "AAPL" }, "units" => 10 },
{ "symbol" => { "symbol" => "MSFT" }, "units" => 5 }
]
@snaptrade_account.upsert_holdings_snapshot!(holdings)
assert_equal holdings, @snaptrade_account.raw_holdings_payload
assert_not_nil @snaptrade_account.last_holdings_sync
end
test "upsert_activities_snapshot! stores activities and updates timestamp" do
activities = [
{ "id" => "act1", "type" => "BUY", "amount" => 1000 },
{ "id" => "act2", "type" => "DIVIDEND", "amount" => 50 }
]
@snaptrade_account.upsert_activities_snapshot!(activities)
assert_equal activities, @snaptrade_account.raw_activities_payload
assert_not_nil @snaptrade_account.last_activities_sync
end
test "upsert_from_snaptrade! extracts data from API response" do
# Use a Hash that mimics the SnapTrade SDK response structure
api_response = {
"id" => "new_account_id",
"brokerage_authorization" => "auth_xyz",
"number" => "9999999",
"name" => "Schwab Brokerage",
"status" => "active",
"balance" => {
"total" => { "amount" => 125000, "currency" => "USD" }
},
"meta" => { "type" => "INDIVIDUAL", "institution_name" => "Charles Schwab" }
}
@snaptrade_account.upsert_from_snaptrade!(api_response)
assert_equal "new_account_id", @snaptrade_account.snaptrade_account_id
assert_equal "auth_xyz", @snaptrade_account.snaptrade_authorization_id
assert_equal "9999999", @snaptrade_account.account_number
assert_equal "Schwab Brokerage", @snaptrade_account.name
assert_equal "Charles Schwab", @snaptrade_account.brokerage_name
assert_equal 125000, @snaptrade_account.current_balance.to_i
assert_equal "INDIVIDUAL", @snaptrade_account.account_type
end
test "snaptrade_credentials returns credentials from parent item" do
credentials = @snaptrade_account.snaptrade_credentials
assert_equal "user_123", credentials[:user_id]
assert_equal "secret_abc", credentials[:user_secret]
end
test "snaptrade_provider returns provider from parent item" do
provider = @snaptrade_account.snaptrade_provider
assert_instance_of Provider::Snaptrade, provider
end
end