mirror of
https://github.com/we-promise/sure.git
synced 2026-08-05 16:42:18 +00:00
* Add support for trading212 integration in investments sync * Respect Trading 212 history rate limits * Replace hex colors with design tokens * Changed withdrawal to withdraw * Address PR review feedback for Trading 212 integration - Replace all Rails.logger calls with DebugLogEntry.capture across syncer, importer, processor, provider, controller, and unlinking - Fix destroy action to surface unlink errors via DebugLogEntry - Replace hardcoded 'GBP' currency fallback with Current.family.currency - Replace hardcoded English status strings in Syncer with i18n - Add config/initializers/trading212.rb with DEBUG_RAW ENV toggle - Add comprehensive test coverage: item, account, data_helpers, holdings_processor, activities_processor, importer, syncer, and controller tests - Rewrite syncer and controller tests to match actual interfaces * Fix tests and bugs found during local test execution - Fix standard_ticker: empty string caused nil.upcase via [].first - Fix parse_date: DateTime < Date, so when Date caught DateTime first - Fix ActivitiesProcessor/HoldingsProcessor: missing instruments_map in DataHelpers caused NameError when processing dividends - Fix test: security fixture ticker collision with test data - Fix test: sync_status_summary i18n matching in assertions - Fix test: trading212_provider ConfigurationError test path - Fix test: controller invalid params needs Turbo-Frame header - Fix test: syncer test uses stubs instead of strict expects 116 tests, 257 assertions, 0 failures, 0 errors * Keep raw provider response bodies out of exception messages. * Fix Secrets leak into page HTML, prevents the API key/secret from appearing in the HTML source while keeping the "leave blank to keep existing" UX. * Address review findings: env gate, sync test, uniqueness test, destroy flow - Gate TRADING212_DEBUG_RAW behind Rails.env.local? so staging/production cannot accidentally enable raw payload logging - Assert SyncJob enqueue in sync controller test, not just redirect - Fix cross-item uniqueness test to actually use two different items - Remove rescue in destroy so unlink failures stop the flow (matching Brex/Akahu pattern) instead of proceeding to destroy_later silently * Add Trading212 tables to db/schema.rb for CI test database CI runs db:test:prepare which loads db/schema.rb, not migrations. Without these table definitions, fixture loading fails with PG::UndefinedTable: relation "trading212_accounts" does not exist. * Removed lint complaint * Register Trading212 in ProviderConnectionStatus::PROVIDERS Fixes CI failure: test_provider_registry_covers_syncable_family_provider_item_associations * Fixed bad merge --------- Signed-off-by: jdcdp <47483528+jdcdp@users.noreply.github.com> Co-authored-by: jdcdp <jdcdp@cdm4.net>
122 lines
4.5 KiB
Ruby
122 lines
4.5 KiB
Ruby
require "test_helper"
|
|
|
|
class Trading212ItemImporterTest < ActiveSupport::TestCase
|
|
setup do
|
|
@family = families(:dylan_family)
|
|
@item = trading212_items(:configured_item)
|
|
end
|
|
|
|
test "import creates trading212_account with fetched data" do
|
|
provider = mock("trading212_provider")
|
|
provider.expects(:fetch_instruments).returns([
|
|
{ "ticker" => "AAPL_US_EQ", "shortName" => "Apple Inc.", "currencyCode" => "USD" }
|
|
])
|
|
provider.expects(:fetch_account_summary).returns({
|
|
"id" => "t212_acc_new",
|
|
"totalValue" => "15000.00",
|
|
"cash" => { "availableToTrade" => "2000.00" }
|
|
})
|
|
provider.expects(:fetch_positions).returns([
|
|
{ "instrument" => { "ticker" => "AAPL_US_EQ" }, "quantity" => "10", "currentPrice" => "175.00" }
|
|
])
|
|
provider.expects(:fetch_all_orders).returns([
|
|
{ "order" => { "id" => "o1", "status" => "FILLED", "side" => "BUY" } }
|
|
])
|
|
provider.expects(:fetch_all_dividends).returns([
|
|
{ "reference" => "d1", "amount" => "10.00" }
|
|
])
|
|
provider.expects(:fetch_all_transactions).returns([
|
|
{ "reference" => "t1", "type" => "DEPOSIT", "amount" => "1000.00" }
|
|
])
|
|
|
|
importer = Trading212Item::Importer.new(@item, provider: provider)
|
|
result = importer.import
|
|
|
|
assert_equal({ success: true }, result)
|
|
|
|
account = @item.trading212_accounts.find_by(trading212_account_id: "t212_acc_new")
|
|
assert_not_nil account
|
|
assert_equal BigDecimal("15000.00"), account.current_balance
|
|
assert_equal BigDecimal("2000.00"), account.cash_balance
|
|
assert account.raw_positions_payload.present?
|
|
assert account.raw_orders_payload.present?
|
|
assert account.raw_dividends_payload.present?
|
|
assert account.raw_transactions_payload.present?
|
|
end
|
|
|
|
test "import updates existing account on subsequent sync" do
|
|
existing = @item.trading212_accounts.create!(
|
|
name: "Existing Account",
|
|
trading212_account_id: "t212_acc_existing",
|
|
currency: "USD",
|
|
current_balance: BigDecimal("1000.00"),
|
|
cash_balance: BigDecimal("100.00")
|
|
)
|
|
|
|
provider = mock("trading212_provider")
|
|
provider.expects(:fetch_instruments).returns([])
|
|
provider.expects(:fetch_account_summary).returns({
|
|
"id" => "t212_acc_existing",
|
|
"totalValue" => "12000.00",
|
|
"cash" => { "availableToTrade" => "1500.00" }
|
|
})
|
|
provider.expects(:fetch_positions).returns([])
|
|
provider.expects(:fetch_all_orders).returns([])
|
|
provider.expects(:fetch_all_dividends).returns([])
|
|
provider.expects(:fetch_all_transactions).returns([])
|
|
|
|
assert_no_difference "Trading212Account.count" do
|
|
Trading212Item::Importer.new(@item, provider: provider).import
|
|
end
|
|
|
|
existing.reload
|
|
assert_equal BigDecimal("12000.00"), existing.current_balance
|
|
assert_equal BigDecimal("1500.00"), existing.cash_balance
|
|
end
|
|
|
|
test "import stores instruments payload on item" do
|
|
instruments = [
|
|
{ "ticker" => "AAPL_US_EQ", "shortName" => "Apple Inc." },
|
|
{ "ticker" => "TSLA_US_EQ", "shortName" => "Tesla Inc." }
|
|
]
|
|
|
|
provider = mock("trading212_provider")
|
|
provider.expects(:fetch_instruments).returns(instruments)
|
|
provider.expects(:fetch_account_summary).returns({
|
|
"id" => "t212_acc_inst",
|
|
"totalValue" => "5000.00",
|
|
"cash" => { "availableToTrade" => "500.00" }
|
|
})
|
|
provider.expects(:fetch_positions).returns([])
|
|
provider.expects(:fetch_all_orders).returns([])
|
|
provider.expects(:fetch_all_dividends).returns([])
|
|
provider.expects(:fetch_all_transactions).returns([])
|
|
|
|
Trading212Item::Importer.new(@item, provider: provider).import
|
|
|
|
assert_equal 2, @item.reload.raw_instruments_payload.size
|
|
end
|
|
|
|
test "import falls back to cached instruments on fetch failure" do
|
|
@item.update!(raw_instruments_payload: [
|
|
{ "ticker" => "CACHED_US_EQ", "shortName" => "Cached Inc." }
|
|
])
|
|
|
|
provider = mock("trading212_provider")
|
|
provider.expects(:fetch_instruments).raises(StandardError.new("Network error"))
|
|
provider.expects(:fetch_account_summary).returns({
|
|
"id" => "t212_acc_cache",
|
|
"totalValue" => "5000.00",
|
|
"cash" => { "availableToTrade" => "500.00" }
|
|
})
|
|
provider.expects(:fetch_positions).returns([])
|
|
provider.expects(:fetch_all_orders).returns([])
|
|
provider.expects(:fetch_all_dividends).returns([])
|
|
provider.expects(:fetch_all_transactions).returns([])
|
|
|
|
# Should not raise
|
|
result = Trading212Item::Importer.new(@item, provider: provider).import
|
|
assert_equal({ success: true }, result)
|
|
end
|
|
end
|