mirror of
https://github.com/we-promise/sure.git
synced 2026-04-07 22:34:47 +00:00
* 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>
140 lines
4.5 KiB
Ruby
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
|